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