From b23336b5891d4a7b142bde3c9171b100d943da94 Mon Sep 17 00:00:00 2001
From: ruevs <dpr@ruevs.com>
Date: Tue, 27 Oct 2015 12:28:33 +0200
Subject: [PATCH] Add a new length-difference constraint.

This constraint requires the lengths of two line segments to
differ by a constant.
It can be useful to define the tolerances when making joints.
---
 exposed/DOC.txt        |  4 ++++
 exposed/VbDemo.vb      |  1 +
 include/slvs.h         |  1 +
 src/constraint.cpp     | 18 ++++++++++++++++++
 src/constrainteq.cpp   | 10 ++++++++++
 src/drawconstraint.cpp |  5 ++++-
 src/graphicswin.cpp    |  1 +
 src/lib.cpp            |  1 +
 src/mouse.cpp          |  4 +++-
 src/sketch.h           |  1 +
 src/solvespace.cpp     |  3 ++-
 src/ui.h               |  1 +
 12 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/exposed/DOC.txt b/exposed/DOC.txt
index 5b04950..743349b 100644
--- a/exposed/DOC.txt
+++ b/exposed/DOC.txt
@@ -313,6 +313,10 @@ SLVS_C_LENGTH_RATIO*
     The length of line entityA divided by the length of line entityB is
     equal to valA.
 
+SLVS_C_LENGTH_DIFFERENCE*
+
+    The lengths of line entityA and line entityB differ by valA.
+
 SLVS_C_EQ_LEN_PT_LINE_D*
 
     The length of the line entityA is equal to the distance from point
diff --git a/exposed/VbDemo.vb b/exposed/VbDemo.vb
index 01eafc7..6781fc4 100644
--- a/exposed/VbDemo.vb
+++ b/exposed/VbDemo.vb
@@ -511,6 +511,7 @@ Module VbDemo
         Public Const SLVS_C_PROJ_PT_DISTANCE As Integer = 100030
         Public Const SLVS_C_WHERE_DRAGGED As Integer = 100031
         Public Const SLVS_C_CURVE_CURVE_TANGENT As Integer = 100032
+        Public Const SLVS_C_LENGTH_DIFFERENCE As Integer = 100033
 
         <StructLayout(LayoutKind.Sequential)> Public Structure Slvs_Constraint
             Public h As UInteger
diff --git a/include/slvs.h b/include/slvs.h
index a06ac92..3181ceb 100644
--- a/include/slvs.h
+++ b/include/slvs.h
@@ -112,6 +112,7 @@ typedef struct {
 #define SLVS_C_PROJ_PT_DISTANCE         100030
 #define SLVS_C_WHERE_DRAGGED            100031
 #define SLVS_C_CURVE_CURVE_TANGENT      100032
+#define SLVS_C_LENGTH_DIFFERENCE        100033
 
 typedef struct {
     Slvs_hConstraint    h;
diff --git a/src/constraint.cpp b/src/constraint.cpp
index 8a02397..1627937 100644
--- a/src/constraint.cpp
+++ b/src/constraint.cpp
@@ -24,6 +24,7 @@ char *Constraint::DescriptionString(void) {
         case EQ_LEN_PT_LINE_D:      s = "eq-length-and-pt-ln-dist"; break;
         case EQ_PT_LN_DISTANCES:    s = "eq-pt-line-distances"; break;
         case LENGTH_RATIO:          s = "length-ratio"; break;
+        case LENGTH_DIFFERENCE:     s = "length-difference"; break;
         case SYMMETRIC:             s = "symmetric"; break;
         case SYMMETRIC_HORIZ:       s = "symmetric-h"; break;
         case SYMMETRIC_VERT:        s = "symmetric-v"; break;
@@ -322,6 +323,23 @@ void Constraint::MenuConstrain(int id) {
             AddConstraint(&c);
             break;
 
+        case GraphicsWindow::MNU_DIFFERENCE:
+            if(gs.lineSegments == 2 && gs.n == 2) {
+                c.type = LENGTH_DIFFERENCE;
+                c.entityA = gs.entity[0];
+                c.entityB = gs.entity[1];
+            } else {
+                Error("Bad selection for length difference constraint. This "
+                      "constraint can apply to:\n\n"
+                      "    * two line segments\n");
+                return;
+            }
+
+            c.valA = 0;
+            c.ModifyToSatisfy();
+            AddConstraint(&c);
+            break;
+
         case GraphicsWindow::MNU_AT_MIDPOINT:
             if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
                 c.type = AT_MIDPOINT;
diff --git a/src/constrainteq.cpp b/src/constrainteq.cpp
index 1c6beb1..5928826 100644
--- a/src/constrainteq.cpp
+++ b/src/constrainteq.cpp
@@ -18,6 +18,7 @@ bool ConstraintBase::HasLabel(void) {
         case PROJ_PT_DISTANCE:
         case DIAMETER:
         case LENGTH_RATIO:
+        case LENGTH_DIFFERENCE:
         case ANGLE:
         case COMMENT:
             return true;
@@ -279,6 +280,15 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
             break;
         }
 
+        case LENGTH_DIFFERENCE: {
+            EntityBase *a = SK.GetEntity(entityA);
+            EntityBase *b = SK.GetEntity(entityB);
+            Expr *la = Distance(workplane, a->point[0], a->point[1]);
+            Expr *lb = Distance(workplane, b->point[0], b->point[1]);
+            AddEq(l, (la->Minus(lb))->Minus(exA), 0);
+            break;
+        }
+
         case DIAMETER: {
             EntityBase *circle = SK.GetEntity(entityA);
             Expr *r = circle->CircleGetRadiusExpr();
diff --git a/src/drawconstraint.cpp b/src/drawconstraint.cpp
index 391ae5c..82a1708 100644
--- a/src/drawconstraint.cpp
+++ b/src/drawconstraint.cpp
@@ -63,6 +63,8 @@ char *Constraint::Label(void) {
         sprintf(Ret, "%.2f", valA);
     } else if(type == LENGTH_RATIO) {
         sprintf(Ret, "%.3f:1", valA);
+    } else if(type == LENGTH_DIFFERENCE) {
+        sprintf(Ret, "%.3f", valA);
     } else if(type == COMMENT) {
         strcpy(Ret, comment.str);
     } else if(type == DIAMETER) {
@@ -853,6 +855,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
         }
 
         case LENGTH_RATIO:
+        case LENGTH_DIFFERENCE:
         case EQUAL_LENGTH_LINES: {
             Vector a, b = Vector::From(0, 0, 0);
             for(int i = 0; i < 2; i++) {
@@ -867,7 +870,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) {
 
                 DoEqualLenTicks(a, b, gn);
             }
-            if(type == LENGTH_RATIO) {
+            if((type == LENGTH_RATIO) || (type == LENGTH_DIFFERENCE)) {
                 Vector ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset);
                 DoLabel(ref, labelPos, gr, gu);
             }
diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp
index 806c32a..a76b3db 100644
--- a/src/graphicswin.cpp
+++ b/src/graphicswin.cpp
@@ -139,6 +139,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
 { 1, "&On Point / Curve / Plane",   MNU_ON_ENTITY,      'O',     IN, mCon  },
 { 1, "E&qual Length / Radius / Angle", MNU_EQUAL,       'Q',     IN, mCon  },
 { 1, "Length Ra&tio",               MNU_RATIO,          'Z',     IN, mCon  },
+{ 1, "Length Diff&erence",          MNU_DIFFERENCE,     'J',     IN, mCon  },
 { 1, "At &Midpoint",                MNU_AT_MIDPOINT,    'M',     IN, mCon  },
 { 1, "S&ymmetric",                  MNU_SYMMETRIC,      'Y',     IN, mCon  },
 { 1, "Para&llel / Tangent",         MNU_PARALLEL,       'L',     IN, mCon  },
diff --git a/src/lib.cpp b/src/lib.cpp
index 67fcb09..bf9a81e 100644
--- a/src/lib.cpp
+++ b/src/lib.cpp
@@ -156,6 +156,7 @@ case SLVS_C_EQ_LEN_PT_LINE_D:   t = Constraint::EQ_LEN_PT_LINE_D; break;
 case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::EQ_PT_LN_DISTANCES; break;
 case SLVS_C_EQUAL_ANGLE:        t = Constraint::EQUAL_ANGLE; break;
 case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::EQUAL_LINE_ARC_LEN; break;
+case SLVS_C_LENGTH_DIFFERENCE:  t = Constraint::LENGTH_DIFFERENCE; break;
 case SLVS_C_SYMMETRIC:          t = Constraint::SYMMETRIC; break;
 case SLVS_C_SYMMETRIC_HORIZ:    t = Constraint::SYMMETRIC_HORIZ; break;
 case SLVS_C_SYMMETRIC_VERT:     t = Constraint::SYMMETRIC_VERT; break;
diff --git a/src/mouse.cpp b/src/mouse.cpp
index 9803be9..2a8db0e 100644
--- a/src/mouse.cpp
+++ b/src/mouse.cpp
@@ -1155,6 +1155,7 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
 
             case Constraint::ANGLE:
             case Constraint::LENGTH_RATIO:
+            case Constraint::LENGTH_DIFFERENCE:
                 sprintf(s, "%.3f", c->valA);
                 break;
 
@@ -1206,7 +1207,8 @@ void GraphicsWindow::EditControlDone(const char *s) {
             case Constraint::PROJ_PT_DISTANCE:
             case Constraint::PT_LINE_DISTANCE:
             case Constraint::PT_FACE_DISTANCE:
-            case Constraint::PT_PLANE_DISTANCE: {
+            case Constraint::PT_PLANE_DISTANCE:
+            case Constraint::LENGTH_DIFFERENCE: {
                 // The sign is not displayed to the user, but this is a signed
                 // distance internally. To flip the sign, the user enters a
                 // negative distance.
diff --git a/src/sketch.h b/src/sketch.h
index 6cf61eb..a79f472 100644
--- a/src/sketch.h
+++ b/src/sketch.h
@@ -555,6 +555,7 @@ public:
         EQ_PT_LN_DISTANCES     =  53,
         EQUAL_ANGLE            =  54,
         EQUAL_LINE_ARC_LEN     =  55,
+        LENGTH_DIFFERENCE      =  56,
         SYMMETRIC              =  60,
         SYMMETRIC_HORIZ        =  61,
         SYMMETRIC_VERT         =  62,
diff --git a/src/solvespace.cpp b/src/solvespace.cpp
index 94eb385..e255d45 100644
--- a/src/solvespace.cpp
+++ b/src/solvespace.cpp
@@ -560,7 +560,8 @@ void SolveSpaceUI::MenuAnalyze(int id) {
                     SS.TW.shown.dimSteps = 10;
                     SS.TW.shown.dimIsDistance =
                         (c->type != Constraint::ANGLE) &&
-                        (c->type != Constraint::LENGTH_RATIO);
+                        (c->type != Constraint::LENGTH_RATIO) &&
+                        (c->type != Constraint::LENGTH_DIFFERENCE);
                     SS.TW.shown.constraint = c->h;
                     SS.TW.shown.screen = TextWindow::SCREEN_STEP_DIMENSION;
 
diff --git a/src/ui.h b/src/ui.h
index 0bb59e7..e068dd0 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -404,6 +404,7 @@ public:
         MNU_REFERENCE,
         MNU_EQUAL,
         MNU_RATIO,
+        MNU_DIFFERENCE,
         MNU_ON_ENTITY,
         MNU_SYMMETRIC,
         MNU_AT_MIDPOINT,