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}