import java.util.Vector; /** *

* Grab bag of helper functions for geometric computation *

* @author Michael Schuresko * @version %I%, %G% * @since 1.0 * @author mds * */ public class GeometryHelpers { double m_lfEps; { m_lfEps = MathUtils.lfEps; } public static class Circle { double [] m_arrLfCenter; double m_lfRad; public Circle() { m_arrLfCenter = new double[2]; m_lfRad = 0.0; } public Circle(int nDim) { m_arrLfCenter = new double[nDim]; m_lfRad = 0.0; } public Circle(double [] arrLfCen, double lfRad) { m_arrLfCenter = MathUtils.arrDblCpy(arrLfCen, arrLfCen.length); m_lfRad = lfRad; } public Circle(double [] arrLfToAvg1, double [] arrLfToAvg2, double lfRad) { m_arrLfCenter = new double[arrLfToAvg1.length]; for(int i = 0; i < arrLfToAvg1.length; ++i) { m_arrLfCenter[i] = 0.5*(arrLfToAvg1[i] + arrLfToAvg2[i]); } m_lfRad = 0.5*lfRad; } public double rad() { return m_lfRad; } public double dist(double [] arrLfPt) { double lfDist = MathUtils.arrDistSqrd(arrLfPt, m_arrLfCenter); return Math.max(0.0, Math.sqrt(lfDist) - m_lfRad); } public double dist(Vector vecLfPt) { double lfDistSqrd=0.0; for(int i = 0; i < m_arrLfCenter.length; ++i) { double lfTmp = vecLfPt.elementAt(i) - m_arrLfCenter[i]; lfDistSqrd += lfTmp*lfTmp; } return Math.max(0.0, Math.sqrt(lfDistSqrd)-m_lfRad); } public void closestPt(double [] arrLfPt, double [] arrLfRslt) { if(m_lfRad < MathUtils.lfEps) { MathUtils.arrCpy(m_arrLfCenter, arrLfRslt); return; } MathUtils.linCombine(1.0, arrLfPt, -1.0, m_arrLfCenter, arrLfRslt); double lfDistSqrd = MathUtils.arrDot(arrLfRslt, arrLfRslt); if(lfDistSqrd < m_lfRad*m_lfRad) { MathUtils.arrCpy(arrLfPt, arrLfRslt); return; } double lfDist = Math.sqrt(lfDistSqrd); MathUtils.linCombine(1.0, m_arrLfCenter, m_lfRad/lfDist, arrLfRslt, arrLfRslt); return; } public double [] closestPt(double [] arrLfPt) { double [] arrLfRslt = new double[arrLfPt.length]; closestPt(arrLfPt, arrLfRslt); return arrLfRslt; } /** * Fails gracelessly if all dimensions are not "2" * @param isectWith circle to intersect with. * @param arrLfPt1 first intersection point * @param arrLfPt2 second intersection point * @param arrLfScratch 2 item scratch array -- can be null * @return true if circles intersect in two distinct points. */ public boolean isectPts2d(Circle isectWith, double [] arrLfPt1, double [] arrLfPt2, double [] arrLfScratch) { if(arrLfScratch == null) { arrLfScratch = new double[2]; } MathUtils.linCombine(1.0, isectWith.m_arrLfCenter, -1.0, m_arrLfCenter, arrLfScratch); double lfNorm = MathUtils.arrDot(arrLfScratch, arrLfScratch); if(lfNorm < MathUtils.lfEps) { MathUtils.arrCpy(m_arrLfCenter, arrLfPt2); MathUtils.arrCpy(isectWith.m_arrLfCenter, arrLfPt2); return false; } double lfCenDist = Math.sqrt(lfNorm); if(lfCenDist + m_lfRad < isectWith.m_lfRad || lfCenDist + isectWith.m_lfRad < m_lfRad) { MathUtils.arrCpy(m_arrLfCenter, arrLfPt2); MathUtils.arrCpy(isectWith.m_arrLfCenter, arrLfPt2); return false; } lfNorm = 1.0/lfCenDist; arrLfScratch[0] *= lfNorm; arrLfScratch[1] *= lfNorm; double lfDistCen1 = (m_lfRad*m_lfRad - isectWith.m_lfRad*isectWith.m_lfRad + lfCenDist*lfCenDist)/(2.0*lfCenDist); double lfHeight = Math.sqrt(m_lfRad*m_lfRad - lfDistCen1*lfDistCen1); arrLfPt1[0] = m_arrLfCenter[0] + lfDistCen1*arrLfScratch[0] + lfHeight*arrLfScratch[1]; arrLfPt1[1] = m_arrLfCenter[1] + lfDistCen1*arrLfScratch[1] - lfHeight*arrLfScratch[0]; arrLfPt2[0] = m_arrLfCenter[0] + lfDistCen1*arrLfScratch[0] - lfHeight*arrLfScratch[1]; arrLfPt2[1] = m_arrLfCenter[1] + lfDistCen1*arrLfScratch[1] + lfHeight*arrLfScratch[0]; return true; } public double closeness(Vector vecLfPt) { double lfDistSqrd = 0.0; double lfTmp = 0.0; for(int i = 0; i < vecLfPt.size(); ++i) { lfTmp = vecLfPt.elementAt(i) - m_arrLfCenter[i]; lfDistSqrd += lfTmp*lfTmp; } double lfCheck = lfDistSqrd - m_lfRad*m_lfRad - MathUtils.lfEps; return lfCheck; } public boolean ptInCirc(Vector vecLfPt) { double lfDistSqrd = 0.0; double lfTmp = 0.0; for(int i = 0; i < vecLfPt.size(); ++i) { lfTmp = vecLfPt.elementAt(i) - m_arrLfCenter[i]; lfDistSqrd += lfTmp*lfTmp; } double lfCheck = lfDistSqrd - m_lfRad*m_lfRad - MathUtils.lfEps; return lfCheck < 0; } public double closeness(double [] arrLfPt) { return MathUtils.arrDistSqrd(arrLfPt, m_arrLfCenter) - m_lfRad*m_lfRad - MathUtils.lfEps; } public boolean ptInCirc(double [] arrLfPt) { return (MathUtils.arrDistSqrd(arrLfPt, m_arrLfCenter) - m_lfRad*m_lfRad - MathUtils.lfEps < 0); } } static void testCirclePrint(double [] arrLf1, double [] arrLf2) { System.out.println("Pt1 is (" + (new Double(arrLf1[0])).toString() + "," + (new Double(arrLf1[1])).toString() + ")"); System.out.println("Pt2 is (" + (new Double(arrLf2[0])).toString() + "," + (new Double(arrLf2[1])).toString() + ")"); } public static void testCircleCode() { double [] arrLfCen1 = new double[2]; double [] arrLfCen2 = new double[2]; arrLfCen2[0] = 3.0; Circle circ1 = new Circle(arrLfCen1, 5.0); Circle circ2 = new Circle(arrLfCen2, 4.0); double [] arrLfPt1 = new double[2]; double [] arrLfPt2 = new double[2]; double [] arrLfScratch = new double[2]; circ1.isectPts2d(circ2, arrLfPt1, arrLfPt2, arrLfScratch); testCirclePrint(arrLfPt1, arrLfPt2); circ2.isectPts2d(circ1, arrLfPt1, arrLfPt2, arrLfScratch); testCirclePrint(arrLfPt1, arrLfPt2); System.out.println("Closeness is " + (new Double(circ1.closeness(arrLfPt1))).toString() + " or " + (new Double(circ1.closeness(arrLfPt2))).toString() + " alt " + (new Double(circ2.closeness(arrLfPt1))).toString() + " or " + (new Double(circ2.closeness(arrLfPt2))).toString() ); arrLfCen2[0] = 0.0; arrLfCen1[1] = -3.0; circ1 = new Circle(arrLfCen1, 5.0); circ2 = new Circle(arrLfCen2, 4.0); circ1.isectPts2d(circ2, arrLfPt1, arrLfPt2, arrLfScratch); testCirclePrint(arrLfPt1, arrLfPt2); circ2.isectPts2d(circ1, arrLfPt1, arrLfPt2, arrLfScratch); testCirclePrint(arrLfPt1, arrLfPt2); System.out.println("Closeness is " + (new Double(circ1.closeness(arrLfPt1))).toString() + " or " + (new Double(circ1.closeness(arrLfPt2))).toString() + " alt " + (new Double(circ2.closeness(arrLfPt1))).toString() + " or " + (new Double(circ2.closeness(arrLfPt2))).toString() ); arrLfPt1[0] = 30; arrLfPt1[1] = 40; System.out.println("Closest pt to 1 is "+ MathUtils.arrToString(circ1.closestPt(arrLfPt1))); System.out.println("Closest pt to 2 is "+ MathUtils.arrToString(circ2.closestPt(arrLfPt1))); System.out.println("Closeness is " + (new Double(circ1.closeness(circ1.closestPt(arrLfPt1)))).toString() + " or " + (new Double(circ1.closeness(circ1.closestPt(arrLfPt2)))).toString()); } /** * * @param arrLfRayDir direction of ray (not necc. unit length) * @param arrLfRayOrg position of ray origin * @param arrLfCircCen position of circle center * @param lfCircRadSqrd rad^2 of circle * @return distance along ray until it first hits circle. */ public static double rayToCircle(double [] arrLfRayDir, double [] arrLfRayOrg, double [] arrLfCircCen, double lfCircRadSqrd) { double [] arrLfScratch = MathUtils.arrDblCpy(arrLfRayOrg, arrLfRayOrg.length); MathUtils.arrSubtrAndStore(arrLfScratch, arrLfCircCen); double lfDirMagSqrd = MathUtils.arrDot(arrLfRayDir, arrLfRayDir); double lfOrgDotRay = MathUtils.arrDot(arrLfScratch, arrLfRayDir); // we have a*b*cosTheta and a^2 and aA and bB we want // aA - aBcosTheta // which is aA - a*b*cosTheta*bB/b^2 double lfFactor = lfOrgDotRay/lfDirMagSqrd; MathUtils.linCombine(1.0, arrLfScratch, -lfFactor, arrLfRayDir, arrLfScratch); double lfPMOff = 0.0; // plus or minus off lfPMOff = MathUtils.arrDot(arrLfScratch, arrLfScratch); if(lfPMOff > lfCircRadSqrd) { System.out.println("Error here"); lfPMOff = lfCircRadSqrd; } lfPMOff = Math.sqrt(lfCircRadSqrd - lfPMOff); double lfOff = -lfFactor*Math.sqrt(lfDirMagSqrd); if(lfOff - lfPMOff < 0.0) { return lfOff + lfPMOff; } else { return lfOff - lfPMOff; } } }