Beispiel #1
0
 /**
  * Returns the result of rotating a matrix about a given unit vector by a given angle.
  *
  * @param matrix initial matrix
  * @param unitvect vector to rotate around, must be normalised
  * @param angle rotation angle in radians
  * @return rotated matrix
  */
 private static double[] rotateAround(double[] matrix, double[] unitvec, double angle) {
   assert Math.abs(
           (unitvec[0] * unitvec[0] + unitvec[1] * unitvec[1] + unitvec[2] * unitvec[2]) - 1)
       < 1e6;
   double[] rm = Matrices.fromPal(new Pal().Dav2m(Matrices.mult(unitvec, angle)));
   return Matrices.mmMult(matrix, rm);
 }
Beispiel #2
0
  /**
   * Takes X,Y,Z normalised coordinate ranges and tries to work out the ranges in projected plane
   * coordinates they represent for a given rotation matrix. The determination is approximate.
   *
   * @param vxyzRanges 3-element array giving ranges of normalised X,Y,Z data coordinates
   * @param rot 9-element rotation matrix to be applied before projection
   * @return 2-element array giving plane coordinate X and Y ranges covered by the supplied data
   *     coordinate ranges
   */
  private Range[] readProjectedRanges(Range[] vxyzRanges, double[] rot) {
    double[] vxBounds = vxyzRanges[0].getBounds();
    double[] vyBounds = vxyzRanges[1].getBounds();
    double[] vzBounds = vxyzRanges[2].getBounds();
    double[] r3 = new double[3];
    Range pxRange = new Range();
    Range pyRange = new Range();

    /* Iterate over each corner of the cuboid represented by the XYZ
     * ranges, and use each of these corners (actually, their projection
     * on the unit sphere surface) as sample points to mark out the
     * limits of X and Y projected position ranges.
     * This is pretty rough, but should provide an overestimate of the
     * actual X, Y ranges (I think?).  The smaller the range in XYZ
     * the better the estimate. */
    Point2D.Double point = new Point2D.Double();
    for (int jx = 0; jx < 2; jx++) {
      r3[0] = vxBounds[jx];
      for (int jy = 0; jy < 2; jy++) {
        r3[1] = vyBounds[jy];
        for (int jz = 0; jz < 2; jz++) {
          r3[2] = vzBounds[jz];
          double[] s3 = Matrices.normalise(Matrices.mvMult(rot, r3));
          if (project(s3[0], s3[1], s3[2], point)) {
            pxRange.submit(point.x);
            pyRange.submit(point.y);
          }
        }
      }
    }
    return new Range[] {pxRange, pyRange};
  }
Beispiel #3
0
 /**
  * Attempts to return a rotation matrix corresponding to moving the plane between two cursor
  * positions, with a given initial rotation matrix in effect.
  *
  * @param rot0 initial rotation matrix
  * @param pos0 initial projected position
  * @param pos1 destination projected position
  * @return destination rotation matrix, or null
  * @see Projection#projRotate
  */
 private double[] genericRotate(double[] rot0, Point2D.Double pos0, Point2D.Double pos1) {
   double[] rv0 = new double[3];
   if (unproject(pos0, rv0)
       && getSkyviewProjecter().validPosition(new double[] {pos1.x, pos1.y})) {
     double[] unrot0 = Matrices.invert(rot0);
     double[] ru0 = Matrices.mvMult(unrot0, rv0);
     return getRotation(ru0, pos1, rot0);
   } else {
     return null;
   }
 }
Beispiel #4
0
  private static double[] getRotation(double[] rv0, Point2D.Double pos1, double[] rot0) {
    boolean reflect = isReflected(rot0);
    final double fr = reflect ? -1 : +1;
    final double rx = rv0[0];
    final double ry = rv0[1];
    final double rz = rv0[2];
    final double px = pos1.x;
    final double py = pos1.y;

    // Use algebra from verticalRotate matrix.
    double delta0 = Math.atan2(-rot0[2], rot0[8]);
    double alpha0 = Math.atan2(-rot0[3], rot0[4] * fr);

    // Find alpha and delta rotation angles which put the given vector
    // at the target screen position.
    double alpha =
        new Solver() {
          double[] derivs(double a) {
            double sa = Math.sin(a);
            double ca = Math.cos(a);
            return new double[] {
              -sa * rx + ca * ry * fr - px, -ca * rx - sa * ry * fr,
            };
          }
        }.solve(alpha0);
    if (Double.isNaN(alpha)) {
      return null;
    }
    final double ca = Math.cos(alpha);
    final double sa = Math.sin(alpha);
    double delta =
        new Solver() {
          double[] derivs(double d) {
            double sd = Math.sin(d);
            double cd = Math.cos(d);
            return new double[] {
              sd * (ca * rx + sa * ry * fr) + cd * rz - py, cd * (ca * rx + sa * ry * fr) - sd * rz,
            };
          }
        }.solve(delta0);
    if (Double.isNaN(delta)) {
      return null;
    }

    double[] rot1 = verticalRotate(delta, alpha, reflect);
    if (Matrices.mvMult(rot1, rv0)[0] >= 0 && Matrices.mvMult(rot1, RZ)[2] > 0) {
      return rot1;
    } else {
      return null;
    }
  }
Beispiel #5
0
 /**
  * Works out the rotation matrix to use to center the positions represented by Cartesian
  * coordinate ranges on the plot surface. Null may be returned if there is no appropriate
  * rotation.
  *
  * @return 9-element rotation matrix or null.
  */
 private static double[] getRangeRotation(Range[] vxyzRanges, boolean reflect) {
   double[] center = new double[3];
   for (int id = 0; id < 3; id++) {
     double[] bounds = vxyzRanges[id].getBounds();
     double mid = 0.5 * (bounds[0] + bounds[1]);
     if (Double.isNaN(mid)) {
       return null;
     }
     assert mid >= -1.001 && mid <= +1.001;
     center[id] = mid;
   }
   if (Matrices.mod(center) < 0.3) {
     return null;
   }
   center = Matrices.normalise(center);
   return rotateToCenter(center, reflect);
 }
Beispiel #6
0
  @Override
  public double[] cursorRotate(double[] rot0, Point2D.Double pos0, Point2D.Double pos1) {

    /* Attempt the rotation that transforms a point from one
     * projected plane position to another. */
    double[] rot1 = genericRotate(rot0, pos0, pos1);
    if (rot1 != null) {
      return rot1;
    }

    /* That may fail because one or other of the supplied points is
     * not in the projection region.  In that case do something
     * that feels like dragging the sphere around.
     * This rotation could be improved.  It is algebraically messy,
     * and it also does not transition smoothly from the genericRotate
     * case, though perhaps that's not possible in general. */
    else {
      boolean reflect = isReflected(rot0);
      double fr = reflect ? -1 : +1;
      double phi = (pos1.x - pos0.x);
      double psi = (pos1.y - pos0.y);
      double[] rm = rot0;
      double[] sightvec = Matrices.mvMult(Matrices.invert(rm), new double[] {1, 0, 0});
      double[] hvec = Matrices.normalise(Matrices.cross(sightvec, RZ));
      rm = rotateAround(rm, hvec, -psi);
      rm = rotateAround(rm, RZ, -phi * fr);
      if (Matrices.mvMult(rm, RZ)[2] >= 0) {
        return rm;
      } else {
        double delta = Math.atan2(-rm[2], rm[8]);
        double alpha = Math.atan2(-rm[3], rm[4] * fr);
        delta = Math.min(+0.5 * Math.PI, delta);
        delta = Math.max(-0.5 * Math.PI, delta);
        return verticalRotate(delta, alpha, reflect);
      }
    }
  }
Beispiel #7
0
 /**
  * Indicates whether a rotation matrix represents reflected coordinates.
  *
  * @param rotmat rotation matrix
  * @return true if determinant is less than 0
  */
 private static boolean isReflected(double[] rotmat) {
   return Matrices.det(rotmat) < 0;
 }