~ubuntu-branches/ubuntu/maverick/freecad/maverick

« back to all changes in this revision

Viewing changes to src/Mod/Sketcher/App/sketchflat/constraint.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Teemu Ikonen
  • Date: 2009-07-16 18:37:41 UTC
  • Revision ID: james.westby@ubuntu.com-20090716183741-oww9kcxqrk991i1n
Tags: upstream-0.8.2237
ImportĀ upstreamĀ versionĀ 0.8.2237

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//-----------------------------------------------------------------------------
 
2
// Copyright 2008 Jonathan Westhues
 
3
//
 
4
// This file is part of SketchFlat.
 
5
// 
 
6
// SketchFlat is free software: you can redistribute it and/or modify
 
7
// it under the terms of the GNU General Public License as published by
 
8
// the Free Software Foundation, either version 3 of the License, or
 
9
// (at your option) any later version.
 
10
// 
 
11
// SketchFlat is distributed in the hope that it will be useful,
 
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
// GNU General Public License for more details.
 
15
// 
 
16
// You should have received a copy of the GNU General Public License
 
17
// along with SketchFlat.  If not, see <http://www.gnu.org/licenses/>.
 
18
//------
 
19
//
 
20
// Math corresponding to each constraint: given each constraint, return one
 
21
// or more metrics that determine how far we are from satisfying that
 
22
// constraint. The solver calls these repeatedly, in an effort to drive them
 
23
// all to zero.
 
24
// Jonathan Westhues, April 2007
 
25
//-----------------------------------------------------------------------------
 
26
#include "PreCompiled.h"
 
27
#ifndef _PreComp_
 
28
#endif
 
29
 
 
30
#include "sketchflat.h"
 
31
 
 
32
static void ModifyConstraintToReflectSketch(SketchConstraint *c);
 
33
 
 
34
Equations EQalloc;
 
35
Equations *EQ = (Equations *)&EQalloc;
 
36
 
 
37
static void AddConstraint(SketchConstraint *c)
 
38
{
 
39
    SK->eqnsDirty = TRUE;
 
40
    UndoRemember();
 
41
 
 
42
    if(SK->constraints >= MAX_CONSTRAINTS_IN_SKETCH) {
 
43
        uiError("Too many constraints!");
 
44
        return;
 
45
    }
 
46
 
 
47
    hConstraint max = 0;
 
48
    int i;
 
49
    for(i = 0; i < SK->constraints; i++) {
 
50
        if(SK->constraint[i].id > max) {
 
51
            max = SK->constraint[i].id;
 
52
        }
 
53
    }
 
54
 
 
55
    memcpy(&(SK->constraint[SK->constraints]), c, sizeof(*c));
 
56
    SK->constraint[SK->constraints].id = (max + 1);
 
57
    SK->constraint[SK->constraints].layer = GetCurrentLayer();
 
58
 
 
59
    (SK->constraints)++;
 
60
 
 
61
    SolvePerMode(FALSE);
 
62
}
 
63
 
 
64
void DeleteConstraint(hConstraint hc)
 
65
{
 
66
    SK->eqnsDirty = TRUE;
 
67
 
 
68
    int i;
 
69
 
 
70
    // So that we can't accidentally get deleted later, remove ourselves
 
71
    // from the selection. This would otherwise cause a problem if our
 
72
    // selection included an entity, and a constraint on that entity, in
 
73
    // that order; we'd delete the entity, and then delete the constraint
 
74
    // because it is on the entity, and then try to delete the constraint
 
75
    // again.
 
76
    for(i = 0; i < MAX_SELECTED_ITEMS; i++) {
 
77
        if(Selected[i].which == SEL_CONSTRAINT && Selected[i].constraint == hc)
 
78
        {
 
79
            Selected[i].which = SEL_NONE;
 
80
            Selected[i].constraint = 0;
 
81
        }
 
82
    }
 
83
 
 
84
    for(i = 0; i < SK->constraints; i++) {
 
85
        if(SK->constraint[i].id == hc) {
 
86
            (SK->constraints)--;
 
87
            memmove(&(SK->constraint[i]), &(SK->constraint[i+1]),
 
88
                (SK->constraints - i)*sizeof(SK->constraint[0]));
 
89
 
 
90
            return;
 
91
        }
 
92
    }
 
93
 
 
94
    oopsnf();
 
95
}
 
96
 
 
97
SketchConstraint *ConstraintById(hConstraint hc)
 
98
{
 
99
    int i;
 
100
    for(i = 0; i < SK->constraints; i++) {
 
101
        if(SK->constraint[i].id == hc) {
 
102
            return &(SK->constraint[i]);
 
103
        }
 
104
    }
 
105
    oops();
 
106
}
 
107
 
 
108
//-----------------------------------------------------------------------------
 
109
// Change the numerical value associated with certain types of constraints
 
110
// (e.g. distances and lengths). If we're solving, then we will do this in
 
111
// multiple steps, to decrease the odds of numerical disaster.
 
112
//-----------------------------------------------------------------------------
 
113
void ChangeConstraintValue(SketchConstraint *c, char *str)
 
114
{
 
115
    double nv;
 
116
 
 
117
    if(c->type == CONSTRAINT_PT_LINE_DISTANCE ||
 
118
       c->type == CONSTRAINT_LINE_LINE_DISTANCE)
 
119
    {
 
120
        // These distances are signed (correspond to above or below a
 
121
        // horizontal line). The sign is never displayed to the user, since
 
122
        // that's confusing, but they can flip the sign by entering a
 
123
        // negative value.
 
124
        BOOL neg = (c->v < 0);
 
125
        if(neg) {
 
126
            nv = -FromDisplay(str);
 
127
        } else {
 
128
            nv = FromDisplay(str);
 
129
        }
 
130
    } else if(c->type == CONSTRAINT_PT_PT_DISTANCE ||
 
131
              c->type == CONSTRAINT_RADIUS)
 
132
    {
 
133
        nv = FromDisplay(str);
 
134
    } else if(c->type == CONSTRAINT_LINE_LINE_ANGLE) {
 
135
        // As for point-line distances: the sign is not displayed to the
 
136
        // user, but may be flipped by entering a negative number.
 
137
        BOOL neg = (c->v  < 0);
 
138
        if(neg) {
 
139
            nv = -atof(str);
 
140
        } else {
 
141
            nv = atof(str);
 
142
        }
 
143
        // And let's force that to lie between -180 and 180.
 
144
        while(nv >  180) nv -= 360;
 
145
        while(nv < -180) nv += 360;
 
146
    } else if(c->type == CONSTRAINT_SCALE_MM ||
 
147
              c->type == CONSTRAINT_SCALE_INCH)
 
148
    {
 
149
        nv = fabs(atof(str));
 
150
    } else {
 
151
        oopsnf();
 
152
        return;
 
153
    }
 
154
 
 
155
    double cv0 = c->v;
 
156
 
 
157
    int i;
 
158
    int n = toint(fabs((nv - cv0)) / 5000);
 
159
    if(n > 15) n = 15;
 
160
    if(n < 5) n = 5;
 
161
 
 
162
    // Step the dimension to the requested value.
 
163
    for(i = n; i >= 0; i--) {
 
164
        c->v = nv + (i*(cv0 - nv))/n;
 
165
        SolvePerMode(FALSE);
 
166
    }
 
167
}
 
168
 
 
169
static void InitConstraint(SketchConstraint *c)
 
170
{
 
171
    c->v = 0;
 
172
    c->ptA = 0;
 
173
    c->ptB = 0;
 
174
    c->paramA = 0;
 
175
    c->paramB = 0;
 
176
    c->entityA = 0;
 
177
    c->entityB = 0;
 
178
    c->lineA = 0;
 
179
    c->lineB = 0;
 
180
    c->offset.x = toMicronsNotAffine(50);
 
181
    c->offset.y = 0;
 
182
}
 
183
 
 
184
static void HandleMenuSelection(int id)
 
185
{
 
186
    // As a convenience, let's count how many of each are selected, because
 
187
    // that determines which (if any) constraint a given menu item will
 
188
    // generate.
 
189
    GroupedSelection gs;
 
190
    GroupSelection(&gs);
 
191
 
 
192
    // Let's make sure that the constraint will be applied to at least
 
193
    // one item on the currently selected layer. If not, then it is surely
 
194
    // inconsistent, since the sketches are solved one layer at a time.
 
195
    hLayer cl = GetCurrentLayer();
 
196
 
 
197
    int i;
 
198
    for(i = 0; i < gs.points; i++) {
 
199
        if(LayerForPoint(gs.point[i]) == cl) {
 
200
            goto items_on_current_layer_selected;
 
201
        }
 
202
    }
 
203
    for(i = 0; i < gs.entities; i++) {
 
204
        if(gs.entity[i] == REFERENCE_ENTITY) continue;
 
205
        SketchEntity *e = EntityById(gs.entity[i]);
 
206
        if(e->layer == cl) {
 
207
            goto items_on_current_layer_selected;
 
208
        }
 
209
    }
 
210
    for(i = 0; i < gs.lines; i++) {
 
211
        if(LayerForLine(gs.line[i]) == cl) {
 
212
            goto items_on_current_layer_selected;
 
213
        }
 
214
    }
 
215
    uiError("Selection for constraint must contain at least one item on "
 
216
            "current layer.");
 
217
    return;
 
218
 
 
219
items_on_current_layer_selected:
 
220
 
 
221
    // Set up a blank constraint, to possibly fill in later and add.
 
222
    SketchConstraint c;
 
223
    InitConstraint(&c);
 
224
 
 
225
    // And now create the appropriate constraint.
 
226
    switch(id) {
 
227
        case MNU_CONSTR_DISTANCE:
 
228
            if(gs.points == 2 && gs.lines == 0 && gs.entities == 0) {
 
229
                // Plain distance, from point to point (2 points)
 
230
                c.type = CONSTRAINT_PT_PT_DISTANCE;
 
231
                c.ptA = gs.point[0];
 
232
                c.ptB = gs.point[1];
 
233
 
 
234
                ModifyConstraintToReflectSketch(&c);
 
235
                AddConstraint(&c);
 
236
                break;
 
237
            }
 
238
            if(gs.entities == 1 && gs.anyLines == 1 && gs.points == 0 && 
 
239
                    gs.lines == 0)
 
240
            {
 
241
                // Plain distance, from point to point. The two endpoints of
 
242
                // a line segment, in this case.
 
243
                c.type = CONSTRAINT_PT_PT_DISTANCE;
 
244
                c.ptA = POINT_FOR_ENTITY(gs.entity[0], 0);
 
245
                c.ptB = POINT_FOR_ENTITY(gs.entity[0], 1);
 
246
 
 
247
                ModifyConstraintToReflectSketch(&c);
 
248
                AddConstraint(&c);
 
249
                break;
 
250
            }
 
251
            if(gs.points == 1 && gs.anyLines == 1 && gs.nonLineEntities == 0) {
 
252
                // Distance from a point to a line; line can be either an
 
253
                // infinite datum line, or the infinite extension of a 
 
254
                // line segment.
 
255
                c.type = CONSTRAINT_PT_LINE_DISTANCE;
 
256
                c.ptA = gs.point[0];
 
257
                if(gs.lines == 1) {
 
258
                    c.lineB = gs.line[0];
 
259
                } else {
 
260
                    c.entityB = gs.entity[0];
 
261
                }
 
262
 
 
263
                ModifyConstraintToReflectSketch(&c);
 
264
                AddConstraint(&c);
 
265
                break;
 
266
            }
 
267
            if(gs.points == 0 && gs.anyLines == 2 && gs.nonLineEntities == 0) {
 
268
                // Minimum distance from a line to a line (2 lines, 2 line
 
269
                // segments, or a line and a line segment, and they have
 
270
                // to be parallel or it doesn't work)
 
271
                c.type = CONSTRAINT_LINE_LINE_DISTANCE;
 
272
                if(gs.lines == 2) {
 
273
                    c.lineA = gs.line[0];
 
274
                    c.lineB = gs.line[1];
 
275
                } else if(gs.lines == 1) {
 
276
                    c.lineA = gs.line[0];
 
277
                    c.entityB = gs.entity[0];
 
278
                } else if(gs.entities == 2) {
 
279
                    c.entityA = gs.entity[0];
 
280
                    c.entityB = gs.entity[1];
 
281
                }
 
282
 
 
283
                ModifyConstraintToReflectSketch(&c);
 
284
                AddConstraint(&c);
 
285
                break;
 
286
            }
 
287
            if(gs.points == 0 && gs.entities == 1 && gs.circlesOrArcs == 1 
 
288
                && gs.lines == 0)
 
289
            {
 
290
                c.type = CONSTRAINT_RADIUS;
 
291
                c.entityA = gs.entity[0];
 
292
 
 
293
                ModifyConstraintToReflectSketch(&c);
 
294
                AddConstraint(&c);
 
295
                break;
 
296
            }
 
297
 
 
298
            uiError("Invalid selection for distance / diameter constraint. A "
 
299
             "distance / diameter constraint may be applied to:\r\n\r\n"
 
300
"       - two line segments/datum lines (perpendicular distance)\r\n"
 
301
"       - a point and a line segment/datum line (perpendicular distance)\r\n"
 
302
"       - two points\r\n"
 
303
"       - a circle or an arc\r\n");
 
304
            break;
 
305
 
 
306
        case MNU_CONSTR_ANGLE:
 
307
            // Line to line (2 lines, 2 line segments, or a line and a line
 
308
            // segment)
 
309
            if(gs.anyLines == 2 && gs.n == 2) {
 
310
                c.type = CONSTRAINT_LINE_LINE_ANGLE;
 
311
                if(gs.lines == 2) {
 
312
                    c.lineA = gs.line[0];
 
313
                    c.lineB = gs.line[1];
 
314
                } else if(gs.entities == 2) {
 
315
                    c.entityA = gs.entity[0];
 
316
                    c.entityB = gs.entity[1];
 
317
                } else {
 
318
                    c.entityA = gs.entity[0];
 
319
                    c.lineB = gs.line[0];
 
320
                }
 
321
 
 
322
                ModifyConstraintToReflectSketch(&c);
 
323
                AddConstraint(&c);
 
324
                break;
 
325
            }
 
326
 
 
327
            uiError("Invalid selection for angle constraint. An angle "
 
328
                  "constraint may be applied to two items "
 
329
                  "from the set of:\r\n\r\n"
 
330
                  "       - datum lines\r\n"
 
331
                  "       - line segments\r\n");
 
332
            break;
 
333
 
 
334
        case MNU_CONSTR_COINCIDENT:
 
335
            if(gs.points == 2 && gs.lines == 0 && gs.entities == 0) {
 
336
                // Point on point
 
337
                c.type = CONSTRAINT_POINTS_COINCIDENT;
 
338
                c.ptA = gs.point[0];
 
339
                c.ptB = gs.point[1];
 
340
                AddConstraint(&c);
 
341
                break;
 
342
            }
 
343
            if(gs.points == 1 && gs.anyLines == 1 && gs.nonLineEntities == 0) {
 
344
                // Point on line or line segment; this corresponds to a
 
345
                // point-to-line distance constraint, with a distance
 
346
                // of zero.
 
347
                c.type = CONSTRAINT_POINT_ON_LINE;
 
348
 
 
349
                c.ptA = gs.point[0];
 
350
                if(gs.lines == 1) {
 
351
                    c.lineB = gs.line[0];
 
352
                } else {
 
353
                    c.entityB = gs.entity[0];
 
354
                }
 
355
                // Zero distance
 
356
                c.v = 0;
 
357
 
 
358
                AddConstraint(&c);
 
359
                break;
 
360
            }
 
361
            if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.entities == 1
 
362
                && gs.lines == 0)
 
363
            {
 
364
                // Point on circle. This is actually just a point-to-point
 
365
                // distance constraint, from the center of the circle.
 
366
                c.type = CONSTRAINT_ON_CIRCLE;
 
367
 
 
368
                c.ptA = gs.point[0];
 
369
                c.entityA = gs.entity[0];
 
370
 
 
371
                AddConstraint(&c);
 
372
                break;
 
373
            }
 
374
 
 
375
            uiError(
 
376
                  "Invalid selection for coincidence constraint. A coincidence "
 
377
                  "constraint may be applied to:\r\n\r\n"
 
378
                  "       - two points (points become coincident)\r\n"
 
379
                  "       - a point and a curve (point lies on curve)\r\n"
 
380
                  "       - a point and a datum line (point lies on line)\r\n");
 
381
            break;
 
382
        
 
383
        case MNU_CONSTR_PERPENDICULAR:
 
384
        case MNU_CONSTR_PARALLEL:
 
385
            if((((id == MNU_CONSTR_PARALLEL) && gs.anyDirections == 2) ||
 
386
                    gs.anyLines == 2) && gs.n == 2)
 
387
            {
 
388
                int have = 0;
 
389
                
 
390
                // Parallelism can be defined between line segments, lines,
 
391
                // circular arc tangents, and cubic spline tangents.
 
392
                while(gs.lines > 0) {
 
393
                    if(have == 0) {
 
394
                        c.lineA = gs.line[gs.lines-1];
 
395
                    } else {
 
396
                        c.lineB = gs.line[gs.lines-1];
 
397
                    }
 
398
                    have++;
 
399
                    gs.lines--;
 
400
                }
 
401
                while(gs.points > 0) {
 
402
                    if(have == 0) {
 
403
                        c.ptA = gs.point[gs.points-1];
 
404
                    } else {
 
405
                        c.ptB = gs.point[gs.points-1];
 
406
                    }
 
407
                    have++;
 
408
                    gs.points--;
 
409
                }
 
410
                while(gs.entities > 0) {
 
411
                    if(have == 0) {
 
412
                        c.entityA = gs.entity[gs.entities-1];
 
413
                    } else {
 
414
                        c.entityB = gs.entity[gs.entities-1];
 
415
                    }
 
416
                    have++;
 
417
                    gs.entities--;
 
418
                }
 
419
                if(have != 2) {
 
420
                    oopsnf();
 
421
                    return;
 
422
                }
 
423
 
 
424
                if(id == MNU_CONSTR_PARALLEL) {
 
425
                    // They make a zero degree angle with each other.
 
426
                    c.type = CONSTRAINT_PARALLEL;
 
427
                    c.v = 0; // degrees
 
428
                } else {
 
429
                    // They make a ninety degree angle with each other.
 
430
                    c.type = CONSTRAINT_PERPENDICULAR;
 
431
                    c.v = 90; // degrees
 
432
                }
 
433
                AddConstraint(&c);
 
434
                break;
 
435
            }
 
436
            if(id == MNU_CONSTR_PARALLEL) {
 
437
                uiError("Invalid selection for parallel constraint. A parallel "
 
438
                      "constraint may be applied to two items from the set "
 
439
                      "of:\r\n\r\n"
 
440
                      "       - datum lines\r\n"
 
441
                      "       - line segments\r\n"
 
442
                      "       - endpoints of circular arcs (for tangency)\r\n"
 
443
                      "       - endpoints of cubic splines (for tangency)\r\n");
 
444
            } else {
 
445
                uiError("Invalid selection for perpendicular constraint. A "
 
446
                      "perpendicular constraint may be applied to two items "
 
447
                      "from the set of:\r\n\r\n"
 
448
                      "       - datum lines\r\n"
 
449
                      "       - line segments\r\n");
 
450
            }
 
451
            break;
 
452
 
 
453
        case MNU_CONSTR_EQUAL:
 
454
            // Equal length (2 line segments)
 
455
            if(gs.points == 0 && gs.lines == 0 && gs.anyLines == 2 &&
 
456
                gs.entities == 2)
 
457
            {
 
458
                c.type = CONSTRAINT_EQUAL_LENGTH;
 
459
                c.entityA = gs.entity[0];
 
460
                c.entityB = gs.entity[1];
 
461
 
 
462
                AddConstraint(&c);
 
463
                break;
 
464
            }
 
465
            // Equal radius (1 each circle or arc)
 
466
            if(gs.circlesOrArcs == 2 && gs.n == 2) {
 
467
                c.type = CONSTRAINT_EQUAL_RADIUS;
 
468
                c.entityA = gs.entity[0];
 
469
                c.entityB = gs.entity[1];
 
470
 
 
471
                AddConstraint(&c);
 
472
                break;
 
473
            }
 
474
 
 
475
            uiError("Invalid selection for equal constraint. An equal "
 
476
                  "constraint may be applied to:\r\n\r\n"
 
477
                  "       - two line segments\r\n"
 
478
                  "       - two circles, or arcs of circles\r\n");
 
479
            break;
 
480
 
 
481
        case MNU_CONSTR_MIDPOINT:
 
482
            // Point on line segment (point and line segment)
 
483
            if(gs.points == 1 && gs.lines == 0 && gs.anyLines == 1 &&
 
484
                gs.entities == 1)
 
485
            {
 
486
                c.type = CONSTRAINT_AT_MIDPOINT;
 
487
                c.entityA = gs.entity[0];
 
488
                c.ptA = gs.point[0];
 
489
 
 
490
                AddConstraint(&c);
 
491
                break;
 
492
            }
 
493
 
 
494
            uiError("Invalid selection for midpoint constraint. An midpoint "
 
495
                  "constraint may be applied to:\r\n\r\n"
 
496
                  "       - a point and a line segment\r\n");
 
497
            break;
 
498
 
 
499
        case MNU_CONSTR_SYMMETRIC:
 
500
            if(gs.points == 2 && gs.lines == 1 && gs.entities == 0) {
 
501
                c.type = CONSTRAINT_SYMMETRIC;
 
502
                c.lineA = gs.line[0];
 
503
                c.ptA = gs.point[0];
 
504
                c.ptB = gs.point[1];
 
505
 
 
506
                AddConstraint(&c);
 
507
                break;
 
508
            } else if(gs.anyLines == 2 && gs.entities == 1 && gs.points == 0 &&
 
509
                      gs.lines == 1 && gs.n == 2)
 
510
            {
 
511
                c.type = CONSTRAINT_SYMMETRIC;
 
512
                c.lineA = gs.line[0];
 
513
                c.ptA = POINT_FOR_ENTITY(gs.entity[0], 0);
 
514
                c.ptB = POINT_FOR_ENTITY(gs.entity[0], 1);
 
515
 
 
516
                AddConstraint(&c);
 
517
                break;
 
518
            }
 
519
 
 
520
            uiError("Invalid selection for symmetry constraint. A symmetry "
 
521
                    "constraint may be applied to:\r\n\r\n"
 
522
                    "       - two points and a datum line\r\n"
 
523
                    "       - a line segment and a datum line\r\n");
 
524
            break;
 
525
 
 
526
        case MNU_CONSTR_HORIZONTAL:
 
527
        case MNU_CONSTR_VERTICAL:
 
528
            if((gs.points == 2 && gs.n == 2) || 
 
529
               (gs.entities == 1 && gs.anyLines == 1 && gs.n == 1))
 
530
            {
 
531
                c.type = (id == MNU_CONSTR_HORIZONTAL) ?
 
532
                    CONSTRAINT_HORIZONTAL : CONSTRAINT_VERTICAL;
 
533
 
 
534
                if(gs.points == 2) {
 
535
                    // Two points that lie on a whicheveral line.
 
536
                    c.ptA = gs.point[0];
 
537
                    c.ptB = gs.point[1];
 
538
                } else {
 
539
                    // A line segment whose endpoints lie on a whicheveral
 
540
                    // line (i.e., that whicheveral line segment).
 
541
                    c.ptA = POINT_FOR_ENTITY(gs.entity[0], 0);
 
542
                    c.ptB = POINT_FOR_ENTITY(gs.entity[0], 1);
 
543
                }
 
544
 
 
545
                AddConstraint(&c);
 
546
                break;
 
547
            }
 
548
 
 
549
            uiError("Invalid selection for horizontal / vertical constraint. "
 
550
                    "A horizontal / vertical constraint may be applied to:"
 
551
                    "\r\n\r\n"
 
552
                    "       - two points\r\n"
 
553
                    "       - a line segment\r\n");
 
554
            break;
 
555
 
 
556
        case MNU_CONSTR_DRAG_HORIZ:
 
557
        case MNU_CONSTR_DRAG_VERT:
 
558
            if(gs.n == 1 && gs.points == 1) {
 
559
                c.type = CONSTRAINT_FORCE_PARAM;
 
560
 
 
561
                if(id == MNU_CONSTR_DRAG_HORIZ) {
 
562
                    c.paramA = X_COORD_FOR_PT(gs.point[0]);
 
563
                } else {
 
564
                    c.paramA = Y_COORD_FOR_PT(gs.point[0]);
 
565
                }
 
566
 
 
567
                AddConstraint(&c);
 
568
                break;
 
569
            }
 
570
 
 
571
            uiError("Invalid selection for draggable constraint. A draggable "
 
572
                    "constraint may be applied to:"
 
573
                    "\r\n\r\n"
 
574
                    "       - a point\r\n");
 
575
            break;
 
576
 
 
577
        case MNU_CONSTR_DRAG_ANGLE:
 
578
            if(gs.n == 2 && gs.points == 2) {
 
579
                c.type = CONSTRAINT_FORCE_ANGLE;
 
580
                c.ptA = gs.point[0];
 
581
                c.ptB = gs.point[1];
 
582
 
 
583
                AddConstraint(&c);
 
584
                break;
 
585
            }
 
586
 
 
587
            uiError("Invalid selection for draggable about point constraint. A "
 
588
                    "draggable about point constraint may be applied to:"
 
589
                    "\r\n\r\n"
 
590
                    "       - two points (the center of rotation, and the "
 
591
                    "point to constrain)\r\n");
 
592
            break;
 
593
 
 
594
        case MNU_CONSTR_SCALE_MM:
 
595
        case MNU_CONSTR_SCALE_INCH:
 
596
            if(gs.n == 1 && gs.entities == 1) {
 
597
                SketchEntity *e = EntityById(gs.entity[0]);
 
598
                if(e && e->type == ENTITY_IMPORTED) {
 
599
                    c.type = (id == MNU_CONSTR_SCALE_MM) ? 
 
600
                                CONSTRAINT_SCALE_MM : CONSTRAINT_SCALE_INCH;
 
601
                    c.entityA = gs.entity[0];
 
602
                    c.v = 1;
 
603
                    
 
604
                    ModifyConstraintToReflectSketch(&c);
 
605
                    AddConstraint(&c);
 
606
                    break;
 
607
                }
 
608
            }
 
609
 
 
610
            uiError("Invalid selection for scale (in inches or mm) constraint. "
 
611
                    "A scale constraint may be applied to:"
 
612
                    "\r\n\r\n"
 
613
                    "       - an imported file\r\n");
 
614
            break;
 
615
    }
 
616
}
 
617
 
 
618
void ConstrainCoincident(hPoint a, hPoint b)
 
619
{
 
620
    if(a == b) {
 
621
        oopsnf();
 
622
        return;
 
623
    }
 
624
 
 
625
    SketchConstraint c;
 
626
    InitConstraint(&c);
 
627
 
 
628
    // Point on point
 
629
    c.type = CONSTRAINT_POINTS_COINCIDENT;
 
630
    c.ptA = a;
 
631
    c.ptB = b;
 
632
    AddConstraint(&c);
 
633
}
 
634
 
 
635
void MenuConstrain(int id)
 
636
{
 
637
    if(id == MNU_CONSTR_SUPPLEMENTARY) {
 
638
        SketchConstraint *c;
 
639
 
 
640
        // Selection should include a single item, an angle constraint
 
641
        if(Selected[0].which != SEL_CONSTRAINT) goto badsel;
 
642
        int i;
 
643
        for(i = 1; i < MAX_SELECTED_ITEMS; i++) {
 
644
            if(Selected[i].which != SEL_NONE) goto badsel;
 
645
        }
 
646
        c = ConstraintById(Selected[0].constraint);
 
647
        if(c->type != CONSTRAINT_LINE_LINE_ANGLE) goto badsel;
 
648
 
 
649
        // An angle that is equal modulo 180 degrees will generate the
 
650
        // equivalent constraint, but display differently on-screen.
 
651
        c->v = c->v + 180;
 
652
        while(c->v > 180) c->v -= 360;
 
653
 
 
654
        // And redraw, and resolve, even though resolving should do nothing.
 
655
        SolvePerMode(FALSE);
 
656
        ClearHoverAndSelected();
 
657
        uiRepaint();
 
658
        return;
 
659
 
 
660
badsel:
 
661
        uiError("Must select an angle constraint.");
 
662
        return;
 
663
    }
 
664
 
 
665
    HandleMenuSelection(id);
 
666
 
 
667
    ClearHoverAndSelected();
 
668
    uiRepaint();
 
669
}
 
670
 
 
671
static Expr *EDistance(hPoint ptA, hPoint ptB)
 
672
{
 
673
    hParam xA = X_COORD_FOR_PT(ptA);
 
674
    hParam yA = Y_COORD_FOR_PT(ptA);
 
675
 
 
676
    hParam xB = X_COORD_FOR_PT(ptB);
 
677
    hParam yB = Y_COORD_FOR_PT(ptB);
 
678
    
 
679
    return ESqrt(EPlus(
 
680
                    ESquare(EMinus(EParam(xA), EParam(xB))),
 
681
                    ESquare(EMinus(EParam(yA), EParam(yB)))));
 
682
}
 
683
 
 
684
static void EGetPointAndDirectionForLine(hLine ln, hEntity e,
 
685
                                Expr **x0, Expr **y0, Expr **dx, Expr **dy)
 
686
{
 
687
    if(e && ln) oops();
 
688
 
 
689
    if(e) {
 
690
        hPoint ptA = POINT_FOR_ENTITY(e, 0);
 
691
        hPoint ptB = POINT_FOR_ENTITY(e, 1);
 
692
 
 
693
        hParam xA = X_COORD_FOR_PT(ptA);
 
694
        hParam yA = Y_COORD_FOR_PT(ptA);
 
695
 
 
696
        hParam xB = X_COORD_FOR_PT(ptB);
 
697
        hParam yB = Y_COORD_FOR_PT(ptB);
 
698
 
 
699
        if(dx) *dx = EMinus(EParam(xA), EParam(xB));
 
700
        if(dy) *dy = EMinus(EParam(yA), EParam(yB));
 
701
 
 
702
        if(x0) *x0 = EParam(xA);
 
703
        if(y0) *y0 = EParam(yA);
 
704
    } else {
 
705
        hParam theta = THETA_FOR_LINE(ln);
 
706
        hParam a = A_FOR_LINE(ln);
 
707
 
 
708
        if(dx) *dx = ECos(EParam(theta));
 
709
        if(dy) *dy = ESin(EParam(theta));
 
710
 
 
711
        if(x0) *x0 = ENegate(ETimes(EParam(a), ESin(EParam(theta))));
 
712
        if(y0) *y0 = ETimes(EParam(a), ECos(EParam(theta)));
 
713
    }
 
714
}
 
715
 
 
716
static void EGetDirectionOrTangent(hLine ln, hEntity he, hPoint p,
 
717
                                                Expr **dx, Expr **dy)
 
718
{   
 
719
    if(ln || he) {
 
720
        if(p) oops();
 
721
        EGetPointAndDirectionForLine(ln, he, NULL, NULL, dx, dy);
 
722
        return;
 
723
    }
 
724
    if(!p) oops();
 
725
 
 
726
    hParam px = X_COORD_FOR_PT(p);
 
727
    hParam py = Y_COORD_FOR_PT(p);
 
728
 
 
729
    hEntity hep = ENTITY_FROM_POINT(p);
 
730
    SketchEntity *e = EntityById(hep);
 
731
    if(e->type == ENTITY_CIRCULAR_ARC) {
 
732
        hPoint c = POINT_FOR_ENTITY(e->id, 2);
 
733
        // This is the center of the circle that defines the arc.
 
734
        hParam cx = X_COORD_FOR_PT(c);
 
735
        hParam cy = Y_COORD_FOR_PT(c);
 
736
        // The tangent is perpendicular to the radius.
 
737
        *dx = EMinus(EParam(cy), EParam(py));
 
738
        *dy = ENegate(EMinus(EParam(cx), EParam(px)));
 
739
    } else if(e->type == ENTITY_CUBIC_SPLINE) {
 
740
        int k = K_FROM_POINT(p);
 
741
        // This is the nearest Bezier control point.
 
742
        hParam bx, by;
 
743
        if(k == 0) {
 
744
            bx = X_COORD_FOR_PT(POINT_FOR_ENTITY(e->id, k+1));
 
745
            by = Y_COORD_FOR_PT(POINT_FOR_ENTITY(e->id, k+1));
 
746
        } else if(k == (e->points -1)) {
 
747
            bx = X_COORD_FOR_PT(POINT_FOR_ENTITY(e->id, k-1));
 
748
            by = Y_COORD_FOR_PT(POINT_FOR_ENTITY(e->id, k-1));
 
749
        } else {
 
750
            oops();
 
751
        }
 
752
 
 
753
        *dx = EMinus(EParam(bx), EParam(px));
 
754
        *dy = EMinus(EParam(by), EParam(py));
 
755
    } else {
 
756
        oops();
 
757
    }
 
758
}
 
759
 
 
760
static Expr *EDistanceFromExprPointToLine(Expr *xp, Expr *yp,
 
761
                                                        hLine ln, hEntity e)
 
762
{
 
763
    if(e && ln) oops();
 
764
 
 
765
    Expr *dx, *dy;
 
766
    Expr *x0, *y0;
 
767
 
 
768
    EGetPointAndDirectionForLine(ln, e, &x0, &y0, &dx, &dy);
 
769
 
 
770
    Expr *d = EDiv( EMinus(  ETimes(dx, EMinus(y0, yp)), 
 
771
                             ETimes(dy, EMinus(x0, xp))),
 
772
                    ESqrt(EPlus(ESquare(dx), ESquare(dy))));
 
773
    return d;
 
774
}
 
775
static Expr *EDistanceFromPointToLine(hPoint pt, hLine ln, hEntity e)
 
776
{
 
777
    Expr *xp = EParam(X_COORD_FOR_PT(pt));
 
778
    Expr *yp = EParam(Y_COORD_FOR_PT(pt));
 
779
 
 
780
    return EDistanceFromExprPointToLine(xp, yp, ln, e);
 
781
}
 
782
 
 
783
static void AddEquation(hConstraint hc, int k, Expr *e)
 
784
{
 
785
    if(EQ->eqns >= arraylen(EQ->eqn)) oops();
 
786
 
 
787
    EQ->eqn[EQ->eqns].e  = e;
 
788
    EQ->eqn[EQ->eqns].he = EQUATION_FOR_CONSTRAINT(hc, k);
 
789
 
 
790
    (EQ->eqns)++;
 
791
}
 
792
 
 
793
static void Make_PtPtDistance(SketchConstraint *c)
 
794
{
 
795
    if(told(c->v, 0) || c->type == CONSTRAINT_POINTS_COINCIDENT) {
 
796
        // Two points, zero distance apart; so we are trying to contrain
 
797
        // the points coincident, for which we should write two equations,
 
798
        // since that restricts two degrees of freedom.
 
799
 
 
800
        hParam pa, pb;
 
801
 
 
802
        // First equation: xA - xB = 0
 
803
        pa = X_COORD_FOR_PT(c->ptA);
 
804
        pb = X_COORD_FOR_PT(c->ptB);
 
805
        
 
806
        AddEquation(c->id, 0, EMinus(EParam(pa), EParam(pb)));
 
807
 
 
808
        // Second equation: yA - yB = 0
 
809
        pa = Y_COORD_FOR_PT(c->ptA);
 
810
        pb = Y_COORD_FOR_PT(c->ptB);
 
811
        
 
812
        AddEquation(c->id, 1, EMinus(EParam(pa), EParam(pb)));
 
813
    } else {
 
814
        Expr *d = EDistance(c->ptA, c->ptB);
 
815
 
 
816
        AddEquation(c->id, 0, EMinus(d, EConstant(c->v)));
 
817
    }
 
818
}
 
819
 
 
820
static void Make_PtLineDistance(SketchConstraint *c)
 
821
{
 
822
    Expr *d = EDistanceFromPointToLine(c->ptA, c->lineB, c->entityB);
 
823
 
 
824
    AddEquation(c->id, 0, EMinus(d, EConstant(c->v)));
 
825
}
 
826
 
 
827
static void Make_LineLineDistance(SketchConstraint *c)
 
828
{
 
829
    // We will choose an arbitrary point on one line, and constrain the
 
830
    // distance to the other.
 
831
    Expr *d;
 
832
    if(c->entityA) {
 
833
        // A is a line segment, so use one of its endpoints.
 
834
        d = EDistanceFromPointToLine(POINT_FOR_ENTITY(c->entityA, 0),
 
835
                                                    c->lineB, c->entityB);
 
836
    } else if(c->entityB) {
 
837
        // B is a line segment, so use one of its endpoints.
 
838
        d = EDistanceFromPointToLine(POINT_FOR_ENTITY(c->entityB, 0),
 
839
                                                    c->lineA, c->entityA);
 
840
    } else {
 
841
        // Two datum lines. Choose an arbtirary point on lineA, and measure
 
842
        // to the other line.
 
843
        Expr *x0, *y0;
 
844
        EGetPointAndDirectionForLine(c->lineA, 0, &x0, &y0, NULL, NULL);
 
845
        d = EDistanceFromExprPointToLine(x0, y0, c->lineB, 0);
 
846
    }
 
847
 
 
848
    AddEquation(c->id, 0, EMinus(d, EConstant(c->v)));
 
849
}
 
850
 
 
851
static void Make_Angle(SketchConstraint *c)
 
852
{
 
853
    Expr *dxA, *dyA;
 
854
    Expr *dxB, *dyB;
 
855
 
 
856
    EGetDirectionOrTangent(c->lineA, c->entityA, c->ptA, &dxA, &dyA);
 
857
    EGetDirectionOrTangent(c->lineB, c->entityB, c->ptB, &dxB, &dyB);
 
858
 
 
859
 
 
860
    // Rotate one of the vectors by the desired angle
 
861
    double theta = (c->v)*PI/180;
 
862
    Expr *dxr, *dyr;
 
863
    dxr = EPlus(ETimes(EConstant( cos(theta)), dxA),
 
864
                ETimes(EConstant( sin(theta)), dyA));
 
865
    dyr = EPlus(ETimes(EConstant(-sin(theta)), dxA),
 
866
                ETimes(EConstant( cos(theta)), dyA));
 
867
 
 
868
    // And we are trying to make them parallel, i.e. to cause
 
869
    //     [  dxr   dyr  ]
 
870
    // det [  dxB   dyB  ] = 0
 
871
 
 
872
    // It's important to normalize by the lengths of our direction vectors.
 
873
    // If we don't, then the solver can satisfy our constraint by driving
 
874
    // the length of the line to zero, at which point it is parallel to
 
875
    // everything.
 
876
    //
 
877
    // The constant is to get it on to the right order, so that the tolerances
 
878
    // for singularity and stuff later are reasonable.
 
879
    AddEquation(c->id, 0,
 
880
        EDiv(EMinus(ETimes(dxr, dyB), ETimes(dyr, dxB)),
 
881
             ETimes(ESqrt(EPlus(ESquare(dxB), ESquare(dyB))),
 
882
                    ESqrt(EPlus(ESquare(dxA), ESquare(dyA))))));
 
883
}
 
884
 
 
885
static void Make_EqualLength(SketchConstraint *c)
 
886
{
 
887
    hPoint ptA0 = POINT_FOR_ENTITY(c->entityA, 0);
 
888
    hPoint ptA1 = POINT_FOR_ENTITY(c->entityA, 1);
 
889
 
 
890
    hPoint ptB0 = POINT_FOR_ENTITY(c->entityB, 0);
 
891
    hPoint ptB1 = POINT_FOR_ENTITY(c->entityB, 1);
 
892
 
 
893
    AddEquation(c->id, 0, EMinus(
 
894
        EDistance(ptA0, ptA1),
 
895
        EDistance(ptB0, ptB1)));
 
896
}
 
897
 
 
898
static void Make_AtMidpoint(SketchConstraint *c)
 
899
{
 
900
    hPoint ptA = POINT_FOR_ENTITY(c->entityA, 0);
 
901
    hPoint ptB = POINT_FOR_ENTITY(c->entityA, 1);
 
902
 
 
903
    hParam xA = X_COORD_FOR_PT(ptA);
 
904
    hParam yA = Y_COORD_FOR_PT(ptA);
 
905
    hParam xB = X_COORD_FOR_PT(ptB);
 
906
    hParam yB = Y_COORD_FOR_PT(ptB);
 
907
 
 
908
    hParam xp = X_COORD_FOR_PT(c->ptA);
 
909
    hParam yp = Y_COORD_FOR_PT(c->ptA);
 
910
 
 
911
    AddEquation(c->id, 0, EMinus(
 
912
        EDiv(EPlus(EParam(xA), EParam(xB)), EConstant(2)),
 
913
        EParam(xp)));
 
914
 
 
915
    AddEquation(c->id, 1, EMinus(
 
916
        EDiv(EPlus(EParam(yA), EParam(yB)), EConstant(2)),
 
917
        EParam(yp)));
 
918
}
 
919
 
 
920
static Expr *RadiusForEntity(hEntity he)
 
921
{
 
922
    int type = EntityById(he)->type;
 
923
 
 
924
    if(type == ENTITY_CIRCLE) {
 
925
        // Straightforward, radius is the parameter of the circle.
 
926
        hParam r = PARAM_FOR_ENTITY(he, 0);
 
927
        return EParam(r);
 
928
    } else if(type == ENTITY_CIRCULAR_ARC) {
 
929
        // Radius is the distance from either on-curve point to the center
 
930
        // point.
 
931
        return EDistance(POINT_FOR_ENTITY(he, 0), POINT_FOR_ENTITY(he, 2));
 
932
    } else {
 
933
        oops();
 
934
    }
 
935
}
 
936
 
 
937
static void Make_Radius(SketchConstraint *c)
 
938
{
 
939
    hParam he = c->entityA;
 
940
 
 
941
    Expr *r = RadiusForEntity(he);
 
942
 
 
943
    // We work with the radius internally, but the user enters the diameter;
 
944
    // so the radii that we get from the sketch get multiplied by two before
 
945
    // we compare them.
 
946
    AddEquation(c->id, 0, EMinus(ETimes(EConstant(2), r), EConstant(c->v)));
 
947
}
 
948
 
 
949
static void Make_EqualRadius(SketchConstraint *c)
 
950
{
 
951
    Expr *rA = RadiusForEntity(c->entityA);
 
952
    Expr *rB = RadiusForEntity(c->entityB);
 
953
 
 
954
    // This should be written simply, so that equal radius for circles will
 
955
    // get solved by forward-substitution.
 
956
    AddEquation(c->id, 0, EMinus(rA, rB));
 
957
}
 
958
 
 
959
static void Make_OnCircle(SketchConstraint *c)
 
960
{
 
961
    SketchEntity *e = EntityById(c->entityA);
 
962
 
 
963
    if(e->type == ENTITY_CIRCLE) {
 
964
        hParam r = PARAM_FOR_ENTITY(c->entityA, 0);
 
965
        hPoint cntr = POINT_FOR_ENTITY(c->entityA, 0);
 
966
 
 
967
        Expr *d = EDistance(cntr, c->ptA);
 
968
 
 
969
        AddEquation(c->id, 0, EMinus(d, EParam(r)));
 
970
    } else if(e->type == ENTITY_CIRCULAR_ARC) {
 
971
        hPoint cntr = POINT_FOR_ENTITY(c->entityA, 2);
 
972
        hPoint onArc = POINT_FOR_ENTITY(c->entityA, 0); // or 1, either way
 
973
 
 
974
        Expr *r = EDistance(cntr, onArc);
 
975
        Expr *d = EDistance(cntr, c->ptA);
 
976
 
 
977
        AddEquation(c->id, 0, EMinus(d, r));
 
978
    } else {
 
979
        oops();
 
980
    }
 
981
}
 
982
 
 
983
static void Make_Symmetric(SketchConstraint *c)
 
984
{
 
985
    Expr *dA = EDistanceFromPointToLine(c->ptA, c->lineA, 0);
 
986
    Expr *dB = EDistanceFromPointToLine(c->ptB, c->lineA, 0);
 
987
 
 
988
    // These are signed distances, and the symmetric points should be on
 
989
    // opposite sides of the line, so they should be equal and magnitude,
 
990
    // and opposite in sign.
 
991
    AddEquation(c->id, 0, EPlus(dA, dB));
 
992
 
 
993
    Expr *xA = EParam(X_COORD_FOR_PT(c->ptA));
 
994
    Expr *yA = EParam(Y_COORD_FOR_PT(c->ptA));
 
995
    Expr *xB = EParam(X_COORD_FOR_PT(c->ptB));
 
996
    Expr *yB = EParam(Y_COORD_FOR_PT(c->ptB));
 
997
 
 
998
    hParam theta = THETA_FOR_LINE(c->lineA);
 
999
    Expr *dx = ECos(EParam(theta));
 
1000
    Expr *dy = ESin(EParam(theta));
 
1001
 
 
1002
    // We want the line between the two points to be perpendicular to the
 
1003
    // datum, so (xA - xB, yA - yB) dot (cos theta, sin theta) = 0.
 
1004
    AddEquation(c->id, 1, EPlus(ETimes(EMinus(xA, xB), dx),
 
1005
                      ETimes(EMinus(yA, yB), dy)));
 
1006
}
 
1007
 
 
1008
static void Make_HorizontalVertical(SketchConstraint *c)
 
1009
{
 
1010
    hParam pA, pB;
 
1011
 
 
1012
    if(c->type == CONSTRAINT_HORIZONTAL) {
 
1013
        // Horizontal lines have equal y coordinates for their endpoints.
 
1014
        pA = Y_COORD_FOR_PT(c->ptA);
 
1015
        pB = Y_COORD_FOR_PT(c->ptB);
 
1016
    } else {
 
1017
        pA = X_COORD_FOR_PT(c->ptA);
 
1018
        pB = X_COORD_FOR_PT(c->ptB);
 
1019
    }
 
1020
 
 
1021
    // A simple equation of this form will be solved by substitution, and
 
1022
    // will therefore solve very quickly.
 
1023
    AddEquation(c->id, 0, EMinus(EParam(pA), EParam(pB)));
 
1024
}
 
1025
 
 
1026
static void Make_ForceParam(SketchConstraint *c)
 
1027
{
 
1028
    hParam hp = c->paramA;
 
1029
 
 
1030
    // This is a weird one. It's basically doing the same job as the
 
1031
    // solver does when it makes assumptions, fixing an unknown wherever
 
1032
    // it has been dragged. A bit silly to write an equation, perhaps,
 
1033
    // but this will solve very easily.
 
1034
    AddEquation(c->id, 0,
 
1035
        EMinus(EParam(hp), EConstant(EvalParam(hp))));
 
1036
}
 
1037
 
 
1038
static void Make_ForceAngle(SketchConstraint *c)
 
1039
{
 
1040
    hParam xA = X_COORD_FOR_PT(c->ptA);
 
1041
    hParam yA = Y_COORD_FOR_PT(c->ptA);
 
1042
 
 
1043
    hParam xB = X_COORD_FOR_PT(c->ptB);
 
1044
    hParam yB = Y_COORD_FOR_PT(c->ptB);
 
1045
 
 
1046
    // We want to fix the angle of the line connecting points A and B, to
 
1047
    // be whatever it is right now in the initial numerical guess. Write
 
1048
    // this in the usual dot/cross product form,
 
1049
    //     (xA - xB, yA - yB) dot (-(yA0 - yB0), xA0 - xB0) = 0
 
1050
 
 
1051
    AddEquation(c->id, 0,
 
1052
        EPlus(
 
1053
            ETimes(EMinus(EParam(xA), EParam(xB)),
 
1054
                   EConstant(-(EvalParam(yA) - EvalParam(yB)))),
 
1055
            ETimes(EMinus(EParam(yA), EParam(yB)),
 
1056
                   EConstant(EvalParam(xA) - EvalParam(xB))))); 
 
1057
}
 
1058
 
 
1059
static void Make_Scale(SketchConstraint *c)
 
1060
{
 
1061
    SketchEntity *e = EntityById(c->entityA);
 
1062
    if(!e) {
 
1063
        oopsnf();
 
1064
        return;
 
1065
    }
 
1066
    char *sought = "so dy = ";
 
1067
    char *s = strstr(e->text, sought);
 
1068
    if(!s) return;
 
1069
    s += strlen(sought);
 
1070
 
 
1071
    double dy = atof(s);
 
1072
    if(c->type == CONSTRAINT_SCALE_MM) {
 
1073
        dy *= 1000;
 
1074
    } else if(c->type == CONSTRAINT_SCALE_INCH) {
 
1075
        dy *= 25400;
 
1076
    } else {
 
1077
        oopsnf();
 
1078
        return;
 
1079
    }
 
1080
    dy *= c->v;
 
1081
 
 
1082
    hPoint pA = POINT_FOR_ENTITY(c->entityA, 0);
 
1083
    hPoint pB = POINT_FOR_ENTITY(c->entityA, 1);
 
1084
 
 
1085
    AddEquation(c->id, 0, EMinus(EDistance(pA, pB), EConstant(dy)));
 
1086
}
 
1087
 
 
1088
void MakeConstraintEquations(SketchConstraint *c)
 
1089
{
 
1090
    switch(c->type) {
 
1091
        case CONSTRAINT_POINTS_COINCIDENT:
 
1092
        case CONSTRAINT_PT_PT_DISTANCE:
 
1093
            Make_PtPtDistance(c);
 
1094
            break;
 
1095
 
 
1096
        case CONSTRAINT_POINT_ON_LINE:
 
1097
        case CONSTRAINT_PT_LINE_DISTANCE:
 
1098
            Make_PtLineDistance(c);
 
1099
            break;
 
1100
 
 
1101
        case CONSTRAINT_LINE_LINE_DISTANCE:
 
1102
            Make_LineLineDistance(c);
 
1103
            break;
 
1104
 
 
1105
        case CONSTRAINT_ON_CIRCLE:
 
1106
            Make_OnCircle(c);
 
1107
            break;
 
1108
 
 
1109
        case CONSTRAINT_RADIUS:
 
1110
            Make_Radius(c);
 
1111
            break;
 
1112
 
 
1113
        case CONSTRAINT_PARALLEL:
 
1114
        case CONSTRAINT_PERPENDICULAR:
 
1115
        case CONSTRAINT_LINE_LINE_ANGLE:
 
1116
            Make_Angle(c);
 
1117
            break;
 
1118
 
 
1119
        case CONSTRAINT_EQUAL_LENGTH:
 
1120
            Make_EqualLength(c);
 
1121
            break;
 
1122
 
 
1123
        case CONSTRAINT_EQUAL_RADIUS:
 
1124
            Make_EqualRadius(c);
 
1125
            break;
 
1126
 
 
1127
        case CONSTRAINT_AT_MIDPOINT:
 
1128
            Make_AtMidpoint(c);
 
1129
            break;
 
1130
 
 
1131
        case CONSTRAINT_SYMMETRIC:
 
1132
            Make_Symmetric(c);
 
1133
            break;
 
1134
 
 
1135
        case CONSTRAINT_HORIZONTAL:
 
1136
        case CONSTRAINT_VERTICAL:
 
1137
            Make_HorizontalVertical(c);
 
1138
            break;
 
1139
 
 
1140
        case CONSTRAINT_FORCE_PARAM:
 
1141
            Make_ForceParam(c);
 
1142
            break;
 
1143
 
 
1144
        case CONSTRAINT_FORCE_ANGLE:
 
1145
            Make_ForceAngle(c);
 
1146
            break;
 
1147
 
 
1148
        case CONSTRAINT_SCALE_MM:
 
1149
        case CONSTRAINT_SCALE_INCH:
 
1150
            Make_Scale(c);
 
1151
            break;
 
1152
 
 
1153
        default:
 
1154
            oops();
 
1155
    }
 
1156
}
 
1157
 
 
1158
void MakeEntityEquations(SketchEntity *e)
 
1159
{
 
1160
    switch(e->type) {
 
1161
        case ENTITY_CIRCULAR_ARC: {
 
1162
            Expr *d0 = EDistance(POINT_FOR_ENTITY(e->id, 0),
 
1163
                                 POINT_FOR_ENTITY(e->id, 2));
 
1164
            Expr *d1 = EDistance(POINT_FOR_ENTITY(e->id, 1),
 
1165
                                 POINT_FOR_ENTITY(e->id, 2));
 
1166
            // Make sure it gets an hEquation that won't conflict with
 
1167
            // some constraint's equation.
 
1168
            AddEquation(CONSTRAINT_FOR_ENTITY(e->id), 0, EMinus(d0, d1));
 
1169
            break;
 
1170
        }
 
1171
 
 
1172
        default:
 
1173
            // Most entities don't generate any equations. Ideally, none
 
1174
            // would.
 
1175
            break;
 
1176
    }
 
1177
}
 
1178
 
 
1179
static void ModifyConstraintToReflectSketch(SketchConstraint *c)
 
1180
{
 
1181
    switch(c->type) {
 
1182
        case CONSTRAINT_PT_PT_DISTANCE: {
 
1183
            // Stupid special case. The point-to-point distance constraint
 
1184
            // becomes a point-on-point constraint when that distance is
 
1185
            // zero, and at that point it restricts two DOF, not one, so
 
1186
            // I have to write two equations. That means that it breaks from
 
1187
            // the usual form for "dimension"-type constraints.
 
1188
            double xA, yA, xB, yB;
 
1189
            EvalPoint(c->ptA, &xA, &yA);
 
1190
            EvalPoint(c->ptB, &xB, &yB);
 
1191
            c->v = Distance(xA, yA, xB, yB);
 
1192
            break;
 
1193
        }
 
1194
        case CONSTRAINT_LINE_LINE_ANGLE: {
 
1195
            // Another special case; this equation has the form of a dot
 
1196
            // product, not so easy to derive present angle from that.
 
1197
            int A = 0, B = 1;
 
1198
            double x0[2], y0[2];
 
1199
            double dx[2], dy[2];
 
1200
            LineOrLineSegment(c->lineA, c->entityA,
 
1201
                                &(x0[A]), &(y0[A]), &(dx[A]), &(dy[A]));
 
1202
            LineOrLineSegment(c->lineB, c->entityB,
 
1203
                                &(x0[B]), &(y0[B]), &(dx[B]), &(dy[B]));
 
1204
            
 
1205
            // A special case for the special case. If these are two line
 
1206
            // segments that share an endpoint, then we clearly should be
 
1207
            // dimensioning the angle between the lines segments, not the
 
1208
            // angle that's hanging in thin air.
 
1209
            if(c->entityA && c->entityB) {
 
1210
 
 
1211
                double x0A, y0A, x1A, y1A;
 
1212
                double x0B, y0B, x1B, y1B;
 
1213
                EvalPoint(POINT_FOR_ENTITY(c->entityA, 0), &x0A, &y0A);
 
1214
                EvalPoint(POINT_FOR_ENTITY(c->entityA, 1), &x1A, &y1A);
 
1215
                EvalPoint(POINT_FOR_ENTITY(c->entityB, 0), &x0B, &y0B);
 
1216
                EvalPoint(POINT_FOR_ENTITY(c->entityB, 1), &x1B, &y1B);
 
1217
    
 
1218
                // Four possible cases for which points are coincident
 
1219
                if(tol(x0A, x0B) && tol(y0A, y0B)) {
 
1220
                    dx[A] = x1A - x0A;
 
1221
                    dy[A] = y1A - y0A;
 
1222
                    dx[B] = x1B - x0B;
 
1223
                    dy[B] = y1B - y0B;
 
1224
                } else if(tol(x0A, x1B) && tol(y0A, y1B)) {
 
1225
                    dx[A] = x1A - x0A;
 
1226
                    dy[A] = y1A - y0A;
 
1227
                    dx[B] = x0B - x1B;
 
1228
                    dy[B] = y0B - y1B;
 
1229
                } else if(tol(x1A, x0B) && tol(y1A, y0B)) {
 
1230
                    dx[A] = x0A - x1A;
 
1231
                    dy[A] = y0A - y1A;
 
1232
                    dx[B] = x1B - x0B;
 
1233
                    dy[B] = y1B - y0B;
 
1234
                } else if(tol(x1A, x1B) && tol(y1A, y1B)) {
 
1235
                    dx[A] = x0A - x1A;
 
1236
                    dy[A] = y0A - y1A;
 
1237
                    dx[B] = x0B - x1B;
 
1238
                    dy[B] = y0B - y1B;
 
1239
                }
 
1240
            }
 
1241
 
 
1242
            double thetaA = atan2(dy[A], dx[A]);
 
1243
            double thetaB = atan2(dy[B], dx[B]);
 
1244
            double dtheta = thetaA - thetaB;
 
1245
            while(dtheta < PI) dtheta += 2*PI;
 
1246
            while(dtheta > PI) dtheta -= 2*PI;
 
1247
            c->v = dtheta*180/PI;
 
1248
            break;
 
1249
        }
 
1250
        case CONSTRAINT_PT_LINE_DISTANCE:
 
1251
        case CONSTRAINT_LINE_LINE_DISTANCE:
 
1252
        case CONSTRAINT_RADIUS: {
 
1253
            // These constraints all have a number associated with them;
 
1254
            // they are trying to make some measurement equal to that
 
1255
            // number. The constraint equation f = 0 is written in the
 
1256
            // form
 
1257
            //
 
1258
            //        actual - desired = 0
 
1259
            //
 
1260
            // and the desired value is stored in c->v.
 
1261
            EQ->eqns = 0;
 
1262
            MakeConstraintEquations(c);
 
1263
            if(EQ->eqns != 1) oops();
 
1264
 
 
1265
            double actualMinusDesired = EEval(EQ->eqn[0].e);
 
1266
 
 
1267
            c->v += actualMinusDesired;
 
1268
            break;
 
1269
        }
 
1270
        case CONSTRAINT_SCALE_MM:
 
1271
        case CONSTRAINT_SCALE_INCH: {
 
1272
            // These constraints are in the form
 
1273
            //
 
1274
            //      actual - desired = 0
 
1275
            //
 
1276
            //  and the desired has the form (c->v)*k, for some constant k.
 
1277
            EQ->eqns = 0;
 
1278
            c->v = 0;
 
1279
            MakeConstraintEquations(c);
 
1280
            if(EQ->eqns != 1) {
 
1281
                // The dimensions of the imported file might not yet be
 
1282
                // calculated, in which case we can't do the constraint yet,
 
1283
                // but it's not an error.
 
1284
                c->v = 1;
 
1285
                break;
 
1286
            }
 
1287
            double actual = EEval(EQ->eqn[0].e);
 
1288
            EQ->eqns = 0;
 
1289
            c->v = 1;
 
1290
            MakeConstraintEquations(c);
 
1291
            if(EQ->eqns != 1) oops();
 
1292
            double actualMinusK = EEval(EQ->eqn[0].e);
 
1293
            double k = actual - actualMinusK;
 
1294
 
 
1295
            c->v = actual/k;
 
1296
            break;
 
1297
        }
 
1298
 
 
1299
        default:
 
1300
            // These constraints have no parameter. Either they hold, or
 
1301
            // they don't, no way to adjust the constraint to make it go.
 
1302
            break;
 
1303
    }
 
1304
}