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;
23
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;
40
41
42
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
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
472 } else if (p_points.length <= 2) {
473
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
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
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
686
687 }
688
689
690 private double[][] curveInterps() {
691 int nintern = 5;
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
699 cif[1][i] = 1. - Math.sin((1. - fi) * Math.PI / 2.);
700 }
701
702
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
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
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 }