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().getSubPoint().get(0);
82 if (sub.getParent() != this.alternativePipeRun)
83 this.alternativePipeRun.addChild(sub);
85 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
89 public void updateParameters() {
90 setParameterMap(updateParameterMap());
91 super.updateParameters();
95 @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue)
96 public Map<String, Object> getParameterMap() {
97 return super.getParameterMap();
101 @CompoundRelatedSetValue(Plant3D.URIs.hasParameter)
102 public void setParameterMap(Map<String, Object> parameters) {
103 super.setParameterMap(parameters);
106 @CompoundGetPropertyValue(name="Parameters",tabId="Parameters",value="parameters")
107 public Map<String,Object> getParameterMapUI() {
108 // TODO : how to filter parameters that are calculated by geometry provider?
109 Map<String,Object> map = new HashMap<String, Object>(getParameterMap());
110 map.remove("radius");
111 map.remove("radius2");
112 map.remove("offset");
116 @CompoundSetPropertyValue(value="parameters")
117 public void setParameterMapUI(Map<String, Object> parameters) {
118 Map<String, Object> curr = getParameterMap();
119 for (Entry<String, Object> entry : curr.entrySet()) {
120 if (!parameters.containsKey(entry.getKey()))
121 parameters.put(entry.getKey(), entry.getValue());
123 setParameterMap(parameters);
126 public abstract void setType(String typeURI) throws Exception;
128 @RelatedGetObj(Plant3D.URIs.HasNext)
129 public PipelineComponent getNext() {
133 @RelatedSetObj(Plant3D.URIs.HasNext)
134 public void setNext(PipelineComponent comp) {
137 if (this.next != null)
138 this.next._removeRef(this);
140 this.syncnext = false;
141 if (DEBUG) System.out.println(this + " next " + comp);
143 firePropertyChanged(Plant3D.URIs.HasNext);
150 @RelatedGetObj(Plant3D.URIs.HasPrevious)
151 public PipelineComponent getPrevious() {
155 @RelatedSetObj(Plant3D.URIs.HasPrevious)
156 public void setPrevious(PipelineComponent comp) {
157 if (previous == comp)
159 if (this.previous != null)
160 this.previous._removeRef(this);
161 this.previous = comp;
162 this.syncprev = false;
163 if (DEBUG) System.out.println(this + " prev " + comp);
165 firePropertyChanged(Plant3D.URIs.HasPrevious);
170 private PipelineComponent branch0;
172 @RelatedGetObj(Plant3D.URIs.HasBranch0)
173 public PipelineComponent getBranch0() {
177 @RelatedSetObj(Plant3D.URIs.HasBranch0)
178 public void setBranch0(PipelineComponent comp) {
181 if (this.branch0 != null)
182 this.branch0._removeRef(this);
184 this.syncbr0 = false;
185 if (DEBUG) System.out.println(this + " br0 " + comp);
187 firePropertyChanged(Plant3D.URIs.HasBranch0);
192 @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
193 public String getPreviousDebug() {
194 if (previous == null)
196 return previous.getName();
199 @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
200 public String getNextDebug() {
203 return next.getName();
206 @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
207 public String getBR0Debug() {
210 return branch0.getName();
215 private PipeControlPoint getBranchPoint() {
216 PipeControlPoint branchPoint;
217 if (getControlPoint().getSubPoint().size() > 0) {
218 branchPoint = getControlPoint().getSubPoint().get(0);
220 if (branch0.getPipeRun() == null)
222 branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
223 branchPoint.setFixed(false);
224 branchPoint.setType(PointType.END);
225 branchPoint.parent = getControlPoint();
226 getControlPoint().children.add(branchPoint);
227 branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
228 branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
233 private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
236 if (pcp.getNext() != nextPCP) {
237 pcp.setNext(nextPCP);
239 if (pcp.isDualInline()) {
240 PipeControlPoint sub = pcp.getSubPoint().get(0);
241 if (sub.getNext() != nextPCP)
242 sub.setNext(nextPCP);
247 private boolean _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
250 if (prevPCP.isDualInline())
251 prevPCP = prevPCP.getSubPoint().get(0);
252 if (pcp.getPrevious() != prevPCP) {
253 pcp.setPrevious(prevPCP);
255 if (pcp.isDualInline()) {
256 PipeControlPoint sub = pcp.getSubPoint().get(0);
257 if (sub.getPrevious() != prevPCP)
258 sub.setPrevious(prevPCP);
263 // When link to a component is removed, also link to the other direction must be removed at the same time, or
264 // Control point structure is left into illegal state.
265 private void _removeRef(PipelineComponent comp) {
269 if (DEBUG) System.out.println(this + " remove next " + comp);
270 firePropertyChanged(Plant3D.URIs.HasNext);
272 } else if (previous == comp) {
275 if (DEBUG) System.out.println(this + " remove prev " + comp);
276 firePropertyChanged(Plant3D.URIs.HasPrevious);
278 } else if (branch0 == comp) {
281 if (DEBUG) System.out.println(this + " remove br0 " + comp);
282 firePropertyChanged(Plant3D.URIs.HasBranch0);
287 boolean syncnext = false;
288 private void syncNext() {
291 syncnext = _syncNext();
295 private boolean _syncNext() {
296 PipeControlPoint pcp = getControlPoint();
300 if (next.getControlPoint() != null) {
302 // TODO, relying that the other direction is connected.
303 boolean nxt = next.getPrevious() == this;
304 boolean br0 = next.getBranch0() == this;
306 return _connectNext(pcp, next.getControlPoint());
308 return _connectNext(pcp, next.getBranchPoint());
316 } else if (pcp.getNext() != null) {
326 boolean syncprev = false;
327 private void syncPrevious() {
330 syncprev = _syncPrevious();
333 private boolean _syncPrevious() {
334 PipeControlPoint pcp = getControlPoint();
336 if (previous != null ) {
337 if (previous.getControlPoint() != null) {
339 // TODO, relying that the other direction is connected.
340 boolean prev = previous.getNext() == this;
341 boolean br0 = previous.getBranch0() == this;
343 return _connectPrev(pcp, previous.getControlPoint());
345 return _connectPrev(pcp, previous.getBranchPoint());
353 } else if (pcp.getPrevious() != null) {
354 pcp.setPrevious(null);
363 boolean syncbr0 = false;
364 private void syncBranch0() {
367 syncbr0 = _syncBranch0();
370 private boolean _syncBranch0() {
371 if (getControlPoint() != null) {
372 if (getControlPoint().isDualInline()) {
376 if (branch0 != null) {
377 if (branch0.getControlPoint() != null) {
378 PipeControlPoint branchPoint = getBranchPoint();
379 if (branchPoint == null)
381 PipeControlPoint pcp = branch0.getControlPoint();
382 // TODO, relying that the other direction is connected.
383 boolean next = branch0.getPrevious() == this; // this --> branch0
384 boolean prev = branch0.getNext() == this;
386 _connectNext(branchPoint, pcp);
388 _connectPrev(branchPoint, pcp);
397 } else if (getControlPoint().getSubPoint().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
398 //getControlPoint().getSubPoint().get(0).remove();
399 //getControlPoint().children.clear();
414 public void sync2() {
415 // if (getControlPoint().isDualInline()) {
416 // PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
417 // next.getControlPoint().getPipeRun().addChild(sub);
419 getControlPoint()._setWorldOrientation(getWorldOrientation());
420 getControlPoint()._setWorldPosition(getWorldPosition());
423 public Map<String,Object> updateParameterMap() {
424 return Collections.EMPTY_MAP;
427 public abstract String getType();
428 public abstract PipeControlPoint getControlPoint();
431 public void remove() {
432 if (DEBUG) System.out.println(this + " remove");
433 PipeControlPoint pcp = getControlPoint();
434 // Second check is needed, when remove process is initiated from control point.
435 if (pcp != null && pcp.getPipelineComponent() != null) {
441 public void removeAndSplit() {
442 PipeControlPoint pcp = getControlPoint();
443 // Second check is needed, when remove process is initiated from control point.
444 if (pcp != null && pcp.getPipelineComponent() != null) {
445 pcp.removeAndSplit();
451 protected double[] getColor() {
452 if (getControlPoint() == null || !getControlPoint().isFixed())
453 return new double[]{0.7,0.7,0.7};
455 return new double[]{1.0,0.0,0.0};
459 protected double[] getSelectedColor() {
460 return new double[]{0.5,0,0.5};
464 public void setOrientation(Quat4d orientation) {
465 if (MathTools.equals(orientation, getOrientation()))
467 super.setOrientation(orientation);
468 if (getControlPoint() != null) {
469 getControlPoint()._setWorldOrientation(getWorldOrientation());
471 PipingRules.requestUpdate(getControlPoint());
472 } catch (Exception e) {
473 // TODO Auto-generated catch block
480 public void setPosition(Vector3d position) {
481 if (MathTools.equals(position, getPosition()))
483 super.setPosition(position);
484 if (getControlPoint() != null) {
485 getControlPoint()._setWorldPosition(getWorldPosition());
487 PipingRules.requestUpdate(getControlPoint());
488 } catch (Exception e) {
489 // TODO Auto-generated catch block
496 public void _setWorldPosition(Vector3d position) {
497 Vector3d localPos = getLocalPosition(position);
498 super.setPosition(localPos);
501 public void _setWorldOrientation(Quat4d orientation) {
502 Quat4d localOr = getLocalOrientation(orientation);
503 super.setOrientation(localOr);
506 @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
507 public Double getFlowLength() {
508 PipeControlPoint pcp = getControlPoint();
511 switch (pcp.getType()) {
513 return pcp.getLength();
517 double r = getPipeRun().getTurnRadius();
518 double a = pcp.getTurnAngle();
526 public void getEnds(Tuple3d p1, Tuple3d p2) {
527 getControlPoint().getControlPointEnds(p1, p2);
530 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
531 getControlPoint().getEndDirections(v1, v2);
534 public void getCentroid(Tuple3d p) {
535 PipeControlPoint pcp = getControlPoint();
537 throw new IllegalStateException("No centroid defined");
539 switch (pcp.getType()) {
542 // Just return the world location
543 if (!pcp.isSizeChange()) {
544 p.set(pcp.getWorldPosition());
548 // Calculate center of mass for the frustum
549 double r1 = getPipeRun().getPipeDiameter();
550 double r2 = getAlternativePipeRun().getPipeDiameter();
552 Vector3d p1 = new Vector3d(), p2 = new Vector3d();
553 pcp.getInlineControlPointEnds(p1, p2);
555 // Squared sum of radii
556 double r12 = r1 + r2;
559 // The larger of the radii form the base of a frustum
560 double rmax = Math.max(r1, r2);
562 // Relative distance from the base of the frustum
563 double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
565 // Relative distance from p1 to p2
575 Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
577 double r = getPipeRun().getTurnRadius();
578 double a = pcp.getTurnAngle();
579 double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
581 // Unit vector in inlet flow direction
582 Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
583 inletDir.scale(-1.0);
584 inletDir.normalize();
586 // Normal to both inletDir and turn axis in world coordinates
587 Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
588 Vector3d normal = new Vector3d(inletDir);
589 normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
592 // Location of turn axis
593 Vector3d center = new Vector3d(normal);
594 center.scaleAdd(r, loc);
596 // Add vector components from axis to centroid
597 double c = r + pipeRadius * pipeRadius / (4 * r);
598 double c1 = c * Math.sin(a) / a;
599 double c2 = c * (1 - Math.cos(a)) / a;
603 center.add(inletDir);
610 throw new IllegalStateException("No centroid defined");
614 public double getVolume() {
615 PipeControlPoint pcp = getControlPoint();
617 throw new IllegalStateException("No centroid defined");
619 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
621 switch (pcp.getType()) {
624 if (!pcp.isSizeChange()) {
625 // Just return the cylinder volume
626 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
629 // Calculate center of mass for the frustum
630 double r1 = pipeRadius;
631 double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
632 return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
634 double r = getPipeRun().getTurnRadius();
635 double a = pcp.getTurnAngle();
636 return r * a * Math.PI * pipeRadius * pipeRadius;
639 throw new IllegalStateException("No centroid defined");