]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/PipelineComponent.java
Handle PipeRun removals with detaching Components.
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / PipelineComponent.java
1 package org.simantics.plant3d.scenegraph;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.Map.Entry;
7
8 import javax.vecmath.Quat4d;
9 import javax.vecmath.Tuple3d;
10 import javax.vecmath.Vector3d;
11
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;
27
28 /**
29  * 
30  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
31  *
32  */
33 @PropertyContributor
34 public abstract class PipelineComponent extends GeometryNode {
35     
36     private static boolean DEBUG = false;
37
38         
39         private PipeRun pipeRun;
40         private PipeRun alternativePipeRun;
41         private PipelineComponent next;
42         private PipelineComponent previous;
43         
44         public PipeRun getPipeRun() {
45                 return pipeRun;
46         }
47         
48         /**
49          * Sets the pipe run.
50          * 
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.
54          * 
55          * @param pipeRun
56          */
57         public void setPipeRun(PipeRun pipeRun) {
58                 if (pipeRun == this.pipeRun)
59                         return;
60                 this.pipeRun = pipeRun;
61                 if (getControlPoint() != null) {
62                         getControlPoint().deattach();
63                         if (pipeRun != null) {
64                                 pipeRun.addChild(getControlPoint());
65                         }
66                 }
67                 updateParameters();
68         }
69         
70         @RelatedGetObj(Plant3D.URIs.HasAlternativePipeRun)
71         public PipeRun getAlternativePipeRun() {
72                 return alternativePipeRun;
73         }
74         
75         @RelatedSetObj(Plant3D.URIs.HasAlternativePipeRun)
76         public void setAlternativePipeRun(PipeRun pipeRun) {
77                 if (this.alternativePipeRun == pipeRun)
78                         return;
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);
88                             }
89                         }
90                                 
91                 }
92                 firePropertyChanged(Plant3D.URIs.HasAlternativePipeRun);
93         }
94         
95         @Override
96         public void updateParameters() {
97                 setParameterMap(updateParameterMap());
98                 super.updateParameters();
99         }
100         
101         @Override
102     @CompoundRelatedGetValue(objRelation=Plant3D.URIs.hasParameter,objType=Plant3D.URIs.Parameter,valRelation=Plant3D.URIs.hasParameterValue)
103     public Map<String, Object> getParameterMap() {
104         return super.getParameterMap();
105     }
106         
107     @Override
108     @CompoundRelatedSetValue(Plant3D.URIs.hasParameter)
109     public void setParameterMap(Map<String, Object> parameters) {
110         super.setParameterMap(parameters);
111     }
112     
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");
120         return map;
121     }
122     
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());
129         }
130         setParameterMap(parameters);
131     }
132     
133     public void setParameter(String name, Object value) {
134         Map<String, Object> parameters = new HashMap<>(getParameterMap());
135         parameters.put(name, value);
136         setParameterMap(parameters);
137     }
138         
139         public abstract void setType(String typeURI) throws Exception;
140         
141         @RelatedGetObj(Plant3D.URIs.HasNext)
142         public PipelineComponent getNext() {
143                 return next;
144         }
145         
146         @RelatedSetObj(Plant3D.URIs.HasNext)
147         public void setNext(PipelineComponent comp) {
148                 if (next == comp)
149                     return;
150                 if (this.next != null)
151                     this.next._removeRef(this);
152         _setNext(comp);
153         this.syncnext = false;
154         if (DEBUG) System.out.println(this + " next " + comp);
155         syncNext();
156         firePropertyChanged(Plant3D.URIs.HasNext);
157         if (comp != null)
158             comp.sync();
159         }
160         
161         protected void _setNext(PipelineComponent comp) {
162             this.next = comp;
163         }
164         
165         
166         @RelatedGetObj(Plant3D.URIs.HasPrevious)
167         public PipelineComponent getPrevious() {
168                 return previous;
169         }
170         
171         @RelatedSetObj(Plant3D.URIs.HasPrevious)
172         public void setPrevious(PipelineComponent comp) {
173                 if (previous == comp)
174                         return;
175                 if (this.previous != null)
176                         this.previous._removeRef(this);
177                 _setPrevious(comp);
178                 this.syncprev = false;
179                 if (DEBUG) System.out.println(this + " prev " + comp);
180                 syncPrevious();
181                 firePropertyChanged(Plant3D.URIs.HasPrevious);
182                 if (comp != null)
183                         comp.sync();
184         }
185         
186         protected void _setPrevious(PipelineComponent comp) {
187         this.previous = comp;
188     }
189         
190         private PipelineComponent branch0;
191         
192         @RelatedGetObj(Plant3D.URIs.HasBranch0)
193         public PipelineComponent getBranch0() {
194                 return branch0;
195         }
196         
197         @RelatedSetObj(Plant3D.URIs.HasBranch0)
198         public void setBranch0(PipelineComponent comp) {
199                 if (branch0 == comp)
200                         return;
201                 if (this.branch0 != null)
202                         this.branch0._removeRef(this);
203                 this.branch0 = comp;
204                 this.syncbr0 = false;
205                 if (DEBUG) System.out.println(this + " br0 " + comp);
206                 syncBranch0();
207                 firePropertyChanged(Plant3D.URIs.HasBranch0);
208                 if (comp != null)
209                         comp.sync();
210         }
211
212         @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
213         public String getPreviousDebug() {
214                 if (previous == null)
215                         return null;
216                 return previous.getName();
217         }
218         
219         @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
220         public String getNextDebug() {
221                 if (next == null)
222                         return null;
223                 return next.getName();
224         }
225         
226         @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
227         public String getBR0Debug() {
228                 if (branch0 == null)
229                         return null;
230                 return branch0.getName();
231         }
232
233         
234         
235         private PipeControlPoint getBranchPoint() {
236                 PipeControlPoint branchPoint;
237                 if (getControlPoint().getChildPoints().size() > 0) {
238                         branchPoint = getControlPoint().getChildPoints().get(0);
239                 } else {
240                         if (branch0.getPipeRun() == null)
241                                 return null;
242                         branchPoint = new PipeControlPoint(this,branch0.getPipeRun());
243                         branchPoint.setFixed(false);
244                         branchPoint.setType(PointType.END);
245                         branchPoint.parent = getControlPoint();
246                         getControlPoint().children.add(branchPoint);
247                         branchPoint.setWorldOrientation(getControlPoint().getWorldOrientation());
248                         branchPoint.setWorldPosition(getControlPoint().getWorldPosition());
249                 }
250                 return branchPoint;
251         }
252         
253         private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
254                 if (nextPCP == null)
255                         return false;
256                 if (pcp.getNext() != nextPCP) {
257                         pcp.setNext(nextPCP);
258                 }
259 //              if (pcp.isDualInline()) {
260 //                      PipeControlPoint sub = pcp.getChildPoints().get(0);
261 //                      if (sub.getNext() != nextPCP)
262 //                              sub.setNext(nextPCP);
263 //              }
264                 return true;
265         }
266         
267         private boolean  _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
268                 if (prevPCP == null)
269                         return false;
270 //              if (prevPCP.isDualInline())
271 //                      prevPCP = prevPCP.getChildPoints().get(0);
272                 if (pcp.getPrevious() != prevPCP) {
273                         pcp.setPrevious(prevPCP);
274                 }
275 //              if (pcp.isDualInline()) {
276 //                      PipeControlPoint sub = pcp.getChildPoints().get(0);
277 //                      if (sub.getPrevious() != prevPCP)
278 //                              sub.setPrevious(prevPCP);
279 //              }
280                 return true;
281         }
282         
283         // When link to a component is removed, also link to the other direction must be removed at the same time, or
284         // Control point structure is left into illegal state.
285         private void _removeRef(PipelineComponent comp) {
286                 if (next == comp) {
287                         _setNext(null);
288                         syncnext = false;
289                         if (DEBUG) System.out.println(this + " remove next " + comp);
290                         firePropertyChanged(Plant3D.URIs.HasNext);
291                         syncNext();
292                 } else if (previous == comp) {
293                         _setPrevious(null);
294                         syncprev = false;
295                         if (DEBUG) System.out.println(this + " remove prev " + comp);
296                         firePropertyChanged(Plant3D.URIs.HasPrevious);
297                         syncPrevious();
298                 } else if (branch0 == comp) {
299                         branch0 = null;
300                         syncbr0 = false;
301                         if (DEBUG) System.out.println(this + " remove br0 " + comp);
302                         firePropertyChanged(Plant3D.URIs.HasBranch0);
303                         syncBranch0();
304                 }
305         }
306         
307         boolean syncnext = false;
308         private void syncNext() {
309                 if (syncnext)
310                         return;
311                 syncnext = _syncNext();
312         }
313         
314         
315         private boolean _syncNext() {
316                 PipeControlPoint pcp = getControlPoint();
317                 if (pcp != null) {
318                         
319                         if (next != null ) {
320                                 if (next.getControlPoint() != null) {
321                                         
322                                         // TODO, relying that the other direction is connected.
323                                         boolean nxt = next.getPrevious() == this;
324                                         boolean br0 = next.getBranch0() == this;
325                                         if (nxt){
326                                                 return _connectNext(pcp, next.getControlPoint());       
327                                         } else if (br0) {
328                                                 return _connectNext(pcp, next.getBranchPoint());
329                                         } else {
330                                                 return false;
331                                         }
332                                 } else {
333                                         return false;
334                                 }
335                                 
336                         } else if (pcp.getNext() != null) {
337                                 pcp.setNext(null);
338                                 return true;
339                         }
340                 } else {
341                         return false;
342                 }
343                 return true;
344         }
345         
346         boolean syncprev = false;
347         private void syncPrevious() {
348                 if (syncprev)
349                         return;
350                 syncprev = _syncPrevious();
351         }
352         
353         private boolean _syncPrevious() {
354                 PipeControlPoint pcp = getControlPoint();
355                 if (pcp != null) {
356                         if (previous != null ) {
357                                 if (previous.getControlPoint() != null) {
358                                         
359                                         // TODO, relying that the other direction is connected.
360                                         boolean prev = previous.getNext() == this;
361                                         boolean br0 = previous.getBranch0() == this;
362                                         if (prev){
363                                                 return _connectPrev(pcp, previous.getControlPoint());   
364                                         } else if (br0) {
365                                                 return _connectPrev(pcp, previous.getBranchPoint());
366                                         } else {
367                                                 return false;
368                                         }
369                                 } else {
370                                         return false;
371                                 }
372                                 
373                         } else if (pcp.getPrevious() != null) {
374                                 pcp.setPrevious(null);
375                                 return true;
376                         }
377                 } else {
378                         return false;
379                 }
380                 return true;
381         }
382         
383         boolean syncbr0 = false;
384         private void syncBranch0() {
385                 if (syncbr0)
386                         return;
387                 syncbr0 = _syncBranch0();
388         }
389         
390         private boolean _syncBranch0() {
391                 if (getControlPoint() != null) {
392                         if (getControlPoint().isDualInline()) {
393                                 branch0 = null;
394                                 return false;
395                         }
396                         if (branch0 != null) {
397                                 if (branch0.getControlPoint() != null) {
398                                         PipeControlPoint branchPoint = getBranchPoint();
399                                         if (branchPoint == null)
400                                                 return false;
401                                         PipeControlPoint pcp = branch0.getControlPoint();
402                                         // TODO, relying that the other direction is connected.
403                                         boolean next = branch0.getPrevious() == this; // this --> branch0
404                                         boolean prev = branch0.getNext() == this;
405                                         if (next) {
406                                                 _connectNext(branchPoint, pcp);
407                                         } else if (prev){
408                                                 _connectPrev(branchPoint, pcp); 
409                                         } else {
410                                                 return false;
411                                         }
412                                         
413                                 } else {
414                                         return false;
415                                 }
416                                 
417                         } else if (getControlPoint().getChildPoints().size() > 0) { // TODO : this may cause problems? (Removes branch point, before branch has been set?)
418                                 //getControlPoint().getSubPoint().get(0).remove();
419                                 //getControlPoint().children.clear();
420                                 return true;
421                         }
422                 } else {
423                         return false;
424                 }
425                 return true;
426         }
427         
428         public void sync() {
429                 syncPrevious();
430                 syncNext();
431                 syncBranch0();
432         }
433         
434         public void sync2() {
435 //              if (getControlPoint().isDualInline()) {
436 //                      PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
437 //                      next.getControlPoint().getPipeRun().addChild(sub);
438 //              }
439                 getControlPoint()._setWorldOrientation(getWorldOrientation());
440                 getControlPoint()._setWorldPosition(getWorldPosition());
441         }
442         
443         public Map<String,Object> updateParameterMap() {
444                 return Collections.emptyMap();
445         }
446         
447         public abstract String getType();
448         public abstract PipeControlPoint getControlPoint();
449         
450         @Override
451         public void remove() {
452             if (DEBUG) System.out.println(this + " remove");
453                 PipeControlPoint pcp = getControlPoint();
454                 // Second check is needed, when remove process is initiated from control point.
455                 if (pcp != null && pcp.getPipelineComponent() != null) {
456                         pcp.remove();
457                 }
458                 setPipeRun(null);
459                 super.remove();
460         }
461         
462         public void removeAndSplit() {
463             PipeControlPoint pcp = getControlPoint();
464         // Second check is needed, when remove process is initiated from control point.
465         if (pcp != null && pcp.getPipelineComponent() != null) {
466             pcp.removeAndSplit();
467         }
468         setPipeRun(null);
469         super.remove();
470         }
471
472         @Override
473         protected double[] getColor() {
474                 if (getControlPoint() == null || !getControlPoint().isFixed())
475                         return new double[]{0.7,0.7,0.7};
476                 else
477                         return new double[]{1.0,0.0,0.0};
478         }
479         
480         @Override
481         protected double[] getSelectedColor() {
482                 return new double[]{0.5,0,0.5};
483         }
484         
485         @Override
486         public void setOrientation(Quat4d orientation) {
487                 if (MathTools.equals(orientation, getOrientation()))
488                         return;
489                 super.setOrientation(orientation);
490                 if (getControlPoint() != null) {
491                         getControlPoint()._setWorldOrientation(getWorldOrientation());
492                         PipingRules.requestUpdate(getControlPoint());
493                 }
494         }
495         
496         @Override
497         public void setPosition(Vector3d position) {
498                 if (MathTools.equals(position, getPosition()))
499                         return;
500                 super.setPosition(position);
501                 if (getControlPoint() != null) {
502                         getControlPoint()._setWorldPosition(getWorldPosition());
503                         PipingRules.requestUpdate(getControlPoint());
504                 }
505         }
506         
507         
508         public void _setWorldPosition(Vector3d position) {
509                 Vector3d localPos = getLocalPosition(position);
510                 super.setPosition(localPos);
511         }
512         
513         public void _setWorldOrientation(Quat4d orientation) {
514                 Quat4d localOr = getLocalOrientation(orientation);
515                 super.setOrientation(localOr);
516         }
517         
518         @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
519         public Double getFlowLength() {
520                 PipeControlPoint pcp = getControlPoint(); 
521                 if (pcp == null)
522                         return null;
523                 switch (pcp.getType()) {
524                         case INLINE:
525                                 return pcp.getLength();
526                         case END:
527                                 return null;
528                         case TURN: {
529                                 double r = ((TurnComponent)this).getTurnRadius();
530                                 double a = pcp.getTurnAngle();
531                                 return a*r;
532                         }
533                         default:
534                                 return null;
535                 }
536         }
537         
538         /**
539          * Returns diameter of the pipe
540          * @return
541          */
542         public Double getDiameter() {
543             return getPipeRun().getPipeDiameter();
544         }
545         
546         /**
547          * Returns secondary diameter of the pipe for size change components
548          * @return
549          */
550         public Double getDiameter2() {
551             if (getAlternativePipeRun() == null)
552                 return null;
553             return getAlternativePipeRun().getPipeDiameter();
554         }
555         
556         public void getEnds(Tuple3d p1, Tuple3d p2) {
557                 getControlPoint().getControlPointEnds(p1, p2);
558         }
559         
560         public void getEndDirections(Tuple3d v1, Tuple3d v2) {
561                 getControlPoint().getEndDirections(v1, v2);
562         }
563         
564         public void getCentroid(Tuple3d p) {
565                 PipeControlPoint pcp = getControlPoint(); 
566                 if (pcp == null)
567                         throw new IllegalStateException("No centroid defined");
568                 
569                 switch (pcp.getType()) {
570                 case INLINE:
571                 case END:
572                         // Just return the world location
573                         if (!pcp.isSizeChange()) {
574                                 p.set(pcp.getWorldPosition());
575                                 return;
576                         }
577                         
578                         // Calculate center of mass for the frustum
579                         double r1 = getPipeRun().getPipeDiameter();
580                         double r2 = getAlternativePipeRun().getPipeDiameter();
581                         
582                         Vector3d p1 = new Vector3d(), p2 = new Vector3d();
583                         pcp.getInlineControlPointEnds(p1, p2);
584                         
585                         // Squared sum of radii
586                         double r12 = r1 + r2;
587                         r12 *= r12;
588                         
589                         // The larger of the radii form the base of a frustum
590                         double rmax = Math.max(r1, r2);
591                         
592                         // Relative distance from the base of the frustum
593                         double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
594                         
595                         // Relative distance from p1 to p2
596                         if (r1 < r2)
597                                 h = 1 - h;
598                         
599                         p2.sub(p1);
600                         p1.scaleAdd(h, p2);
601                         
602                         p.set(p1);
603                         return;
604                 case TURN: {
605                         Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
606                         
607                         double r = ((TurnComponent)this).getTurnRadius();
608                         double a = pcp.getTurnAngle();
609                         double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
610                         
611                         // Unit vector in inlet flow direction
612                         Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
613                         inletDir.scale(-1.0);
614                         inletDir.normalize();
615                         
616                         // Normal to both inletDir and turn axis in world coordinates
617                         Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
618                         Vector3d normal = new Vector3d(inletDir);
619                         normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
620                         normal.normalize();
621                         
622                         // Location of turn axis
623                         Vector3d center = new Vector3d(normal);
624                         center.scaleAdd(r, loc);
625                         
626                         // Add vector components from axis to centroid
627                         double c = r + pipeRadius * pipeRadius / (4 * r);
628                         double c1 = c * Math.sin(a) / a;
629                         double c2 = c * (1 - Math.cos(a)) / a;
630                         normal.scale(-c1);
631                         inletDir.scale(c2);
632                         center.add(normal);
633                         center.add(inletDir);
634                 
635                         // Return value
636                         p.set(center);
637                         return;
638                 }
639                 default:
640                         throw new IllegalStateException("No centroid defined");
641                 }
642         }
643         
644         public double getVolume() {
645                 PipeControlPoint pcp = getControlPoint(); 
646                 if (pcp == null)
647                         throw new IllegalStateException("No centroid defined");
648                 
649                 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
650                 
651                 switch (pcp.getType()) {
652                 case INLINE:
653                 case END:
654                         if (!pcp.isSizeChange()) {
655                                 // Just return the cylinder volume
656                                 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
657                         }
658                         
659                         // Calculate center of mass for the frustum
660                         double r1 = pipeRadius;
661                         double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
662                         return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
663                 case TURN: {
664                         double r = ((TurnComponent)this).getTurnRadius();
665                         double a = pcp.getTurnAngle();
666                         return r * a * Math.PI * pipeRadius * pipeRadius;
667                 }
668                 default:
669                         throw new IllegalStateException("No centroid defined");
670                 }
671         }
672 }