]> gerrit.simantics Code Review - simantics/3d.git/commitdiff
Added support for eccentric reducers 19/3019/1
authorMarko Luukkainen <marko.luukkainen@semantum.fi>
Tue, 16 Jul 2019 10:50:28 +0000 (13:50 +0300)
committerMarko Luukkainen <marko.luukkainen@semantum.fi>
Tue, 16 Jul 2019 10:50:28 +0000 (13:50 +0300)
* Various fixes to PipingRules
* At the moment, offset calculation is hard-coded. (Introduced component
calculated offset, but that has not been tested)
* AddComponentAction is able to place components in proper location
after reducer
* Reducer uses custom mesh

gitlab #12
gitlab #10

Change-Id: I62fdd4df5acc9e8e02823d36b738b48f70f56ac4

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/ReducerGeometryProvider.java
org.simantics.plant3d/src/org/simantics/plant3d/geometry/StraightGeometryProvider.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/InlineComponent.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/ControlPointFactory.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java

index 5c3868711bb612542200dfd0c7754eb5a1e224e5..49bec3ea8cc8de2dfd6a44d1f2e4fb2ccbf3275f 100644 (file)
@@ -26,6 +26,7 @@ P3D.Builtin.ConcentricReducer <T P3D.InlineComponent : P3D.InlineComponent
 //  HasControlPoint 
 //    _ : SizeChangeControlPoint
 P3D.Builtin.EccentricReducer <T P3D.InlineComponent : P3D.InlineComponent 
+    @L0.assert P3D.hasGeometry P3D.Builtin.ReducerGeometryProvider
     @L0.tag P3D.OffsetComponent
     @L0.tag P3D.SizeChangeComponent
     @L0.tag P3D.FixedLengthInlineComponent
index bf220dd8776dc03959acc6a1410e78771a4adac1..4c116c1f26efb73d9453e850e299a43cb89992f9 100644 (file)
@@ -159,11 +159,7 @@ public class AddComponentAction extends vtkAction {
                        PipeControlPoint newPcp = newComponent.getControlPoint();
                        
                        PipeControlPoint toPcp = component.getControlPoint();
-                       Vector3d start = new Vector3d();
-                       Vector3d end = new Vector3d();
-                       Vector3d dir = new Vector3d();
-                       toPcp.getInlineControlPointEnds(start, end, dir);
-                       dir.normalize();
+                       
                        
                        switch (position) {
                        case NEXT: 
@@ -175,6 +171,13 @@ public class AddComponentAction extends vtkAction {
                                if (toPcp.isDualSub())
                                        toPcp = toPcp.parent;
                        }
+                       
+                       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()) {
index d3387f799181226eda8fe0ae373ec674d8a5bb4d..5ca0330dd34d0c38e12c248af81e7902d4d1605b 100644 (file)
@@ -1,15 +1,24 @@
 package org.simantics.plant3d.geometry;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
+import javax.vecmath.Point3d;
+import javax.vecmath.Tuple3d;
+import javax.vecmath.Vector3d;
+
 import org.jcae.opencascade.jni.TopoDS_Shape;
 import org.simantics.db.Resource;
 import org.simantics.g3d.math.MathTools;
+import org.simantics.g3d.shape.Mesh;
+import org.simantics.g3d.shape.Tube;
 import org.simantics.opencascade.OccTriangulator;
 
-public class ReducerGeometryProvider extends BuiltinGeometryProvider  {
+//public class ReducerGeometryProvider extends BuiltinGeometryProvider  {
+public class ReducerGeometryProvider extends BuiltinMeshProvider  {
        
        public ReducerGeometryProvider(Resource resource) {
                super(resource);
@@ -17,8 +26,9 @@ public class ReducerGeometryProvider extends BuiltinGeometryProvider  {
 
        private double radius = 0.01;
        private double radius2 = 0.02;
+       private double offset = 0.0;
        
-       @Override
+//     @Override
        public Collection<TopoDS_Shape> getModel() throws Exception {
 //             GP_Circ circ = new GP_Circ(new double[]{-length*0.5, 0.0, 0.0,1.0,0.0,0.0}, radius);
 //             GP_Circ circ2 = new GP_Circ(new double[]{length*0.5, 0.0, 0.0,1.0,0.0,0.0}, radius2);
@@ -33,6 +43,29 @@ public class ReducerGeometryProvider extends BuiltinGeometryProvider  {
                return Collections.singletonList(shape);
        }
        
+       @Override
+       public Mesh getMesh() {
+               double length = Math.max(0.1, Math.abs(radius-radius2)*4.0);
+               if (length < .0001)
+                       return null;
+               Tube tube = new Tube();
+               tube.setResolution(16);
+               List<Tuple3d> vertices = new ArrayList<Tuple3d>();
+               List<Double> radius = new ArrayList<Double>();
+               List<Vector3d> tangents = new ArrayList<Vector3d>();
+               vertices.add(new Point3d(-length*0.5, 0.0, 0.0));
+               vertices.add(new Point3d( length*0.5, offset, 0.0));
+               radius.add(this.radius);
+               radius.add(this.radius2);
+               tangents.add(new Vector3d(1.0,0.0,0.0));
+               tangents.add(new Vector3d(1.0,0.0,0.0));
+               tube.setVertices(vertices);
+               tube.setRadiis(radius);
+               tube.setTangents(tangents);
+               tube.setCap(false);
+               return tube.create();
+       }
+       
        @Override
        public void setProperties(Map<String, Object> props) {
 
@@ -44,6 +77,10 @@ public class ReducerGeometryProvider extends BuiltinGeometryProvider  {
                        radius2 = (Double)props.get("radius2");
                }
                
+               if (props.containsKey("offset")) {
+                       offset = (Double)props.get("offset");
+               }
+               
                
                
        }
index df3281eceb92607246837dabc9f0e9e1e99d448d..6879b2238628ff2c759912d5d1a891972b03f31f 100644 (file)
@@ -32,7 +32,7 @@ public class StraightGeometryProvider extends BuiltinMeshProvider {
        
        @Override
        public Mesh getMesh() {
-               if (length < .0001)
+               if (length < .001)
                        return null;
                Tube tube = new Tube();
                tube.setResolution(16);
index 88b1fab3a29a6f8d5214d30fe4b46533914d1a64..a1558c8642f4f527abc13cda702c8484db441aa1 100644 (file)
@@ -16,6 +16,7 @@ public class InlineComponent extends PipelineComponent {
 
        private String type;
        private PipeControlPoint controlPoint;
+       private boolean componentCalculatedOffset = false;
        
        @GetType(Plant3D.URIs.InlineComponent)
        public String getType() {
@@ -52,9 +53,39 @@ public class InlineComponent extends PipelineComponent {
                        if (calculated.containsKey("length")) {
                                controlPoint.setLength((Double)calculated.get("length"));
                        }
+                       if (calculated.containsKey("offset")) {
+                               controlPoint.setOffset((Double)calculated.get("offset"));
+                               componentCalculatedOffset = true;
+                       } else {
+                               componentCalculatedOffset = false;
+                       }
                }
        }
        
+       @Override
+       public void setPipeRun(PipeRun pipeRun) {
+               // TODO Auto-generated method stub
+               super.setPipeRun(pipeRun);
+               if (getPipeRun() != null && getAlternativePipeRun() != null) {
+                       updateOffset();
+               }
+       }
+       
+       @Override
+       public void setAlternativePipeRun(PipeRun pipeRun) {
+               // TODO Auto-generated method stub
+               super.setAlternativePipeRun(pipeRun);
+               if (getPipeRun() != null && getAlternativePipeRun() != null) {
+                       updateOffset();
+               }
+       }
+       
+       private void updateOffset() {
+               if (!componentCalculatedOffset && getControlPoint().isOffset()) {
+                       getControlPoint().setOffset(getPipeRun().getPipeDiameter()*0.5 - getAlternativePipeRun().getPipeDiameter()*0.5);
+               }
+       }
+
        @Override
        public Map<String, Object> updateParameterMap() {
                Map<String,Object> map = new HashMap<String, Object>();
@@ -63,9 +94,12 @@ public class InlineComponent extends PipelineComponent {
                                map.put("length", controlPoint.getLength());
                        if (controlPoint.isDualInline()) {
                                PipeControlPoint sub = controlPoint.getSubPoint().get(0);
-                               PipeRun pipeRun = sub.getPipeRun();
-                               if (pipeRun != null) {
-                                       map.put("radius2", pipeRun.getPipeDiameter() * 0.5);
+                               PipeRun pipeRun2 = sub.getPipeRun();
+                               if (pipeRun2 != null) {
+                                       map.put("radius2", pipeRun2.getPipeDiameter() * 0.5);
+                               }
+                               if (controlPoint.isOffset() && !componentCalculatedOffset) {
+                                       map.put("offset", controlPoint.getOffset());
                                }
                        }
                }
index 193cdd27d16745d6483b83ee99a802185604ca06..a6d1eb26880e7faed3b609181569980d2228e89f 100644 (file)
@@ -57,6 +57,7 @@ public class ControlPointFactory {
                                sub.setType(inst.type);
                                sub.setFixed(inst.fixed);
                                sub.setSub(true);
+                               sub.setDeletable(false);
 //                             pcp.setOffset(0.0);
                                if (inst.isOffset)
                                        pcp.setOffset(0.0);
index 02bce62246e8748612ac72f7e2b64afae4da06de..21f2564d781e87b44fc1713264cf0640e002a618 100644 (file)
@@ -173,7 +173,9 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                component.setNext(next != null ? next.component : null);
                        else
                                component.setBranch0(next != null ? next.component : null);
+                       updateSubPoint();
                }
+               
        }
        
        public void setPrevious(PipeControlPoint previous) {
@@ -182,11 +184,14 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
 //             if (previous != null && getPipeRun() == null)
 //                     throw new RuntimeException("Cannot connect control point befor piperun has been set");
                this.previous = previous;
-               if (component != null)
+               if (component != null) {
                        if (parent == null || sub)
                                component.setPrevious(previous != null ? previous.component : null);
                        else
                                component.setBranch0(previous != null ? previous.component : null);
+                       updateSubPoint();
+               }
+               
        }
        
        public PipeControlPoint parent;
@@ -290,7 +295,17 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                if (rotationAngle == null)
                        rotationAngle = 0.0;
                Quat4d q = getControlPointOrientationQuat(dir, rotationAngle);
-               Vector3d v = new Vector3d(0.0,0.0,offset);
+               Vector3d v = new Vector3d(0.0,offset,0.0);
+       Vector3d offset = new Vector3d();
+       MathTools.rotate(q, v, offset);
+       return offset;
+       }
+       
+       public Vector3d getSizeChangeOffsetVector() {
+               if (rotationAngle == null)
+                       rotationAngle = 0.0;
+               Quat4d q = getControlPointOrientationQuat(rotationAngle);
+               Vector3d v = new Vector3d(0.0,offset,0.0);
        Vector3d offset = new Vector3d();
        MathTools.rotate(q, v, offset);
        return offset;
@@ -547,8 +562,12 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                        
                                } else {
                                        if (isInline()) {
+                                               PipeControlPoint pcp = this;
+                                               if (pcp.isDualSub()) {
+                                                       pcp = pcp.getParentPoint();
+                                               }
                                                Vector3d v = new Vector3d();
-                                               v.sub(getWorldPosition(),previous.getWorldPosition());
+                                               v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
                                                return v;
                                        } else if (isDirected()) {
                                                return getDirectedControlPointDirection();
@@ -579,8 +598,12 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                                        return v;
                                } else {
                                        if (isInline()) {
+                                               PipeControlPoint pcp = this;
+                                               if (pcp.isDualInline()) {
+                                                       pcp = pcp.getSubPoint().get(0);
+                                               }
                                                Vector3d v = new Vector3d();
-                                               v.sub(getWorldPosition(),next.getWorldPosition());
+                                               v.sub(pcp.getWorldPosition(),next.getWorldPosition());
                                                return v;
                                        } else if (isDirected()) {
                                                Vector3d v = getDirectedControlPointDirection();
@@ -1031,10 +1054,7 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                super.setOrientation(orientation);
                if (getParentPoint() == null && component != null)
                        component._setWorldOrientation(getWorldOrientation());
-               for (PipeControlPoint sub : getSubPoint()) {
-                       sub.setWorldPosition(getWorldPosition());
-                       sub.setWorldOrientation(getWorldOrientation());
-               }
+               updateSubPoint();
        }
        
        @Override
@@ -1044,9 +1064,31 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                super.setPosition(position);
                if (getParentPoint() == null && component != null)
                        component._setWorldPosition(getWorldPosition());
-               for (PipeControlPoint sub : getSubPoint()) {
-                       sub.setWorldPosition(getWorldPosition());
-                       sub.setWorldOrientation(getWorldOrientation());
+               if (isDualSub())
+                       System.out.println();
+               updateSubPoint();
+       }
+       
+       private void updateSubPoint() {
+               if (isOffset()) {
+                       if (next == null && previous == null) {
+                               for (PipeControlPoint sub : getSubPoint()) {
+                                       sub.setWorldPosition(getWorldPosition());
+                                       sub.setWorldOrientation(getWorldOrientation());
+                               }
+                               return;
+                       }
+                       for (PipeControlPoint sub : getSubPoint()) {
+                               Vector3d wp = getWorldPosition();
+                               wp.add(getSizeChangeOffsetVector());
+                               sub.setWorldPosition(wp);
+                               sub.setWorldOrientation(getWorldOrientation());
+                       }
+               } else {
+                       for (PipeControlPoint sub : getSubPoint()) {
+                               sub.setWorldPosition(getWorldPosition());
+                               sub.setWorldOrientation(getWorldOrientation());
+                       }
                }
        }
 
@@ -1054,19 +1096,13 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
        public void _setWorldPosition(Vector3d position) {
                Vector3d localPos = getLocalPosition(position);
                super.setPosition(localPos);
-               for (PipeControlPoint sub : getSubPoint()) {
-                       sub.setWorldPosition(getWorldPosition());
-                       sub.setWorldOrientation(getWorldOrientation());
-               }
+               updateSubPoint();
        }
        
        public void _setWorldOrientation(Quat4d orientation) {
                Quat4d localOr = getLocalOrientation(orientation);
                super.setOrientation(localOr);
-               for (PipeControlPoint sub : getSubPoint()) {
-                       sub.setWorldPosition(getWorldPosition());
-                       sub.setWorldOrientation(getWorldOrientation());
-               }
+               updateSubPoint();
        }
        
        @Override
index d7c53d4a085675b603473ac67d2f6579dfafe467..8cbe5a8ae997e9808df5a34e37cf587af2d625c5 100644 (file)
@@ -337,18 +337,64 @@ public class PipingRules {
 
        private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, ArrayList<PipeControlPoint> list, Vector3d dir, Vector3d offset) {
                boolean hasOffsets = false;
-               dir.set(startPoint);
-               dir.sub(endPoint);
-               if (dir.lengthSquared() > MathTools.NEAR_ZERO)
-                       dir.normalize();
-               offset.set(0.0, 0.0, 0.0);
+               List<PipeControlPoint> offsets = new ArrayList<PipeControlPoint>(list.size());
                for (PipeControlPoint icp : list) {
                        if (icp.isOffset()) {
-                               hasOffsets = true;
-                               offset.add(icp.getSizeChangeOffsetVector(dir));
+                               offsets.add(icp);
                        } else if (icp.isDualSub())
                                ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
                }
+               if (offsets.size() == 0) {
+                       dir.set(startPoint);
+                       dir.sub(endPoint);
+                       double l = dir.lengthSquared(); 
+                       if (l > MathTools.NEAR_ZERO)
+                               dir.scale(1.0/Math.sqrt(l));
+                       offset.set(0.0, 0.0, 0.0);
+                       return false;
+               } else {
+                       Vector3d sp = new Vector3d(startPoint);
+                       Point3d ep = new Point3d(endPoint);
+                       dir.set(sp);
+                       dir.sub(ep);
+                       double l = dir.lengthSquared(); 
+                       if (l > MathTools.NEAR_ZERO)
+                               dir.scale(1.0/Math.sqrt(l));
+                       int iter = 100;
+                       while (iter >= 0) {
+                               iter--;
+                               offset.set(0.0, 0.0, 0.0);
+                               
+                               for (PipeControlPoint icp : offsets) {
+                                       Vector3d v = icp.getSizeChangeOffsetVector(dir);
+                                       offset.add(v);
+                               }
+                               Point3d nep = new Point3d(endPoint);
+                               nep.sub(offset);
+                               if (nep.distance(ep) < 0.0000000001) {
+                                       break;
+                               } 
+                               ep = nep;
+                               dir.set(sp);
+                               dir.sub(ep);
+                               l = dir.lengthSquared(); 
+                               if (l > MathTools.NEAR_ZERO)
+                                       dir.scale(1.0/Math.sqrt(l));
+                       }
+                       hasOffsets = true;
+               }
+               
+//             for (PipeControlPoint icp : list) {
+//                     if (icp.isOffset()) {
+//                             icp.setOffset(((InlineComponent)icp.getPipelineComponent()).getOffset());
+//                             hasOffsets = true;
+//                             Vector3d v = icp.getSizeChangeOffsetVector(dir);
+//                             offset.add(v);
+//                     } else if (icp.isDualSub())
+//                             ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+//             }
+               if (DEBUG && hasOffsets)
+                       System.out.println("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ;
                return hasOffsets;
        }
 
@@ -420,6 +466,13 @@ public class PipingRules {
                                MathTools.mad(end, u.dir, -0.1);
                                for (PipeControlPoint icp : u.list) {
                                        updateInlineControlPoint(icp, start, end, u.dir);
+                                       
+                                       if (icp.isOffset()) {
+                                               // TODO : offset vector is already calculated and should be
+                                               // cached
+                                               Vector3d off = icp.getSizeChangeOffsetVector(u.dir);
+                                               updateOffsetPoint(icp, off);
+                                       }
                                }
                                return;
                        }
@@ -438,51 +491,13 @@ public class PipingRules {
                        for (int i = 1; i < pathLegPoints.size(); i++) {
                                PipeControlPoint icp = pathLegPoints.get(i);
 
-                               PipeControlPoint prev;
-                               Vector3d prevPos;
-                               prev = pathLegPoints.get(i - 1);
-                               prevPos = prev.getWorldPosition();
-                               Vector3d currentPos = icp.getWorldPosition();
+                               PipeControlPoint prev = pathLegPoints.get(i - 1);
+                               
 
                                if (icp.isVariableLength()) {
                                        if (i != pathLegPoints.size() - 1) {
-                                               PipeControlPoint next;
-                                               Vector3d nextPos;
-                                               next = pathLegPoints.get(i + 1);
-                                               nextPos = next.getWorldPosition();
-                                               Vector3d dir = new Vector3d(nextPos);
-                                               dir.sub(prevPos);
-                                               double l = dir.lengthSquared(); // distance between
-                                                                                                               // control points
-                                                                                                               // (square)
-                                               double l2prev = prev.getInlineLength(); // distance
-                                                                                                                                                                       // taken
-                                                                                                                                                                       // by
-                                                                                                                                                                       // components
-                                               double l2next = next.getInlineLength();
-                                               double l2 = l2prev + l2next;
-                                               double l2s = MathTools.square(l2);
-                                               if (l2s < l) { // check if there is enough space for
-                                                                               // variable length component.
-                                                       // components fit
-                                                       dir.normalize();
-                                                       double length = Math.sqrt(l) - l2; // true length of
-                                                                                                                               // the variable
-                                                                                                                               // length
-                                                                                                                               // component
-                                                       dir.scale(length * 0.5 + l2prev); // calculate
-                                                                                                                               // center
-                                                                                                                               // position of
-                                                                                                                               // the component
-                                                       dir.add(prevPos);
-                                                       icp.setWorldPosition(dir);
-                                                       icp.setLength(length);
-                                               } else {
-                                                       // components leave no space to the component and it
-                                                       // must be removed
-                                                       if (icp.isDeletable())
-                                                               icp._remove();
-                                               }
+                                               PipeControlPoint next = pathLegPoints.get(i + 1);
+                                               updateVariableLength(icp,  prev, next);
 
                                        } else {
                                                // this is variable length component at the end of the
@@ -490,41 +505,7 @@ public class PipingRules {
                                                // the problem is that we want to keep unconnected end
                                                // of the component in the same
                                                // place, but center of the component must be moved.
-                                               double currentLength = icp.getLength();
-                                               
-                                               Vector3d dir = new Vector3d();
-                                               dir.sub(currentPos, prevPos);
-                                               
-                                               if (currentLength < MathTools.NEAR_ZERO) {
-                                                       currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
-                                               }
-                                               
-                                               if (dir.lengthSquared() > MathTools.NEAR_ZERO)
-                                                       dir.normalize();
-                                               Point3d endPos = new Point3d(dir);
-                                               endPos.scale(currentLength * 0.5);
-                                               endPos.add(currentPos); // this is the free end of the
-                                                                                               // component
-
-                                               double offset = prev.getInlineLength();
-                                               Point3d beginPos = new Point3d(dir);
-                                               beginPos.scale(offset);
-                                               beginPos.add(prevPos); // this is the connected end of
-                                                                                               // the component
-
-                                               double l = beginPos.distance(endPos);
-                                               
-                                               if (Double.isNaN(l))
-                                                       System.out.println();
-
-                                               dir.scale(l * 0.5);
-                                               beginPos.add(dir); // center position
-
-                                               if (DEBUG)
-                                                       System.out.println("PipingRules.updateInlineControlPoints() setting variable length to " + l);
-                                               icp.setLength(l);
-
-                                               icp.setWorldPosition(new Vector3d(beginPos));
+                                               updateVariableLengthEnd(icp, prev);
                                        }
 
 
@@ -534,6 +515,8 @@ public class PipingRules {
                                        // space between them.
                                        // I there is, we'll have to create new variable length
                                        // component between them.
+                                       Vector3d currentPos = icp.getWorldPosition();
+                                       Vector3d prevPos = prev.getWorldPosition();
                                        Vector3d dir = new Vector3d(currentPos);
                                        dir.sub(prevPos);
                                        double l = dir.lengthSquared();
@@ -559,23 +542,143 @@ public class PipingRules {
                                }
                        }
                } else {
-                       u.endPoint.sub(u.offset);
-                       // FIXME : straights
+                       Vector3d sp = new Vector3d(u.startPoint);
+                       Vector3d ep = new Vector3d(u.endPoint);
+                       ep.sub(u.offset);
+                       
+                       ArrayList<PipeControlPoint> pathLegPoints = new ArrayList<PipeControlPoint>();
+                       pathLegPoints.add(u.start);
+
                        for (PipeControlPoint icp : u.list) {
-                               updateInlineControlPoint(icp, u.startPoint, u.endPoint, u.dir);
+                               updateInlineControlPoint(icp, sp, ep, u.dir);
                                updateBranchControlPointBranches(icp);
-                               
+                               pathLegPoints.add(icp);
                                if (icp.isOffset()) {
                                        // TODO : offset vector is already calculated and should be
                                        // cached
-                                       u.offset = icp.getSizeChangeOffsetVector(u.dir);
-                                       updateOffsetPoint(icp, u.offset);
-                                       u.startPoint.add(u.offset);
-                                       u.endPoint.add(u.offset);
+                                       Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
+                                       updateOffsetPoint(icp, offset);
+                                       sp.add(offset);
+                                       ep.add(offset);
+                               }
+                       }
+                       pathLegPoints.add(u.end);
+                       
+                       sp = new Vector3d(u.startPoint);
+                       ep = new Vector3d(u.endPoint);
+                       ep.sub(u.offset);
+                       
+                       for (int i = 1; i < pathLegPoints.size(); i++) {
+                               PipeControlPoint icp = pathLegPoints.get(i);
+
+                               PipeControlPoint prev = pathLegPoints.get(i - 1);
+                               if (prev.isDualInline())
+                                       prev = prev.getSubPoint().get(0);
+                               
+
+                               if (icp.isVariableLength()) {
+                                       if (i != pathLegPoints.size() - 1) {
+                                               PipeControlPoint next;
+                                               next = pathLegPoints.get(i + 1);
+                                               updateVariableLength(icp,  prev, next);
+
+                                       } else {
+                                               // this is variable length component at the end of the
+                                               // piperun.
+                                               // the problem is that we want to keep unconnected end
+                                               // of the component in the same
+                                               // place, but center of the component must be moved.
+                                               updateVariableLengthEnd(icp, prev);
+                                       }
+                               } else if (icp.isOffset()) {
+                                       // TODO : offset vector is already calculated and should be
+                                       // cached
+                                       Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
+                                       sp.add(offset);
+                                       ep.add(offset);
                                }
                        }
                }
        }
+       
+       private static void updateVariableLength(PipeControlPoint icp, PipeControlPoint prev,  PipeControlPoint next) {
+               Vector3d prevPos = prev.getWorldPosition();
+               Vector3d nextPos = next.getWorldPosition();
+               
+               Vector3d dir = new Vector3d(nextPos);
+               dir.sub(prevPos);
+               double l = dir.lengthSquared(); // distance between
+                                                                               // control points
+                                                                               // (square)
+               double l2prev = prev.getInlineLength(); // distance
+                                                                                                                                       // taken
+                                                                                                                                       // by
+                                                                                                                                       // components
+               double l2next = next.getInlineLength();
+               double l2 = l2prev + l2next;
+               double l2s = MathTools.square(l2);
+               if (l2s < l) { // check if there is enough space for
+                                               // variable length component.
+                       // components fit
+                       dir.normalize();
+                       double length = Math.sqrt(l) - l2; // true length of
+                                                                                               // the variable
+                                                                                               // length
+                                                                                               // component
+                       dir.scale(length * 0.5 + l2prev); // calculate
+                                                                                               // center
+                                                                                               // position of
+                                                                                               // the component
+                       dir.add(prevPos);
+                       icp.setWorldPosition(dir);
+                       icp.setLength(length);
+               } else {
+                       // components leave no space to the component and it
+                       // must be removed
+                       if (icp.isDeletable())
+                               icp._remove();
+               }
+       }
+       
+       private static void updateVariableLengthEnd(PipeControlPoint icp,  PipeControlPoint prev) {
+               double currentLength = icp.getLength();
+               Vector3d currentPos = icp.getWorldPosition();
+               Vector3d prevPos = prev.getWorldPosition();
+               
+               Vector3d dir = new Vector3d();
+               dir.sub(currentPos, prevPos);
+               
+               if (currentLength < MathTools.NEAR_ZERO) {
+                       currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
+               }
+               
+               if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                       dir.normalize();
+               Point3d endPos = new Point3d(dir);
+               endPos.scale(currentLength * 0.5);
+               endPos.add(currentPos); // this is the free end of the
+                                                               // component
+
+               double offset = prev.getInlineLength();
+               Point3d beginPos = new Point3d(dir);
+               beginPos.scale(offset);
+               beginPos.add(prevPos); // this is the connected end of
+                                                               // the component
+
+               double l = beginPos.distance(endPos);
+               
+               if (Double.isNaN(l))
+                       System.out.println();
+
+               dir.scale(l * 0.5);
+               beginPos.add(dir); // center position
+
+               if (DEBUG)
+                       System.out.println("PipingRules.updateInlineControlPoints() setting variable length to " + l);
+               icp.setLength(l);
+
+               icp.setWorldPosition(new Vector3d(beginPos));
+       }
 
        private static void ppNoOffset(UpdateStruct2 u) throws Exception {
                if (DEBUG)
@@ -1076,7 +1179,7 @@ public class PipingRules {
 
        private static void processPathLeg(UpdateStruct2 u, boolean updateEnds, boolean updateInline) throws Exception {
                if (DEBUG)
-                       System.out.println("PipingRules.processPathLeg " + u.start + " " + u.end);
+                       System.out.println("PipingRules.processPathLeg " + (updateEnds ? "ends " : "") + (updateInline ? "inline " : "") + u.start + " " + u.end);
 
                if (u.toRemove.size() > 0) {
                        for (ExpandIterInfo info : u.toRemove) {