]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/PipelineComponent.java
Fix orientation of new straight components in insertStraight
[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                     syncNext();
150                     return;
151                 }
152                 if (this.next != null)
153                     this.next._removeRef(this);
154         _setNext(comp);
155         this.syncnext = false;
156         if (DEBUG) System.out.println(this + " next " + comp);
157         syncNext();
158         firePropertyChanged(Plant3D.URIs.HasNext);
159         if (comp != null)
160             comp.sync();
161         }
162         
163         protected void _setNext(PipelineComponent comp) {
164             this.next = comp;
165         }
166         
167         
168         @RelatedGetObj(Plant3D.URIs.HasPrevious)
169         public PipelineComponent getPrevious() {
170                 return previous;
171         }
172         
173         @RelatedSetObj(Plant3D.URIs.HasPrevious)
174         public void setPrevious(PipelineComponent comp) {
175                 if (previous == comp) {
176                     syncPrevious();
177                         return;
178                 }
179                 if (this.previous != null)
180                         this.previous._removeRef(this);
181                 _setPrevious(comp);
182                 this.syncprev = false;
183                 if (DEBUG) System.out.println(this + " prev " + comp);
184                 syncPrevious();
185                 firePropertyChanged(Plant3D.URIs.HasPrevious);
186                 if (comp != null)
187                         comp.sync();
188         }
189         
190         protected void _setPrevious(PipelineComponent comp) {
191         this.previous = comp;
192     }
193         
194         private PipelineComponent branch0;
195         
196         @RelatedGetObj(Plant3D.URIs.HasBranch0)
197         public PipelineComponent getBranch0() {
198                 return branch0;
199         }
200         
201         @RelatedSetObj(Plant3D.URIs.HasBranch0)
202         public void setBranch0(PipelineComponent comp) {
203                 if (branch0 == comp) {
204                     syncBranch0();
205                         return;
206                 }
207                 if (this.branch0 != null)
208                         this.branch0._removeRef(this);
209                 this.branch0 = comp;
210                 this.syncbr0 = false;
211                 if (DEBUG) System.out.println(this + " br0 " + comp);
212                 syncBranch0();
213                 firePropertyChanged(Plant3D.URIs.HasBranch0);
214                 if (comp != null)
215                         comp.sync();
216         }
217
218         @GetPropertyValue(name="Previous",tabId="Debug",value=Plant3D.URIs.HasPrevious)
219         public String getPreviousDebug() {
220                 if (previous == null)
221                         return null;
222                 return previous.getName();
223         }
224         
225         @GetPropertyValue(name="Next",tabId="Debug",value=Plant3D.URIs.HasNext)
226         public String getNextDebug() {
227                 if (next == null)
228                         return null;
229                 return next.getName();
230         }
231         
232         @GetPropertyValue(name="Branch0",tabId="Debug",value=Plant3D.URIs.HasBranch0)
233         public String getBR0Debug() {
234                 if (branch0 == null)
235                         return null;
236                 return branch0.getName();
237         }
238
239         
240         
241         private PipeControlPoint getBranchPoint() {
242                 PipeControlPoint branchPoint;
243                 if (getControlPoint().getChildPoints().size() > 0) {
244                         branchPoint = getControlPoint().getChildPoints().get(0);
245                 } else {
246                         if (branch0.getPipeRun() == null)
247                                 return 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());
255                 }
256                 return branchPoint;
257         }
258         
259         private boolean _connectNext(PipeControlPoint pcp, PipeControlPoint nextPCP) {
260                 if (nextPCP == null)
261                         return false;
262                 if (pcp.getNext() != nextPCP) {
263                         pcp.setNext(nextPCP);
264                 }
265 //              if (pcp.isDualInline()) {
266 //                      PipeControlPoint sub = pcp.getChildPoints().get(0);
267 //                      if (sub.getNext() != nextPCP)
268 //                              sub.setNext(nextPCP);
269 //              }
270                 return true;
271         }
272         
273         private boolean  _connectPrev(PipeControlPoint pcp, PipeControlPoint prevPCP) {
274                 if (prevPCP == null)
275                         return false;
276 //              if (prevPCP.isDualInline())
277 //                      prevPCP = prevPCP.getChildPoints().get(0);
278                 if (pcp.getPrevious() != prevPCP) {
279                         pcp.setPrevious(prevPCP);
280                 }
281 //              if (pcp.isDualInline()) {
282 //                      PipeControlPoint sub = pcp.getChildPoints().get(0);
283 //                      if (sub.getPrevious() != prevPCP)
284 //                              sub.setPrevious(prevPCP);
285 //              }
286                 return true;
287         }
288         
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) {
292                 if (next == comp) {
293                         _setNext(null);
294                         syncnext = false;
295                         if (DEBUG) System.out.println(this + " remove next " + comp);
296                         firePropertyChanged(Plant3D.URIs.HasNext);
297                         syncNext();
298                 } else if (previous == comp) {
299                         _setPrevious(null);
300                         syncprev = false;
301                         if (DEBUG) System.out.println(this + " remove prev " + comp);
302                         firePropertyChanged(Plant3D.URIs.HasPrevious);
303                         syncPrevious();
304                 } else if (branch0 == comp) {
305                         branch0 = null;
306                         syncbr0 = false;
307                         if (DEBUG) System.out.println(this + " remove br0 " + comp);
308                         firePropertyChanged(Plant3D.URIs.HasBranch0);
309                         syncBranch0();
310                 }
311         }
312         
313         boolean syncnext = false;
314         protected void syncNext() {
315                 if (syncnext)
316                         return;
317                 syncnext = _syncNext();
318         }
319         
320         
321         private boolean _syncNext() {
322                 PipeControlPoint pcp = getControlPoint();
323                 if (pcp != null) {
324                         
325                         if (next != null ) {
326                                 if (next.getControlPoint() != null) {
327                                         
328                                         // TODO, relying that the other direction is connected.
329                                         boolean nxt = next.getPrevious() == this;
330                                         boolean br0 = next.getBranch0() == this;
331                                         if (nxt){
332                                                 return _connectNext(pcp, next.getControlPoint());       
333                                         } else if (br0) {
334                                                 return _connectNext(pcp, next.getBranchPoint());
335                                         } else {
336                                                 return false;
337                                         }
338                                 } else {
339                                         return false;
340                                 }
341                                 
342                         } else if (pcp.getNext() != null) {
343                                 pcp.setNext(null);
344                                 return true;
345                         }
346                 } else {
347                         return false;
348                 }
349                 return true;
350         }
351         
352         boolean syncprev = false;
353         protected void syncPrevious() {
354                 if (syncprev)
355                         return;
356                 syncprev = _syncPrevious();
357         }
358         
359         private boolean _syncPrevious() {
360                 PipeControlPoint pcp = getControlPoint();
361                 if (pcp != null) {
362                         if (previous != null ) {
363                                 if (previous.getControlPoint() != null) {
364                                         
365                                         // TODO, relying that the other direction is connected.
366                                         boolean prev = previous.getNext() == this;
367                                         boolean br0 = previous.getBranch0() == this;
368                                         if (prev){
369                                                 return _connectPrev(pcp, previous.getControlPoint());   
370                                         } else if (br0) {
371                                                 return _connectPrev(pcp, previous.getBranchPoint());
372                                         } else {
373                                                 return false;
374                                         }
375                                 } else {
376                                         return false;
377                                 }
378                                 
379                         } else if (pcp.getPrevious() != null) {
380                                 pcp.setPrevious(null);
381                                 return true;
382                         }
383                 } else {
384                         return false;
385                 }
386                 return true;
387         }
388         
389         boolean syncbr0 = false;
390         protected void syncBranch0() {
391                 if (syncbr0)
392                         return;
393                 syncbr0 = _syncBranch0();
394         }
395         
396         private boolean _syncBranch0() {
397                 if (getControlPoint() != null) {
398                         if (getControlPoint().isDualInline()) {
399                                 branch0 = null;
400                                 return false;
401                         }
402                         if (branch0 != null) {
403                                 if (branch0.getControlPoint() != null) {
404                                         PipeControlPoint branchPoint = getBranchPoint();
405                                         if (branchPoint == null)
406                                                 return false;
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;
411                                         if (next) {
412                                                 _connectNext(branchPoint, pcp);
413                                         } else if (prev){
414                                                 _connectPrev(branchPoint, pcp); 
415                                         } else {
416                                                 return false;
417                                         }
418                                         
419                                 } else {
420                                         return false;
421                                 }
422                                 
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();
426                                 return true;
427                         }
428                 } else {
429                         return false;
430                 }
431                 return true;
432         }
433         
434         public void sync() {
435                 syncPrevious();
436                 syncNext();
437                 syncBranch0();
438         }
439         
440         public void sync2() {
441 //              if (getControlPoint().isDualInline()) {
442 //                      PipeControlPoint sub = getControlPoint().getSubPoint().get(0);
443 //                      next.getControlPoint().getPipeRun().addChild(sub);
444 //              }
445                 getControlPoint()._setWorldOrientation(getWorldOrientation());
446                 getControlPoint()._setWorldPosition(getWorldPosition());
447         }
448         
449         public Map<String,Object> updateParameterMap() {
450                 return Collections.emptyMap();
451         }
452         
453         public abstract String getType();
454         public abstract PipeControlPoint getControlPoint();
455         
456         @Override
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                         pcp.remove();
463                 }
464                 setPipeRun(null);
465                 super.remove();
466         }
467         
468         public void removeAndSplit() {
469             PipeControlPoint pcp = getControlPoint();
470         // Second check is needed, when remove process is initiated from control point.
471         if (pcp != null && pcp.getPipelineComponent() != null) {
472             pcp.removeAndSplit();
473         }
474         setPipeRun(null);
475         super.remove();
476         }
477
478         @Override
479         protected double[] getColor() {
480                 if (getControlPoint() == null || !getControlPoint().isFixed())
481                         return new double[]{0.7,0.7,0.7};
482                 else
483                         return new double[]{1.0,0.0,0.0};
484         }
485         
486         @Override
487         protected double[] getSelectedColor() {
488                 return new double[]{0.5,0,0.5};
489         }
490         
491         @Override
492         public void setOrientation(Quat4d orientation) {
493                 if (MathTools.equals(orientation, getOrientation()))
494                         return;
495                 super.setOrientation(orientation);
496                 if (getControlPoint() != null) {
497                         getControlPoint()._setWorldOrientation(getWorldOrientation());
498                         PipingRules.requestUpdate(getControlPoint());
499                 }
500         }
501         
502         @Override
503         public void setPosition(Vector3d position) {
504                 if (MathTools.equals(position, getPosition()))
505                         return;
506                 super.setPosition(position);
507                 if (getControlPoint() != null) {
508                         getControlPoint()._setWorldPosition(getWorldPosition());
509                         PipingRules.requestUpdate(getControlPoint());
510                 }
511         }
512         
513         
514         public void _setWorldPosition(Vector3d position) {
515                 Vector3d localPos = getLocalPosition(position);
516                 super.setPosition(localPos);
517         }
518         
519         public void _setWorldOrientation(Quat4d orientation) {
520                 Quat4d localOr = getLocalOrientation(orientation);
521                 super.setOrientation(localOr);
522         }
523         
524         @GetPropertyValue(name="Flow Length", value="flowlength", tabId = "Default")
525         public Double getFlowLength() {
526                 PipeControlPoint pcp = getControlPoint(); 
527                 if (pcp == null)
528                         return null;
529                 switch (pcp.getType()) {
530                         case INLINE:
531                                 return pcp.getLength();
532                         case END:
533                                 return null;
534                         case TURN: {
535                                 double r = ((TurnComponent)this).getTurnRadius();
536                                 double a = pcp.getTurnAngle();
537                                 return a*r;
538                         }
539                         default:
540                                 return null;
541                 }
542         }
543         
544         /**
545          * Returns diameter of the pipe
546          * @return
547          */
548         public Double getDiameter() {
549             return getPipeRun().getPipeDiameter();
550         }
551         
552         /**
553          * Returns secondary diameter of the pipe for size change components
554          * @return
555          */
556         public Double getDiameter2() {
557             if (getAlternativePipeRun() == null)
558                 return null;
559             return getAlternativePipeRun().getPipeDiameter();
560         }
561         
562         public void getEnds(Tuple3d p1, Tuple3d p2) {
563                 getControlPoint().getControlPointEnds(p1, p2);
564         }
565         
566         public void getEndDirections(Tuple3d v1, Tuple3d v2) {
567                 getControlPoint().getEndDirections(v1, v2);
568         }
569         
570         public void getCentroid(Tuple3d p) {
571                 PipeControlPoint pcp = getControlPoint(); 
572                 if (pcp == null)
573                         throw new IllegalStateException("No centroid defined");
574                 
575                 switch (pcp.getType()) {
576                 case INLINE:
577                 case END:
578                         // Just return the world location
579                         if (!pcp.isSizeChange()) {
580                                 p.set(pcp.getWorldPosition());
581                                 return;
582                         }
583                         
584                         // Calculate center of mass for the frustum
585                         double r1 = getPipeRun().getPipeDiameter();
586                         double r2 = getAlternativePipeRun().getPipeDiameter();
587                         
588                         Vector3d p1 = new Vector3d(), p2 = new Vector3d();
589                         pcp.getInlineControlPointEnds(p1, p2);
590                         
591                         // Squared sum of radii
592                         double r12 = r1 + r2;
593                         r12 *= r12;
594                         
595                         // The larger of the radii form the base of a frustum
596                         double rmax = Math.max(r1, r2);
597                         
598                         // Relative distance from the base of the frustum
599                         double h = (r12 + 2*rmax*rmax) / (4 * (r12 - r1*r2));
600                         
601                         // Relative distance from p1 to p2
602                         if (r1 < r2)
603                                 h = 1 - h;
604                         
605                         p2.sub(p1);
606                         p1.scaleAdd(h, p2);
607                         
608                         p.set(p1);
609                         return;
610                 case TURN: {
611                         Vector3d loc = pcp.getRealPosition(PositionType.PREVIOUS);
612                         
613                         double r = ((TurnComponent)this).getTurnRadius();
614                         double a = pcp.getTurnAngle();
615                         double pipeRadius = pcp.getPipeRun().getPipeDiameter() / 2;
616                         
617                         // Unit vector in inlet flow direction
618                         Vector3d inletDir = pcp.getPathLegDirection(Direction.PREVIOUS);
619                         inletDir.scale(-1.0);
620                         inletDir.normalize();
621                         
622                         // Normal to both inletDir and turn axis in world coordinates
623                         Vector3d outletDir = pcp.getPathLegDirection(Direction.NEXT);
624                         Vector3d normal = new Vector3d(inletDir);
625                         normal.scaleAdd(-inletDir.dot(outletDir), outletDir);
626                         normal.normalize();
627                         
628                         // Location of turn axis
629                         Vector3d center = new Vector3d(normal);
630                         center.scaleAdd(r, loc);
631                         
632                         // Add vector components from axis to centroid
633                         double c = r + pipeRadius * pipeRadius / (4 * r);
634                         double c1 = c * Math.sin(a) / a;
635                         double c2 = c * (1 - Math.cos(a)) / a;
636                         normal.scale(-c1);
637                         inletDir.scale(c2);
638                         center.add(normal);
639                         center.add(inletDir);
640                 
641                         // Return value
642                         p.set(center);
643                         return;
644                 }
645                 default:
646                         throw new IllegalStateException("No centroid defined");
647                 }
648         }
649         
650         public double getVolume() {
651                 PipeControlPoint pcp = getControlPoint(); 
652                 if (pcp == null)
653                         throw new IllegalStateException("No centroid defined");
654                 
655                 double pipeRadius = getPipeRun().getPipeDiameter() / 2;
656                 
657                 switch (pcp.getType()) {
658                 case INLINE:
659                 case END:
660                         if (!pcp.isSizeChange()) {
661                                 // Just return the cylinder volume
662                                 return pcp.getLength() * Math.PI * pipeRadius * pipeRadius;
663                         }
664                         
665                         // Calculate center of mass for the frustum
666                         double r1 = pipeRadius;
667                         double r2 = getAlternativePipeRun().getPipeDiameter() / 2;
668                         return pcp.getLength() * Math.PI * (r1*r1 + r1*r2 + r2*r2) / 4;
669                 case TURN: {
670                         double r = ((TurnComponent)this).getTurnRadius();
671                         double a = pcp.getTurnAngle();
672                         return r * a * Math.PI * pipeRadius * pipeRadius;
673                 }
674                 default:
675                         throw new IllegalStateException("No centroid defined");
676                 }
677         }
678         
679         
680         private String error;
681         
682         /**
683          * Returns possible pipe modelling error, or null;
684          * @return
685          */
686         @GetPropertyValue(name="Error", value="error", tabId = "Default")
687         public String getError() {
688             return error;
689         }
690         
691         /**
692          * Sets pipe modelling error. 
693          * 
694          * Error is usually set by PipingRules.
695          * @param error
696          */
697         public void setError(String error) {
698             if (this.error == null) {
699                 if (error == null)
700                     return;
701             } else if (this.error.equals(error))
702                 return;
703             this.error = error;         
704             firePropertyChanged("error");
705         }
706 }