/** find minimum distance from ray to barb tail */
  public synchronized float checkClose(double[] origin, double[] direction) {
    if (barbValues == null) return Float.MAX_VALUE;
    float o_x = (float) origin[0];
    float o_y = (float) origin[1];
    float o_z = (float) origin[2];
    float d_x = (float) direction[0];
    float d_y = (float) direction[1];
    float d_z = (float) direction[2];
    /*
    System.out.println("origin = " + o_x + " " + o_y + " " + o_z);
    System.out.println("direction = " + d_x + " " + d_y + " " + d_z);
    */
    float x = barbValues[2] - o_x;
    float y = barbValues[3] - o_y;
    float z = 0.0f - o_z;
    float dot = x * d_x + y * d_y + z * d_z;
    x = x - dot * d_x;
    y = y - dot * d_y;
    z = z - dot * d_z;

    offsetx = x;
    offsety = y;
    offsetz = z;

    return (float) Math.sqrt(x * x + y * y + z * z); // distance
  }
示例#2
0
 /**
  * Get the curve length; assumes curve is float[2][len] and seg_length is float[len-1]
  *
  * @param curve the curve
  * @param seg_length the segment lengths
  * @return the length of the curve
  */
 public static float curveLength(float[][] curve, float[] seg_length) {
   int len = curve[0].length;
   float curve_length = 0.0f;
   for (int i = 0; i < len - 1; i++) {
     seg_length[i] =
         (float)
             Math.sqrt(
                 ((curve[0][i + 1] - curve[0][i]) * (curve[0][i + 1] - curve[0][i]))
                     + ((curve[1][i + 1] - curve[1][i]) * (curve[1][i + 1] - curve[1][i])));
     curve_length += seg_length[i];
   }
   return curve_length;
 }
示例#3
0
  /**
   * Create a front from the curve
   *
   * @param curve the curve coordinates
   * @param flip true to flip the pips
   * @return The front as a FieldImpl
   * @throws RemoteException On badness
   * @throws VisADException On badness
   */
  private FieldImpl curveToFront(float[][] curve, boolean flip)
      throws VisADException, RemoteException {

    if (flipTheFlip) {
      flip = !flip;
    }

    // compute various scaling factors
    int len = curve[0].length;
    if (len < 2) {
      return null;
    }
    float[] seg_length = new float[len - 1];
    float curve_length = curveLength(curve, seg_length);
    float delta = curve_length / (len - 1);
    // curve[findex] where
    // float findex = ibase + mul * repeat_shapes[shape][0][j]
    float mul = rprofile_length * zoom / rsegment_length;
    // curve_perp[][findex] * ratio * repeat_shapes[shape][1][j]
    float ratio = delta * mul;

    // compute unit perpendiculars to curve
    float[][] curve_perp = new float[2][len];
    for (int i = 0; i < len; i++) {
      int im = i - 1;
      int ip = i + 1;
      if (im < 0) {
        im = 0;
      }
      if (ip > len - 1) {
        ip = len - 1;
      }
      float yp = curve[0][ip] - curve[0][im];
      float xp = curve[1][ip] - curve[1][im];
      xp = -xp;
      float d = (float) Math.sqrt(xp * xp + yp * yp);
      if (flip) {
        d = -d;
      }
      xp = xp / d;
      yp = yp / d;
      curve_perp[0][i] = xp;
      curve_perp[1][i] = yp;
    }

    // build Vector of FlatFields for each shape of each segment
    Vector inner_field_vector = new Vector();
    for (int segment = 0; true; segment++) {

      // curve[findex] where
      // float findex = ibase + mul * repeat_shapes[shape][0][j]
      float segment_length = (segment == 0) ? fsegment_length : rsegment_length;
      int profile_length = (segment == 0) ? fprofile_length : rprofile_length;
      mul = profile_length * zoom / segment_length;
      // curve_perp[][findex] * ratio * repeat_shapes[shape][1][j]
      // float ratio = delta * mul;

      // figure out if clipping is needed for this segment
      // only happens for last segment
      boolean clip = false;
      float xclip = 0.0f;
      // int ibase = segment * profile_length;
      int ibase = (segment == 0) ? 0 : fprofile_length + (segment - 1) * rprofile_length;
      int iend = ibase + profile_length;
      if (ibase > len - 1) {
        break;
      }
      if (iend > len - 1) {
        clip = true;
        iend = len - 1;
        xclip = (iend - ibase) / mul;
      }

      // set up shapes for first or repeating segment
      int nshapes = nrshapes;
      float[][][] shapes = repeat_shapes;
      int[][][] tris = repeat_tris;
      float[] red = repeat_red;
      float[] green = repeat_green;
      float[] blue = repeat_blue;
      if (segment == 0) {
        nshapes = nfshapes;
        shapes = first_shapes;
        tris = first_tris;
        red = first_red;
        green = first_green;
        blue = first_blue;
      }

      // iterate over shapes for segment
      for (int shape = 0; shape < nshapes; shape++) {
        float[][] samples = shapes[shape];
        int[][] ts = tris[shape];
        /*
        // if needed, clip shape
        if (clip) {
        float[][][] outs = new float[1][][];
        int[][][] outt = new int[1][][];
        DelaunayCustom.clip(samples, ts, 1.0f, 0.0f, xclip, outs, outt);
        samples = outs[0];
        ts = outt[0];
        }
        */
        if ((samples == null) || (samples[0].length < 1)) {
          break;
        }

        float[][] ss = mapShape(samples, len, ibase, mul, ratio, curve, curve_perp);

        // **** get rid of previous calls to fill() ****
        ts = DelaunayCustom.fill(ss);

        // jeffmc: For now don't clip. This seems to fix the problem of too short a front
        boolean DOCLIP = false;
        if (clip && DOCLIP) {
          float[][] clip_samples = {
            {xclip, xclip, xclip - CLIP_DELTA},
            {CLIP_DELTA, -CLIP_DELTA, 0.0f}
          };
          float[][] clip_ss = mapShape(clip_samples, len, ibase, mul, ratio, curve, curve_perp);
          // now solve for:
          //   xc * clip_samples[0][0] + yc * clip_samples[1][0] = 1
          //   xc * clip_samples[0][1] + yc * clip_samples[1][1] = 1
          //   xc * clip_samples[0][2] + yc * clip_samples[1][2] < 1
          float det =
              (clip_samples[0][1] * clip_samples[1][0] - clip_samples[0][0] * clip_samples[1][1]);
          float xc = (clip_samples[1][0] - clip_samples[1][1]) / det;
          float yc = (clip_samples[0][1] - clip_samples[0][0]) / det;
          float v = 1.0f;
          if (xc * clip_samples[0][2] + yc * clip_samples[1][2] > v) {
            xc = -xc;
            yc = -yc;
            v = -v;
          }

          float[][][] outs = new float[1][][];
          int[][][] outt = new int[1][][];
          DelaunayCustom.clip(ss, ts, xc, yc, v, outs, outt);
          ss = outs[0];
          ts = outt[0];
        }

        if (ss == null) {
          break;
        }
        int n = ss[0].length;

        // create color values for field
        float[][] values = new float[3][n];
        float r = red[shape];
        float g = green[shape];
        float b = blue[shape];
        for (int i = 0; i < n; i++) {
          values[0][i] = r;
          values[1][i] = g;
          values[2][i] = b;
        }

        // construct set and field
        DelaunayCustom delaunay = new DelaunayCustom(ss, ts);
        Irregular2DSet set = new Irregular2DSet(curve_type, ss, null, null, null, delaunay);
        FlatField field = new FlatField(front_inner, set);
        field.setSamples(values, false);
        inner_field_vector.addElement(field);
        // some crazy bug - see Gridded3DSet.makeNormals()
      } // end for (int shape=0; shape<nshapes; shape++)
    } // end for (int segment=0; true; segment++)

    int nfields = inner_field_vector.size();
    Integer1DSet iset = new Integer1DSet(front_index, nfields);
    FieldImpl front = new FieldImpl(front_type, iset);
    FlatField[] fields = new FlatField[nfields];
    for (int i = 0; i < nfields; i++) {
      fields[i] = (FlatField) inner_field_vector.elementAt(i);
    }
    front.setSamples(fields, false);
    return front;
  }
  public float[] makeVector(
      boolean south,
      float x,
      float y,
      float z,
      float scale,
      float pt_size,
      float f0,
      float f1,
      float[] vx,
      float[] vy,
      float[] vz,
      int[] numv,
      float[] tx,
      float[] ty,
      float[] tz,
      int[] numt) {

    float wsp25, slant, barb, d, c195, s195;
    float x0, y0;
    float x1, y1, x2, y2, x3, y3;
    int nbarb50, nbarb10, nbarb5;

    float[] mbarb = new float[4];
    mbarb[0] = x;
    mbarb[1] = y;

    if (getKnotsConvert()) {
      // convert meters per second to knots
      f0 *= (3600.0 / 1853.248);
      f1 *= (3600.0 / 1853.248);
    }

    float wnd_spd = (float) Math.sqrt(f0 * f0 + f1 * f1);
    int lenv = vx.length;
    int lent = tx.length;
    int nv = numv[0];
    int nt = numt[0];

    // determine the initial (minimum) length of the flag pole
    if (wnd_spd >= 2.5) {

      wsp25 = (float) Math.max(wnd_spd + 2.5, 5.0);
      slant = 0.15f * scale;
      barb = 0.4f * scale;
      // WLH 6 Aug 99 - barbs point the other way (duh)
      x0 = -f0 / wnd_spd;
      y0 = -f1 / wnd_spd;

      // plot the flag pole
      // lengthen to 'd = 3.0f * barb'
      // was 'd = barb' in original BOM code
      d = 3.0f * barb;
      x1 = (x + x0 * d);
      y1 = (y + y0 * d);

      /*
            // commented out in original BOM code
            vx[nv] = x;
            vy[nv] = y;
            vz[nv] = z;
            nv++;
            vx[nv] = x1;
            vy[nv] = y1;
            vz[nv] = z;
            nv++;
            // g.drawLine(x,y,x1,y1);
      */

      // determine number of wind barbs needed for 10 and 50 kt winds
      nbarb50 = (int) (wsp25 / 50.f);
      nbarb10 = (int) ((wsp25 - (nbarb50 * 50.f)) / 10.f);
      nbarb5 = (int) ((wsp25 - (nbarb50 * 50.f) - (nbarb10 * 10.f)) / 5.f);

      // 2.5 to 7.5 kt winds are plotted with the barb part way done the pole
      if (nbarb5 == 1) {
        barb = barb * 0.4f;
        slant = slant * 0.4f;
        x1 = (x + x0 * d);
        y1 = (y + y0 * d);

        if (south) {
          x2 = (x + x0 * (d + slant) - y0 * barb);
          y2 = (y + y0 * (d + slant) + x0 * barb);
        } else {
          x2 = (x + x0 * (d + slant) + y0 * barb);
          y2 = (y + y0 * (d + slant) - x0 * barb);
        }

        vx[nv] = x1;
        vy[nv] = y1;
        vz[nv] = z;
        nv++;
        vx[nv] = x2;
        vy[nv] = y2;
        vz[nv] = z;
        nv++;
        // System.out.println("barb5 " + x1 + " " + y1 + "" + x2 + " " + y2);
        // g.drawLine(x1, y1, x2, y2);
      }

      // add a little more pole
      if (wsp25 >= 5.0f && wsp25 < 10.0f) {
        d = d + 0.125f * scale;
        x1 = (x + x0 * d);
        y1 = (y + y0 * d);
        /* WLH 24 April 99
                vx[nv] = x;
                vy[nv] = y;
                vz[nv] = z;
                nv++;
                vx[nv] = x1;
                vy[nv] = y1;
                vz[nv] = z;
                nv++;
        */
        // System.out.println("wsp25 " + x + " " + y + "" + x1 + " " + y1);
        // g.drawLine(x, y, x1, y1);
      }

      // now plot any 10 kt wind barbs
      barb = 0.4f * scale;
      slant = 0.15f * scale;
      for (int j = 0; j < nbarb10; j++) {
        d = d + 0.125f * scale;
        x1 = (x + x0 * d);
        y1 = (y + y0 * d);
        if (south) {
          x2 = (x + x0 * (d + slant) - y0 * barb);
          y2 = (y + y0 * (d + slant) + x0 * barb);
        } else {
          x2 = (x + x0 * (d + slant) + y0 * barb);
          y2 = (y + y0 * (d + slant) - x0 * barb);
        }

        vx[nv] = x1;
        vy[nv] = y1;
        vz[nv] = z;
        nv++;
        vx[nv] = x2;
        vy[nv] = y2;
        vz[nv] = z;
        nv++;
        // System.out.println("barb10 " + j + " " + x1 + " " + y1 + "" + x2 + " " + y2);
        // g.drawLine(x1,y1,x2,y2);
      }
      /* WLH 24 April 99
            vx[nv] = x;
            vy[nv] = y;
            vz[nv] = z;
            nv++;
            vx[nv] = x1;
            vy[nv] = y1;
            vz[nv] = z;
            nv++;
      */
      // System.out.println("line " + x + " " + y + "" + x1 + " " + y1);
      // g.drawLine(x,y,x1,y1);

      // lengthen the pole to accomodate the 50 knot barbs
      if (nbarb50 > 0) {
        d = d + 0.125f * scale;
        x1 = (x + x0 * d);
        y1 = (y + y0 * d);
        /* WLH 24 April 99
                vx[nv] = x;
                vy[nv] = y;
                vz[nv] = z;
                nv++;
                vx[nv] = x1;
                vy[nv] = y1;
                vz[nv] = z;
                nv++;
        */
        // System.out.println("line50 " + x + " " + y + "" + x1 + " " + y1);
        // g.drawLine(x,y,x1,y1);
      }

      // plot the 50 kt wind barbs
      /* WLH 5 Nov 99
            s195 = (float) Math.sin(195 * Data.DEGREES_TO_RADIANS);
            c195 = (float) Math.cos(195 * Data.DEGREES_TO_RADIANS);
      */
      for (int j = 0; j < nbarb50; j++) {
        x1 = (x + x0 * d);
        y1 = (y + y0 * d);
        d = d + 0.3f * scale;
        x3 = (x + x0 * d);
        y3 = (y + y0 * d);
        /* WLH 5 Nov 99
                if (south) {
                  x2 = (x3+barb*(x0*s195+y0*c195));
                  y2 = (y3-barb*(x0*c195-y0*s195));
                }
                else {
                  x2 = (x3-barb*(x0*s195+y0*c195));
                  y2 = (y3+barb*(x0*c195-y0*s195));
                }
        */
        if (south) {
          x2 = (x + x0 * (d + slant) - y0 * barb);
          y2 = (y + y0 * (d + slant) + x0 * barb);
        } else {
          x2 = (x + x0 * (d + slant) + y0 * barb);
          y2 = (y + y0 * (d + slant) - x0 * barb);
        }

        float[] xp = {x1, x2, x3};
        float[] yp = {y1, y2, y3};

        tx[nt] = x1;
        ty[nt] = y1;
        tz[nt] = z;
        nt++;
        tx[nt] = x2;
        ty[nt] = y2;
        tz[nt] = z;
        nt++;
        tx[nt] = x3;
        ty[nt] = y3;
        tz[nt] = z;
        nt++;
        /*
        System.out.println("barb50 " + x1 + " " + y1 + "" + x2 + " " + y2 +
                         "  " + x3 + " " + y3);
        */
        // g.fillPolygon(xp,yp,3);
        // start location for the next barb
        x1 = x3;
        y1 = y3;
      }

      // grf 17 Nov 2003 change this to shorten the pole and print the speed
      if (noNumbers) {
        // WLH 24 April 99 - now plot the pole
        vx[nv] = x;
        vy[nv] = y;
        vz[nv] = z;
        nv++;
        vx[nv] = x1;
        vy[nv] = y1;
        vz[nv] = z;
        nv++;

        mbarb[2] = x1;
        mbarb[3] = y1;
      } else { // add numerical value to wind barbs
        // guess some factors to shorten the start of the pole
        float start_pole = 0.4f * scale;
        x1 = (x + x0 * start_pole);
        y1 = (y + y0 * start_pole);
        x2 = (x + x0 * d);
        y2 = (y + y0 * d);

        // draw the shaft
        vx[nv] = x1;
        vy[nv] = y1;
        vz[nv] = z;
        nv++;
        vx[nv] = x2;
        vy[nv] = y2;
        vz[nv] = z;
        nv++;

        mbarb[2] = x2;
        mbarb[3] = y2;

        // draw the speed to 1 dec place by default
        // Experimental factors in front of scale - get same as Swell
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(numDecPlaces);
        String speedString = nf.format((double) wnd_spd);
        // grf 2 Jun 2004 set z value the same as the barb
        double[] start = {x, y - 0.20 * scale, z};
        double[] base = {0.375 * scale, 0.0, 0.0};
        double up[] = {0.0, 0.375 * scale, 0.0};
        VisADLineArray array = PlotText.render_label(speedString, start, base, up, true);
        int nl = array.vertexCount;
        int k = 0;
        for (int i = 0; i < nl; i++) {
          vx[nv] = array.coordinates[k++];
          vy[nv] = array.coordinates[k++];
          vz[nv] = array.coordinates[k++];

          nv++;
        }
      }
    } else { // if (wnd_spd < 2.5)

      // wind < 2.5 kts.  Plot a circle
      float rad = (0.7f * pt_size);

      // draw 8 segment circle, center = (x, y), radius = rad
      // 1st segment
      vx[nv] = x - rad;
      vy[nv] = y;
      vz[nv] = z;
      nv++;
      vx[nv] = x - 0.7f * rad;
      vy[nv] = y + 0.7f * rad;
      vz[nv] = z;
      nv++;
      // 2nd segment
      vx[nv] = x - 0.7f * rad;
      vy[nv] = y + 0.7f * rad;
      vz[nv] = z;
      nv++;
      vx[nv] = x;
      vy[nv] = y + rad;
      vz[nv] = z;
      nv++;
      // 3rd segment
      vx[nv] = x;
      vy[nv] = y + rad;
      vz[nv] = z;
      nv++;
      vx[nv] = x + 0.7f * rad;
      vy[nv] = y + 0.7f * rad;
      vz[nv] = z;
      nv++;
      // 4th segment
      vx[nv] = x + 0.7f * rad;
      vy[nv] = y + 0.7f * rad;
      vz[nv] = z;
      nv++;
      vx[nv] = x + rad;
      vy[nv] = y;
      vz[nv] = z;
      nv++;
      // 5th segment
      vx[nv] = x + rad;
      vy[nv] = y;
      vz[nv] = z;
      nv++;
      vx[nv] = x + 0.7f * rad;
      vy[nv] = y - 0.7f * rad;
      vz[nv] = z;
      nv++;
      // 6th segment
      vx[nv] = x + 0.7f * rad;
      vy[nv] = y - 0.7f * rad;
      vz[nv] = z;
      nv++;
      vx[nv] = x;
      vy[nv] = y - rad;
      vz[nv] = z;
      nv++;
      // 7th segment
      vx[nv] = x;
      vy[nv] = y - rad;
      vz[nv] = z;
      nv++;
      vx[nv] = x - 0.7f * rad;
      vy[nv] = y - 0.7f * rad;
      vz[nv] = z;
      nv++;
      // 8th segment
      vx[nv] = x - 0.7f * rad;
      vy[nv] = y - 0.7f * rad;
      vz[nv] = z;
      nv++;
      vx[nv] = x - rad;
      vy[nv] = y;
      vz[nv] = z;
      nv++;
      // System.out.println("circle " + x + " " + y + "" + rad);
      // g.drawOval(x-rad,y-rad,2*rad,2*rad);

      mbarb[2] = x;
      mbarb[3] = y;
    }

    numv[0] = nv;
    numt[0] = nt;
    return mbarb;
  }
  public synchronized void drag_direct(VisADRay ray, boolean first, int mouseModifiers) {
    if (barbValues == null || ref == null || shadow == null) return;

    if (first) {
      stop = false;
    } else {
      if (stop) return;
    }

    // modify direction if mshift != 0
    // modify speed if mctrl != 0
    // modify speed and direction if neither
    int mshift = mouseModifiers & InputEvent.SHIFT_MASK;
    int mctrl = mouseModifiers & InputEvent.CTRL_MASK;

    float o_x = (float) ray.position[0];
    float o_y = (float) ray.position[1];
    float o_z = (float) ray.position[2];
    float d_x = (float) ray.vector[0];
    float d_y = (float) ray.vector[1];
    float d_z = (float) ray.vector[2];

    if (pickCrawlToCursor) {
      if (first) {
        offset_count = OFFSET_COUNT_INIT;
      } else {
        if (offset_count > 0) offset_count--;
      }
      if (offset_count > 0) {
        float mult = ((float) offset_count) / ((float) OFFSET_COUNT_INIT);
        o_x += mult * offsetx;
        o_y += mult * offsety;
        o_z += mult * offsetz;
      }
    }

    if (first || refirst) {
      point_x = barbValues[2];
      point_y = barbValues[3];
      point_z = 0.0f;
      line_x = 0.0f;
      line_y = 0.0f;
      line_z = 1.0f; // lineAxis == 2 in DataRenderer.drag_direct
    } // end if (first || refirst)

    float[] x = new float[3]; // x marks the spot
    // DirectManifoldDimension = 2
    // intersect ray with plane
    float dot = (point_x - o_x) * line_x + (point_y - o_y) * line_y + (point_z - o_z) * line_z;
    float dot2 = d_x * line_x + d_y * line_y + d_z * line_z;
    if (dot2 == 0.0) return;
    dot = dot / dot2;
    // x is intersection
    x[0] = o_x + dot * d_x;
    x[1] = o_y + dot * d_y;
    x[2] = o_z + dot * d_z;
    /*
    System.out.println("x = " + x[0] + " " + x[1] + " " + x[2]);
    */
    try {

      Tuple data = (Tuple) link.getData();
      int n = ((TupleType) data.getType()).getNumberOfRealComponents();
      Real[] reals = new Real[n];

      int k = 0;
      int m = data.getDimension();
      for (int i = 0; i < m; i++) {
        Data component = data.getComponent(i);
        if (component instanceof Real) {
          reals[k++] = (Real) component;
        } else if (component instanceof RealTuple) {
          for (int j = 0; j < ((RealTuple) component).getDimension(); j++) {
            reals[k++] = (Real) ((RealTuple) component).getComponent(j);
          }
        }
      }

      if (first || refirst) {
        // get first Data flow vector
        for (int i = 0; i < 3; i++) {
          int j = flowToComponent[i];
          data_flow[i] = (j >= 0) ? (float) reals[j].getValue() : 0.0f;
        }

        if (coord != null) {
          float[][] ds = {{data_flow[0]}, {data_flow[1]}, {data_flow[2]}};
          ds = coord.toReference(ds);
          data_flow[0] = ds[0][0];
          data_flow[1] = ds[1][0];
          data_flow[2] = ds[2][0];
        }

        data_speed =
            (float)
                Math.sqrt(
                    data_flow[0] * data_flow[0]
                        + data_flow[1] * data_flow[1]
                        + data_flow[2] * data_flow[2]);
        float barb0 = barbValues[2] - barbValues[0];
        float barb1 = barbValues[3] - barbValues[1];
        /*
        System.out.println("data_flow = " + data_flow[0] + " " + data_flow[1] +
                           " " + data_flow[2]);
        System.out.println("barbValues = " + barbValues[0] + " " + barbValues[1] +
                           "   " + barbValues[2] + " " + barbValues[3]);
        System.out.println("data_speed = " + data_speed);
        */
      } // end if (first || refirst)

      // convert x to a flow vector, and from spatial to earth
      if (getRealVectorTypes(which_barb) instanceof EarthVectorType) {
        // don't worry about vector magnitude -
        // data_speed & display_speed take care of that
        float eps = 0.0001f; // estimate derivative with a little vector
        float[][] spatial_locs = {
          {barbValues[0], barbValues[0] + eps * (x[0] - barbValues[0])},
          {barbValues[1], barbValues[1] + eps * (x[1] - barbValues[1])},
          {0.0f, 0.0f}
        };
        /*
        System.out.println("spatial_locs = " + spatial_locs[0][0] + " " +
                           spatial_locs[0][1] + " " + spatial_locs[1][0] + " " +
                           spatial_locs[1][1]);
        */
        float[][] earth_locs = spatialToEarth(spatial_locs);
        // WLH - 18 Aug 99
        if (earth_locs == null) return;
        /*
        System.out.println("earth_locs = " + earth_locs[0][0] + " " +
                           earth_locs[0][1] + " " + earth_locs[1][0] + " " +
                           earth_locs[1][1]);
        */
        x[2] = 0.0f;
        x[0] =
            (earth_locs[1][1] - earth_locs[1][0])
                * ((float) Math.cos(Data.DEGREES_TO_RADIANS * earth_locs[0][0]));
        x[1] = earth_locs[0][1] - earth_locs[0][0];
        /*
        System.out.println("x = " + x[0] + " " + x[1] + " " + x[2]);
        */
      } else { // if (!(getRealVectorTypes(which_barb) instanceof EarthVectorType))
        // convert x to vector
        x[0] -= barbValues[0];
        x[1] -= barbValues[1];

        // adjust for spatial map scalings but don't worry about vector
        // magnitude - data_speed & display_speed take care of that
        // also, spatial is Cartesian
        double[] ranges = getRanges();
        for (int i = 0; i < 3; i++) {
          x[i] /= ranges[i];
        }
        /*
        System.out.println("ranges = " + ranges[0] + " " + ranges[1] +
                           " " + ranges[2]);
        System.out.println("x = " + x[0] + " " + x[1] + " " + x[2]);
        */
      }

      // WLH 6 August 99
      x[0] = -x[0];
      x[1] = -x[1];
      x[2] = -x[2];

      /* may need to do this for performance
            float[] xx = {x[0], x[1], x[2]};
            addPoint(xx);
      */

      float x_speed = (float) Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
      /* WLH 16 April 2002 - from Ken
            if (x_speed < 0.000001f) x_speed = 0.000001f;
      */
      if (x_speed < 0.01f) x_speed = 0.01f;
      if (first || refirst) {
        display_speed = x_speed;
      }
      refirst = false;

      if (mshift != 0) {
        // only modify data_flow direction
        float ratio = data_speed / x_speed;
        x[0] *= ratio;
        x[1] *= ratio;
        x[2] *= ratio;
        /*
        System.out.println("direction, ratio = " + ratio + " " +
                           data_speed + " " + x_speed);
        System.out.println("x = " + x[0] + " " + x[1] + " " + x[2]);
        */
      } else if (mctrl != 0) {
        // only modify data_flow speed
        float ratio = x_speed / display_speed;
        if (data_speed < EPS) {
          data_flow[0] = 2.0f * EPS;
          refirst = true;
        }
        x[0] = ratio * data_flow[0];
        x[1] = ratio * data_flow[1];
        x[2] = ratio * data_flow[2];
        /*
        System.out.println("speed, ratio = " + ratio + " " +
                           x_speed + " " + display_speed);
        System.out.println("x = " + x[0] + " " + x[1] + " " + x[2]);
        */
      } else {
        // modify data_flow speed and direction
        float ratio = data_speed / display_speed;
        /*
        System.out.println("data_speed = " + data_speed +
                           " display_speed = " + display_speed +
                           " ratio = " + ratio + " EPS = " + EPS);
        System.out.println("x = " + x[0] + " " + x[1] +" " + x[2] +
                           " x_speed = " + x_speed);
          data_speed = 21.213203 display_speed = 0.01 ratio = 2121.3203 EPS = 0.2
          x = 1.6170928E-4 1.6021729E-4 -0.0 x_speed = 0.01
          wind = (0.3430372, 0.33987218) at (-35.0, 5.0)
        */
        if (data_speed < EPS) {
          data_flow[0] = 2.0f * EPS;
          x[0] = data_flow[0];
          x[1] = data_flow[1];
          x[2] = data_flow[2];
          refirst = true;
        } else {
          x[0] *= ratio;
          x[1] *= ratio;
          x[2] *= ratio;
        }
      }

      if (coord != null) {
        float[][] xs = {{x[0]}, {x[1]}, {x[2]}};
        xs = coord.fromReference(xs);
        x[0] = xs[0][0];
        x[1] = xs[1][0];
        x[2] = xs[2][0];
      }

      // now replace flow values
      Vector vect = new Vector();
      for (int i = 0; i < 3; i++) {
        int j = flowToComponent[i];
        if (j >= 0) {
          RealType rtype = (RealType) reals[j].getType();
          reals[j] = new Real(rtype, (double) x[i], rtype.getDefaultUnit(), null);

          // WLH 31 Aug 2000
          Real r = reals[j];
          Unit overrideUnit = null;
          if (directMap[i] != null) {
            overrideUnit = directMap[i].getOverrideUnit();
          }
          Unit rtunit = rtype.getDefaultUnit();
          // units not part of Time string
          if (overrideUnit != null
              && !overrideUnit.equals(rtunit)
              && !RealType.Time.equals(rtype)) {
            double d = (float) overrideUnit.toThis((double) x[0], rtunit);
            r = new Real(rtype, d, overrideUnit);
            String valueString = r.toValueString();
            vect.addElement(rtype.getName() + " = " + valueString);
          } else {
            // create location string
            vect.addElement(rtype.getName() + " = " + x[i]);
          }
        }
      }
      getDisplayRenderer().setCursorStringVector(vect);

      Data newData = null;
      // now build new RealTuple or Flat Tuple
      if (data instanceof RealTuple) {
        newData =
            new RealTuple(
                ((RealTupleType) data.getType()), reals, ((RealTuple) data).getCoordinateSystem());
      } else {
        Data[] new_components = new Data[m];
        k = 0;
        for (int i = 0; i < m; i++) {
          Data component = data.getComponent(i);
          if (component instanceof Real) {
            new_components[i] = reals[k++];
          } else if (component instanceof RealTuple) {
            Real[] sub_reals = new Real[((RealTuple) component).getDimension()];
            for (int j = 0; j < ((RealTuple) component).getDimension(); j++) {
              sub_reals[j] = reals[k++];
            }
            new_components[i] =
                new RealTuple(
                    ((RealTupleType) component.getType()),
                    sub_reals,
                    ((RealTuple) component).getCoordinateSystem());
          }
        }
        newData = new Tuple(new_components, false);
      }
      ref.setData(newData);
    } catch (VisADException e) {
      // do nothing
      System.out.println("drag_direct " + e);
      e.printStackTrace();
    } catch (RemoteException e) {
      // do nothing
      System.out.println("drag_direct " + e);
      e.printStackTrace();
    }
  }
  /** test BarbManipulationRendererJ3D */
  public static void main(String args[]) throws VisADException, RemoteException {

    System.out.println("BMR.main()");

    // construct RealTypes for wind record components
    RealType lat = RealType.Latitude;
    RealType lon = RealType.Longitude;
    RealType windx = RealType.getRealType("windx", CommonUnit.meterPerSecond);
    RealType windy = RealType.getRealType("windy", CommonUnit.meterPerSecond);
    RealType red = RealType.getRealType("red");
    RealType green = RealType.getRealType("green");

    // EarthVectorType extends RealTupleType and says that its
    // components are vectors in m/s with components parallel
    // to Longitude (positive east) and Latitude (positive north)
    EarthVectorType windxy = new EarthVectorType(windx, windy);

    RealType wind_dir = RealType.getRealType("wind_dir", CommonUnit.degree);
    RealType wind_speed = RealType.getRealType("wind_speed", CommonUnit.meterPerSecond);
    RealTupleType windds = null;
    if (args.length > 0) {
      System.out.println("polar winds");
      windds =
          new RealTupleType(
              new RealType[] {wind_dir, wind_speed}, new WindPolarCoordinateSystem(windxy), null);
    }

    // construct Java3D display and mappings that govern
    // how wind records are displayed
    DisplayImpl display = new DisplayImplJ3D("display1", new TwoDDisplayRendererJ3D());
    ScalarMap lonmap = new ScalarMap(lon, Display.XAxis);
    display.addMap(lonmap);
    ScalarMap latmap = new ScalarMap(lat, Display.YAxis);
    display.addMap(latmap);

    FlowControl flow_control;
    if (args.length > 0) {
      ScalarMap winds_map = new ScalarMap(wind_speed, Display.Flow1Radial);
      display.addMap(winds_map);
      winds_map.setRange(0.0, 1.0); // do this for barb rendering
      ScalarMap windd_map = new ScalarMap(wind_dir, Display.Flow1Azimuth);
      display.addMap(windd_map);
      windd_map.setRange(0.0, 360.0); // do this for barb rendering
      flow_control = (FlowControl) windd_map.getControl();
      flow_control.setFlowScale(0.15f); // this controls size of barbs
    } else {
      ScalarMap windx_map = new ScalarMap(windx, Display.Flow1X);
      display.addMap(windx_map);
      windx_map.setRange(-1.0, 1.0); // do this for barb rendering
      ScalarMap windy_map = new ScalarMap(windy, Display.Flow1Y);
      display.addMap(windy_map);
      windy_map.setRange(-1.0, 1.0); // do this for barb rendering
      flow_control = (FlowControl) windy_map.getControl();
      flow_control.setFlowScale(0.15f); // this controls size of barbs
    }

    display.addMap(new ScalarMap(red, Display.Red));
    display.addMap(new ScalarMap(green, Display.Green));
    display.addMap(new ConstantMap(1.0, Display.Blue));

    DataReferenceImpl[] refs = new DataReferenceImpl[N * N];
    int k = 0;
    // create an array of N by N winds
    for (int i = 0; i < N; i++) {
      for (int j = 0; j < N; j++) {
        double u = 2.0 * i / (N - 1.0) - 1.0;
        double v = 2.0 * j / (N - 1.0) - 1.0;

        // each wind record is a Tuple (lon, lat, (windx, windy), red, green)
        // set colors by wind components, just for grins
        Tuple tuple;
        double fx = 30.0 * u;
        double fy = 30.0 * v;
        if (args.length > 0) {
          double fd = Data.RADIANS_TO_DEGREES * Math.atan2(-fx, -fy);
          double fs = Math.sqrt(fx * fx + fy * fy);
          tuple =
              new Tuple(
                  new Data[] {
                    new Real(lon, 10.0 * u),
                    new Real(lat, 10.0 * v - 40.0),
                    new RealTuple(windds, new double[] {fd, fs}),
                    new Real(red, u),
                    new Real(green, v)
                  });
        } else {
          tuple =
              new Tuple(
                  new Data[] {
                    new Real(lon, 10.0 * u),
                    new Real(lat, 10.0 * v - 40.0),
                    new RealTuple(windxy, new double[] {fx, fy}),
                    new Real(red, u),
                    new Real(green, v)
                  });
        }

        // construct reference for wind record
        refs[k] = new DataReferenceImpl("ref_" + k);
        refs[k].setData(tuple);

        // link wind record to display via BarbManipulationRendererJ3D
        // so user can change barb by dragging it
        // drag with right mouse button and shift to change direction
        // drag with right mouse button and no shift to change speed
        BarbManipulationRendererJ3D renderer = new BarbManipulationRendererJ3D();
        renderer.setKnotsConvert(true);
        display.addReferences(renderer, refs[k]);

        // link wind record to a CellImpl that will listen for changes
        // and print them
        WindGetterJ3D cell = new WindGetterJ3D(flow_control, refs[k]);
        cell.addReference(refs[k]);

        k++;
      }
    }

    // instead of linking the wind record "DataReferenceImpl refs" to
    // the WindGetterJ3Ds, you can have some user interface event (e.g.,
    // the user clicks on "DONE") trigger code that does a getData() on
    // all the refs and stores the records in a file.

    // create JFrame (i.e., a window) for display and slider
    JFrame frame = new JFrame("test BarbManipulationRendererJ3D");
    frame.addWindowListener(
        new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            System.exit(0);
          }
        });

    // create JPanel in JFrame
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    panel.setAlignmentY(JPanel.TOP_ALIGNMENT);
    panel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
    frame.getContentPane().add(panel);

    // add display to JPanel
    panel.add(display.getComponent());

    // set size of JFrame and make it visible
    frame.setSize(500, 500);
    frame.setVisible(true);
  }