View Javadoc

1   package org.catacomb.graph.drawing;
2   
3   
4   import java.awt.Color;
5   
6   import org.catacomb.be.DeReferencable;
7   import org.catacomb.be.Position;
8   import org.catacomb.be.ReReferencable;
9   import org.catacomb.datalish.SColor;
10  import org.catacomb.graph.gui.Geom;
11  import org.catacomb.graph.gui.PickableRegion;
12  import org.catacomb.interlish.content.*;
13  import org.catacomb.interlish.structure.*;
14  import org.catacomb.report.E;
15  import org.catacomb.util.ArrayUtil;
16  
17  
18  public class Shape extends Polypoint
19      implements ReReferencable, DeReferencable, TablePeer {
20  
21  
22      public double curviness; // 0 rectangle, 1 circle, in between for rounded
23      // corners;
24  
25  
26      public double lineWidth;
27  
28      public SColor lineColor;
29      public SColor fillColor;
30  
31      private Color p_lineColor;
32      private Color p_fillColor;
33  
34  
35  
36  
37  
38      public String symmetry;
39      private int p_symmetry; // values in ShapeSymmetry
40  
41  
42      // strategies for resolving point motions;
43      public final static int MOVE_POINT = 0;
44      public final static int ROTATE_SHAPE = 1;
45      public final static int SHIFT_SHAPE = 2;
46  
47  
48  
49      private ShapePoint[] p_points;
50      private ShapePoint[] p_protos;
51      private boolean p_protosUTD;
52  
53  
54      private Position p_position;
55      private Position p_cachePosition;
56      private Position p_pressPosition;
57  
58  
59      private PickableRegion p_pickableRegion;
60  
61      private boolean updatePickable;
62  
63  
64      private IntPosition p_intPosition;
65      private boolean intIsDefinitive;
66  
67  
68      private Assembly r_parent;
69  
70      private boolean pointLeadsArrays;
71      private boolean p_rotating;
72  
73      private int pointColor = 0x00ff00;
74      private int protoPointColor = 0xff00ff;
75  
76  
77  
78      private int p_index;
79  
80      private Table p_table;
81  
82  
83      public Shape() {
84          p_position = new Position();
85          p_cachePosition = new Position();
86          p_lineColor = Color.blue;
87          p_fillColor = Color.yellow;
88          lineWidth = 1.0;
89          curviness = 0.;
90          setClosure(FILLED);
91      }
92  
93  
94      public Shape(Shape s) {
95          this();
96          curviness = s.curviness;
97          p_symmetry = s.p_symmetry;
98          setClosure(s.getClosure());
99          lineWidth = s.lineWidth;
100         p_lineColor = s.p_lineColor;
101         p_fillColor = s.p_fillColor;
102 
103         xpts = copyArray(s.xpts);
104         ypts = copyArray(s.ypts);
105 
106         makePoints();
107 
108         syncArrays();
109 
110         initPickable();
111 
112     }
113 
114 
115 
116     public void setLineWidth(double d) {
117         lineWidth = d;
118     }
119 
120     public void setIndex(int ind) {
121         p_index = ind;
122     }
123 
124     public int getIndex() {
125         return p_index;
126     }
127 
128 
129     public void select() {
130         cachePositions();
131     }
132 
133 
134     public ShapePoint[] getPoints() {
135         return p_points;
136     }
137 
138 
139     public ShapePoint[] getProtoPoints() {
140         if (p_protos == null || !p_protosUTD) {
141             makeProtos();
142         }
143         return p_protos;
144     }
145 
146     public String getStringClosure() {
147         return p_closureNames[getClosure()];
148     }
149 
150     public void setParent(Assembly a) {
151         r_parent = a;
152     }
153 
154 
155     public Assembly getParent() {
156         return r_parent;
157     }
158 
159 
160 
161     public void setCurviness(double d) {
162         curviness = d;
163     }
164 
165     public double getCurviness() {
166         return curviness;
167     }
168 
169 
170 
171     public void setClosure(String s) {
172         setClosure(OPEN);
173         int iia = ArrayUtil.getIndexInArray(s, p_closureNames);
174         if (iia >= 0) {
175             setClosure(iia);
176         }
177     }
178 
179 
180 
181     private double[] copyArray(double[] da) {
182         double[] ret = new double[da.length];
183         System.arraycopy(da, 0, ret, 0, da.length);
184         return ret;
185     }
186 
187 
188     public Shape makeCopy() {
189         syncArrays();
190         return new Shape(this);
191     }
192 
193 
194     public void setSymmetryString(String s) {
195         p_symmetry = ShapeSymmetry.NONE;
196         int iia = ArrayUtil.getIndexInArray(s, ShapeSymmetry.getSymmetryNames());
197         if (iia >= 0) {
198             p_symmetry = iia;
199         }
200     }
201 
202 
203     public void reReference() {
204         setClosure(closure);
205 
206         setSymmetryString(symmetry);
207 
208         makePoints();
209 
210         syncArrays();
211 
212         if (lineColor != null) {
213             p_lineColor = lineColor.getColor();
214         }
215         if (fillColor != null) {
216             p_fillColor = fillColor.getColor();
217         }
218     }
219 
220 
221 
222     public void deReference() {
223         closure = p_closureNames[getClosure()];
224         String[] symnms = ShapeSymmetry.getSymmetryNames();
225         symmetry = symnms[p_symmetry];
226     }
227 
228 
229 
230     public double getSmoothness() {
231         return curviness;
232     }
233 
234 
235     public Color getLineColor() {
236         return p_lineColor;
237     }
238 
239 
240     public Color getFillColor() {
241         return p_fillColor;
242     }
243 
244 
245     public double getLineWidth() {
246         return lineWidth;
247     }
248 
249 
250     public boolean isExtensible() {
251         return (p_symmetry == ShapeSymmetry.NONE);
252     }
253 
254 
255     public int getSymmetry() {
256         return p_symmetry;
257     }
258 
259 
260 
261     private void cachePositions() {
262         p_cachePosition.set(p_position);
263 
264         for (int i = 0; i < p_points.length; i++) {
265             p_points[i].cachePosition();
266         }
267 
268     }
269 
270 
271 
272     public void setPosition(Position p) {
273         updatePickable = true;
274 
275         Position prel = new Position(p);
276         prel.subtract(p_cachePosition);
277 
278         for (int i = 0; i < p_points.length; i++) {
279             p_points[i].shiftFromCache(prel);
280         }
281 
282         flagPointMoved();
283         // syncArrays();
284         intIsDefinitive = false;
285     }
286 
287 
288 
289     public void initPickable() {
290         p_pickableRegion = new PickableRegion(getXPts(), getYPts(), this);
291         if (!isClosed()) {
292             p_pickableRegion.setPoints(Geom.makeLineBoundary(getXPts(), getYPts()));
293         }
294         p_pickableRegion.setReferencePoint(p_position);
295         updatePickable = false;
296     }
297 
298 
299 
300     public PickableRegion getBoundaryRegion() {
301         if (p_pickableRegion == null) {
302             initPickable();
303 
304         } else if (updatePickable) {
305             if (isClosed()) {
306                 p_pickableRegion.setPoints(getXPts(), getYPts());
307                 p_pickableRegion.setReferencePoint(p_position);
308             } else {
309                 p_pickableRegion.setPoints(Geom.makeLineBoundary(getXPts(), getYPts()));
310                 p_pickableRegion.setReferencePoint(p_position);
311             }
312 
313             updatePickable = false;
314         }
315         return p_pickableRegion;
316     }
317 
318 
319     public void makePoints() {
320         int np = xpts.length;
321         p_points = new ShapePoint[np];
322         for (int i = 0; i < np; i++) {
323             p_points[i] = new ShapePoint(this, new Position(xpts[i], ypts[i]), pointColor);
324         }
325     }
326 
327 
328 
329     private void makeProtos() {
330         int np = p_points.length;
331         int nproto = np;
332         if (isOpen()) {
333             nproto -= 1;
334         }
335 
336         p_protos = new ShapePoint[nproto];
337 
338         for (int i = 0; i < nproto; i++) {
339             Position pa = p_points[i].getPosition();
340             Position pb = p_points[(i + 1) % np].getPosition();
341 
342             Position midpos = Position.aXPlusBY(0.5, pa, 0.5, pb);
343             ShapePoint sp = new ShapePoint(this, midpos, protoPointColor);
344             p_protos[i] = sp;
345             sp.setIndex(i);
346             sp.setType("proto");
347         }
348         p_protosUTD = true;
349     }
350 
351 
352     public Position getPosition() {
353         return p_position;
354     }
355 
356 
357     public void setIntPosition(IntPosition intp) {
358         if (p_intPosition == null) {
359             p_intPosition = new IntPosition();
360         }
361         p_intPosition.set(intp);
362         intIsDefinitive = true;
363     }
364 
365 
366     public boolean hasIntPosition() {
367         return intIsDefinitive;
368     }
369 
370 
371     public IntPosition getIntPosition() {
372         return p_intPosition;
373     }
374 
375 
376 
377     public double[] getXPts() {
378         if (pointLeadsArrays) {
379             syncArrays();
380         }
381         return xpts;
382     }
383 
384 
385     public double[] getYPts() {
386         if (pointLeadsArrays) {
387             syncArrays();
388         }
389         return ypts;
390     }
391 
392 
393     public void flagPointMoved() {
394         pointLeadsArrays = true;
395         p_protosUTD = false;
396     }
397 
398 
399     public void syncArrays() {
400         int np = p_points.length;
401         if (xpts == null || xpts.length != np) {
402             xpts = new double[np];
403             ypts = new double[np];
404         }
405 
406         for (int i = 0; i < np; i++) {
407             Position pos = p_points[i].getPosition();
408             xpts[i] = pos.getX();
409             ypts[i] = pos.getY();
410         }
411 
412         p_position.set(cog(xpts), cog(ypts));
413 
414         p_protosUTD = false;
415         updatePickable = true;
416         pointLeadsArrays = false;
417     }
418 
419 
420 
421     public void addPoint(int ipr, double x, double y) {
422         addPoint(ipr, new ShapePoint(this, new Position(x, y), pointColor));
423     }
424 
425 
426     public void addPoint(int ipr, ShapePoint sp) {
427         sp.setType("normal");
428         sp.setColor(pointColor);
429 
430         int np = p_points.length;
431         ShapePoint[] ap = new ShapePoint[np + 1];
432 
433         for (int i = 0; i <= ipr; i++) {
434             ap[i] = p_points[i];
435         }
436 
437         ap[ipr + 1] = sp;
438 
439         for (int i = ipr + 1; i < np; i++) {
440             ap[i + 1] = p_points[i];
441         }
442 
443         p_points = ap;
444 
445         p_protosUTD = false;
446         syncArrays();
447     }
448 
449 
450     public boolean deletePoint(ShapePoint sp) {
451         boolean ret = false;
452         int itg = -1;
453         for (int i = 0; i < p_points.length; i++) {
454             if (sp == p_points[i]) {
455                 itg = i;
456                 break;
457             }
458         }
459         if (itg >= 0) {
460             ret = deletePoint(itg);
461         } else {
462             E.error("cant find point");
463         }
464         return ret;
465     }
466 
467 
468     public boolean deletePoint(int itg) {
469         boolean bdone = false;
470         if (isRectangular()) {
471             // cant delete;
472         } else if (p_points.length <= 2) {
473             // cante delete - kill whole shape;
474         } else {
475             int np = p_points.length;
476             ShapePoint[] ap = new ShapePoint[np -1];
477             for (int i = 0; i < itg; i++) {
478                 ap[i] = p_points[i];
479             }
480             for (int i = itg; i < np-1; i++) {
481                 ap[i] = p_points[i+1];
482             }
483             p_points = ap;
484             p_protosUTD = false;
485             syncArrays();
486             bdone = true;
487         }
488         return bdone;
489     }
490 
491 
492     private double cog(double[] ap) {
493         int n = ap.length;
494         double c = 0.;
495         for (int i = 0; i < n; i++) {
496             c += ap[i];
497         }
498         c /= n;
499         return c;
500     }
501 
502 
503     public void regionPressed() {
504         p_rotating = false;
505     }
506 
507     public void pointPressed(ShapePoint sp) {
508         p_rotating = false;
509         cachePositions();
510     }
511 
512 
513     public void movePoint(ShapePoint sp, Position pos,  int action) {
514 
515         if (action == SHIFT_SHAPE) {
516             // shift(xnew - pcache.getX(), ynew - pcache.getY());
517 
518 
519         } else if (action == ROTATE_SHAPE) {
520             rotate(sp, pos);
521 
522 
523         } else if (action == MOVE_POINT) {
524             ShapeSymmetry.applySymmetry(this, sp, pos);
525         }
526 
527         flagPointMoved();
528     }
529 
530 
531     public void rotate(Position pos) {
532         if (p_rotating) {
533             rotateFromTo(p_pressPosition, pos);
534         } else {
535             p_pressPosition = new Position(pos);
536             p_rotating = true;
537         }
538     }
539 
540     private void rotate(ShapePoint sp, Position pos) {
541         rotateFromTo(sp.getCachedPosition(), pos);
542     }
543 
544     private void rotateFromTo(Position pc, Position pos) {
545         double pxc = p_cachePosition.getX();
546         double pyc = p_cachePosition.getY();
547 
548         double xa = pc.getX() - pxc;
549         double ya = pc.getY() - pyc;
550         double xb = pos.getX() - pxc;
551         double yb = pos.getY() - pyc;
552 
553         double a2 = xa*xa + ya*ya;
554         double b2 = xb*xb + yb*yb;
555 
556         double mp = Math.sqrt(a2 * b2);
557         double cos = (xa*xb + ya*yb) / mp;
558         double sin = (xa*yb - xb*ya) / mp;
559 
560 
561         for (int i = 0; i < p_points.length; i++) {
562             Position pca = p_points[i].getCachedPosition();
563             double px = pca.getX() - pxc;
564             double py = pca.getY() - pyc;
565             double pxn =  pxc +  cos * px - sin * py;
566             double pyn = pyc + sin * px + cos * py;
567             p_points[i].setPosition(new Position(pxn, pyn));
568         }
569 
570     }
571 
572 
573     public void setLineColor(Color col) {
574         p_lineColor = col;
575     }
576 
577 
578     public void setFillColor(Color col) {
579         p_fillColor = col;
580     }
581 
582 
583     public boolean isRectangular() {
584 
585         return (p_symmetry == ShapeSymmetry.RECTANGLE ||
586                 p_symmetry == ShapeSymmetry.SQUARE);
587     }
588 
589     public boolean overlaps(Shape shape) {
590         // TODO - should work it out!!!
591         return true;
592     }
593 
594 
595     public void attachTable(Table tbl) {
596         p_table = tbl;
597     }
598 
599     public void initFromTable(Table tbl) {
600         p_table = tbl;
601     }
602 
603 
604     public Table getTable() {
605         return p_table;
606     }
607 
608     public void removeChild(TablePeer child) {
609         E.error("shapes dont have children");
610     }
611 
612     public void setSymmetry(int isym) {
613         p_symmetry = isym;
614     }
615 
616 
617     public void rescale(double d) {
618         expand(d);
619     }
620 
621 
622     public void expand(double d) {
623         for (int i = 0; i < xpts.length; i++) {
624             xpts[i] *= d;
625             ypts[i] *= d;
626         }
627         makePoints();
628         syncArrays();
629     }
630 
631     public void shiftExpand(double ox, double oy, double d) {
632         for (int i = 0; i < xpts.length; i++) {
633             xpts[i] = ox + d * xpts[i];
634             ypts[i] = oy + d * ypts[i];
635         }
636         makePoints();
637         syncArrays();
638     }
639 
640 
641     public void contract(double d) {
642         expand(1. / d);
643     }
644 
645 
646     public String getStringSymmetry() {
647         return ShapeSymmetry.getStringSymmetry(p_symmetry);
648     }
649 
650 
651     public boolean isQuadrilateral() {
652         return (xpts.length == 4);
653     }
654 
655 
656     public boolean isFullyRounded() {
657         return curviness > 0.99;
658     }
659 
660 
661     public boolean isFullyAngular() {
662         return curviness < 0.01;
663     }
664 
665 
666     public boolean isSymmetric() {
667         return (p_symmetry == ShapeSymmetry.RECTANGLE  ||
668                 p_symmetry == ShapeSymmetry.SQUARE);
669     }
670 
671 
672     public boolean isRectangleSymmetry() {
673         return (p_symmetry == ShapeSymmetry.RECTANGLE);
674 
675     }
676 
677 
678     public boolean isSquareSymmetry() {
679         return (p_symmetry == ShapeSymmetry.SQUARE);
680 
681     }
682 
683 
684     public void notifyObservers() {
685         // TODO Auto-generated method stub
686 
687     }
688 
689 // REFAC - to go in singleton
690     private double[][] curveInterps() {
691         int nintern = 5;  // ADHOC - 5 - smoothness...
692 
693         double[][] cif = new double[2][nintern];
694         for (int i = 0; i < nintern; i++) {
695             double fi = (1. * i) / (nintern);
696 
697             cif[0][i] = Math.sin(fi * Math.PI / 2.);
698             // cif[1][i] = Math.cos(fi * Math.PI / 2.);
699             cif[1][i] = 1. - Math.sin((1. - fi) * Math.PI / 2.);
700         }
701 //  E.dump("cif0", cif[0]);
702 //  E.dump("cif1", cif[1]);
703 
704         return cif;
705     }
706 
707 
708 
709 
710 
711     public double[][] getBoundaryPoints() {
712         double[][] ret = null;
713         ShapePoint[] spts = getPoints();
714         int n = spts.length;
715         if (curviness <= 0.1) {
716             // just return the points themselves
717             ret = new double[2][n];
718             for (int i = 0; i < n; i++) {
719                 ShapePoint sp = spts[i];
720                 Position ap = sp.getAbsolutePosition();
721                 ret[0][i] = ap.getX();
722                 ret[1][i] = ap.getY();
723             }
724         } else {
725             // assumes closed shapes...
726             double[][] cif = curveInterps();
727             int nin = cif[0].length;
728             ret = new double[2][n * nin];
729             for (int ipt = 0; ipt < n; ipt++) {
730                 Position pa = spts[ipt].getAbsolutePosition();
731                 Position pb = spts[(ipt+1)%n].getAbsolutePosition();
732                 Position pc = spts[(ipt+2)%n].getAbsolutePosition();
733 
734                 Position mab = Position.midpoint(pa, pb);
735                 Position mbc = Position.midpoint(pb, pc);
736                 double v1x = pb.getX() - mab.getX();
737                 double v1y = pb.getY() - mab.getY();
738                 double v2x = mbc.getX() - pb.getX();
739                 double v2y = mbc.getY() - pb.getY();
740                 for (int j = 0; j < nin; j++) {
741                     int iin = ipt * nin + j;
742                     ret[0][iin] = mab.getX() + cif[0][j] * v1x + cif[1][j] * v2x;
743                     ret[1][iin] = mab.getY() + cif[0][j] * v1y + cif[1][j] * v2y;
744                 }
745             }
746         }
747         return ret;
748     }
749 
750 
751 
752     public RShape exportRunish(double f) {
753         double[][] xy = getBoundaryPoints();
754         for (int i = 0; i < xy[0].length; i++) {
755             xy[0][i] *= f;
756             xy[1][i] *= f;
757         }
758 
759 
760         int rsc = RShape.CLOSED;
761 
762         if (closure.equals("open")) {
763             rsc = RShape.OPEN;
764         } else if (closure.equals("closed")) {
765             rsc = RShape.CLOSED;
766         } else if (closure.equals("filled")) {
767             rsc = RShape.FILLED;
768         } else {
769             E.error("unrecognized " + closure);
770         }
771         RShape rs = new RShape(xy[0], xy[1], lineWidth, lineColor, fillColor, rsc);
772         return rs;
773     }
774 
775 }