@@ -251,19 +251,22 @@ public static float getNormalThroughPoints(T3 pointA, T3 pointB,
251251 }
252252
253253 /**
254- * Project a point onto a plane, also returning the normal vector.
254+ * Project a point onto a plane, also returning the normal vector and the directed distance to the plane .
255255 *
256256 * @param pt
257257 * @param plane
258258 * @param retPtProj returned pt (can be pt)
259259 * @param retNorm returned normal vector
260+ * @return directed distance to plane
260261 */
261- public static void getPlaneProjection (T3 pt , P4 plane , T3 retPtProj , V3 retNorm ) {
262+ public static float getPlaneProjection (T3 pt , P4 plane , T3 retPtProj , V3 retNorm ) {
262263 float dist = distanceToPlane (plane , pt );
263264 retNorm .set (plane .x , plane .y , plane .z );
264265 retNorm .normalize ();
265- retNorm .scale (-dist );
266- retPtProj .add2 (pt , retNorm );
266+ if (dist > 0 )
267+ retNorm .scale (-1 );
268+ retPtProj .scaleAdd2 (Math .abs (dist ), retNorm , pt );
269+ return dist ;
267270 }
268271
269272 /**
@@ -561,125 +564,125 @@ public static P3 getIntersection(P3 pt1, V3 v,
561564 return ptRet ;
562565 }
563566
564- /*
565- * public static Point3f getTriangleIntersection(Point3f a1, Point3f a2,
566- * Point3f a3, Point4f plane, Point3f b1, Point3f b2, Point3f b3, Vector3f
567- * vNorm, Vector3f vTemp, Point3f ptRet, Point3f ptTemp, Vector3f vTemp2,
568- * Point4f pTemp, Vector3f vTemp3) {
569- *
570- * if (getTriangleIntersection(b1, b2, a1, a2, a3, vTemp, plane, vNorm,
571- * vTemp2, vTemp3, ptRet, ptTemp)) return ptRet; if
572- * (getTriangleIntersection(b2, b3, a1, a2, a3, vTemp, plane, vNorm, vTemp2,
573- * vTemp3, ptRet, ptTemp)) return ptRet; if (getTriangleIntersection(b3, b1,
574- * a1, a2, a3, vTemp, plane, vNorm, vTemp2, vTemp3, ptRet, ptTemp)) return
575- * ptRet; return null; }
576- */
577- /*
578- * public static boolean getTriangleIntersection(Point3f b1, Point3f b2,
579- * Point3f a1, Point3f a2, Point3f a3, Vector3f vTemp, Point4f plane, Vector3f
580- * vNorm, Vector3f vTemp2, Vector3f vTemp3, Point3f ptRet, Point3f ptTemp) {
581- * if (distanceToPlane(plane, b1) * distanceToPlane(plane, b2) >= 0) return
582- * false; vTemp.sub(b2, b1); vTemp.normalize(); if (getIntersection(b1, vTemp,
583- * plane, ptRet, vNorm, vTemp2) != null) { if (isInTriangle(ptRet, a1, a2, a3,
584- * vTemp, vTemp2, vTemp3)) return true; } return false; } private static
585- * boolean isInTriangle(Point3f p, Point3f a, Point3f b, Point3f c, Vector3f
586- * v0, Vector3f v1, Vector3f v2) { // from
587- * http://www.blackpawn.com/texts/pointinpoly/default.html // Compute
588- * barycentric coordinates v0.sub(c, a); v1.sub(b, a); v2.sub(p, a); float
589- * dot00 = v0.dot(v0); float dot01 = v0.dot(v1); float dot02 = v0.dot(v2);
590- * float dot11 = v1.dot(v1); float dot12 = v1.dot(v2); float invDenom = 1 /
591- * (dot00 * dot11 - dot01 * dot01); float u = (dot11 * dot02 - dot01 * dot12)
592- * * invDenom; float v = (dot00 * dot12 - dot01 * dot02) * invDenom; return (u
593- * > 0 && v > 0 && u + v < 1); }
594- */
595-
596- /**
597- * Closed-form solution of absolute orientation requiring 1:1 mapping of
598- * positions.
599- *
600- * @param centerAndPoints
601- * @param retStddev
602- * @return unit quaternion representation rotation
603- *
604- * @author hansonr Bob Hanson
605- *
606- */
607- public static Quat calculateQuaternionRotation (P3 [][] centerAndPoints ,
608- float [] retStddev ) {
609- /*
610- * see Berthold K. P. Horn,
611- * "Closed-form solution of absolute orientation using unit quaternions" J.
612- * Opt. Soc. Amer. A, 1987, Vol. 4, pp. 629-642
613- * http://www.opticsinfobase.org/viewmedia.cfm?uri=josaa-4-4-629&seq=0
614- *
615- *
616- * A similar treatment was developed independently (and later!) by G.
617- * Kramer, in G. R. Kramer,
618- * "Superposition of Molecular Structures Using Quaternions" Molecular
619- * Simulation, 1991, Vol. 7, pp. 113-119.
620- *
621- * In that treatment there is a lot of unnecessary calculation along the
622- * trace of matrix M (eqn 20). I'm not sure why the extra x^2 + y^2 + z^2 +
623- * x'^2 + y'^2 + z'^2 is in there, but they are unnecessary and only
624- * contribute to larger numerical averaging errors and additional processing
625- * time, as far as I can tell. Adding aI, where a is a scalar and I is the
626- * 4x4 identity just offsets the eigenvalues but doesn't change the
627- * eigenvectors.
628- *
629- * and Lydia E. Kavraki, "Molecular Distance Measures"
630- * http://cnx.org/content/m11608/latest/
631- */
632-
633-
634- retStddev [1 ] = Float .NaN ;
635- Quat q = new Quat ();
636- P3 [] ptsA = centerAndPoints [0 ];
637- P3 [] ptsB = centerAndPoints [1 ];
638- int nPts = ptsA .length - 1 ;
639- if (nPts < 2 || ptsA .length != ptsB .length )
640- return q ;
641- double Sxx = 0 , Sxy = 0 , Sxz = 0 , Syx = 0 , Syy = 0 , Syz = 0 , Szx = 0 , Szy = 0 , Szz = 0 ;
642- P3 ptA = new P3 ();
643- P3 ptB = new P3 ();
644- P3 ptA0 = ptsA [0 ];
645- P3 ptB0 = ptsB [0 ];
646- for (int i = nPts + 1 ; --i >= 1 ;) {
647- ptA .sub2 (ptsA [i ], ptA0 );
648- ptB .sub2 (ptsB [i ], ptB0 );
649- Sxx += (double ) ptA .x * (double ) ptB .x ;
650- Sxy += (double ) ptA .x * (double ) ptB .y ;
651- Sxz += (double ) ptA .x * (double ) ptB .z ;
652- Syx += (double ) ptA .y * (double ) ptB .x ;
653- Syy += (double ) ptA .y * (double ) ptB .y ;
654- Syz += (double ) ptA .y * (double ) ptB .z ;
655- Szx += (double ) ptA .z * (double ) ptB .x ;
656- Szy += (double ) ptA .z * (double ) ptB .y ;
657- Szz += (double ) ptA .z * (double ) ptB .z ;
658- }
659- retStddev [0 ] = getRmsd (centerAndPoints , q );
660- double [][] N = new double [4 ][4 ];
661- N [0 ][0 ] = Sxx + Syy + Szz ;
662- N [0 ][1 ] = N [1 ][0 ] = Syz - Szy ;
663- N [0 ][2 ] = N [2 ][0 ] = Szx - Sxz ;
664- N [0 ][3 ] = N [3 ][0 ] = Sxy - Syx ;
665-
666- N [1 ][1 ] = Sxx - Syy - Szz ;
667- N [1 ][2 ] = N [2 ][1 ] = Sxy + Syx ;
668- N [1 ][3 ] = N [3 ][1 ] = Szx + Sxz ;
669-
670- N [2 ][2 ] = -Sxx + Syy - Szz ;
671- N [2 ][3 ] = N [3 ][2 ] = Syz + Szy ;
672-
673- N [3 ][3 ] = -Sxx - Syy + Szz ;
674-
675- // this construction prevents JavaScript from requiring preloading of Eigen
676-
677- float [] v = ((EigenInterface ) Interface .getInterface ("javajs.util.Eigen" ))
678- .setM (N ).getEigenvectorsFloatTransposed ()[3 ];
679- q = Quat .newP4 (P4 .new4 (v [1 ], v [2 ], v [3 ], v [0 ]));
680- retStddev [1 ] = getRmsd (centerAndPoints , q );
681- return q ;
682- }
567+ /*
568+ * public static Point3f getTriangleIntersection(Point3f a1, Point3f a2,
569+ * Point3f a3, Point4f plane, Point3f b1, Point3f b2, Point3f b3, Vector3f
570+ * vNorm, Vector3f vTemp, Point3f ptRet, Point3f ptTemp, Vector3f vTemp2,
571+ * Point4f pTemp, Vector3f vTemp3) {
572+ *
573+ * if (getTriangleIntersection(b1, b2, a1, a2, a3, vTemp, plane, vNorm,
574+ * vTemp2, vTemp3, ptRet, ptTemp)) return ptRet; if
575+ * (getTriangleIntersection(b2, b3, a1, a2, a3, vTemp, plane, vNorm, vTemp2,
576+ * vTemp3, ptRet, ptTemp)) return ptRet; if (getTriangleIntersection(b3, b1,
577+ * a1, a2, a3, vTemp, plane, vNorm, vTemp2, vTemp3, ptRet, ptTemp)) return
578+ * ptRet; return null; }
579+ */
580+ /*
581+ * public static boolean getTriangleIntersection(Point3f b1, Point3f b2,
582+ * Point3f a1, Point3f a2, Point3f a3, Vector3f vTemp, Point4f plane, Vector3f
583+ * vNorm, Vector3f vTemp2, Vector3f vTemp3, Point3f ptRet, Point3f ptTemp) {
584+ * if (distanceToPlane(plane, b1) * distanceToPlane(plane, b2) >= 0) return
585+ * false; vTemp.sub(b2, b1); vTemp.normalize(); if (getIntersection(b1, vTemp,
586+ * plane, ptRet, vNorm, vTemp2) != null) { if (isInTriangle(ptRet, a1, a2, a3,
587+ * vTemp, vTemp2, vTemp3)) return true; } return false; } private static
588+ * boolean isInTriangle(Point3f p, Point3f a, Point3f b, Point3f c, Vector3f
589+ * v0, Vector3f v1, Vector3f v2) { // from
590+ * http://www.blackpawn.com/texts/pointinpoly/default.html // Compute
591+ * barycentric coordinates v0.sub(c, a); v1.sub(b, a); v2.sub(p, a); float
592+ * dot00 = v0.dot(v0); float dot01 = v0.dot(v1); float dot02 = v0.dot(v2);
593+ * float dot11 = v1.dot(v1); float dot12 = v1.dot(v2); float invDenom = 1 /
594+ * (dot00 * dot11 - dot01 * dot01); float u = (dot11 * dot02 - dot01 * dot12)
595+ * * invDenom; float v = (dot00 * dot12 - dot01 * dot02) * invDenom; return (u
596+ * > 0 && v > 0 && u + v < 1); }
597+ */
598+
599+ /**
600+ * Closed-form solution of absolute orientation requiring 1:1 mapping of
601+ * positions.
602+ *
603+ * @param centerAndPoints
604+ * @param retStddev
605+ * @return unit quaternion representation rotation
606+ *
607+ * @author hansonr Bob Hanson
608+ *
609+ */
610+ public static Quat calculateQuaternionRotation (P3 [][] centerAndPoints ,
611+ float [] retStddev ) {
612+ /*
613+ * see Berthold K. P. Horn,
614+ * "Closed-form solution of absolute orientation using unit quaternions" J.
615+ * Opt. Soc. Amer. A, 1987, Vol. 4, pp. 629-642
616+ * http://www.opticsinfobase.org/viewmedia.cfm?uri=josaa-4-4-629&seq=0
617+ *
618+ *
619+ * A similar treatment was developed independently (and later!) by G.
620+ * Kramer, in G. R. Kramer,
621+ * "Superposition of Molecular Structures Using Quaternions" Molecular
622+ * Simulation, 1991, Vol. 7, pp. 113-119.
623+ *
624+ * In that treatment there is a lot of unnecessary calculation along the
625+ * trace of matrix M (eqn 20). I'm not sure why the extra x^2 + y^2 + z^2 +
626+ * x'^2 + y'^2 + z'^2 is in there, but they are unnecessary and only
627+ * contribute to larger numerical averaging errors and additional processing
628+ * time, as far as I can tell. Adding aI, where a is a scalar and I is the
629+ * 4x4 identity just offsets the eigenvalues but doesn't change the
630+ * eigenvectors.
631+ *
632+ * and Lydia E. Kavraki, "Molecular Distance Measures"
633+ * http://cnx.org/content/m11608/latest/
634+ */
635+
636+
637+ retStddev [1 ] = Float .NaN ;
638+ Quat q = new Quat ();
639+ P3 [] ptsA = centerAndPoints [0 ];
640+ P3 [] ptsB = centerAndPoints [1 ];
641+ int nPts = ptsA .length - 1 ;
642+ if (nPts < 2 || ptsA .length != ptsB .length )
643+ return q ;
644+ double Sxx = 0 , Sxy = 0 , Sxz = 0 , Syx = 0 , Syy = 0 , Syz = 0 , Szx = 0 , Szy = 0 , Szz = 0 ;
645+ P3 ptA = new P3 ();
646+ P3 ptB = new P3 ();
647+ P3 ptA0 = ptsA [0 ];
648+ P3 ptB0 = ptsB [0 ];
649+ for (int i = nPts + 1 ; --i >= 1 ;) {
650+ ptA .sub2 (ptsA [i ], ptA0 );
651+ ptB .sub2 (ptsB [i ], ptB0 );
652+ Sxx += (double ) ptA .x * (double ) ptB .x ;
653+ Sxy += (double ) ptA .x * (double ) ptB .y ;
654+ Sxz += (double ) ptA .x * (double ) ptB .z ;
655+ Syx += (double ) ptA .y * (double ) ptB .x ;
656+ Syy += (double ) ptA .y * (double ) ptB .y ;
657+ Syz += (double ) ptA .y * (double ) ptB .z ;
658+ Szx += (double ) ptA .z * (double ) ptB .x ;
659+ Szy += (double ) ptA .z * (double ) ptB .y ;
660+ Szz += (double ) ptA .z * (double ) ptB .z ;
661+ }
662+ retStddev [0 ] = getRmsd (centerAndPoints , q );
663+ double [][] N = new double [4 ][4 ];
664+ N [0 ][0 ] = Sxx + Syy + Szz ;
665+ N [0 ][1 ] = N [1 ][0 ] = Syz - Szy ;
666+ N [0 ][2 ] = N [2 ][0 ] = Szx - Sxz ;
667+ N [0 ][3 ] = N [3 ][0 ] = Sxy - Syx ;
668+
669+ N [1 ][1 ] = Sxx - Syy - Szz ;
670+ N [1 ][2 ] = N [2 ][1 ] = Sxy + Syx ;
671+ N [1 ][3 ] = N [3 ][1 ] = Szx + Sxz ;
672+
673+ N [2 ][2 ] = -Sxx + Syy - Szz ;
674+ N [2 ][3 ] = N [3 ][2 ] = Syz + Szy ;
675+
676+ N [3 ][3 ] = -Sxx - Syy + Szz ;
677+
678+ // this construction prevents JavaScript from requiring preloading of Eigen
679+
680+ float [] v = ((EigenInterface ) Interface .getInterface ("javajs.util.Eigen" ))
681+ .setM (N ).getEigenvectorsFloatTransposed ()[3 ];
682+ q = Quat .newP4 (P4 .new4 (v [1 ], v [2 ], v [3 ], v [0 ]));
683+ retStddev [1 ] = getRmsd (centerAndPoints , q );
684+ return q ;
685+ }
683686
684687 /**
685688 * Fills a 4x4 matrix with rotation-translation of mapped points A to B.
@@ -720,18 +723,18 @@ public static float getTransformMatrix4(Lst<P3> ptsA, Lst<P3> ptsB, M4 m,
720723 * @param vPts
721724 * @return array of points with first point center
722725 */
723- public static P3 [] getCenterAndPoints (Lst <P3 > vPts ) {
724- int n = vPts .size ();
725- P3 [] pts = new P3 [n + 1 ];
726- pts [0 ] = new P3 ();
727- if (n > 0 ) {
728- for (int i = 0 ; i < n ; i ++) {
729- pts [0 ].add (pts [i + 1 ] = vPts .get (i ));
730- }
731- pts [0 ].scale (1f / n );
732- }
733- return pts ;
734- }
726+ public static P3 [] getCenterAndPoints (Lst <P3 > vPts ) {
727+ int n = vPts .size ();
728+ P3 [] pts = new P3 [n + 1 ];
729+ pts [0 ] = new P3 ();
730+ if (n > 0 ) {
731+ for (int i = 0 ; i < n ; i ++) {
732+ pts [0 ].add (pts [i + 1 ] = vPts .get (i ));
733+ }
734+ pts [0 ].scale (1f / n );
735+ }
736+ return pts ;
737+ }
735738
736739 public static float getRmsd (P3 [][] centerAndPoints , Quat q ) {
737740 double sum2 = 0 ;
@@ -978,7 +981,6 @@ static void testRnd() {
978981 pts [i ] = new P3 ();
979982 P3 p = rndPt ();
980983 getPlaneProjection (p , plane , ptProj , vNorm );
981- vNorm .scale (1 /vNorm .length ());
982984 pts [i ].setT (ptProj );
983985 float d = (float )Math .random ()*0.1f ;
984986 pts [i ].scaleAdd2 (d , vNorm , ptProj );
0 commit comments