001package icy.type.geom; 002 003import java.awt.Point; 004import java.awt.geom.AffineTransform; 005import java.awt.geom.Line2D; 006import java.awt.geom.Point2D; 007import java.awt.geom.Rectangle2D; 008 009/** 010 * Utilities for geom objects 011 * 012 * @author Stephane Dallongeville 013 */ 014public class GeomUtil 015{ 016 /** 017 * Get intersection point between 2 lines. 018 * 019 * @return the intersection point between 2 lines, it can return <code>null</code> if the 2 lines are // 020 */ 021 public static Point2D getIntersection(Line2D lineA, Line2D lineB) 022 { 023 final double x1 = lineA.getX1(); 024 final double y1 = lineA.getY1(); 025 final double x2 = lineA.getX2(); 026 final double y2 = lineA.getY2(); 027 028 final double x3 = lineB.getX1(); 029 final double y3 = lineB.getY1(); 030 final double x4 = lineB.getX2(); 031 final double y4 = lineB.getY2(); 032 033 final double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); 034 035 if (d != 0) 036 { 037 final double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; 038 final double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; 039 040 return new Point2D.Double(xi, yi); 041 } 042 043 return null; 044 } 045 046 /** 047 * Get intersection point between 2 lines. 048 * 049 * @param lineA 050 * first line 051 * @param lineB 052 * second line 053 * @param limitToSegmentA 054 * Limit intersection to segment <i>lineA</i>, if intersection point is outside <i>lineA</i> then 055 * <code>null</code> is returned 056 * @param limitToSegmentB 057 * Limit intersection to segment <i>lineB</i>, if intersection point is outside <i>lineB</i> then 058 * <code>null</code> is returned 059 */ 060 public static Point2D getIntersection(Line2D lineA, Line2D lineB, boolean limitToSegmentA, boolean limitToSegmentB) 061 { 062 final Point2D result = getIntersection(lineA, lineB); 063 064 if (result == null) 065 return null; 066 067 final Rectangle2D rectPt = new Rectangle2D.Double(result.getX() - 0.5d, result.getY() - 0.5d, 1d, 1d); 068 069 // constraint to line segment if wanted 070 if (limitToSegmentA && !lineA.intersects(rectPt)) 071 return null; 072 if (limitToSegmentB && !lineB.intersects(rectPt)) 073 return null; 074 075 return result; 076 } 077 078 /** 079 * Returns rotation information from AffineTransform.<br> 080 * <b>WARNING:</b> this method may not be 100% accurate because of possible combined transformation 081 */ 082 public static double getRotation(AffineTransform transform) 083 { 084 // use the iterative polar decomposition algorithm described by Ken Shoemake: 085 // http://www.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf 086 087 // start with the contents of the upper 2x2 portion of the matrix 088 double m00 = transform.getScaleX(); 089 double m01 = transform.getShearX(); 090 double m10 = transform.getShearY(); 091 double m11 = transform.getScaleY(); 092 093 for (int ii = 0; ii < 10; ii++) 094 { 095 // store the results of the previous iteration 096 final double o00 = m00; 097 final double o10 = m10; 098 final double o01 = m01; 099 final double o11 = m11; 100 101 // compute average of the matrix with its inverse transpose 102 final double det = o00 * o11 - o10 * o01; 103 104 // determinant is zero; matrix is not invertible --> use alternative method 105 if (Math.abs(det) == 0d) 106 return getRotationFast(transform); 107 108 final double hrdet = 0.5f / det; 109 110 m00 = +o11 * hrdet + o00 * 0.5f; 111 m10 = -o01 * hrdet + o10 * 0.5f; 112 m01 = -o10 * hrdet + o01 * 0.5f; 113 m11 = +o00 * hrdet + o11 * 0.5f; 114 115 // compute the difference; if it's small enough, we're done 116 final double d00 = m00 - o00; 117 final double d10 = m10 - o10; 118 final double d01 = m01 - o01; 119 final double d11 = m11 - o11; 120 121 if (((d00 * d00) + (d10 * d10) + (d01 * d01) + (d11 * d11)) < 0.0001d) 122 break; 123 } 124 125 // now that we have a nice orthogonal matrix, we can extract the rotation 126 return Math.atan2(m01, m00); 127 } 128 129 /** 130 * Returns rotation information from AffineTransform (fast method).<br> 131 * <b>WARNING:</b> this method may not be 100% accurate because of possible combined transformation 132 */ 133 public static double getRotationFast(AffineTransform transform) 134 { 135 // m00, m01 136 final double a = transform.getScaleX(); 137 final double b = transform.getShearX(); 138 139 if ((Math.abs(a) >= 0.001d) && (Math.abs(b) >= 0.001d)) 140 return Math.atan(-b / a); 141 142 // m10, m11 143 final double c = transform.getShearY(); 144 final double d = transform.getScaleY(); 145 146 if ((Math.abs(c) >= 0.001d) && (Math.abs(d) >= 0.001d)) 147 return Math.atan(c / d); 148 149 // use alternative method 150 final Point2D pp0 = transform.transform(new Point(0, 0), null); 151 final Point2D pp1 = transform.transform(new Point(1, 0), null); 152 final double dx = pp1.getX() - pp0.getX(); 153 final double dy = pp1.getY() - pp0.getY(); 154 155 return Math.atan2(dy, dx); 156 } 157 158 /** 159 * Returns uniform scale (X=Y) information from AffineTransform.<br> 160 * <b>WARNING:</b> this method may not be 100% accurate because of possible combined transformation 161 */ 162 public static double getScale(AffineTransform transform) 163 { 164 // m00, m01 165 final double a = transform.getScaleX(); 166 final double b = transform.getShearX(); 167 // m10, m11 168 final double c = transform.getShearY(); 169 final double d = transform.getScaleY(); 170 171 // the square root of the signed area of the parallelogram spanned by the axis vectors 172 final double cp = (a * d) - (b * c); 173 174 return (cp < 0f) ? -Math.sqrt(-cp) : Math.sqrt(cp); 175 } 176 177 /** 178 * Returns scale X information from AffineTransform.<br> 179 * <b>WARNING:</b> this method may not be 100% accurate because of possible combined transformation 180 */ 181 public static double getScaleX(AffineTransform transform) 182 { 183 // m00, m10 184 final double a = transform.getScaleX(); 185 final double c = transform.getShearY(); 186 187 return Math.sqrt((a * a) + (c * c)); 188 } 189 190 /** 191 * Returns scale Y information from AffineTransform.<br> 192 * <b>WARNING:</b> this method may not be 100% accurate because of possible combined transformation 193 */ 194 public static double getScaleY(AffineTransform transform) 195 { 196 // m01, m11 197 final double b = transform.getShearX(); 198 final double d = transform.getScaleY(); 199 200 return Math.sqrt((b * b) + (d * d)); 201 } 202}