]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/PipelineComponent.java
Allow adding adjustable length components in the middle of a pipe
[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                 super.remove();
459         }
460         
461         public void removeAndSplit() {
462             PipeControlPoint pcp = getControlPoint();
463         // Second check is needed, when remove process is initiated from control point.
464         if (pcp != null && pcp.getPipelineComponent() != null) {
465             pcp.removeAndSplit();
466         }
467         super.remove();
468         }
469
470         @Override
471         protected double[] getColor() {
472                 if (getControlPoint() == null || !getControlPoint().isFixed())
473                         return new double[]{0.7,0.7,0.7};
474                 else
475                         return new double[]{1.0,0.0,0.0};
476         }
477         
478         @Override
479         protected double[] getSelectedColor() {
480                 return new double[]{0.5,0,0.5};
481         }
482         
483         @Override
484         public void setOrientation(Quat4d orientation) {
485                 if (MathTools.equals(orientation, getOrientation()))
486                         return;
487                 super.setOrientation(orientation);
488                 if (getControlPoint() != null) {
489                         getControlPoint()._setWorldOrientation(getWorldOrientation());
490                         PipingRules.requestUpdate(getControlPoint());
491                 }
492         }
493         
494         @Override
495         public void setPosition(Vector3d position) {
496                 if (MathTools.equals(position, getPosition()))
497                         return;
498                 super.setPosition(position);
499                 if (getControlPoint() != null) {
500                         getControlPoint()._setWorldPosition(getWorldPosition());
501                         PipingRules.requestUpdate(getControlPoint());
502                 }
503         }
504         
505         
506         public void _setWorldPosition(Vector3d position) {
507                 Vector3d localPos = getLocalPosition(position);
508                 super.setPosition(localPos);
509         }
510         
511         public void _setWorldOrientation(Quat4d orientation) {
512                 Quat4d localOr = getLocalOrientation(orientation);
513                 super.setOrientation(localOr);
514         }
515         
516         @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
517         public Double getFlowLength() {
518                 PipeControlPoint pcp = getControlPoint(); 
519                 if (pcp == null)
520                         return null;
521                 switch (pcp.getType()) {
522                         case INLINE:
523                                 return pcp.getLength();
524                         case END:
525                                 return null;
526                         case TURN: {
527                                 double r = ((TurnComponent)this).getTurnRadius();
528                                 double a = pcp.getTurnAngle();
529                                 return a*r;
530                         }
531                         default:
532                                 return null;
533                 }
534         }
535         
536         /**
537          * Returns diameter of the pipe
538          * @return
539          */
540         public Double getDiameter() {
541             return getPipeRun().getPipeDiameter();
542         }
543         
544         /**
545          * Returns secondary diameter of the pipe for size change components
546          * @return
547          */
548         public Double getDiameter2() {
549             if (getAlternativePipeRun() == null)
550                 return null;
551             return getAlternativePipeRun().getPipeDiameter();
552         }
553         
554         public void getEnds(Tuple3d p1, Tuple3d p2) {
555                 getControlPoint().getControlPointEnds(p1, p2);
556         }
557         
558         public void getEndDirections(Tuple3d v1, Tuple3d v2) {
559                 getControlPoint().getEndDirections(v1, v2);
560         }
561         
562         public void getCentroid(Tuple3d p) {
563                 PipeControlPoint pcp = getControlPoint(); 
564                 if (pcp == null)
565                         throw new IllegalStateException("No centroid defined");
566                 
567                 switch (pcp.getType()) {
568                 case INLINE:
569                 case END:
570                         // Just return the world location
571                         if (!pcp.isSizeChange()) {
572                                 p.set(pcp.getWorldPosition());
573                                 return;
574                         }
575                         
576                         // Calculate center of mass for the frustum
577                         double r1 = getPipeRun().getPipeDiameter();
578                         double r2 = getAlternativePipeRun().getPipeDiameter();
579                         
580                         Vector3d p1 = new Vector3d(), p2 = new Vector3d();
581                         pcp.getInlineControlPointEnds(p1, p2);
582                         
583                         // Squared sum of radii
584                         double r12 = r1 + r2;
585                         r12 *= r12;
586                         
587                         // The larger of the radii form the base of a frustum
588                         double rmax = Math.max(r1, r2);
589                         
590                         // Relative distance from the base of the frustum
591                         double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
592                         
593                         // Relative distance from p1 to p2
594                         if (r1 < r2)
595                                 h = 1 - h;
596                         
597                         p2.sub(p1);
598                         p1.scaleAdd(h, p2);
599                         
600                         p.set(p1);
601                         return;
602                 case TURN: {
603                         Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
604                         
605                         double r = ((TurnComponent)this).getTurnRadius();
606                         double a = pcp.getTurnAngle();
607                         double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
608                         
609                         // Unit vector in inlet flow direction
610                         Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
611                         inletDir.scale(-1.0);
612                         inletDir.normalize();
613                         
614                         // Normal to both inletDir and turn axis in world coordinates
615                         Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
616                         Vector3d normal = new Vector3d(inletDir);
617                         normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
618                         normal.normalize();
619                         
620                         // Location of turn axis
621                         Vector3d center = new Vector3d(normal);
622                         center.scaleAdd(r, loc);
623                         
624                         // Add vector components from axis to centroid
625                         double c = r + pipeRadius * pipeRadius / (4 * r);
626                         double c1 = c * Math.sin(a) / a;
627                         double c2 = c * (1 - Math.cos(a)) / a;
628                         normal.scale(-c1);
629                         inletDir.scale(c2);
630                         center.add(normal);
631                         center.add(inletDir);
632                 
633                         // Return value
634                         p.set(center);
635                         return;
636                 }
637                 default:
638                         throw new IllegalStateException("No centroid defined");
639                 }
640         }
641         
642         public double getVolume() {
643                 PipeControlPoint pcp = getControlPoint(); 
644                 if (pcp == null)
645                         throw new IllegalStateException("No centroid defined");
646                 
647                 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
648                 
649                 switch (pcp.getType()) {
650                 case INLINE:
651                 case END:
652                         if (!pcp.isSizeChange()) {
653                                 // Just return the cylinder volume
654                                 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
655                         }
656                         
657                         // Calculate center of mass for the frustum
658                         double r1 = pipeRadius;
659                         double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
660                         return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
661                 case TURN: {
662                         double r = ((TurnComponent)this).getTurnRadius();
663                         double a = pcp.getTurnAngle();
664                         return r * a * Math.PI * pipeRadius * pipeRadius;
665                 }
666                 default:
667                         throw new IllegalStateException("No centroid defined");
668                 }
669         }
670 }