1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.elementclass;
\r
14 import java.awt.BasicStroke;
\r
15 import java.awt.Color;
\r
16 import java.awt.Shape;
\r
17 import java.awt.geom.AffineTransform;
\r
18 import java.awt.geom.Path2D;
\r
19 import java.awt.geom.Rectangle2D;
\r
20 import java.util.Collection;
\r
22 import org.simantics.g2d.diagram.IDiagram;
\r
23 import org.simantics.g2d.diagram.handler.DataElementMap;
\r
24 import org.simantics.g2d.diagram.handler.Topology.Terminal;
\r
25 import org.simantics.g2d.element.ElementClass;
\r
26 import org.simantics.g2d.element.ElementUtils;
\r
27 import org.simantics.g2d.element.IElement;
\r
28 import org.simantics.g2d.element.SceneGraphNodeKey;
\r
29 import org.simantics.g2d.element.handler.InternalSize;
\r
30 import org.simantics.g2d.element.handler.LifeCycle;
\r
31 import org.simantics.g2d.element.handler.Outline;
\r
32 import org.simantics.g2d.element.handler.Rotate;
\r
33 import org.simantics.g2d.element.handler.SceneGraph;
\r
34 import org.simantics.g2d.element.handler.TerminalLayout;
\r
35 import org.simantics.g2d.element.handler.TerminalTopology;
\r
36 import org.simantics.g2d.element.handler.Text;
\r
37 import org.simantics.g2d.element.handler.impl.BorderColorImpl;
\r
38 import org.simantics.g2d.element.handler.impl.DefaultTransform;
\r
39 import org.simantics.g2d.element.handler.impl.FillColorImpl;
\r
40 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
\r
41 import org.simantics.g2d.element.handler.impl.StaticSymbolImpl;
\r
42 import org.simantics.g2d.element.handler.impl.TextImpl;
\r
43 import org.simantics.g2d.image.Image;
\r
44 import org.simantics.g2d.image.impl.ShapeImage;
\r
45 import org.simantics.g2d.utils.Alignment;
\r
46 import org.simantics.g2d.utils.geom.DirectionSet;
\r
47 import org.simantics.scenegraph.Node;
\r
48 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
49 import org.simantics.scenegraph.g2d.nodes.FlagNode;
\r
50 import org.simantics.scenegraph.g2d.nodes.TextNode;
\r
51 import org.simantics.ui.colors.Colors;
\r
52 import org.simantics.ui.fonts.Fonts;
\r
53 import org.simantics.utils.datastructures.hints.IHintContext.Key;
\r
54 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
\r
57 * @author Tuukka Lehtonen
\r
59 public class FlagClass {
\r
61 public static enum Type {
\r
62 /// The input part of a pair of flags.
\r
64 /// The output part of a pair of flags.
\r
66 public Type other() {
\r
67 return this == Out ? In: Out;
\r
71 public static class Mode {
\r
72 public static final Mode External = new External(1);
\r
73 public static final Mode Internal = new Mode() {
\r
74 public String toString() { return "Internal"; }
\r
78 public static class External extends Mode {
\r
79 public final int count;
\r
80 public External(int count) {
\r
84 public String toString() {
\r
85 return "External(" + count + ")";
\r
89 private static final double GLOBAL_SCALE = 0.1;
\r
90 private static final double FLAG_SIZE_SCALE = 3 * GLOBAL_SCALE;
\r
92 public static final double DEFAULT_WIDTH = 70 * FLAG_SIZE_SCALE;
\r
93 public static final double DEFAULT_HEIGHT = 20 * FLAG_SIZE_SCALE;
\r
94 public static final double DEFAULT_BEAK_ANGLE = 60;
\r
96 public static final Key KEY_FLAG_TYPE = new KeyOf(Type.class, "FLAG_TYPE");
\r
97 public static final Key KEY_EXTERNAL = new KeyOf(Boolean.class, "FLAG_EXTERNAL");
\r
98 public static final Key KEY_FLAG_MODE = new KeyOf(Mode.class, "FLAG_MODE");
\r
99 public static final Key KEY_FLAG_WIDTH = new KeyOf(Double.class, "FLAG_WIDTH");
\r
100 public static final Key KEY_FLAG_HEIGHT = new KeyOf(Double.class, "FLAG_HEIGHT");
\r
101 public static final Key KEY_FLAG_BEAK_ANGLE = new KeyOf(Double.class, "FLAG_BEAK_ANGLE");
\r
102 public static final Key KEY_FLAG_TEXT = new KeyOf(String[].class, "FLAG_TEXT");
\r
103 public static final Key KEY_FLAG_TEXT_AREA = new KeyOf(Rectangle2D.class, "FLAG_TEXT_AREA_SIZE");
\r
104 public static final Key KEY_SHAPE = new KeyOf(Shape.class, "SHAPE");
\r
105 public static final Key KEY_TEXT_HORIZONTAL_ALIGN = new KeyOf(Alignment.class, "TEXT_HORIZONTAL_ALIGN");
\r
106 public static final Key KEY_TEXT_VERTICAL_ALIGN = new KeyOf(Alignment.class, "TEXT_VERTICAL_ALIGN");
\r
108 public static final Key KEY_SG_NODE = new SceneGraphNodeKey(Node.class, "FLAG_SG_NODE");
\r
111 * Indicates that this flag is connected to another flag.
\r
113 private static final Key KEY_FLAG_CONNECTION_DATA = new KeyOf(DataConnection.class, "FLAG_CONNECTION_DATA");
\r
114 private static final Key KEY_FLAG_CONNECTION_ELEMENTS = new KeyOf(ElementConnection.class, "FLAG_CONNECTION_ELEMENTS");
\r
116 public interface Connection<T> {
\r
121 private static class Conn<T> implements Connection<T> {
\r
122 private final T first;
\r
123 private final T second;
\r
124 public Conn(T first, T second) {
\r
125 this.first = first;
\r
126 this.second = second;
\r
129 public T getFirst() {
\r
133 public T getSecond() {
\r
137 private static class ElementConnection extends Conn<IElement> {
\r
138 public ElementConnection(IElement first, IElement second) {
\r
139 super(first, second);
\r
141 throw new IllegalArgumentException("first is null");
\r
142 if (second == null)
\r
143 throw new IllegalArgumentException("second is null");
\r
146 private static class DataConnection extends Conn<Object> {
\r
147 public DataConnection(Object first, Object second) {
\r
148 super(first, second);
\r
150 throw new IllegalArgumentException("first is null");
\r
151 // Second may be null to indicate "not-connected"
\r
153 public boolean isConnected() {
\r
154 return getSecond() != null;
\r
158 public static final FlagHandler FLAG_HANDLER = new FlagHandler() {
\r
160 private static final long serialVersionUID = -4258875745321808416L;
\r
163 public Type getType(IElement e) {
\r
164 return FlagClass.getType(e);
\r
168 public void setType(IElement e, Type type) {
\r
169 e.setHint(KEY_FLAG_TYPE, type);
\r
173 public boolean isExternal(IElement e) {
\r
174 return Boolean.TRUE.equals(e.getHint(KEY_EXTERNAL));
\r
178 public void setExternal(IElement e, boolean external) {
\r
179 e.setHint(KEY_EXTERNAL, Boolean.valueOf(external));
\r
183 public Connection<IElement> getConnection(IElement e) {
\r
184 return e.getHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
188 public Connection<Object> getConnectionData(IElement e) {
\r
189 DataConnection dc = e.getHint(KEY_FLAG_CONNECTION_DATA);
\r
190 return (dc != null && dc.isConnected()) ? dc : null;
\r
194 public void connect(IElement e1, IElement e2) {
\r
195 assert e1 != null && e2 != null;
\r
197 ElementConnection ce = new ElementConnection(e1, e2);
\r
198 e1.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);
\r
199 e2.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);
\r
203 public void connectData(IElement e1, Object o1, Object o2) {
\r
204 e1.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
205 e1.setHint(KEY_FLAG_CONNECTION_DATA, new DataConnection(o1, o2));
\r
209 public void disconnect(IElement local) {
\r
210 assert local != null;
\r
211 local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
212 DataConnection c = (DataConnection) local.removeHint(KEY_FLAG_CONNECTION_DATA);
\r
214 IElement remote = otherElement(local, c);
\r
215 if (remote != null) {
\r
216 local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
217 remote.removeHint(KEY_FLAG_CONNECTION_DATA);
\r
223 public boolean isWithinDiagram(IDiagram d, Connection<?> c) {
\r
226 if (c instanceof DataConnection)
\r
227 return bothOnDiagram(d, (DataConnection) c);
\r
228 if (c instanceof ElementConnection)
\r
229 return bothOnDiagram(d, (ElementConnection) c);
\r
234 public IElement getCorrespondence(IElement end) {
\r
235 assert end != null;
\r
236 DataConnection dc = (DataConnection) end.getHint(KEY_FLAG_CONNECTION_DATA);
\r
237 if (dc != null && dc.isConnected())
\r
238 return otherElement(end, dc);
\r
239 ElementConnection ec = (ElementConnection) end.getHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
241 return otherElement(end, ec);
\r
245 boolean bothOnDiagram(IDiagram d, DataConnection c) {
\r
246 if (!c.isConnected())
\r
249 DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
\r
250 IElement eout = dem.getElement(d, c.getFirst());
\r
251 IElement ein = dem.getElement(d, c.getSecond());
\r
252 return eout != null && ein != null;
\r
255 boolean bothOnDiagram(IDiagram d, ElementConnection c) {
\r
256 DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
\r
257 Object o1 = dem.getData(d, c.getFirst());
\r
258 Object o2 = dem.getData(d, c.getSecond());
\r
259 return o1 != null && o2 != null;
\r
262 public IElement otherElement(IElement e, DataConnection c) {
\r
263 if (!c.isConnected())
\r
266 IDiagram d = ElementUtils.peekDiagram(e);
\r
270 DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
\r
271 Object o = dem.getData(d, e);
\r
272 if (c.getFirst().equals(o))
\r
273 return dem.getElement(d, c.getSecond());
\r
274 if (c.getSecond().equals(o))
\r
275 return dem.getElement(d, c.getFirst());
\r
276 throw new IllegalArgumentException("specified object '" + o + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");
\r
279 public IElement otherElement(IElement e, ElementConnection c) {
\r
280 IElement a = c.getFirst();
\r
281 IElement b = c.getSecond();
\r
286 throw new IllegalArgumentException("specified element '" + e + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");
\r
290 static final Shape staticShape;
\r
293 Path2D path = new Path2D.Double();
\r
294 staticShape = path;
\r
295 createFlagShape(path, Type.In, Mode.External, DEFAULT_WIDTH, DEFAULT_HEIGHT, getBeakLength(DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE));
\r
298 public static final BasicStroke STROKE = new BasicStroke(0.15f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
\r
299 static final Image DEFAULT_IMAGE = new ShapeImage(staticShape, null, STROKE);
\r
300 static final StaticSymbolImpl DEFAULT_STATIC_SYMBOL = new StaticSymbolImpl(DEFAULT_IMAGE);
\r
301 static final FlagSize DEFAULT_FLAG_SIZE = new FlagSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE);
\r
302 static final Initializer DEFAULT_INITIALIZER = new Initializer(Type.In, Mode.External);
\r
304 public static final ElementClass FLAGCLASS =
\r
305 ElementClass.compile(
\r
306 DEFAULT_INITIALIZER,
\r
308 DefaultTransform.INSTANCE,
\r
310 BorderColorImpl.BLACK,
\r
311 FillColorImpl.WHITE,
\r
313 FlagTerminalTopology.DEFAULT,
\r
314 FlagSceneGraph.INSTANCE,
\r
315 DEFAULT_STATIC_SYMBOL
\r
316 ).setId(FlagClass.class.getSimpleName());
\r
318 public static ElementClass create(Terminal terminal) {
\r
319 return ElementClass.compile(
\r
320 DEFAULT_INITIALIZER,
\r
322 DefaultTransform.INSTANCE,
\r
324 BorderColorImpl.BLACK,
\r
325 FillColorImpl.WHITE,
\r
327 new FlagTerminalTopology(terminal),
\r
328 FlagSceneGraph.INSTANCE,
\r
329 DEFAULT_STATIC_SYMBOL,
\r
330 SimpleElementLayers.INSTANCE
\r
331 ).setId(FlagClass.class.getSimpleName());
\r
334 public static ElementClass create(Terminal terminal, SceneGraph scn) {
\r
335 return ElementClass.compile(
\r
336 DEFAULT_INITIALIZER,
\r
338 DefaultTransform.INSTANCE,
\r
340 BorderColorImpl.BLACK,
\r
341 FillColorImpl.WHITE,
\r
343 new FlagTerminalTopology(terminal),
\r
345 DEFAULT_STATIC_SYMBOL,
\r
346 SimpleElementLayers.INSTANCE
\r
347 ).setId(FlagClass.class.getSimpleName());
\r
350 static class Initializer implements LifeCycle {
\r
351 private static final long serialVersionUID = 4404942036933073584L;
\r
353 private final Type type;
\r
354 private final Mode mode;
\r
356 Initializer(Type type, Mode mode) {
\r
357 assert type != null;
\r
358 assert mode != null;
\r
364 public void onElementActivated(IDiagram d, IElement e) {
\r
368 public void onElementCreated(IElement e) {
\r
369 e.setHint(KEY_FLAG_TYPE, type);
\r
370 e.setHint(KEY_FLAG_MODE, mode);
\r
371 //e.setHint(ElementHints.KEY_COMPOSITE, AlphaComposite.SrcOver.derive(0.5f));
\r
375 public void onElementDeactivated(IDiagram d, IElement e) {
\r
379 public void onElementDestroyed(IElement e) {
\r
383 public int hashCode() {
\r
384 final int prime = 31;
\r
386 result = prime * result + mode.hashCode();
\r
387 result = prime * result + type.hashCode();
\r
392 public boolean equals(Object obj) {
\r
397 if (getClass() != obj.getClass())
\r
399 Initializer other = (Initializer) obj;
\r
400 if (!mode.equals(other.mode))
\r
402 if (!type.equals(other.type))
\r
408 public static Path2D createFlagShape(Path2D path, Type type, Mode mode, double width, double height, double beakLength) {
\r
409 double hh = height / 2;
\r
413 if (mode instanceof External) {
\r
414 path.moveTo(0, hh);
\r
415 path.lineTo(width, hh);
\r
416 path.lineTo(width+beakLength, 0);
\r
417 path.lineTo(width, -hh);
\r
418 path.lineTo(0, -hh);
\r
420 path.moveTo(width, hh);
\r
421 path.lineTo(width, -hh);
\r
422 int count = ((External)mode).count;
\r
424 double shadow=hh*0.25;
\r
425 double ix = beakLength
\r
426 - 0.5*shadow*(1.0 + beakLength/hh);
\r
427 double iy = hh * (ix / beakLength - 1.0);
\r
428 for(int sid=1;sid<=Math.min(count-1, 4);++sid) {
\r
429 double dis = sid*shadow;
\r
430 path.moveTo(dis, hh+dis-shadow);
\r
431 path.lineTo(dis, hh+dis);
\r
432 path.lineTo(dis+width, hh+dis);
\r
433 path.lineTo(dis+width+beakLength, dis);
\r
434 path.lineTo(width + ix + dis, iy + dis);
\r
438 double right = width - 0;
\r
439 if (left < right) {
\r
440 path.moveTo(left, 0);
\r
441 path.lineTo(right, 0);
\r
444 } else if (mode == Mode.Internal) {
\r
445 path.moveTo(0, hh);
\r
446 path.lineTo(beakLength, 0);
\r
447 path.lineTo(0, -hh);
\r
453 if (mode instanceof External) {
\r
454 path.lineTo(-beakLength, -hh);
\r
455 path.lineTo(-width-beakLength, -hh);
\r
456 path.lineTo(-width-beakLength, hh);
\r
457 path.lineTo(-beakLength, hh);
\r
459 path.moveTo(-beakLength, -hh);
\r
460 path.lineTo(-beakLength, hh);
\r
461 int count = ((External)mode).count;
\r
463 double shadow=hh*0.25;
\r
464 double ix = beakLength
\r
465 - 0.5*shadow*(1.0 + beakLength/hh);
\r
466 double iy = hh * (ix / beakLength - 1.0);
\r
467 double xDisp = -width-beakLength;
\r
468 for(int sid=1;sid<=Math.min(count-1, 4);++sid) {
\r
469 double dis = sid*shadow;
\r
470 path.moveTo(xDisp+dis, hh+dis-shadow);
\r
471 path.lineTo(xDisp+dis, hh+dis);
\r
472 path.lineTo(xDisp+dis+width, hh+dis);
\r
473 path.lineTo(xDisp+dis+width+beakLength, dis);
\r
474 path.lineTo(xDisp+width + ix + dis, iy + dis);
\r
477 double left = -width-beakLength+0;
\r
478 double right = -beakLength-0;
\r
479 if (left < right) {
\r
480 path.moveTo(left, 0);
\r
481 path.lineTo(right, 0);
\r
484 } else if (mode == Mode.Internal) {
\r
485 path.lineTo(-beakLength, -hh);
\r
486 path.lineTo(-beakLength, hh);
\r
494 public static Path2D createFlagShape(IElement e) {
\r
495 Type type = getType(e);
\r
496 Mode mode = e.getHint(KEY_FLAG_MODE);
\r
497 double width = e.getHint(KEY_FLAG_WIDTH);
\r
498 double height = e.getHint(KEY_FLAG_HEIGHT);
\r
499 double beakLength = getBeakLength(e);
\r
500 Path2D path = new Path2D.Double();
\r
501 createFlagShape(path, type, mode, width, height, beakLength);
\r
505 static class FlagSize implements InternalSize, Outline, LifeCycle {
\r
507 private static final long serialVersionUID = 829379327756475944L;
\r
509 private final double length;
\r
510 private final double thickness;
\r
511 private final double beakAngle;
\r
513 public FlagSize(double length, double thickness, double beakAngle) {
\r
514 this.length = length;
\r
515 this.thickness = thickness;
\r
516 this.beakAngle = beakAngle;
\r
520 public Shape getElementShape(IElement e) {
\r
521 Shape shape = e.getHint(KEY_SHAPE);
\r
524 return createFlagShape(e);
\r
528 public Rectangle2D getBounds(IElement e, Rectangle2D size) {
\r
530 size = new Rectangle2D.Double();
\r
531 Shape shape = getElementShape(e);
\r
532 size.setFrame(shape.getBounds2D());
\r
537 public void onElementActivated(IDiagram d, IElement e) {
\r
541 public void onElementCreated(IElement e) {
\r
542 e.setHint(KEY_FLAG_WIDTH, length);
\r
543 e.setHint(KEY_FLAG_HEIGHT, thickness);
\r
544 e.setHint(KEY_FLAG_BEAK_ANGLE, beakAngle);
\r
548 public void onElementDeactivated(IDiagram d, IElement e) {
\r
552 public void onElementDestroyed(IElement e) {
\r
556 public int hashCode() {
\r
557 final int prime = 31;
\r
560 temp = Double.doubleToLongBits(beakAngle);
\r
561 result = prime * result + (int) (temp ^ (temp >>> 32));
\r
562 temp = Double.doubleToLongBits(length);
\r
563 result = prime * result + (int) (temp ^ (temp >>> 32));
\r
564 temp = Double.doubleToLongBits(thickness);
\r
565 result = prime * result + (int) (temp ^ (temp >>> 32));
\r
570 public boolean equals(Object obj) {
\r
575 if (getClass() != obj.getClass())
\r
577 FlagSize other = (FlagSize) obj;
\r
578 if (Double.doubleToLongBits(beakAngle) != Double.doubleToLongBits(other.beakAngle))
\r
580 if (Double.doubleToLongBits(length) != Double.doubleToLongBits(other.length))
\r
582 if (Double.doubleToLongBits(thickness) != Double.doubleToLongBits(other.thickness))
\r
588 static class FlagSceneGraph implements SceneGraph {
\r
589 private static final long serialVersionUID = 35208146123929197L;
\r
591 public static final FlagSceneGraph INSTANCE = new FlagSceneGraph();
\r
594 public void cleanup(IElement e) {
\r
595 ElementUtils.removePossibleNode(e, KEY_SG_NODE);
\r
599 public void init(IElement e, G2DParentNode parent) {
\r
600 Color fc = ElementUtils.getFillColor(e, Color.WHITE);
\r
601 Color bc = ElementUtils.getBorderColor(e, Color.BLACK);
\r
602 Color tc = ElementUtils.getTextColor(e, Color.BLACK);
\r
604 Outline outline = e.getElementClass().getSingleItem(Outline.class);
\r
605 Shape shape = outline.getElementShape(e);
\r
606 Type type = getType(e);
\r
607 double dir = getDirection(e);
\r
608 double width = e.getHint(KEY_FLAG_WIDTH);
\r
609 double height = e.getHint(KEY_FLAG_HEIGHT);
\r
610 double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);
\r
612 String[] flagText = e.getHint(KEY_FLAG_TEXT);
\r
613 if (flagText == null) {
\r
614 // fallback option.
\r
615 Text t = e.getElementClass().getAtMostOneItemOfClass(Text.class);
\r
617 String text = t.getText(e);
\r
619 flagText = new String[] { text };
\r
624 //flagText = new String[] { String.format("%3.1f", dir) + " deg", "FOO"};
\r
626 Rectangle2D textArea = e.getHint(KEY_FLAG_TEXT_AREA);
\r
627 if (textArea == null) {
\r
628 double beakLength = getBeakLength(height, beakAngle);
\r
629 textArea = type == Type.In
\r
630 ? new Rectangle2D.Double(-width-beakLength, -height*0.5, width, height)
\r
631 : new Rectangle2D.Double(0, -height*0.5, width, height);
\r
634 Alignment horizAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_HORIZONTAL_ALIGN, Alignment.LEADING);
\r
635 Alignment vertAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_VERTICAL_ALIGN, Alignment.CENTER);
\r
637 FlagNode flag = ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, ElementUtils.generateNodeId(e), FlagNode.class);
\r
649 horizAlign.ordinal(),
\r
650 vertAlign.ordinal());
\r
651 AffineTransform at = ElementUtils.getTransform(e);
\r
652 if(at != null) flag.setTransform(at);
\r
657 static class TerminalPoint implements Terminal {
\r
660 public static class FlagTerminalTopology implements TerminalTopology, TerminalLayout {
\r
661 private static final long serialVersionUID = -4194634598346105458L;
\r
663 public static final Terminal DEFAULT_T0 = new TerminalPoint();
\r
664 public static final FlagTerminalTopology DEFAULT = new FlagTerminalTopology(DEFAULT_T0);
\r
668 public FlagTerminalTopology(Terminal t) {
\r
673 public void getTerminals(IElement e, Collection<Terminal> result) {
\r
678 public AffineTransform getTerminalPosition(IElement node, Terminal t) {
\r
680 return new AffineTransform();
\r
686 public boolean getTerminalDirection(IElement node, Terminal t, DirectionSet directions) {
\r
687 Type type = getType(node);
\r
688 double d = getDirection(node);
\r
691 case In: directions.add(d); break;
\r
692 case Out: directions.add(Math.IEEEremainder(d + 180.0, 360.0)); break;
\r
694 //System.out.println("directions T0: " + Arrays.toString(directions.toArray()));
\r
700 // static final Path2D terminalShape;
\r
704 // Path2D p = new Path2D.Double();
\r
706 // p.lineTo(s, -s);
\r
707 // p.lineTo(-s, -s);
\r
708 // p.lineTo(-s, s);
\r
710 // terminalShape = p;
\r
714 public Shape getTerminalShape(IElement node, Terminal t) {
\r
715 //return terminalShape;
\r
717 // For each terminal, return the whole shape of the element.
\r
718 return ElementUtils.getElementShapeOrBounds(node);
\r
722 public int hashCode() {
\r
723 final int prime = 31;
\r
725 result = prime * result + ((T0 == null) ? 0 : T0.hashCode());
\r
730 public boolean equals(Object obj) {
\r
735 if (getClass() != obj.getClass())
\r
737 FlagTerminalTopology other = (FlagTerminalTopology) obj;
\r
739 if (other.T0 != null)
\r
741 } else if (!T0.equals(other.T0))
\r
747 public static AffineTransform getTransform(IElement e) {
\r
748 AffineTransform at = ElementUtils.getTransform(e);
\r
750 return new AffineTransform();
\r
754 public static double getDirection(IElement e) {
\r
755 Rotate rotate = e.getElementClass().getAtMostOneItemOfClass(Rotate.class);
\r
756 if (rotate != null) {
\r
757 return rotate.getAngle(e);
\r
762 public static Type getType(IElement e) {
\r
763 Type t = e.getHint(KEY_FLAG_TYPE);
\r
764 return t != null ? t : Type.In;
\r
767 public static Mode getMode(IElement e) {
\r
768 Mode m = e.getHint(KEY_FLAG_MODE);
\r
769 return m != null ? m : Mode.External;
\r
772 public static double getBeakLength(IElement e) {
\r
773 double height = e.getHint(KEY_FLAG_HEIGHT);
\r
774 double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);
\r
775 beakAngle = Math.min(180, Math.max(10, beakAngle));
\r
776 return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
\r
779 public static double getBeakLength(double height, double beakAngle) {
\r
780 beakAngle = Math.min(180, Math.max(10, beakAngle));
\r
781 return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
\r