]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java
Fix handling of offsets in directed path leg updates
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / controlpoint / PipingRules.java
index e8e39c081d7c1461e297777bd08336335a9641e9..d00e4018de1407887cb1982e578bb2b3d967a4a5 100644 (file)
@@ -138,8 +138,11 @@ public class PipingRules {
        
        public static void setEnabled(boolean enabled) {
                PipingRules.enabled = enabled;
-               if(!enabled)
-                       currentUpdates.clear();
+               if(!enabled) {
+                       synchronized (ruleMutex) {
+                               currentUpdates.clear();
+                       }
+               }
        }
        
        public static boolean isEnabled() {
@@ -277,6 +280,9 @@ public class PipingRules {
                scp.insert(pcp1, pcp2);
 
                scp.setWorldPosition(pos);
+               Vector3d dir = new Vector3d();
+               dir.sub(pcp2.getWorldPosition(), pcp1.getWorldPosition());
+               updateControlPointOrientation(scp, dir);
                scp.setLength(length);
                validate(scp.getPipeRun());
                return scp;
@@ -363,26 +369,18 @@ public class PipingRules {
 
        }
 
-       @SuppressWarnings("unused")
-       private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
-               boolean hasOffsets = false;
-               List<PipeControlPoint> offsets = new ArrayList<PipeControlPoint>(list.size());
-               // Only start offset affects the calculation
-               if (start.isOffset())
-                   offsets.add(start);
-               for (PipeControlPoint icp : list) {
-                       if (icp.isOffset()) {
-                               offsets.add(icp);
-                       } else if (icp.isDualSub())
-                               ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
-               }
+       public static boolean calculateDirectedOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
+               return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, true);
+       }
+
+       public static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
+               return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, false);
+       }
+       
+       private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset, boolean directed) {
+               List<PipeControlPoint> offsets = getOffsetPoints(start, list);
                if (offsets.size() == 0) {
-                       dir.set(endPoint);
-                       dir.sub(startPoint);
-                       double l = dir.lengthSquared(); 
-                       if (l > MathTools.NEAR_ZERO)
-                               dir.scale(1.0/Math.sqrt(l));
-                       offset.set(0.0, 0.0, 0.0);
+                       setZeroOffset(startPoint, endPoint, dir, offset);
                        return false;
                } else {
                        Vector3d sp = new Vector3d(startPoint);
@@ -392,8 +390,9 @@ public class PipingRules {
                        double l = dir.lengthSquared(); 
                        if (l > MathTools.NEAR_ZERO)
                                dir.scale(1.0/Math.sqrt(l));
+                       
                        int iter = 100;
-                       while (iter >= 0) {
+                       while (true) {
                                iter--;
                                offset.set(0.0, 0.0, 0.0);
                                
@@ -401,11 +400,16 @@ public class PipingRules {
                                        Vector3d v = icp.getSizeChangeOffsetVector(dir);
                                        offset.add(v);
                                }
+                               
+                               if (directed)
+                                       break;
+                               
                                Point3d nep = new Point3d(endPoint);
                                nep.sub(offset);
-                               if (nep.distance(ep) < 0.0000000001) {
+                               if (nep.distance(ep) < 0.0000000001 || iter <= 0) {
                                        break;
-                               } 
+                               }
+                               
                                ep = nep;
                                dir.set(ep);
                                dir.sub(sp);
@@ -413,14 +417,37 @@ public class PipingRules {
                                if (l > MathTools.NEAR_ZERO)
                                        dir.scale(1.0/Math.sqrt(l));
                        }
-                       hasOffsets = true;
+                       
+                       if (DEBUG)
+                               System.out.println("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ;
+                       
+                       return true;
                }
-               
-               if (DEBUG && hasOffsets)
-                       System.out.println("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ;
-               return hasOffsets;
        }
        
+       public static void setZeroOffset(Vector3d startPoint, Vector3d endPoint, Vector3d dir, Vector3d offset) {
+               dir.set(endPoint);
+               dir.sub(startPoint);
+               double l = dir.lengthSquared(); 
+               if (l > MathTools.NEAR_ZERO)
+                       dir.scale(1.0/Math.sqrt(l));
+               offset.set(0.0, 0.0, 0.0);
+       }
+
+       public static List<PipeControlPoint> getOffsetPoints(PipeControlPoint start, ArrayList<PipeControlPoint> list) {
+               List<PipeControlPoint> offsets = new ArrayList<PipeControlPoint>(list.size());
+               // Only start offset affects the calculation
+               if (start.isOffset())
+                   offsets.add(start);
+               for (PipeControlPoint icp : list) {
+                       if (icp.isOffset()) {
+                               offsets.add(icp);
+                       } else if (icp.isDualSub())
+                               ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+               }
+               return offsets;
+       }
+
        private static UpdateStruct2 createUS(PipeControlPoint start, Direction direction, int iter, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated) {
                ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
                PipeControlPoint end = null;
@@ -450,6 +477,21 @@ public class PipingRules {
                return new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, direction == Direction.PREVIOUS, toRemove, updated);
        }
        
+       private static Vector3d pathLegDirection(PipeControlPoint start) {
+               ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
+               PipeControlPoint end = start.findNextEnd(list);
+               if (start == end) {
+                       return start.getDirection(Direction.NEXT);
+               }
+               
+               Vector3d offset = new Vector3d();
+               Vector3d startPoint = start.getWorldPosition();
+               Vector3d endPoint = end.getWorldPosition();
+               Vector3d dir = new Vector3d();
+               calculateOffset(startPoint, endPoint, start, list, end, dir, offset);
+               return dir;
+       }
+       
        private static boolean asDirected(PipeControlPoint pcp, Direction direction) {
                if (pcp.isDirected())
                        return true;
@@ -887,7 +929,11 @@ public class PipingRules {
                Vector3d dir = new Vector3d();
                dir.sub(currentPos, prevPos);
                
-               boolean simple = currentUpdates.contains(icp);
+               boolean simple;
+               synchronized (ruleMutex) {
+                       simple = currentUpdates.contains(icp);
+               }
+               
                if (simple) {
                        // Update based on position -> adjust length
                        double currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
@@ -1016,22 +1062,27 @@ public class PipingRules {
                                return;
                        }
                }
-               Point3d directedEndPoint = new Point3d(u.endPoint);
-               if (u.hasOffsets)
-                       directedEndPoint.sub(u.offset);
+               
+               Point3d otherPosition = new Point3d(dcpStart ? u.endPoint : u.startPoint);
+               if (u.hasOffsets) {
+                       Vector3d dir = new Vector3d(), offset = new Vector3d();
+                       calculateDirectedOffset(u.startPoint, u.endPoint, u.start, u.list, u.end, dir, offset);
+                       u.dir = dir;
+                       u.offset = offset;
+                       
+                       if (dcpStart)
+                               otherPosition.add(offset);
+                       else
+                               otherPosition.sub(offset);
+               }
 
                double mu[] = new double[2];
 
                Vector3d closest;
                Vector3d t = new Vector3d();
 
-               if (dcpStart) {
-                       closest = MathTools.closestPointOnStraight(directedEndPoint, u.startPoint, directedDirection, mu);
-                       t.sub(closest, directedEndPoint);
-               } else {
-                       closest = MathTools.closestPointOnStraight(u.startPoint, directedEndPoint, directedDirection, mu);
-                       t.sub(closest, u.startPoint);
-               }
+               closest = MathTools.closestPointOnStraight(otherPosition, position, directedDirection, mu);
+               t.sub(closest, otherPosition);
 
                double distance = t.length();
                boolean aligned = (distance < ALLOWED_OFFSET);
@@ -1049,7 +1100,6 @@ public class PipingRules {
                        //if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle())
                    //    processPathLeg(u, true, false);
                        checkExpandPathLeg(u, lengthChange, inlineEnd || u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle());
-                       
                } else {
                        if (u.iter > 0) {
                                backIter(u);
@@ -1082,13 +1132,26 @@ public class PipingRules {
                                        if (canMoveOther) {
                                                if (DEBUG)
                                                        System.out.println("PipingRules.updateDirectedPipeRun() moved end " + other + " to " + closest);
+                                               
+                                               // Not aligned - we need to recalculate the offset to reflect new end points.
+                                               Vector3d offset;
+                                               if (u.hasOffsets) {
+                                                       offset = new Vector3d();
+                                                       Vector3d newDir = new Vector3d();
+                                                       calculateDirectedOffset(position, closest, u.start, u.list, u.end, newDir, offset);
+                                                       closest.add(offset);
+                                               } else {
+                                                       offset = new Vector3d();
+                                               }
+                                               
                                                other.setWorldPosition(closest);
+                                               
                                                if (dcpStart) {
-                                                       ppNoOffset(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Vector3d(closest), directedDirection, null, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated),true);
+                                                       checkExpandPathLeg(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Vector3d(closest), directedDirection, offset, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated), PathLegUpdateType.NONE, true);
                                                        if (u.end.getNext() != null)
                                                                updatePathLegNext(u.end, u.updated, PathLegUpdateType.NEXT);
                                                } else {
-                                                       ppNoOffset(new UpdateStruct2(u.start, new Vector3d(closest), u.list, u.end, u.endPoint, directedDirection, null, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated),true);
+                                                       checkExpandPathLeg(new UpdateStruct2(u.start, new Vector3d(closest), u.list, u.end, u.endPoint, directedDirection, offset, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated), PathLegUpdateType.NONE, true);
                                                        if (u.start.getPrevious() != null)
                                                                updatePathLegPrev(u.start, u.updated, PathLegUpdateType.PREV);
                                                }
@@ -1096,7 +1159,6 @@ public class PipingRules {
                                                // TODO : calculate needed space from next run end.
                                                if (allowInsertRemove)
                                                        insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
-                                                       
                                                else
                                                        triedIR = true;
                                        }
@@ -1148,8 +1210,6 @@ public class PipingRules {
                                }
                        }
                }
-               
-               
        }
 
        private static void updateDualDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
@@ -1160,14 +1220,19 @@ public class PipingRules {
                PipeControlPoint dcp2 = u.end;
                Point3d position1 = new Point3d(u.startPoint);
                Point3d position2 = new Point3d(u.endPoint);
+               
+               Vector3d dir = new Vector3d(), offset = new Vector3d();
+               calculateDirectedOffset(new Vector3d(position1), new Vector3d(position2), u.start, u.list, u.end, dir, offset);
+               
                Point3d position1offset = new Point3d(position1);
-               position1offset.sub(u.offset);
+               position1offset.add(offset);
                Point3d position2offset = new Point3d(position2);
-               position2offset.add(u.offset);
+               position2offset.sub(offset);
                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);
+               
+               Vector3d p1 = MathTools.closestPointOnStraight(position1, position2offset, dir2);
+               Vector3d p2 = MathTools.closestPointOnStraight(position2, position1offset, dir1);
                double d1 = position1.distance(new Point3d(p1));
                double d2 = position2.distance(new Point3d(p2));
 
@@ -1207,9 +1272,9 @@ public class PipingRules {
                                p1.add(v);
 
                                if (!u.reversed)
-                                       p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2, dir2);
+                                       p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2offset, dir2);
                                else
-                                       p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1, dir1);
+                                       p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1offset, dir1);
 
                                // By default, the elbows are placed next to each other, by using 90 deg angles.
                                // If the distance between elbows is not enough, we must move the other elbow (and create more shallow angle elbows)
@@ -1269,34 +1334,49 @@ public class PipingRules {
            if (other == null)
                return tr; // space for 90 deg
            Vector3d dir = dcp.getDirectedControlPointDirection();
-           Vector3d dp = dcp.getWorldPosition();
-           Vector3d op = other.getWorldPosition();
-           double u[] = new double[1];
-           Vector3d closest = MathTools.closestPointOnStraight(op, dp, dir,u);
-           if (MathTools.distanceSquared(closest, op) <= MIN_INLINE_LENGTH) {
-               if (u[0] > -MIN_INLINE_LENGTH)
-                   return 0.0; // point following turn is directly in the front of the nozzle.
-               else
-                   return tr*2.0; // point following turn is directly behind the nozzle, in theory, we should return Double.Inf...
+           Vector3d dir2;
+           if (other == ne) {
+               dir2 = pathLegDirection(tcp);
+           } else {
+               dir2 = pathLegDirection(pe);
+               dir2.negate();
            }
+
+           double d = dir.dot(dir2);
+           if (d > 0.9999)
+               return 0.0; // point following turn is directly in the front of the nozzle.
+           else if (d < -0.9999)
+            return tr*2.0; // point following turn is directly behind the nozzle, in theory, we should return Double.Inf...
+           
            double curr = tr*0.1; 
            int iter = 10;
-           Vector3d v1 = new Vector3d();
-           Vector3d v2 = new Vector3d();
-           while (iter > 0) {
-               Vector3d tp = new Vector3d(dp);
-               MathTools.mad(tp, dir, curr);
-               v1.sub(tp, dp); // Vector from nozzle to turn 
-               v2.sub(op,tp);  // Vector from turn to other 
-               double a = v1.angle(v2);
-               double t = Math.tan((Math.PI - a) * 0.5);
-               double R = 0.0;
-               if (t > MathTools.NEAR_ZERO)
-                   R = tr / t;
-               if (R <= curr)
-                   break;
-               curr = R*1.001;
-               iter--;
+           Vector3d tp0 = tcp.getPosition();
+           try {
+                   Vector3d dp = dcp.getWorldPosition();
+                   while (iter > 0) {
+                       Vector3d tp = new Vector3d(dir);
+                       tp.scaleAdd(curr, dp);
+                       tcp.setPosition(tp);
+                           if (other == ne) {
+                               dir2 = pathLegDirection(tcp);
+                           } else {
+                               dir2 = pathLegDirection(pe);
+                               dir2.negate();
+                           }
+                           
+                       double a = dir.angle(dir2);
+                       double t = Math.tan(a * 0.5);
+                       double R = 0.0;
+                       if (t > MathTools.NEAR_ZERO)
+                           R = tr * t;
+                       if (R <= curr)
+                           break;
+                       curr = R*1.001;
+                       iter--;
+                   }
+           }
+           finally {
+               tcp.setPosition(tp0);
            }
            return curr;
        }
@@ -1308,26 +1388,19 @@ public class PipingRules {
 //             closest.add(directedDirection);
                
                PipeControlPoint tcp = null;
-               Vector3d closest;
+               Vector3d closest = new Vector3d(directedDirection);
+               closest.scaleAdd(dcp.getPipeRun().getTurnRadius(), position);
                if (dcpStart) {
-                       closest = MathTools.closestPointOnStraight(next.getWorldPosition(), position, directedDirection);
                        tcp = insertElbow(dcp, next, closest);
                } else {
-                       closest = MathTools.closestPointOnStraight(dcp.getWorldPosition(), position, directedDirection);
                        tcp = insertElbow(next, dcp, closest);
                }
-               double d = MathTools.distance(position, closest);
-               double s = spaceForTurn(tcp,dcp);
-               if (d < s)  {
-                       d = s - d;
-                       Vector3d p = new Vector3d(directedDirection);
-                       p.scale(d);
-                       p.add(closest);
-                       tcp.setPosition(p);
-                       closest = p;
-               }
-               
                
+               double s = spaceForTurn(tcp,dcp);
+               Vector3d p = new Vector3d(directedDirection);
+               p.scaleAdd(s, position);
+               tcp.setPosition(p);
+               closest = p;
 
                if (DEBUG)
                        System.out.println("PipingRules.updateDirectedPipeRun() inserted " + tcp);
@@ -1771,13 +1844,11 @@ public class PipingRules {
                        }
                        double turnAngle = prev.angle(next);
        
-                       double angle = Math.PI - turnAngle;
-       
                        Vector3d turnAxis = new Vector3d();
                        turnAxis.cross(prev, next);
                        if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) {
                                double elbowRadius = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius();
-                               double R = elbowRadius / Math.tan(angle * 0.5);
+                               double R = elbowRadius * Math.tan(turnAngle * 0.5);
                                
                                turnAxis.normalize();
                                tcp.setTurnAngle(turnAngle);