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.utils.datastructures.hints.IHintContext.Key;
\r
51 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
\r
54 * @author Tuukka Lehtonen
\r
56 public class FlagClass {
\r
58 public static enum Type {
\r
59 /// The input part of a pair of flags.
\r
61 /// The output part of a pair of flags.
\r
63 public Type other() {
\r
64 return this == Out ? In: Out;
\r
68 public static class Mode {
\r
69 public static final Mode External = new External(1);
\r
70 public static final Mode Internal = new Mode() {
\r
71 public String toString() { return "Internal"; }
\r
75 public static class External extends Mode {
\r
76 public final int count;
\r
77 public External(int count) {
\r
81 public String toString() {
\r
82 return "External(" + count + ")";
\r
86 private static final double GLOBAL_SCALE = 0.1;
\r
87 private static final double FLAG_SIZE_SCALE = 3 * GLOBAL_SCALE;
\r
89 public static final double DEFAULT_WIDTH = 70 * FLAG_SIZE_SCALE;
\r
90 public static final double DEFAULT_HEIGHT = 20 * FLAG_SIZE_SCALE;
\r
91 public static final double DEFAULT_BEAK_ANGLE = 60;
\r
93 public static final Key KEY_FLAG_TYPE = new KeyOf(Type.class, "FLAG_TYPE");
\r
94 public static final Key KEY_EXTERNAL = new KeyOf(Boolean.class, "FLAG_EXTERNAL");
\r
95 public static final Key KEY_FLAG_MODE = new KeyOf(Mode.class, "FLAG_MODE");
\r
96 public static final Key KEY_FLAG_WIDTH = new KeyOf(Double.class, "FLAG_WIDTH");
\r
97 public static final Key KEY_FLAG_HEIGHT = new KeyOf(Double.class, "FLAG_HEIGHT");
\r
98 public static final Key KEY_FLAG_BEAK_ANGLE = new KeyOf(Double.class, "FLAG_BEAK_ANGLE");
\r
99 public static final Key KEY_FLAG_TEXT = new KeyOf(String[].class, "FLAG_TEXT");
\r
100 public static final Key KEY_FLAG_TEXT_AREA = new KeyOf(Rectangle2D.class, "FLAG_TEXT_AREA_SIZE");
\r
101 public static final Key KEY_SHAPE = new KeyOf(Shape.class, "SHAPE");
\r
102 public static final Key KEY_TEXT_HORIZONTAL_ALIGN = new KeyOf(Alignment.class, "TEXT_HORIZONTAL_ALIGN");
\r
103 public static final Key KEY_TEXT_VERTICAL_ALIGN = new KeyOf(Alignment.class, "TEXT_VERTICAL_ALIGN");
\r
105 public static final Key KEY_SG_NODE = new SceneGraphNodeKey(Node.class, "FLAG_SG_NODE");
\r
108 * Indicates that this flag is connected to another flag.
\r
110 private static final Key KEY_FLAG_CONNECTION_DATA = new KeyOf(DataConnection.class, "FLAG_CONNECTION_DATA");
\r
111 private static final Key KEY_FLAG_CONNECTION_ELEMENTS = new KeyOf(ElementConnection.class, "FLAG_CONNECTION_ELEMENTS");
\r
113 public interface Connection<T> {
\r
118 private static class Conn<T> implements Connection<T> {
\r
119 private final T first;
\r
120 private final T second;
\r
121 public Conn(T first, T second) {
\r
122 this.first = first;
\r
123 this.second = second;
\r
126 public T getFirst() {
\r
130 public T getSecond() {
\r
134 private static class ElementConnection extends Conn<IElement> {
\r
135 public ElementConnection(IElement first, IElement second) {
\r
136 super(first, second);
\r
138 throw new IllegalArgumentException("first is null");
\r
139 if (second == null)
\r
140 throw new IllegalArgumentException("second is null");
\r
143 private static class DataConnection extends Conn<Object> {
\r
144 public DataConnection(Object first, Object second) {
\r
145 super(first, second);
\r
147 throw new IllegalArgumentException("first is null");
\r
148 // Second may be null to indicate "not-connected"
\r
150 public boolean isConnected() {
\r
151 return getSecond() != null;
\r
155 public static final FlagHandler FLAG_HANDLER = new FlagHandler() {
\r
157 private static final long serialVersionUID = -4258875745321808416L;
\r
160 public Type getType(IElement e) {
\r
161 return FlagClass.getType(e);
\r
165 public void setType(IElement e, Type type) {
\r
166 e.setHint(KEY_FLAG_TYPE, type);
\r
170 public boolean isExternal(IElement e) {
\r
171 return Boolean.TRUE.equals(e.getHint(KEY_EXTERNAL));
\r
175 public void setExternal(IElement e, boolean external) {
\r
176 e.setHint(KEY_EXTERNAL, Boolean.valueOf(external));
\r
180 public Connection<IElement> getConnection(IElement e) {
\r
181 return e.getHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
185 public Connection<Object> getConnectionData(IElement e) {
\r
186 DataConnection dc = e.getHint(KEY_FLAG_CONNECTION_DATA);
\r
187 return (dc != null && dc.isConnected()) ? dc : null;
\r
191 public void connect(IElement e1, IElement e2) {
\r
192 assert e1 != null && e2 != null;
\r
194 ElementConnection ce = new ElementConnection(e1, e2);
\r
195 e1.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);
\r
196 e2.setHint(KEY_FLAG_CONNECTION_ELEMENTS, ce);
\r
200 public void connectData(IElement e1, Object o1, Object o2) {
\r
201 e1.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
202 e1.setHint(KEY_FLAG_CONNECTION_DATA, new DataConnection(o1, o2));
\r
206 public void disconnect(IElement local) {
\r
207 assert local != null;
\r
208 local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
209 DataConnection c = (DataConnection) local.removeHint(KEY_FLAG_CONNECTION_DATA);
\r
211 IElement remote = otherElement(local, c);
\r
212 if (remote != null) {
\r
213 local.removeHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
214 remote.removeHint(KEY_FLAG_CONNECTION_DATA);
\r
220 public boolean isWithinDiagram(IDiagram d, Connection<?> c) {
\r
223 if (c instanceof DataConnection)
\r
224 return bothOnDiagram(d, (DataConnection) c);
\r
225 if (c instanceof ElementConnection)
\r
226 return bothOnDiagram(d, (ElementConnection) c);
\r
231 public IElement getCorrespondence(IElement end) {
\r
232 assert end != null;
\r
233 DataConnection dc = (DataConnection) end.getHint(KEY_FLAG_CONNECTION_DATA);
\r
234 if (dc != null && dc.isConnected())
\r
235 return otherElement(end, dc);
\r
236 ElementConnection ec = (ElementConnection) end.getHint(KEY_FLAG_CONNECTION_ELEMENTS);
\r
238 return otherElement(end, ec);
\r
242 boolean bothOnDiagram(IDiagram d, DataConnection c) {
\r
243 if (!c.isConnected())
\r
246 DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
\r
247 IElement eout = dem.getElement(d, c.getFirst());
\r
248 IElement ein = dem.getElement(d, c.getSecond());
\r
249 return eout != null && ein != null;
\r
252 boolean bothOnDiagram(IDiagram d, ElementConnection c) {
\r
253 DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
\r
254 Object o1 = dem.getData(d, c.getFirst());
\r
255 Object o2 = dem.getData(d, c.getSecond());
\r
256 return o1 != null && o2 != null;
\r
259 public IElement otherElement(IElement e, DataConnection c) {
\r
260 if (!c.isConnected())
\r
263 IDiagram d = ElementUtils.peekDiagram(e);
\r
267 DataElementMap dem = d.getDiagramClass().getSingleItem(DataElementMap.class);
\r
268 Object o = dem.getData(d, e);
\r
269 if (c.getFirst().equals(o))
\r
270 return dem.getElement(d, c.getSecond());
\r
271 if (c.getSecond().equals(o))
\r
272 return dem.getElement(d, c.getFirst());
\r
273 throw new IllegalArgumentException("specified object '" + o + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");
\r
276 public IElement otherElement(IElement e, ElementConnection c) {
\r
277 IElement a = c.getFirst();
\r
278 IElement b = c.getSecond();
\r
283 throw new IllegalArgumentException("specified element '" + e + "' is neither of the connected objects: first='" + c.getSecond() + "', second='" + c.getFirst() + "'");
\r
287 static final Shape staticShape;
\r
290 Path2D path = new Path2D.Double();
\r
291 staticShape = path;
\r
292 createFlagShape(path, Type.In, Mode.External, DEFAULT_WIDTH, DEFAULT_HEIGHT, getBeakLength(DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE));
\r
295 public static final BasicStroke STROKE = new BasicStroke(0.15f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
\r
296 static final Image DEFAULT_IMAGE = new ShapeImage(staticShape, null, STROKE);
\r
297 static final StaticSymbolImpl DEFAULT_STATIC_SYMBOL = new StaticSymbolImpl(DEFAULT_IMAGE);
\r
298 static final FlagSize DEFAULT_FLAG_SIZE = new FlagSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_BEAK_ANGLE);
\r
299 static final Initializer DEFAULT_INITIALIZER = new Initializer(Type.In, Mode.External);
\r
301 public static final ElementClass FLAGCLASS =
\r
302 ElementClass.compile(
\r
303 DEFAULT_INITIALIZER,
\r
305 DefaultTransform.INSTANCE,
\r
307 BorderColorImpl.BLACK,
\r
308 FillColorImpl.WHITE,
\r
310 FlagTerminalTopology.DEFAULT,
\r
311 FlagSceneGraph.INSTANCE,
\r
312 DEFAULT_STATIC_SYMBOL
\r
313 ).setId(FlagClass.class.getSimpleName());
\r
315 public static ElementClass create(Terminal terminal) {
\r
316 return ElementClass.compile(
\r
317 DEFAULT_INITIALIZER,
\r
319 DefaultTransform.INSTANCE,
\r
321 BorderColorImpl.BLACK,
\r
322 FillColorImpl.WHITE,
\r
324 new FlagTerminalTopology(terminal),
\r
325 FlagSceneGraph.INSTANCE,
\r
326 DEFAULT_STATIC_SYMBOL,
\r
327 SimpleElementLayers.INSTANCE
\r
328 ).setId(FlagClass.class.getSimpleName());
\r
331 public static ElementClass create(Terminal terminal, SceneGraph scn) {
\r
332 return ElementClass.compile(
\r
333 DEFAULT_INITIALIZER,
\r
335 DefaultTransform.INSTANCE,
\r
337 BorderColorImpl.BLACK,
\r
338 FillColorImpl.WHITE,
\r
340 new FlagTerminalTopology(terminal),
\r
342 DEFAULT_STATIC_SYMBOL,
\r
343 SimpleElementLayers.INSTANCE
\r
344 ).setId(FlagClass.class.getSimpleName());
\r
347 static class Initializer implements LifeCycle {
\r
348 private static final long serialVersionUID = 4404942036933073584L;
\r
350 private final Type type;
\r
351 private final Mode mode;
\r
353 Initializer(Type type, Mode mode) {
\r
354 assert type != null;
\r
355 assert mode != null;
\r
361 public void onElementActivated(IDiagram d, IElement e) {
\r
365 public void onElementCreated(IElement e) {
\r
366 e.setHint(KEY_FLAG_TYPE, type);
\r
367 e.setHint(KEY_FLAG_MODE, mode);
\r
368 //e.setHint(ElementHints.KEY_COMPOSITE, AlphaComposite.SrcOver.derive(0.5f));
\r
372 public void onElementDeactivated(IDiagram d, IElement e) {
\r
376 public void onElementDestroyed(IElement e) {
\r
380 public int hashCode() {
\r
381 final int prime = 31;
\r
383 result = prime * result + mode.hashCode();
\r
384 result = prime * result + type.hashCode();
\r
389 public boolean equals(Object obj) {
\r
394 if (getClass() != obj.getClass())
\r
396 Initializer other = (Initializer) obj;
\r
397 if (!mode.equals(other.mode))
\r
399 if (!type.equals(other.type))
\r
405 public static Path2D createFlagShape(Path2D path, Type type, Mode mode, double width, double height, double beakLength) {
\r
406 double hh = height / 2;
\r
410 if (mode instanceof External) {
\r
411 path.moveTo(0, hh);
\r
412 path.lineTo(width, hh);
\r
413 path.lineTo(width+beakLength, 0);
\r
414 path.lineTo(width, -hh);
\r
415 path.lineTo(0, -hh);
\r
417 path.moveTo(width, hh);
\r
418 path.lineTo(width, -hh);
\r
419 int count = ((External)mode).count;
\r
421 double shadow=hh*0.25;
\r
422 double ix = beakLength
\r
423 - 0.5*shadow*(1.0 + beakLength/hh);
\r
424 double iy = hh * (ix / beakLength - 1.0);
\r
425 for(int sid=1;sid<=Math.min(count-1, 4);++sid) {
\r
426 double dis = sid*shadow;
\r
427 path.moveTo(dis, hh+dis-shadow);
\r
428 path.lineTo(dis, hh+dis);
\r
429 path.lineTo(dis+width, hh+dis);
\r
430 path.lineTo(dis+width+beakLength, dis);
\r
431 path.lineTo(width + ix + dis, iy + dis);
\r
435 double right = width - 0;
\r
436 if (left < right) {
\r
437 path.moveTo(left, 0);
\r
438 path.lineTo(right, 0);
\r
441 } else if (mode == Mode.Internal) {
\r
442 path.moveTo(0, hh);
\r
443 path.lineTo(beakLength, 0);
\r
444 path.lineTo(0, -hh);
\r
450 if (mode instanceof External) {
\r
451 path.lineTo(-beakLength, -hh);
\r
452 path.lineTo(-width-beakLength, -hh);
\r
453 path.lineTo(-width-beakLength, hh);
\r
454 path.lineTo(-beakLength, hh);
\r
456 path.moveTo(-beakLength, -hh);
\r
457 path.lineTo(-beakLength, hh);
\r
458 int count = ((External)mode).count;
\r
460 double shadow=hh*0.25;
\r
461 double ix = beakLength
\r
462 - 0.5*shadow*(1.0 + beakLength/hh);
\r
463 double iy = hh * (ix / beakLength - 1.0);
\r
464 double xDisp = -width-beakLength;
\r
465 for(int sid=1;sid<=Math.min(count-1, 4);++sid) {
\r
466 double dis = sid*shadow;
\r
467 path.moveTo(xDisp+dis, hh+dis-shadow);
\r
468 path.lineTo(xDisp+dis, hh+dis);
\r
469 path.lineTo(xDisp+dis+width, hh+dis);
\r
470 path.lineTo(xDisp+dis+width+beakLength, dis);
\r
471 path.lineTo(xDisp+width + ix + dis, iy + dis);
\r
474 double left = -width-beakLength+0;
\r
475 double right = -beakLength-0;
\r
476 if (left < right) {
\r
477 path.moveTo(left, 0);
\r
478 path.lineTo(right, 0);
\r
481 } else if (mode == Mode.Internal) {
\r
482 path.lineTo(-beakLength, -hh);
\r
483 path.lineTo(-beakLength, hh);
\r
491 public static Path2D createFlagShape(IElement e) {
\r
492 Type type = getType(e);
\r
493 Mode mode = e.getHint(KEY_FLAG_MODE);
\r
494 double width = e.getHint(KEY_FLAG_WIDTH);
\r
495 double height = e.getHint(KEY_FLAG_HEIGHT);
\r
496 double beakLength = getBeakLength(e);
\r
497 Path2D path = new Path2D.Double();
\r
498 createFlagShape(path, type, mode, width, height, beakLength);
\r
502 static class FlagSize implements InternalSize, Outline, LifeCycle {
\r
504 private static final long serialVersionUID = 829379327756475944L;
\r
506 private final double length;
\r
507 private final double thickness;
\r
508 private final double beakAngle;
\r
510 public FlagSize(double length, double thickness, double beakAngle) {
\r
511 this.length = length;
\r
512 this.thickness = thickness;
\r
513 this.beakAngle = beakAngle;
\r
517 public Shape getElementShape(IElement e) {
\r
518 Shape shape = e.getHint(KEY_SHAPE);
\r
521 return createFlagShape(e);
\r
525 public Rectangle2D getBounds(IElement e, Rectangle2D size) {
\r
527 size = new Rectangle2D.Double();
\r
528 Shape shape = getElementShape(e);
\r
529 size.setFrame(shape.getBounds2D());
\r
534 public void onElementActivated(IDiagram d, IElement e) {
\r
538 public void onElementCreated(IElement e) {
\r
539 e.setHint(KEY_FLAG_WIDTH, length);
\r
540 e.setHint(KEY_FLAG_HEIGHT, thickness);
\r
541 e.setHint(KEY_FLAG_BEAK_ANGLE, beakAngle);
\r
545 public void onElementDeactivated(IDiagram d, IElement e) {
\r
549 public void onElementDestroyed(IElement e) {
\r
553 public int hashCode() {
\r
554 final int prime = 31;
\r
557 temp = Double.doubleToLongBits(beakAngle);
\r
558 result = prime * result + (int) (temp ^ (temp >>> 32));
\r
559 temp = Double.doubleToLongBits(length);
\r
560 result = prime * result + (int) (temp ^ (temp >>> 32));
\r
561 temp = Double.doubleToLongBits(thickness);
\r
562 result = prime * result + (int) (temp ^ (temp >>> 32));
\r
567 public boolean equals(Object obj) {
\r
572 if (getClass() != obj.getClass())
\r
574 FlagSize other = (FlagSize) obj;
\r
575 if (Double.doubleToLongBits(beakAngle) != Double.doubleToLongBits(other.beakAngle))
\r
577 if (Double.doubleToLongBits(length) != Double.doubleToLongBits(other.length))
\r
579 if (Double.doubleToLongBits(thickness) != Double.doubleToLongBits(other.thickness))
\r
585 static class FlagSceneGraph implements SceneGraph {
\r
586 private static final long serialVersionUID = 35208146123929197L;
\r
588 public static final FlagSceneGraph INSTANCE = new FlagSceneGraph();
\r
591 public void cleanup(IElement e) {
\r
592 ElementUtils.removePossibleNode(e, KEY_SG_NODE);
\r
596 public void init(IElement e, G2DParentNode parent) {
\r
597 Color fc = ElementUtils.getFillColor(e, Color.WHITE);
\r
598 Color bc = ElementUtils.getBorderColor(e, Color.BLACK);
\r
599 Color tc = ElementUtils.getTextColor(e, Color.BLACK);
\r
601 Outline outline = e.getElementClass().getSingleItem(Outline.class);
\r
602 Shape shape = outline.getElementShape(e);
\r
603 Type type = getType(e);
\r
604 double dir = getDirection(e);
\r
605 double width = e.getHint(KEY_FLAG_WIDTH);
\r
606 double height = e.getHint(KEY_FLAG_HEIGHT);
\r
607 double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);
\r
609 String[] flagText = e.getHint(KEY_FLAG_TEXT);
\r
610 if (flagText == null) {
\r
611 // fallback option.
\r
612 Text t = e.getElementClass().getAtMostOneItemOfClass(Text.class);
\r
614 String text = t.getText(e);
\r
616 flagText = new String[] { text };
\r
621 //flagText = new String[] { String.format("%3.1f", dir) + " deg", "FOO"};
\r
623 Rectangle2D textArea = e.getHint(KEY_FLAG_TEXT_AREA);
\r
624 if (textArea == null) {
\r
625 double beakLength = getBeakLength(height, beakAngle);
\r
626 textArea = type == Type.In
\r
627 ? new Rectangle2D.Double(-width-beakLength, -height*0.5, width, height)
\r
628 : new Rectangle2D.Double(0, -height*0.5, width, height);
\r
631 Alignment horizAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_HORIZONTAL_ALIGN, Alignment.LEADING);
\r
632 Alignment vertAlign = ElementUtils.getHintOrDefault(e, KEY_TEXT_VERTICAL_ALIGN, Alignment.CENTER);
\r
634 FlagNode flag = ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, ElementUtils.generateNodeId(e), FlagNode.class);
\r
646 horizAlign.ordinal(),
\r
647 vertAlign.ordinal());
\r
648 AffineTransform at = ElementUtils.getTransform(e);
\r
649 if(at != null) flag.setTransform(at);
\r
654 static class TerminalPoint implements Terminal {
\r
657 public static class FlagTerminalTopology implements TerminalTopology, TerminalLayout {
\r
658 private static final long serialVersionUID = -4194634598346105458L;
\r
660 public static final Terminal DEFAULT_T0 = new TerminalPoint();
\r
661 public static final FlagTerminalTopology DEFAULT = new FlagTerminalTopology(DEFAULT_T0);
\r
665 public FlagTerminalTopology(Terminal t) {
\r
670 public void getTerminals(IElement e, Collection<Terminal> result) {
\r
675 public AffineTransform getTerminalPosition(IElement node, Terminal t) {
\r
677 return new AffineTransform();
\r
683 public boolean getTerminalDirection(IElement node, Terminal t, DirectionSet directions) {
\r
684 Type type = getType(node);
\r
685 double d = getDirection(node);
\r
688 case In: directions.add(d); break;
\r
689 case Out: directions.add(Math.IEEEremainder(d + 180.0, 360.0)); break;
\r
691 //System.out.println("directions T0: " + Arrays.toString(directions.toArray()));
\r
697 // static final Path2D terminalShape;
\r
701 // Path2D p = new Path2D.Double();
\r
703 // p.lineTo(s, -s);
\r
704 // p.lineTo(-s, -s);
\r
705 // p.lineTo(-s, s);
\r
707 // terminalShape = p;
\r
711 public Shape getTerminalShape(IElement node, Terminal t) {
\r
712 //return terminalShape;
\r
714 // For each terminal, return the whole shape of the element.
\r
715 return ElementUtils.getElementShapeOrBounds(node);
\r
719 public int hashCode() {
\r
720 final int prime = 31;
\r
722 result = prime * result + ((T0 == null) ? 0 : T0.hashCode());
\r
727 public boolean equals(Object obj) {
\r
732 if (getClass() != obj.getClass())
\r
734 FlagTerminalTopology other = (FlagTerminalTopology) obj;
\r
736 if (other.T0 != null)
\r
738 } else if (!T0.equals(other.T0))
\r
744 public static AffineTransform getTransform(IElement e) {
\r
745 AffineTransform at = ElementUtils.getTransform(e);
\r
747 return new AffineTransform();
\r
751 public static double getDirection(IElement e) {
\r
752 Rotate rotate = e.getElementClass().getAtMostOneItemOfClass(Rotate.class);
\r
753 if (rotate != null) {
\r
754 return rotate.getAngle(e);
\r
759 public static Type getType(IElement e) {
\r
760 Type t = e.getHint(KEY_FLAG_TYPE);
\r
761 return t != null ? t : Type.In;
\r
764 public static Mode getMode(IElement e) {
\r
765 Mode m = e.getHint(KEY_FLAG_MODE);
\r
766 return m != null ? m : Mode.External;
\r
769 public static double getBeakLength(IElement e) {
\r
770 double height = e.getHint(KEY_FLAG_HEIGHT);
\r
771 double beakAngle = e.getHint(KEY_FLAG_BEAK_ANGLE);
\r
772 beakAngle = Math.min(180, Math.max(10, beakAngle));
\r
773 return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
\r
776 public static double getBeakLength(double height, double beakAngle) {
\r
777 beakAngle = Math.min(180, Math.max(10, beakAngle));
\r
778 return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
\r