001/* 002 * To change this template, choose Tools | Templates 003 * and open the template in the editor. 004 */ 005package armyc2.c5isr.graphics2d; 006 007import android.os.Build; 008 009import java.util.ArrayList; 010import java.util.HashMap; 011import java.util.Map; 012 013import armyc2.c5isr.JavaLineArray.POINT2; 014import armyc2.c5isr.JavaLineArray.lineutility; 015import armyc2.c5isr.renderer.utilities.ErrorLogger; 016import armyc2.c5isr.renderer.utilities.RendererException; 017/** 018 * 019* 020 */ 021public class Area extends GeneralPath{ 022 private static final String _className="Area"; 023 //private ArrayList<POINT2>_pts=null; 024 public Area(Polygon poly) 025 { 026 int j=0; 027 int n=poly.npoints; 028 //for(j=0;j<poly.npoints;j++) 029 for(j=0;j<n;j++) 030 { 031 if(j==0) 032 moveTo(poly.xpoints[j],poly.ypoints[j]); 033 else 034 lineTo(poly.xpoints[j],poly.ypoints[j]); 035 } 036 } 037 public Area(Shape shape) 038 { 039 int j=0; 040 PathIterator p=shape.getPathIterator(null); 041 ArrayList<POINT2>pts=p.getPoints(); 042 POINT2 pt=null; 043 int n=pts.size(); 044 //for(j=0;j<pts.size();j++) 045 for(j=0;j<n;j++) 046 { 047 pt=pts.get(j); 048 switch(pt.style) 049 { 050 case IPathIterator.SEG_MOVETO: 051 moveTo(pt.x,pt.y); 052 break; 053 case IPathIterator.SEG_LINETO: 054 lineTo(pt.x,pt.y); 055 break; 056 default: 057 break; 058 } 059 } 060 } 061 /** 062 * organizes intersect points by increasing distance from the hatch line origin 063 * @param hatchLine 064 * @param pts 065 */ 066 private static void reorderPointsByDistance(Line2D hatchLine, ArrayList<Point2D>pts) 067 { 068 try 069 { 070 double minDistance=0,dist=0; 071 int j=0,minIndex=-1; 072 Map<Integer,Double>distances=new HashMap(); 073 ArrayList<Point2D>ptsOrdered=new ArrayList(); 074 Point2D origin=hatchLine.getP1(); 075 POINT2 pt0=new POINT2(origin.getX(),origin.getY()); 076 POINT2 pt1=null; 077 //build the distances array 078 int n=pts.size(); 079 //for(j=0;j<pts.size();j++) 080 for(j=0;j<n;j++) 081 { 082 pt1=new POINT2(pts.get(j).getX(), pts.get(j).getY()); 083 dist=lineutility.CalcDistanceDouble(pt0, pt1); 084 distances.put(j, dist); 085 } 086 while (distances.size()>0) 087 { 088 //initialize minDistance after an array element was removed 089 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 090 minIndex=distances.keySet().stream().findFirst().get(); 091 minDistance=distances.get(minIndex); 092 } 093 else 094 { 095 //for(j=0;j<pts.size();j++) 096 for(j=0;j<n;j++) 097 { 098 if(distances.containsKey(j)) 099 { 100 minIndex=j; 101 minDistance=distances.get(j); 102 break; 103 } 104 } 105 } 106 107 108 //loop through the remaining elements to find the next minimum distance 109 //for(j=0;j<pts.size();j++) 110 for(j=0;j<n;j++) 111 { 112 if(distances.containsKey(j)) 113 { 114 dist=distances.get(j); 115 if(dist<minDistance) 116 { 117 minDistance=dist; 118 minIndex=j; 119 } 120 } 121 } 122 //add the next point to the array 123 ptsOrdered.add(pts.get(minIndex)); 124 distances.remove(minIndex); 125 } 126 pts.clear(); 127 n=ptsOrdered.size(); 128 //for(j=0;j<ptsOrdered.size();j++) 129 for(j=0;j<n;j++) 130 pts.add(ptsOrdered.get(j)); 131 } 132 catch(Exception exc) 133 { 134 ErrorLogger.LogException(_className, "reorderPointsByDistance", 135 new RendererException("Failed inside reorderPointsByDistance", exc)); 136 } 137 } 138 Rectangle2D getMBR(ArrayList<POINT2>polygon) 139 { 140 int j=0; 141 double left=polygon.get(0).x; 142 double top=polygon.get(0).y; 143 double right=polygon.get(0).x; 144 double bottom=polygon.get(0).y; 145 int n=polygon.size(); 146 //for (j=1;j<polygon.size();j++) 147 for (j=1;j<n;j++) 148 { 149 if(polygon.get(j).x<left) 150 left=polygon.get(j).x; 151 if(polygon.get(j).x>right) 152 right=polygon.get(j).x; 153 154 if(polygon.get(j).y<top) 155 top=polygon.get(j).y; 156 if(polygon.get(j).y>bottom) 157 bottom=polygon.get(j).y; 158 } 159 return new Rectangle2D.Double(left,top,right-left,bottom-top); 160 } 161 static boolean isVertical(Line2D edge) 162 { 163 if(edge.getX1()==edge.getX2()) 164 return true; 165 else return false; 166 } 167 private static void adjustVerticalLine(Line2D line) 168 { 169 Point2D linePt0=line.getP1(); 170 Point2D linePt1=line.getP1(); 171 if(isVertical(line)) 172 { 173 double x=line.getX2()+.001; 174 double y=line.getY2(); 175 linePt1.setLocation(x, y); 176 line.setLine(linePt0, linePt1); 177 } 178 } 179 /** 180 * 181 * @param hatchLine the hatch line to intersect against the area points. 182 * the thatch line is assumed to start outside the area (polygon) MBR 183 * @return the GeneralPath which represents the intersection 184 */ 185 private static ArrayList<POINT2> getLineIntersectPoints(ArrayList<POINT2> polygon, Line2D hatchLine) 186 { 187 ArrayList<POINT2>pts=null; 188 try 189 { 190 int j=0,k=0; 191 Line2D segment=null; 192 Point2D pt0=null,pt1=null; 193 //no (exactly) vertical hatch lines 194 adjustVerticalLine(hatchLine); 195 ArrayList<Point2D>ptsPath=new ArrayList(); 196 double x=0,y=0; 197 double m1=0, //hatch line 198 m2=0, //segment slope 199 b1=0, //hatch line y intercept 200 b2=0; //segment y intercept 201 int n=polygon.size(); 202 //for(j=0;j<polygon.size()-1;j++) 203 for(j=0;j<n-1;j++) 204 { 205 pt0=new Point2D.Double(polygon.get(j)); 206 pt1=new Point2D.Double(polygon.get(j+1)); 207 segment=new Line2D.Double(pt0,pt1); 208 //no vertical segments 209 adjustVerticalLine(segment); 210 pt0=segment.getP1(); 211 pt1=segment.getP2(); 212 m1=(hatchLine.getY1()-hatchLine.getY2())/(hatchLine.getX1()-hatchLine.getX2()); 213 m2=(pt0.getY()-pt1.getY())/(pt0.getX()-pt1.getX()); 214 if( hatchLine.intersectsLine(segment) ) 215 { 216 //m1=(hatchLine.getY1()-hatchLine.getY2())/(hatchLine.getX1()-hatchLine.getX2()); 217 //m2=(pt0.getY()-pt1.getY())/(pt0.getX()-pt1.getX()); 218 if(m1==m2) 219 { 220 ptsPath.add(pt0); 221 ptsPath.add(pt1); 222 } 223 else //slopes not equal 224 { 225 //add one intersection point 226 b1=hatchLine.getY1()-m1*hatchLine.getX1(); 227 b2=segment.getY1()-m2*segment.getX1(); 228 x=(b2-b1)/(m1-m2); //cannot blow up 229 y=(m1*x+b1); 230 231 /* 232 Touching vertex logic: 233 If intersect vertex the line is entering or exiting a shape, add point once 234 if tangent to vertex the line is not entering or exiting shape, add point twice to negate changes 235 236 Intersect vertex => points before and after in the shape are on different sides of the line 237 Tangent to vertex => points before and after in the shape are on the same side of the line 238 239 Every vertex is in two segments of the shape, one where its pt0 and another as pt1 240 Always add vertex if pt0 of polygon 241 If pt1 of polygon and pts before and after are on same side then add pt1 242 */ 243 if (Math.abs(pt1.getX() - x) < .001 && Math.abs(pt1.getY() - y) < .001) 244 { 245 Point2D.Double ptBefore = new Point2D.Double(polygon.get(j)); 246 Point2D.Double ptAfter = new Point2D.Double(polygon.get((j + 2) % (polygon.size() - 1))); 247 if ((ptBefore.getY() > m1 * ptBefore.getX() + b1 && ptAfter.getY() > m1 * ptAfter.getX() + b1) || 248 (ptBefore.getY() < m1 * ptBefore.getX() + b1 && ptAfter.getY() < m1 * ptAfter.getX() + b1)) 249 { 250 // Points before and after vertex on the same side 251 ptsPath.add(new Point2D.Double(x,y)); 252 } 253 } 254 else 255 { 256 ptsPath.add(new Point2D.Double(x,y)); 257 } 258 } 259 } 260 } 261 //reorder ptsPath by distance from the hatch line origin 262 reorderPointsByDistance(hatchLine,ptsPath); 263 Point2D pt=null; 264 pts=new ArrayList(); 265 n=ptsPath.size(); 266 //for(k=0;k<ptsPath.size();k++) 267 for(k=0;k<n;k++) 268 { 269 pt=ptsPath.get(k); 270 if(k%2==0) 271 { 272 pts.add(new POINT2(pt.getX(),pt.getY(),IPathIterator.SEG_MOVETO)); 273 } 274 else 275 { 276 pts.add(new POINT2(pt.getX(),pt.getY(),IPathIterator.SEG_LINETO)); 277 } 278 279 } 280 ptsPath.clear(); 281 } 282 catch(Exception exc) 283 { 284 ErrorLogger.LogException(_className, "getLineIntersectPoints", 285 new RendererException("Failed inside getLineIntersectPoints", exc)); 286 } 287 return pts; 288 } 289 /** 290 * this is functionality for clsUtilityGE.buildHatchFillwhich calls hatchLineArea.intersect(shapeArea). 291 * so it assumes that this._pts is the hatch lines so it is hatchLines.intersect(shape) where 292 * shape is the polygon to be filled with hatch lines 293 * @param area 294 */ 295 public void intersect(Area area) 296 { 297 try 298 { 299 //assume area is the polygon and "this" is the hatch line shape 300 int j=0; 301 ArrayList<POINT2>polygon=area.getPathIterator(null).getPoints(); 302 ArrayList<POINT2>hatchLines=this.getPathIterator(null).getPoints(); 303 // Remove duplicates from the shape 304 for (int i = 0; i < polygon.size() - 1; i++) 305 { 306 POINT2 pt0 = polygon.get(i); 307 POINT2 pt1 = polygon.get(i + 1); 308 if (pt0.x == pt1.x && pt0.y == pt1.y) 309 { 310 polygon.remove(i+1); 311 i--; 312 } 313 } 314 //close the polygon 315 if(polygon.get(0).x != polygon.get(polygon.size()-1).x || polygon.get(0).y != polygon.get(polygon.size()-1).y) 316 { 317 polygon.add(new POINT2(polygon.get(0))); 318 } 319 //GeneralPath gp=null; 320 //GeneralPath masterGP=null; 321 Line2D hatchLine=null; 322 Rectangle2D rectHatch=null; 323 Rectangle2D rectPoly=getMBR(polygon); 324 ArrayList<POINT2> pts=new ArrayList(); 325 ArrayList<POINT2> ptsTemp=null; 326 int n=hatchLines.size(); 327 //for(j=0;j<hatchLines.size()-1;j++) 328 for(j=0;j<n-1;j++) 329 { 330 hatchLine=new Line2D.Double(hatchLines.get(j).x,hatchLines.get(j).y,hatchLines.get(j+1).x,hatchLines.get(j+1).y); 331 rectHatch=hatchLine.getBounds2D(); 332 if(rectHatch.intersects(rectPoly)==false) 333 continue; 334 335 ptsTemp=getLineIntersectPoints(polygon,hatchLine); 336 if(ptsTemp != null) 337 pts.addAll(ptsTemp); 338 } 339 POINT2 pt=null; 340 //area.getPathIterator(null).reset(); 341 //area.getPathIterator(null).getPoints().clear(); 342 //this._pts.clear(); 343 this.getPathIterator(null).getPoints().clear(); 344 //area._pts.clear(); 345 n=pts.size(); 346 //for(j=0;j<pts.size();j++) 347 for(j=0;j<n;j++) 348 { 349 pt=pts.get(j); 350 switch(pt.style) 351 { 352 case IPathIterator.SEG_MOVETO: 353 moveTo(pt.x,pt.y); 354 break; 355 case IPathIterator.SEG_LINETO: 356 lineTo(pt.x,pt.y); 357 break; 358 default: 359 break; 360 } 361 } 362 this.getPathIterator(null).reset(); 363 } 364 catch(Exception exc) 365 { 366 ErrorLogger.LogException(_className, "intersect", 367 new RendererException("Failed inside intersect", exc)); 368 } 369 } 370}