001/*
002 * To change this template, choose Tools | Templates
003 * and open the template in the editor.
004 */
005package sec.sun.awt.geom;
006
007/**
008 *
009 * @author Michael Deutch
010 */
011public class AreaOp2 {
012    public static final int EOWINDOP = 0;
013    public static final int NZWINDOP = 1;
014    /* Constants to tag the left and right curves in the edge list */
015    public static final int CTAG_LEFT = 0;
016    public static final int CTAG_RIGHT = 1;
017
018    /* Constants to classify edges */
019    public static final int ETAG_IGNORE = 0;
020    public static final int ETAG_ENTER = 1;
021    public static final int ETAG_EXIT = -1;
022
023    /* Constants used to classify result state */
024    public static final int RSTAG_INSIDE = 1;
025    public static final int RSTAG_OUTSIDE = -1;
026
027    private EOWindOp eo=null;
028    private NZWindOp nz=null;
029    public AreaOp2(int type) {
030        //_type=type;
031        switch(type)
032        {
033            case EOWINDOP:
034                eo=new EOWindOp();
035                break;
036            case NZWINDOP:
037                nz=new NZWindOp();
038                break;
039            default:
040                break;
041        }
042    }    
043
044    public Vector calculate(Vector left, Vector right) {
045        Vector edges = new Vector();
046        addEdges(edges, left, CTAG_LEFT);
047        addEdges(edges, right, CTAG_RIGHT);
048        edges = pruneEdges(edges);
049        if (false) {
050            System.out.println("result: ");
051            int numcurves = edges.size();
052            //Curve[] curvelist = (Curve[]) edges.toArray(new Curve[numcurves]);
053            Curve[] curvelist = (Curve[]) edges.toArray2();
054            for (int i = 0; i < numcurves; i++) {
055                System.out.println("curvelist["+i+"] = "+curvelist[i]);
056            }
057        }
058        return edges;
059    }
060
061    private static void addEdges(Vector edges, Vector curves, int curvetag) {
062        Enumeration enum_ = curves.elements();
063        CurveObject c=null;
064        Object obj=null;
065        while (enum_.hasMoreElements()) {
066            obj=enum_.nextElement();
067            if(obj instanceof CurveObject)
068                c=(CurveObject)obj;
069            else
070                c=new CurveObject(obj);
071            if (c.getOrder() > 0) {
072                edges.add(new Edge(c, curvetag));
073            }
074        }
075    }
076
077    private Vector pruneEdges(Vector edges) {
078        EOWindOp eo=new EOWindOp();
079        NZWindOp nz=new NZWindOp();
080        int numedges = edges.size();
081        if (numedges < 2) {
082            return edges;
083        }
084        Edge[] edgelist = new Edge[numedges];
085        Enumeration _enum=edges.elements();
086        int k=0;
087        while(_enum.hasMoreElements())
088        {
089            edgelist[k++]=(Edge)_enum.nextElement();
090        }
091        Arrays.sort(edgelist);        
092        if (false) {
093            System.out.println("pruning: ");
094            for (int i = 0; i < numedges; i++) {
095                System.out.println("edgelist["+i+"] = "+edgelist[i]);
096            }
097        }
098        Edge e;
099        int left = 0;
100        int right = 0;
101        int cur = 0;
102        int next = 0;
103        double yrange[] = new double[2];
104        Vector subcurves = new Vector();
105        Vector chains = new Vector();
106        Vector links = new Vector();
107        // Active edges are between left (inclusive) and right (exclusive)
108        while (left < numedges) {
109            double y = yrange[0];
110            // Prune active edges that fall off the top of the active y range
111            for (cur = next = right - 1; cur >= left; cur--) {
112                e = edgelist[cur];
113                if (e.getCurve().getYBot() > y) {
114                    if (next > cur) {
115                        edgelist[next] = e;
116                    }
117                    next--;
118                }
119            }
120            left = next + 1;
121            // Grab a new "top of Y range" if the active edges are empty
122            if (left >= right) {
123                if (right >= numedges) {
124                    break;
125                }
126                y = edgelist[right].getCurve().getYTop();
127                if (y > yrange[0]) {
128                    finalizeSubCurves(subcurves, chains);
129                }
130                yrange[0] = y;
131            }
132            // Incorporate new active edges that enter the active y range
133            while (right < numedges) {
134                e = edgelist[right];
135                if (e.getCurve().getYTop() > y) {
136                    break;
137                }
138                right++;
139            }
140            // Sort the current active edges by their X values and
141            // determine the maximum valid Y range where the X ordering
142            // is correct
143            yrange[1] = edgelist[left].getCurve().getYBot();
144            if (right < numedges) {
145                y = edgelist[right].getCurve().getYTop();
146                if (yrange[1] > y) {
147                    yrange[1] = y;
148                }
149            }
150            if (false) {
151                System.out.println("current line: y = ["+
152                                   yrange[0]+", "+yrange[1]+"]");
153                for (cur = left; cur < right; cur++) {
154                    System.out.println("  "+edgelist[cur]);
155                }
156            }
157            // Note: We could start at left+1, but we need to make
158            // sure that edgelist[left] has its equivalence set to 0.
159            int nexteq = 1;
160            for (cur = left; cur < right; cur++) {
161                e = edgelist[cur];
162                e.setEquivalence(0);
163                for (next = cur; next > left; next--) {
164                    Edge prevedge = edgelist[next-1];
165                    int ordering = e.compareTo(prevedge, yrange);
166                    if (yrange[1] <= yrange[0]) {
167                        throw new InternalError("backstepping to "+yrange[1]+
168                                                " from "+yrange[0]);
169                    }
170                    if (ordering >= 0) {
171                        if (ordering == 0) {
172                            // If the curves are equal, mark them to be
173                            // deleted later if they cancel each other
174                            // out so that we avoid having extraneous
175                            // curve segments.
176                            int eq = prevedge.getEquivalence();
177                            if (eq == 0) {
178                                eq = nexteq++;
179                                prevedge.setEquivalence(eq);
180                            }
181                            e.setEquivalence(eq);
182                        }
183                        break;
184                    }
185                    edgelist[next] = prevedge;
186                }
187                edgelist[next] = e;                
188            }
189            if (false) {
190                System.out.println("current sorted line: y = ["+
191                                   yrange[0]+", "+yrange[1]+"]");
192                for (cur = left; cur < right; cur++) {
193                    System.out.println("  "+edgelist[cur]);
194                }
195            }
196            // Now prune the active edge list.
197            // For each edge in the list, determine its classification
198            // (entering shape, exiting shape, ignore - no change) and
199            // record the current Y range and its classification in the
200            // Edge object for use later in constructing the new outline.
201            newRow();
202            double ystart = yrange[0];
203            double yend = yrange[1];
204            for (cur = left; cur < right; cur++) {
205                e = edgelist[cur];
206                int etag;
207                int eq = e.getEquivalence();
208                if (eq != 0) {
209                    // Find one of the segments in the "equal" range
210                    // with the right transition state and prefer an
211                    // edge that was either active up until ystart
212                    // or the edge that extends the furthest downward
213                    // (i.e. has the most potential for continuation)
214                    int origstate = getState();
215                    etag = (origstate == RSTAG_INSIDE
216                            ? ETAG_EXIT
217                            : ETAG_ENTER);
218                    Edge activematch = null;
219                    Edge longestmatch = e;
220                    double furthesty = yend;
221                    do {
222                        // Note: classify() must be called
223                        // on every edge we consume here.
224                        classify(e);
225                        if (activematch == null &&
226                            e.isActiveFor(ystart, etag))
227                        {
228                            activematch = e;
229                        }
230                        y = e.getCurve().getYBot();
231                        if (y > furthesty) {
232                            longestmatch = e;
233                            furthesty = y;
234                        }
235                    } while (++cur < right &&
236                             (e = edgelist[cur]).getEquivalence() == eq);
237                    --cur;
238                    if (getState() == origstate) 
239                    {
240                        etag = ETAG_IGNORE;
241                    } 
242                    else 
243                    {
244                        e = (activematch != null ? activematch : longestmatch);
245                    }
246                } else 
247                {
248                    etag = classify(e);
249                }
250                if (etag != ETAG_IGNORE) {
251                    e.record(yend, etag);
252                    links.add(new CurveLink(e.getCurve(), ystart, yend, etag));
253                }
254            }
255            // assert(getState() == AreaOp.RSTAG_OUTSIDE);
256            if (getState() != RSTAG_OUTSIDE) {
257                System.out.println("Still inside at end of active edge list!");
258                System.out.println("num curves = "+(right-left));
259                System.out.println("num links = "+links.size());
260                System.out.println("y top = "+yrange[0]);
261                if (right < numedges) {
262                    System.out.println("y top of next curve = "+
263                                       edgelist[right].getCurve().getYTop());
264                } else {
265                    System.out.println("no more curves");
266                }
267                for (cur = left; cur < right; cur++) {
268                    e = edgelist[cur];
269                    System.out.println(e);
270                    int eq = e.getEquivalence();
271                    if (eq != 0) {
272                        System.out.println("  was equal to "+eq+"...");
273                    }
274                }
275            }
276            if (false) {
277                System.out.println("new links:");
278                for (int i = 0; i < links.size(); i++) {
279                    CurveLink link = (CurveLink) links.elementAt(i);
280                    System.out.println("  "+link.getSubCurve());
281                }
282            }
283            resolveLinks(subcurves, chains, links);
284            links.clear();
285            // Finally capture the bottom of the valid Y range as the top
286            // of the next Y range.
287            yrange[0] = yend;
288        }
289        finalizeSubCurves(subcurves, chains);
290        Vector ret = new Vector();
291        Enumeration enum_ = subcurves.elements();
292        CurveObject c=null;
293        Object obj=null;
294        while (enum_.hasMoreElements()) {
295            CurveLink link = (CurveLink) enum_.nextElement();
296            ret.add(link.getMoveto());
297            CurveLink nextlink = link;
298            while ((nextlink = nextlink.getNext()) != null) {
299                if (!link.absorb(nextlink)) 
300                {
301                    //ret.add(link.getSubCurve());
302                    obj=link.getSubCurve();
303                    if(obj instanceof Order0)
304                        c=((Order0)obj).getParent();
305                    else if(obj instanceof Order1)
306                        c=((Order1)obj).getParent();
307                    else if(obj instanceof Order2)
308                        c=((Order2)obj).getParent();
309                    else if(obj instanceof Order3)
310                        c=((Order3)obj).getParent();
311                    else if(obj instanceof CurveObject)
312                        c=(CurveObject)obj;
313                    if(c==null)                    
314                        c=new CurveObject(obj);
315                    
316                    
317                    ret.add(c);
318                    link = nextlink;
319                }
320            }
321            obj=link.getSubCurve();
322            //ret.add(link.getSubCurve());
323            if(obj instanceof Order0)
324                c=((Order0)obj).getParent();
325            else if(obj instanceof Order1)
326                c=((Order1)obj).getParent();
327            else if(obj instanceof Order2)
328                c=((Order2)obj).getParent();
329            else if(obj instanceof Order3)
330                c=((Order3)obj).getParent();
331            else if(obj instanceof CurveObject)
332                c=(CurveObject)obj;
333            if(c==null)                    
334                c=new CurveObject(obj);
335            
336            ret.add(c);
337        }
338        return ret;
339    }
340
341    public static void finalizeSubCurves(Vector subcurves, Vector chains) {
342        int numchains = chains.size();
343        if (numchains == 0) {
344            return;
345        }
346        if ((numchains & 1) != 0) {
347            throw new InternalError("Odd number of chains!");
348        }
349        ChainEnd[] endlist = new ChainEnd[numchains];
350        chains.toArray(endlist);
351        for (int i = 1; i < numchains; i += 2) {
352            ChainEnd open = endlist[i - 1];
353            ChainEnd close = endlist[i];
354            CurveLink subcurve = open.linkTo(close);
355            if (subcurve != null) {
356                subcurves.add(subcurve);
357            }
358        }
359        chains.clear();
360    }
361
362    private static CurveLink[] EmptyLinkList = new CurveLink[2];
363    private static ChainEnd[] EmptyChainList = new ChainEnd[2];
364
365    public static void resolveLinks(Vector subcurves,
366                                    Vector chains,
367                                    Vector links)
368    {
369        int numlinks = links.size();
370        CurveLink[] linklist;
371        if (numlinks == 0) {
372            linklist = EmptyLinkList;
373        } else {
374            if ((numlinks & 1) != 0) {
375                throw new InternalError("Odd number of new curves!");
376            }
377            linklist = new CurveLink[numlinks+2];
378            links.toArray(linklist);
379        }
380        int numchains = chains.size();
381        ChainEnd[] endlist;
382        if (numchains == 0) {
383            endlist = EmptyChainList;
384        } else {
385            if ((numchains & 1) != 0) {
386                throw new InternalError("Odd number of chains!");
387            }
388            endlist = new ChainEnd[numchains+2];
389            chains.toArray(endlist);
390        }
391        int curchain = 0;
392        int curlink = 0;
393        chains.clear();
394        ChainEnd chain = endlist[0];
395        ChainEnd nextchain = endlist[1];
396        CurveLink link = linklist[0];
397        CurveLink nextlink = linklist[1];
398        while (chain != null || link != null) {
399            /*
400             * Strategy 1:
401             * Connect chains or links if they are the only things left...
402             */
403            boolean connectchains = (link == null);
404            boolean connectlinks = (chain == null);
405
406            if (!connectchains && !connectlinks) {
407                // assert(link != null && chain != null);
408                /*
409                 * Strategy 2:
410                 * Connect chains or links if they close off an open area...
411                 */
412                connectchains = ((curchain & 1) == 0 &&
413                                 chain.getX() == nextchain.getX());
414                connectlinks = ((curlink & 1) == 0 &&
415                                link.getX() == nextlink.getX());
416
417                if (!connectchains && !connectlinks) {
418                    /*
419                     * Strategy 3:
420                     * Connect chains or links if their successor is
421                     * between them and their potential connectee...
422                     */
423                    double cx = chain.getX();
424                    double lx = link.getX();
425                    connectchains =
426                        (nextchain != null && cx < lx &&
427                         obstructs(nextchain.getX(), lx, curchain));
428                    connectlinks =
429                        (nextlink != null && lx < cx &&
430                         obstructs(nextlink.getX(), cx, curlink));
431                }
432            }
433            if (connectchains) {
434                CurveLink subcurve = chain.linkTo(nextchain);
435                if (subcurve != null) {
436                    subcurves.add(subcurve);
437                }
438                curchain += 2;
439                chain = endlist[curchain];
440                nextchain = endlist[curchain+1];
441            }
442            if (connectlinks) {
443                ChainEnd openend = new ChainEnd(link, null);
444                ChainEnd closeend = new ChainEnd(nextlink, openend);
445                openend.setOtherEnd(closeend);
446                chains.add(openend);
447                chains.add(closeend);
448                curlink += 2;
449                link = linklist[curlink];
450                nextlink = linklist[curlink+1];
451            }
452            if (!connectchains && !connectlinks) {
453                // assert(link != null);
454                // assert(chain != null);
455                // assert(chain.getEtag() == link.getEtag());
456                chain.addLink(link);
457                chains.add(chain);
458                curchain++;
459                chain = nextchain;
460                nextchain = endlist[curchain+1];
461                curlink++;
462                link = nextlink;
463                nextlink = linklist[curlink+1];
464            }
465        }
466        if ((chains.size() & 1) != 0) {
467            System.out.println("Odd number of chains!");
468        }
469    }
470
471    /*
472     * Does the position of the next edge at v1 "obstruct" the
473     * connectivity between current edge and the potential
474     * partner edge which is positioned at v2?
475     *
476     * Phase tells us whether we are testing for a transition
477     * into or out of the interior part of the resulting area.
478     *
479     * Require 4-connected continuity if this edge and the partner
480     * edge are both "entering into" type edges
481     * Allow 8-connected continuity for "exiting from" type edges
482     */
483    public static boolean obstructs(double v1, double v2, int phase) {
484        return (((phase & 1) == 0) ? (v1 <= v2) : (v1 < v2));
485    }
486    private void newRow()
487    {
488        if(eo!=null)
489            eo.newRow();
490        else if(nz!=null)
491            nz.newRow();
492    }
493    private int getState()
494    {
495        if(eo!=null)
496            return eo.getState();
497        else if(nz!=null)
498            return nz.getState();
499        else return -1;        
500    }
501    private int classify(Edge e)
502    {
503        if(eo!=null)
504            return eo.classify(e);
505        else if(nz!=null)
506            return nz.classify(e);
507        else return -1;
508    }
509}