]> gerrit.simantics Code Review - simantics/3d.git/commitdiff
Support for fixed turn components 92/3092/1
authorMarko Luukkainen <marko.luukkainen@semantum.fi>
Mon, 12 Aug 2019 09:26:46 +0000 (12:26 +0300)
committerMarko Luukkainen <marko.luukkainen@semantum.fi>
Mon, 12 Aug 2019 09:26:46 +0000 (12:26 +0300)
gitlab #23

Change-Id: Idfee9d27e1209748199446734f94988e0f7d0e71

org.simantics.plant3d.ontology/graph/plant3d.pgraph
org.simantics.plant3d.ontology/graph/plant3d_builtins.pgraph
org.simantics.plant3d/src/org/simantics/plant3d/actions/AddComponentAction.java
org.simantics.plant3d/src/org/simantics/plant3d/geometry/ElbowGeometryProvider.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/TurnComponent.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java

index 46a6a34bd0476b1c238c332ef84ae519012e84d4..040dd889321339f6d9134157546ca380224cd4ee 100644 (file)
@@ -105,12 +105,13 @@ P3D.HasBranch9 <R P3D.Connects
     
 P3D.HasPipeDiameter <R G3D.hasNonTransformation
     L0.HasRange L0.Double
-//P3D.HasLength <R G3D.hasNonTransformation
-//    L0.HasRange L0.Double
-//P3D.HasTurnAngle <R G3D.hasNonTransformation
-//    L0.HasRange L0.Double
 P3D.HasRotationAngle <R G3D.hasTransformation
     L0.HasRange L0.Double
+P3D.HasLength <R G3D.hasNonTransformation
+    L0.HasRange L0.Double
+P3D.HasTurnAngle <R G3D.hasNonTransformation
+    L0.HasRange L0.Double
+
 //P3D.HasOffset <R L0.HasProperty
 //    L0.HasRange L0.Double
 //HasRelativePosition <R HasProperty
index 1f1f7cff06702d8a5f5d5b25b470bef457b5a99c..ed8cc0f16c4bc4bad81a6bdb3411a23f5c65d5ba 100644 (file)
@@ -136,3 +136,24 @@ P3D.Builtin.Cap <T P3D.EndComponent : P3D.EndComponent
            L0.HasName "radius" : L0.String
            P3D.hasParameterValue 0.1 : L0.Double
      
+P3D.Builtin.Elbow90 <T P3D.TurnComponent : P3D.TurnComponent  
+    @L0.tag P3D.FixedAngleTurnComponent
+    @L0.tag P3D.DualConnectedComponent
+    @L0.assert P3D.hasGeometry P3D.Builtin.ElbowGeometryProvider
+    @L0.assert P3D.hasParameter
+         _ :P3D.Parameter
+           L0.HasName "turnAngle" : L0.String
+           P3D.hasParameterValue 90.0 : L0.Double
+    @L0.assert P3D.HasTurnAngle
+         1.5707963267948966192313216916398 : L0.Double
+         
+P3D.Builtin.Elbow45 <T P3D.TurnComponent : P3D.TurnComponent  
+    @L0.tag P3D.FixedAngleTurnComponent
+    @L0.tag P3D.DualConnectedComponent
+    @L0.assert P3D.hasGeometry P3D.Builtin.ElbowGeometryProvider
+    @L0.assert P3D.hasParameter
+         _ :P3D.Parameter
+           L0.HasName "turnAngle" : L0.String
+           P3D.hasParameterValue 45.0 : L0.Double
+    @L0.assert P3D.HasTurnAngle
+         0.78539816339744830961566084581988 : L0.Double
\ No newline at end of file
index 5187025fc58c88fc42fa91e81812c38537d023e3..a4398a5455b582fab3c3192800593c9f921e5c7d 100644 (file)
@@ -159,26 +159,50 @@ public class AddComponentAction extends vtkSwtAction {
                        PipeControlPoint newPcp = newComponent.getControlPoint();
                        
                        PipeControlPoint toPcp = component.getControlPoint();
+                       PipeRun pipeRun = toPcp.getPipeRun();
                        
+                       Vector3d dir = null;
+                       Vector3d pos = null;
+               
                        
-                       switch (position) {
-                       case NEXT: 
-                               if (toPcp.isDualInline())
-                                       toPcp = toPcp.getSubPoint().get(0);
-                               
-                               break;
-                       case PREVIOUS:
-                               if (toPcp.isDualSub())
-                                       toPcp = toPcp.parent;
+                       if (toPcp.isInline()) {
+                           switch (position) {
+                   case NEXT: 
+                       if (toPcp.isDualInline())
+                           toPcp = toPcp.getSubPoint().get(0);
+                       
+                       break;
+                   case PREVIOUS:
+                       if (toPcp.isDualSub())
+                           toPcp = toPcp.parent;
+                   }
+                           Vector3d start = new Vector3d();
+                           Vector3d end = new Vector3d();
+                   dir = new Vector3d();
+                   toPcp.getInlineControlPointEnds(start, end, dir);
+                   dir.normalize();
+                   switch (position) {
+                case NEXT:
+                    pos = new Vector3d(end);
+                    break;
+                case PREVIOUS:
+                    pos = new Vector3d(start);
+                    break;
+                case SPLIT:
+                    break;
+                }
+                  
+                       } else if (toPcp.isDirected()) {
+                           dir = new Vector3d(toPcp.getDirection(Direction.NEXT));
+                           pos = new Vector3d(toPcp.getWorldPosition());
+                       } else if (toPcp.isTurn() && toPcp.isFixed()) {
+                           dir = new Vector3d(toPcp.getDirection(position == PositionType.NEXT ? Direction.NEXT : Direction.PREVIOUS));
+                pos = new Vector3d(toPcp.getWorldPosition());
+                Vector3d v = new Vector3d(dir);
+                v.scale(toPcp.getInlineLength());
+                pos.add(v);
                        }
                        
-                       Vector3d start = new Vector3d();
-                       Vector3d end = new Vector3d();
-                       Vector3d dir = new Vector3d();
-                       toPcp.getInlineControlPointEnds(start, end, dir);
-                       dir.normalize();
-                       
-                       PipeRun pipeRun = toPcp.getPipeRun();
                        
                        if (!toAdd.isSizeChange()) {
                                String name = component.getPipeRun().getUniqueName(toAdd.getName());
@@ -196,29 +220,37 @@ public class AddComponentAction extends vtkSwtAction {
                                        }
                                }
                                newComponent.updateParameters();
-
-                               dir.scale(newComponent.getControlPoint().getLength()*0.5);
-                               start.sub(dir);
-                               end.add(dir);
+                               
+                           Vector3d v = new Vector3d(dir);
+                v.scale(newComponent.getControlPoint().getInlineLength());
+                switch (position) {
+                case NEXT:
+                    pos.add(v);
+                    break;
+                case PREVIOUS:
+                    pos.sub(v);
+                    break;
+                case SPLIT:
+                    break;
+                }
+                               
                                switch (position) {
                                case NEXT: 
                                        if (toPcp.isDualInline())
                                                toPcp = toPcp.getSubPoint().get(0);
                                        newPcp.insert(toPcp, Direction.NEXT);
-                                       newPcp.setWorldPosition(end);
+                                       newPcp.setWorldPosition(pos);
                                        break;
                                case PREVIOUS:
                                        if (toPcp.isDualSub())
                                                toPcp = toPcp.parent;
                                        newPcp.insert(toPcp, Direction.PREVIOUS);
-                                       newPcp.setWorldPosition(start);
+                                       newPcp.setWorldPosition(pos);
                                        break;
                                case SPLIT:
                                        PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, true);
                                }
                        } else {
-                               
-                               
                                PipeRun other = new PipeRun();
                                String n = root.getUniqueName("PipeRun");
                                other.setName(n);
@@ -227,24 +259,28 @@ public class AddComponentAction extends vtkSwtAction {
                                root.addChild(other);
                                
                                
-                               
                                if (position == PositionType.NEXT) {
                                        PipingRules.addSizeChange(false, pipeRun, other, (InlineComponent)newComponent, toPcp, null);
-                                       newPcp.setWorldPosition(end);
                        } else if (position == PositionType.PREVIOUS){
                                PipingRules.addSizeChange(true, pipeRun, other, (InlineComponent)newComponent, toPcp, null);
-                               newPcp.setWorldPosition(start);
                        }
+                               newPcp.setWorldPosition(pos);
                                // TODO : chicken-egg problem
                                newComponent.updateParameters();
-                               dir.scale(newComponent.getControlPoint().getLength()*0.5);
-                               start.sub(dir);
-                               end.add(dir);
-                               if (position == PositionType.NEXT) {
-                                       newPcp.setWorldPosition(end);
-                               } else if (position == PositionType.PREVIOUS){
-                                       newPcp.setWorldPosition(start);
-                               }
+                           Vector3d v = new Vector3d(dir);
+                v.scale(newComponent.getControlPoint().getLength()*0.5);
+                switch (position) {
+                case NEXT:
+                    pos.add(v);
+                    break;
+                case PREVIOUS:
+                    pos.sub(v);
+                    break;
+                case SPLIT:
+                    break;
+                }
+                newPcp.setWorldPosition(pos);
+                               
                        }
                        
                        
index 70463979c56041af1673d92624ff622b51394afe..d3af06ad43c3a98f460a0d63c1aba3b831f16d34 100644 (file)
@@ -64,6 +64,15 @@ public class ElbowGeometryProvider extends BuiltinMeshProvider {
                if (radius < MathTools.NEAR_ZERO)
                        radius = MathTools.NEAR_ZERO;
        }
+       
+       @Override
+       public void updateCalculatedProperties(Map<String, Object> returnProps) {
+           double t = Math.tan((Math.PI - turnAngle) * 0.5);
+        double R = 0.0;
+        if (t > MathTools.NEAR_ZERO)
+            R = turnRadius / t;
+        returnProps.put("length", R);
+       }
 
 }
 
index 5f8139f42d919bfb40f3a22131f7302a1a25f85d..4f787b8c2a3f91b788d10e7c250c4812f04bdd71 100644 (file)
@@ -7,13 +7,17 @@ import javax.vecmath.Vector3d;
 
 import org.simantics.g3d.math.MathTools;
 import org.simantics.g3d.property.annotations.GetPropertyValue;
+import org.simantics.g3d.property.annotations.SetPropertyValue;
 import org.simantics.g3d.scenegraph.base.ParentNode;
 import org.simantics.objmap.graph.annotations.DynamicGraphType;
 import org.simantics.objmap.graph.annotations.GetType;
+import org.simantics.objmap.graph.annotations.RelatedGetValue;
+import org.simantics.objmap.graph.annotations.RelatedSetValue;
 import org.simantics.objmap.graph.annotations.SetType;
 import org.simantics.plant3d.ontology.Plant3D;
 import org.simantics.plant3d.scenegraph.controlpoint.ControlPointFactory;
 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
+import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
 
 @DynamicGraphType(Plant3D.URIs.TurnComponent)
 public class TurnComponent extends PipelineComponent {
@@ -59,11 +63,33 @@ public class TurnComponent extends PipelineComponent {
                return map;
        }
        
+       public boolean isVariableAngle() {
+        return !controlPoint.isFixed();
+    }
        
+       @Override
+       public void updateParameters() {
+           super.updateParameters();
+           if (!isVariableAngle()) {
+            Map<String,Object> calculated = getCalculatedParameters();
+            if (calculated.containsKey("length")) {
+                controlPoint.setLength((Double)calculated.get("length"));
+            }
+        }
+       }
+       
+       @RelatedGetValue(Plant3D.URIs.HasTurnAngle)
        public Double getTurnAngle() {
                return getControlPoint().getTurnAngle();
        }
        
+       @RelatedSetValue(Plant3D.URIs.HasTurnAngle)
+       public void setTurnAngle(Double a) {
+           if (!getControlPoint().isFixed())
+               return;
+           getControlPoint().setTurnAngle(a);            
+       }
+       
        @GetPropertyValue(name="Turn Angle", value="turn angle", tabId = "Default")
        public Double getTurnAngleDeg() {
                Double d = getControlPoint().getTurnAngle();
@@ -81,6 +107,37 @@ public class TurnComponent extends PipelineComponent {
                return getTurnAxis();
        }
        
+       @RelatedGetValue(Plant3D.URIs.HasRotationAngle)
+    @GetPropertyValue(name="Rotation Angle", value=Plant3D.URIs.HasRotationAngle, tabId = "Default")
+    public Double getRotationAngle() {
+        if (!controlPoint.isFixed())
+            return null;
+        Double d = controlPoint.getRotationAngle();
+        if (d == null)
+            return 0.0;
+        return MathTools.radToDeg(d);
+    }
+    @RelatedSetValue(Plant3D.URIs.HasRotationAngle)
+    @SetPropertyValue(value=Plant3D.URIs.HasRotationAngle)
+    public void setRotationAngle(Double angle) {
+        if (!controlPoint.isFixed())
+            return;
+        
+        if (angle == null || Double.isInfinite(angle) || Double.isNaN(angle)) {
+            return;
+        }
+        angle = MathTools.degToRad(angle);
+        if (controlPoint.getRotationAngle() != null && Math.abs(controlPoint.getRotationAngle()-angle) < MathTools.NEAR_ZERO)
+            return;
+        controlPoint.setRotationAngle(angle);
+        try {
+            PipingRules.requestUpdate(getControlPoint());
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }   
+    }
+       
        @Override
        protected double[] getColor() {
                if (getControlPoint() == null || !getControlPoint().isFixed())
index ecaeb7ae7c41d2b70017d4de433dfac968270ecc..22c58ddb160be1a1f43303b34e63d1dd3614bc17 100644 (file)
@@ -283,6 +283,12 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                return reversed;
        }
        
+       public boolean _getReversed() {
+           if (reversed == null)
+               return false;
+        return reversed;
+    }
+       
        public void setTurnAngle(Double turnAngle) {
                if (Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
                        return;
@@ -320,7 +326,7 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
        
        public void setReversed(Boolean reversed) {
                this.reversed = reversed;
-               firePropertyChanged("rotationAngle");
+               firePropertyChanged("reversed");
        }
        
        public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
@@ -466,28 +472,59 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                        return q1;
            }
        
-       public Vector3d getDirection() {
-               return getDirectedControlPointDirection();
+       public Vector3d getDirection(Direction direction) {
+           if (isDirected())
+               return getDirectedControlPointDirection();
+           if (isTurn() && isFixed()) {
+               if (direction == Direction.NEXT) {
+                   if (previous != null) {
+                   PipeControlPoint pcp = this;
+                    Vector3d dir = new Vector3d();
+                    dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
+                    if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                         dir.normalize();
+                    Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
+                    AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
+                    Quat4d q2 = MathTools.getQuat(aa);
+                    Vector3d v = new Vector3d(1.,0.,0.);
+                    Vector3d offset = new Vector3d();
+                    MathTools.rotate(q2, v, offset);
+                    MathTools.rotate(q, offset, dir);
+                    return dir;
+                   }
+               } else {
+                   if (next != null) {
+                   PipeControlPoint pcp = this;
+                    Vector3d dir = new Vector3d();
+                    dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
+                    if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                         dir.normalize();
+                    Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
+                    AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
+                    Quat4d q2 = MathTools.getQuat(aa);
+                    Vector3d v = new Vector3d(1.,0.,0.);
+                    Vector3d offset = new Vector3d();
+                    MathTools.rotate(q2, v, offset);
+                    MathTools.rotate(q, offset, dir);
+                    return dir;
+                   }
+               }
+           }
+           return null;
        }
        
-       
-       
-       
-       
-
-       
        public void insert(PipeControlPoint previous, PipeControlPoint next) {
                // inserting an offsetpoint is error, 
                if (isDualSub())
-                       throw new RuntimeException();
+                       throw new RuntimeException("Dual sub points cannot be inserted.");
                // size change control point cannot be inserted this way, because it ends PipeRun
                if (isSizeChange())
-                       throw new RuntimeException();
+                       throw new RuntimeException("Size change points cannot be inserted.");
                PipeRun piperun = previous.getPipeRun();
                // and just to make sure that control point structure is not corrupted
                if (getPipeRun() != null) {
                        if (piperun != getPipeRun() || piperun != next.getPipeRun())
-                               throw new RuntimeException();
+                               throw new RuntimeException("All controls points must be located on the same pipe run");
                } else {
                        piperun.addChild(this);
                }
@@ -623,14 +660,14 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                v.sub(next.getWorldPosition(),pcp.getWorldPosition());
                                return v;
                        } else {
-                               if (isVariableAngle())
-                                       throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
                                if (previous == null) {
                                        if (!isDirected())
                                                throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
                                        return getDirectedControlPointDirection();
                                        
                                } else {
+                                   if (isVariableAngle())
+                           throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
                                        if (isInline()) {
                                                PipeControlPoint pcp = this;
                                                if (pcp.isDualSub()) {
@@ -645,6 +682,8 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                                Vector3d v = new Vector3d();
                                                v.sub(getWorldPosition(),previous.getWorldPosition());
                                                return v;
+                                       } else if (isTurn() && isFixed() && !_getReversed()) {
+                                          return getDirection(Direction.NEXT);
                                        }
                                        throw new RuntimeException("Missing implementation");
                                }
@@ -658,8 +697,6 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
                                return v;
                        } else {
-                               if (isVariableAngle())
-                                       throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
                                if (next == null)  {
                                        if (!isDirected())
                                                throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
@@ -667,7 +704,9 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                        v.negate();
                                        return v;
                                } else {
-                                       if (isInline()) {
+                                   if (isVariableAngle())
+                           throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
+                       if (isInline()) {
                                                PipeControlPoint pcp = this;
                                                if (pcp.isDualInline()) {
                                                        pcp = pcp.getSubPoint().get(0);
@@ -683,7 +722,9 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                                Vector3d v = new Vector3d();
                                                v.sub(getWorldPosition(),next.getWorldPosition());
                                                return v;
-                                       }
+                                       } else if (isTurn() && isFixed() && _getReversed()) {
+                                           return getDirection(Direction.PREVIOUS);
+                    }
                                        throw new RuntimeException("Missing implementation");
                                }
                        }
index 3fc0488105fe8a24a8bd7197a188fb448ec3bfe7..be66cf1441954cdebad851999b4b58c96599b7c4 100644 (file)
@@ -442,12 +442,28 @@ public class PipingRules {
                updatePathLeg(new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, reversed, toRemove, updated), lengthChange);
 
        }
-
+       
+       private static boolean asDirected(PipeControlPoint pcp, Direction direction) {
+           if (pcp.isDirected())
+               return true;
+           if (pcp.isTurn() && pcp.isFixed()) {
+              if (!pcp._getReversed())
+                  return direction == Direction.NEXT;
+              else
+                  return direction == Direction.PREVIOUS;
+           }
+           return false;
+       }
+       
+       private static Vector3d direction(PipeControlPoint pcp, Direction direction) {
+           return pcp.getDirection(direction);
+       }
        private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
                int directed = 0;
-               if (u.start.isDirected())
+               if (asDirected(u.start, Direction.NEXT))
                        directed++;
-               if (u.end.isDirected())
+               if (asDirected(u.end, Direction.PREVIOUS))
                        directed++;
                switch (directed) {
                case 0:
@@ -467,7 +483,7 @@ public class PipingRules {
                if (DEBUG)
                        System.out.println("PipingRules.updateFreePipeRun " + u + " " + lengthChange);
                checkExpandPathLeg(u, lengthChange);
-               if (u.start.isInline() || u.end.isInline())
+               if (u.start.isInline() || u.end.isInline() || u.start.isFixed() || u.end.isFixed())
                        processPathLeg(u, true, false);
        }
 
@@ -789,7 +805,7 @@ public class PipingRules {
                boolean dcpStart = false;
                boolean inlineEnd = false;
                Vector3d position;
-               if (u.start.isDirected()) {
+               if (asDirected(u.start, Direction.NEXT)) {
                        dcp = u.start;
                        other = u.end;
                        position = u.startPoint;
@@ -807,7 +823,7 @@ public class PipingRules {
                        inlineEnd = u.start.isInline();
                }
 
-               Vector3d directedDirection = dcp.getDirection();
+               Vector3d directedDirection = direction(dcp, dcpStart ? Direction.NEXT : Direction.PREVIOUS);
                Point3d directedEndPoint = new Point3d(u.endPoint);
                if (u.hasOffsets)
                        directedEndPoint.add(u.offset);
@@ -828,6 +844,8 @@ public class PipingRules {
                double distance = t.length();
                boolean aligned = (distance < ALLOWED_OFFSET);
                if (aligned) {
+                   if (u.start.isInline() || u.end.isInline() || u.start.isFixed() || u.end.isFixed())
+                   processPathLeg(u, true, false);
                        checkExpandPathLeg(u, lengthChange, inlineEnd);
                        
                } else {
@@ -944,8 +962,8 @@ public class PipingRules {
                position1offset.sub(u.offset);
                Point3d position2offset = new Point3d(position2);
                position2offset.add(u.offset);
-               Vector3d dir1 = dcp1.getDirection();
-               Vector3d dir2 = dcp2.getDirection();
+               Vector3d dir1 = direction(dcp1, Direction.NEXT);
+               Vector3d dir2 = direction(dcp2, Direction.PREVIOUS);
                Vector3d p1 = MathTools.closestPointOnStraight(position1offset, position2, dir2);
                Vector3d p2 = MathTools.closestPointOnStraight(position2offset, position1, dir1);
                double d1 = position1.distance(new Point3d(p1));
@@ -1516,12 +1534,43 @@ public class PipingRules {
        private static double updateTurnControlPointTurn(PipeControlPoint tcp, PipeControlPoint prev, PipeControlPoint next) {
                if (DEBUG)
                        System.out.println("PipingTools.updateTurnControlPointTurn()" + tcp);
-               if (next == null || prev == null)
-                       return Math.PI; // FIXME : argh
-               Vector3d middlePoint = tcp.getWorldPosition();
-               Vector3d nextPoint = next.getWorldPosition();
-               Vector3d prevPoint = prev.getWorldPosition();
-               return updateTurnControlPointTurn(tcp, middlePoint, prevPoint, nextPoint);
+               
+               if (!tcp.isFixed()) {
+                   if (next == null || prev == null)
+                   return Math.PI; // FIXME : argh
+               Vector3d middlePoint = tcp.getWorldPosition();
+               Vector3d nextPoint = next.getWorldPosition();
+               Vector3d prevPoint = prev.getWorldPosition();
+               return updateTurnControlPointTurn(tcp, middlePoint, prevPoint, nextPoint);
+               } else {
+                   Vector3d dir;
+                   if (!tcp._getReversed()) {
+                   if (prev == null)
+                   return Math.PI; // FIXME : argh
+                   
+                   Vector3d middlePoint = tcp.getWorldPosition();
+                   Vector3d prevPoint = prev.getWorldPosition();
+                   dir = new Vector3d();
+                   dir.sub(middlePoint, prevPoint);        
+                  
+                   } else {
+                       if (next == null)
+                    return Math.PI; // FIXME : argh
+                
+                Vector3d middlePoint = tcp.getWorldPosition();
+                Vector3d nextPoint = next.getWorldPosition();
+                dir = new Vector3d();
+                dir.sub(nextPoint,middlePoint);
+                   }
+                   dir.normalize();
+            
+            Quat4d q = PipeControlPoint.getControlPointOrientationQuat(dir, tcp.getRotationAngle() != null ? tcp.getRotationAngle() : 0.0);
+            Vector3d v = new Vector3d();
+            MathTools.rotate(q, MathTools.Y_AXIS,v);
+            tcp.setTurnAxis(v);
+            tcp.setWorldOrientation(q);
+            return tcp.getTurnAngle();
+               }
        }
 
        /**