1 package org.simantics.plant3d.scenegraph;
3 import java.util.Collections;
4 import java.util.HashMap;
6 import java.util.Map.Entry;
8 import javax.vecmath.Quat4d;
9 import javax.vecmath.Tuple3d;
10 import javax.vecmath.Vector3d;
12 import org.simantics.g3d.math.MathTools;
13 import org.simantics.g3d.property.annotations.CompoundGetPropertyValue;
14 import org.simantics.g3d.property.annotations.CompoundSetPropertyValue;
15 import org.simantics.g3d.property.annotations.GetPropertyValue;
16 import org.simantics.g3d.property.annotations.PropertyContributor;
17 import org.simantics.objmap.graph.annotations.CompoundRelatedGetValue;
18 import org.simantics.objmap.graph.annotations.CompoundRelatedSetValue;
19 import org.simantics.objmap.graph.annotations.RelatedGetObj;
20 import org.simantics.objmap.graph.annotations.RelatedSetObj;
21 import org.simantics.plant3d.ontology.Plant3D;
22 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
23 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
24 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PointType;
25 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
26 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
30 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
34 public abstract class PipelineComponent extends GeometryNode {
36 private static boolean DEBUG = false;
39 private PipeRun pipeRun;
40 private PipeRun alternativePipeRun;
41 private PipelineComponent next;
42 private PipelineComponent previous;
44 public PipeRun getPipeRun() {
51 * With in-line,turn, and end components, the pipe run is the parent object in the scene-graph.
52 * With nozzles, the pipe run setting is explicit (nozzle has to be linked to the piperun, since the parent object is equipment).
53 * With size change components (in-line), there is also alternative pipe run, which must match the next component's pipe run.
57 public void setPipeRun(PipeRun pipeRun) {
58 if (pipeRun == this.pipeRun)
60 this.pipeRun = pipeRun;
61 if (getControlPoint() != null) {
62 getControlPoint().deattach();
63 if (pipeRun != null) {
64 pipeRun.addChild(getControlPoint());
70 @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun)
71 public PipeRun getAlternativePipeRun() {
72 return alternativePipeRun;
75 @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun)
76 public void setAlternativePipeRun(PipeRun pipeRun) {
77 if (this.alternativePipeRun == pipeRun)
79 this.alternativePipeRun = pipeRun;
80 if (getControlPoint().isDualInline()) {
81 PipeControlPoint sub = getControlPoint().getDualSub();
82 if (sub.getParent() != this.alternativePipeRun) {
83 if (this.alternativePipeRun != null) {
84 this.alternativePipeRun.addChild(sub);
85 } else if (sub.getPipeRun() != null) {
86 // FIXME : how to handle child point without proper run?
87 sub.getPipeRun().remChild(sub);
92 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
96 public void updateParameters() {
97 setParameterMap(updateParameterMap());
98 super.updateParameters();
102 @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue)
103 public Map<String, Object> getParameterMap() {
104 return super.getParameterMap();
108 @CompoundRelatedSetValue(Plant3D.URIs.hasParameter)
109 public void setParameterMap(Map<String, Object> parameters) {
110 super.setParameterMap(parameters);
113 @CompoundGetPropertyValue(name="Parameters",tabId="Parameters",value="parameters")
114 public Map<String,Object> getParameterMapUI() {
115 // TODO : how to filter parameters that are calculated by geometry provider?
116 Map<String,Object> map = new HashMap<String, Object>(getParameterMap());
117 map.remove("radius");
118 map.remove("radius2");
119 map.remove("offset");
123 @CompoundSetPropertyValue(value="parameters")
124 public void setParameterMapUI(Map<String, Object> parameters) {
125 Map<String, Object> curr = getParameterMap();
126 for (Entry<String, Object> entry : curr.entrySet()) {
127 if (!parameters.containsKey(entry.getKey()))
128 parameters.put(entry.getKey(), entry.getValue());
130 setParameterMap(parameters);
133 public void setParameter(String name, Object value) {
134 Map<String, Object> parameters = new HashMap<>(getParameterMap());
135 parameters.put(name, value);
136 setParameterMap(parameters);
139 public abstract void setType(String typeURI) throws Exception;
141 @RelatedGetObj(Plant3D.URIs.HasNext)
142 public PipelineComponent getNext() {
146 @RelatedSetObj(Plant3D.URIs.HasNext)
147 public void setNext(PipelineComponent comp) {
152 if (this.next != null)
153 this.next._removeRef(this);
155 this.syncnext = false;
156 if (DEBUG) System.out.println(this + " next " + comp);
158 firePropertyChanged(Plant3D.URIs.HasNext);
163 protected void _setNext(PipelineComponent comp) {
168 @RelatedGetObj(Plant3D.URIs.HasPrevious)
169 public PipelineComponent getPrevious() {
173 @RelatedSetObj(Plant3D.URIs.HasPrevious)
174 public void setPrevious(PipelineComponent comp) {
175 if (previous == comp) {
179 if (this.previous != null)
180 this.previous._removeRef(this);
182 this.syncprev = false;
183 if (DEBUG) System.out.println(this + " prev " + comp);
185 firePropertyChanged(Plant3D.URIs.HasPrevious);
190 protected void _setPrevious(PipelineComponent comp) {
191 this.previous = comp;
194 private PipelineComponent branch0;
196 @RelatedGetObj(Plant3D.URIs.HasBranch0)
197 public PipelineComponent getBranch0() {
201 @RelatedSetObj(Plant3D.URIs.HasBranch0)
202 public void setBranch0(PipelineComponent comp) {
203 if (branch0 == comp) {
207 if (this.branch0 != null)
208 this.branch0._removeRef(this);
210 this.syncbr0 = false;
211 if (DEBUG) System.out.println(this + " br0 " + comp);
213 firePropertyChanged(Plant3D.URIs.HasBranch0);
218 @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
219 public String getPreviousDebug() {
220 if (previous == null)
222 return previous.getName();
225 @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
226 public String getNextDebug() {
229 return next.getName();
232 @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
233 public String getBR0Debug() {
236 return branch0.getName();
241 private PipeControlPoint getBranchPoint() {
242 PipeControlPoint branchPoint;
243 if (getControlPoint().getChildPoints().size() > 0) {
244 branchPoint = getControlPoint().getChildPoints().get(0);
246 if (branch0.getPipeRun() == null)
248 branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
249 branchPoint.setFixed(false);
250 branchPoint.setType(PointType.END);
251 branchPoint.parent = getControlPoint();
252 getControlPoint().children.add(branchPoint);
253 branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
254 branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
259 private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
262 if (pcp.getNext() != nextPCP) {
263 pcp.setNext(nextPCP);
265 // if (pcp.isDualInline()) {
266 // PipeControlPoint sub = pcp.getChildPoints().get(0);
267 // if (sub.getNext() != nextPCP)
268 // sub.setNext(nextPCP);
273 private boolean _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
276 // if (prevPCP.isDualInline())
277 // prevPCP = prevPCP.getChildPoints().get(0);
278 if (pcp.getPrevious() != prevPCP) {
279 pcp.setPrevious(prevPCP);
281 // if (pcp.isDualInline()) {
282 // PipeControlPoint sub = pcp.getChildPoints().get(0);
283 // if (sub.getPrevious() != prevPCP)
284 // sub.setPrevious(prevPCP);
289 // When link to a component is removed, also link to the other direction must be removed at the same time, or
290 // Control point structure is left into illegal state.
291 private void _removeRef(PipelineComponent comp) {
295 if (DEBUG) System.out.println(this + " remove next " + comp);
296 firePropertyChanged(Plant3D.URIs.HasNext);
298 } else if (previous == comp) {
301 if (DEBUG) System.out.println(this + " remove prev " + comp);
302 firePropertyChanged(Plant3D.URIs.HasPrevious);
304 } else if (branch0 == comp) {
307 if (DEBUG) System.out.println(this + " remove br0 " + comp);
308 firePropertyChanged(Plant3D.URIs.HasBranch0);
313 boolean syncnext = false;
314 protected void syncNext() {
317 syncnext = _syncNext();
321 private boolean _syncNext() {
322 PipeControlPoint pcp = getControlPoint();
326 if (next.getControlPoint() != null) {
328 // TODO, relying that the other direction is connected.
329 boolean nxt = next.getPrevious() == this;
330 boolean br0 = next.getBranch0() == this;
332 return _connectNext(pcp, next.getControlPoint());
334 return _connectNext(pcp, next.getBranchPoint());
342 } else if (pcp.getNext() != null) {
352 boolean syncprev = false;
353 protected void syncPrevious() {
356 syncprev = _syncPrevious();
359 private boolean _syncPrevious() {
360 PipeControlPoint pcp = getControlPoint();
362 if (previous != null ) {
363 if (previous.getControlPoint() != null) {
365 // TODO, relying that the other direction is connected.
366 boolean prev = previous.getNext() == this;
367 boolean br0 = previous.getBranch0() == this;
369 return _connectPrev(pcp, previous.getControlPoint());
371 return _connectPrev(pcp, previous.getBranchPoint());
379 } else if (pcp.getPrevious() != null) {
380 pcp.setPrevious(null);
389 boolean syncbr0 = false;
390 protected void syncBranch0() {
393 syncbr0 = _syncBranch0();
396 private boolean _syncBranch0() {
397 if (getControlPoint() != null) {
398 if (getControlPoint().isDualInline()) {
402 if (branch0 != null) {
403 if (branch0.getControlPoint() != null) {
404 PipeControlPoint branchPoint = getBranchPoint();
405 if (branchPoint == null)
407 PipeControlPoint pcp = branch0.getControlPoint();
408 // TODO, relying that the other direction is connected.
409 boolean next = branch0.getPrevious() == this; // this --> branch0
410 boolean prev = branch0.getNext() == this;
412 _connectNext(branchPoint, pcp);
414 _connectPrev(branchPoint, pcp);
423 } else if (getControlPoint().getChildPoints().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
424 //getControlPoint().getSubPoint().get(0).remove();
425 //getControlPoint().children.clear();
440 public void sync2() {
441 // if (getControlPoint().isDualInline()) {
442 // PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
443 // next.getControlPoint().getPipeRun().addChild(sub);
445 getControlPoint()._setWorldOrientation(getWorldOrientation());
446 getControlPoint()._setWorldPosition(getWorldPosition());
449 public Map<String,Object> updateParameterMap() {
450 return Collections.emptyMap();
453 public abstract String getType();
454 public abstract PipeControlPoint getControlPoint();
457 public void remove() {
458 if (DEBUG) System.out.println(this + " remove");
459 PipeControlPoint pcp = getControlPoint();
460 // Second check is needed, when remove process is initiated from control point.
461 if (pcp != null && pcp.getPipelineComponent() != null) {
462 if (pcp.isSizeChange()) {
463 mergeWithAlternative();
473 private void mergeWithAlternative() {
474 PipeRun run = getPipeRun();
475 PipeRun alternative = getAlternativePipeRun();
476 if (alternative != null) {
477 // Move components from alternative pipe run to main run
478 PipelineComponent p = getNext();
479 while (p != null && p.getPipeRun() == alternative) {
480 if (p.getParent() == alternative) {
481 p.deattach(); // For components
485 p.setPipeRun(run); // For nozzles
488 p.updateParameters();
489 PipingRules.requestUpdate(p.getControlPoint());
494 setAlternativePipeRun(run);
496 if (alternative.getChild().isEmpty())
497 alternative.remove();
501 public void removeAndSplit() {
502 PipeControlPoint pcp = getControlPoint();
503 // Second check is needed, when remove process is initiated from control point.
504 if (pcp != null && pcp.getPipelineComponent() != null) {
505 pcp.removeAndSplit();
512 protected double[] getColor() {
513 if (getControlPoint() == null || !getControlPoint().isFixed())
514 return new double[]{0.7,0.7,0.7};
516 return new double[]{1.0,0.0,0.0};
520 protected double[] getSelectedColor() {
521 return new double[]{0.5,0,0.5};
525 public void setOrientation(Quat4d orientation) {
526 if (MathTools.equals(orientation, getOrientation()))
528 super.setOrientation(orientation);
529 if (getControlPoint() != null) {
530 getControlPoint()._setWorldOrientation(getWorldOrientation());
531 PipingRules.requestUpdate(getControlPoint());
536 public void setPosition(Vector3d position) {
537 if (MathTools.equals(position, getPosition()))
539 super.setPosition(position);
540 if (getControlPoint() != null) {
541 getControlPoint()._setWorldPosition(getWorldPosition());
542 PipingRules.requestUpdate(getControlPoint());
547 public void _setWorldPosition(Vector3d position) {
548 Vector3d localPos = getLocalPosition(position);
549 super.setPosition(localPos);
552 public void _setWorldOrientation(Quat4d orientation) {
553 Quat4d localOr = getLocalOrientation(orientation);
554 super.setOrientation(localOr);
557 @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
558 public Double getFlowLength() {
559 PipeControlPoint pcp = getControlPoint();
562 switch (pcp.getType()) {
564 return pcp.getLength();
568 double r = ((TurnComponent)this).getTurnRadius();
569 double a = pcp.getTurnAngle();
578 * Returns diameter of the pipe
581 public Double getDiameter() {
582 return getPipeRun().getPipeDiameter();
586 * Returns secondary diameter of the pipe for size change components
589 public Double getDiameter2() {
590 if (getAlternativePipeRun() == null)
592 return getAlternativePipeRun().getPipeDiameter();
595 public void getEnds(Tuple3d p1, Tuple3d p2) {
596 getControlPoint().getControlPointEnds(p1, p2);
599 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
600 getControlPoint().getEndDirections(v1, v2);
603 public void getCentroid(Tuple3d p) {
604 PipeControlPoint pcp = getControlPoint();
606 throw new IllegalStateException("No centroid defined");
608 switch (pcp.getType()) {
611 // Just return the world location
612 if (!pcp.isSizeChange()) {
613 p.set(pcp.getWorldPosition());
617 // Calculate center of mass for the frustum
618 double r1 = getPipeRun().getPipeDiameter();
619 double r2 = getAlternativePipeRun().getPipeDiameter();
621 Vector3d p1 = new Vector3d(), p2 = new Vector3d();
622 pcp.getInlineControlPointEnds(p1, p2);
624 // Squared sum of radii
625 double r12 = r1 + r2;
628 // The larger of the radii form the base of a frustum
629 double rmax = Math.max(r1, r2);
631 // Relative distance from the base of the frustum
632 double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
634 // Relative distance from p1 to p2
644 Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
646 double r = ((TurnComponent)this).getTurnRadius();
647 double a = pcp.getTurnAngle();
648 double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
650 // Unit vector in inlet flow direction
651 Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
652 inletDir.scale(-1.0);
653 inletDir.normalize();
655 // Normal to both inletDir and turn axis in world coordinates
656 Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
657 Vector3d normal = new Vector3d(inletDir);
658 normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
661 // Location of turn axis
662 Vector3d center = new Vector3d(normal);
663 center.scaleAdd(r, loc);
665 // Add vector components from axis to centroid
666 double c = r + pipeRadius * pipeRadius / (4 * r);
667 double c1 = c * Math.sin(a) / a;
668 double c2 = c * (1 - Math.cos(a)) / a;
672 center.add(inletDir);
679 throw new IllegalStateException("No centroid defined");
683 public double getVolume() {
684 PipeControlPoint pcp = getControlPoint();
686 throw new IllegalStateException("No centroid defined");
688 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
690 switch (pcp.getType()) {
693 if (!pcp.isSizeChange()) {
694 // Just return the cylinder volume
695 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
698 // Calculate center of mass for the frustum
699 double r1 = pipeRadius;
700 double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
701 return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
703 double r = ((TurnComponent)this).getTurnRadius();
704 double a = pcp.getTurnAngle();
705 return r * a * Math.PI * pipeRadius * pipeRadius;
708 throw new IllegalStateException("No centroid defined");
713 private String error;
716 * Returns possible pipe modelling error, or null;
719 @GetPropertyValue(name="Error", value="error", tabId = "Default")
720 public String getError() {
725 * Sets pipe modelling error.
727 * Error is usually set by PipingRules.
730 public void setError(String error) {
731 if (this.error == null) {
734 } else if (this.error.equals(error))
737 firePropertyChanged("error");