Solvespace

Форк
0
/
constraint.cpp 
925 строк · 39.4 Кб
1
//-----------------------------------------------------------------------------
2
// Implementation of the Constraint menu, to create new constraints in
3
// the sketch.
4
//
5
// Copyright 2008-2013 Jonathan Westhues.
6
//-----------------------------------------------------------------------------
7
#include "solvespace.h"
8

9
std::string Constraint::DescriptionString() const {
10
    std::string s;
11
    switch(type) {
12
        case Type::POINTS_COINCIDENT:   s = C_("constr-name", "pts-coincident"); break;
13
        case Type::PT_PT_DISTANCE:      s = C_("constr-name", "pt-pt-distance"); break;
14
        case Type::PT_LINE_DISTANCE:    s = C_("constr-name", "pt-line-distance"); break;
15
        case Type::PT_PLANE_DISTANCE:   s = C_("constr-name", "pt-plane-distance"); break;
16
        case Type::PT_FACE_DISTANCE:    s = C_("constr-name", "pt-face-distance"); break;
17
        case Type::PROJ_PT_DISTANCE:    s = C_("constr-name", "proj-pt-pt-distance"); break;
18
        case Type::PT_IN_PLANE:         s = C_("constr-name", "pt-in-plane"); break;
19
        case Type::PT_ON_LINE:          s = C_("constr-name", "pt-on-line"); break;
20
        case Type::PT_ON_FACE:          s = C_("constr-name", "pt-on-face"); break;
21
        case Type::EQUAL_LENGTH_LINES:  s = C_("constr-name", "eq-length"); break;
22
        case Type::EQ_LEN_PT_LINE_D:    s = C_("constr-name", "eq-length-and-pt-ln-dist"); break;
23
        case Type::EQ_PT_LN_DISTANCES:  s = C_("constr-name", "eq-pt-line-distances"); break;
24
        case Type::LENGTH_RATIO:        s = C_("constr-name", "length-ratio"); break;
25
        case Type::ARC_ARC_LEN_RATIO:   s = C_("constr-name", "arc-arc-length-ratio"); break;
26
        case Type::ARC_LINE_LEN_RATIO:  s = C_("constr-name", "arc-line-length-ratio"); break;
27
        case Type::LENGTH_DIFFERENCE:   s = C_("constr-name", "length-difference"); break;
28
        case Type::ARC_ARC_DIFFERENCE:  s = C_("constr-name", "arc-arc-len-difference"); break;
29
        case Type::ARC_LINE_DIFFERENCE: s = C_("constr-name", "arc-line-len-difference"); break;
30
        case Type::SYMMETRIC:           s = C_("constr-name", "symmetric"); break;
31
        case Type::SYMMETRIC_HORIZ:     s = C_("constr-name", "symmetric-h"); break;
32
        case Type::SYMMETRIC_VERT:      s = C_("constr-name", "symmetric-v"); break;
33
        case Type::SYMMETRIC_LINE:      s = C_("constr-name", "symmetric-line"); break;
34
        case Type::AT_MIDPOINT:         s = C_("constr-name", "at-midpoint"); break;
35
        case Type::HORIZONTAL:          s = C_("constr-name", "horizontal"); break;
36
        case Type::VERTICAL:            s = C_("constr-name", "vertical"); break;
37
        case Type::DIAMETER:            s = C_("constr-name", "diameter"); break;
38
        case Type::PT_ON_CIRCLE:        s = C_("constr-name", "pt-on-circle"); break;
39
        case Type::SAME_ORIENTATION:    s = C_("constr-name", "same-orientation"); break;
40
        case Type::ANGLE:               s = C_("constr-name", "angle"); break;
41
        case Type::PARALLEL:            s = C_("constr-name", "parallel"); break;
42
        case Type::ARC_LINE_TANGENT:    s = C_("constr-name", "arc-line-tangent"); break;
43
        case Type::CUBIC_LINE_TANGENT:  s = C_("constr-name", "cubic-line-tangent"); break;
44
        case Type::CURVE_CURVE_TANGENT: s = C_("constr-name", "curve-curve-tangent"); break;
45
        case Type::PERPENDICULAR:       s = C_("constr-name", "perpendicular"); break;
46
        case Type::EQUAL_RADIUS:        s = C_("constr-name", "eq-radius"); break;
47
        case Type::EQUAL_ANGLE:         s = C_("constr-name", "eq-angle"); break;
48
        case Type::EQUAL_LINE_ARC_LEN:  s = C_("constr-name", "eq-line-len-arc-len"); break;
49
        case Type::WHERE_DRAGGED:       s = C_("constr-name", "lock-where-dragged"); break;
50
        case Type::COMMENT:             s = C_("constr-name", "comment"); break;
51
        default:                        s = "???"; break;
52
    }
53

54
    return ssprintf("c%03x-%s", h.v, s.c_str());
55
}
56

57
#ifndef LIBRARY
58

59
//-----------------------------------------------------------------------------
60
// Delete all constraints with the specified type, entityA, ptA. We use this
61
// when auto-removing constraints that would become redundant.
62
//-----------------------------------------------------------------------------
63
void Constraint::DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA)
64
{
65
    SK.constraint.ClearTags();
66
    for(auto &constraint : SK.constraint) {
67
        ConstraintBase *ct = &constraint;
68
        if(ct->type != type) continue;
69

70
        if(ct->entityA != entityA) continue;
71
        if(ct->ptA != ptA) continue;
72
        ct->tag = 1;
73
    }
74
    SK.constraint.RemoveTagged();
75
    // And no need to do anything special, since nothing
76
    // ever depends on a constraint. But do clear the
77
    // hover, in case the just-deleted constraint was
78
    // hovered.
79
    SS.GW.hover.Clear();
80
}
81

82
hConstraint Constraint::AddConstraint(Constraint *c, bool rememberForUndo) {
83
    if(rememberForUndo) SS.UndoRemember();
84

85
    hConstraint hc = SK.constraint.AddAndAssignId(c);
86
    SK.GetConstraint(hc)->Generate(&SK.param);
87

88
    SS.MarkGroupDirty(c->group);
89
    SK.GetGroup(c->group)->dofCheckOk = false;
90
    return c->h;
91
}
92

93
hConstraint Constraint::Constrain(Constraint::Type type, hEntity ptA, hEntity ptB,
94
                                  hEntity entityA, hEntity entityB,
95
                                  bool other, bool other2)
96
{
97
    Constraint c = {};
98
    c.group = SS.GW.activeGroup;
99
    c.workplane = SS.GW.ActiveWorkplane();
100
    c.type = type;
101
    c.ptA = ptA;
102
    c.ptB = ptB;
103
    c.entityA = entityA;
104
    c.entityB = entityB;
105
    c.other = other;
106
    c.other2 = other2;
107
    return AddConstraint(&c, /*rememberForUndo=*/false);
108
}
109

110
hConstraint Constraint::TryConstrain(Constraint::Type type, hEntity ptA, hEntity ptB,
111
                                     hEntity entityA, hEntity entityB,
112
                                     bool other, bool other2) {
113
    int rankBefore, rankAfter;
114
    SolveResult howBefore = SS.TestRankForGroup(SS.GW.activeGroup, &rankBefore);
115
    hConstraint hc = Constrain(type, ptA, ptB, entityA, entityB, other, other2);
116
    SolveResult howAfter = SS.TestRankForGroup(SS.GW.activeGroup, &rankAfter);
117
    // There are two cases where the constraint is clearly redundant:
118
    //   * If the group wasn't overconstrained and now it is;
119
    //   * If the group was overconstrained, and adding the constraint doesn't change rank at all.
120
    if((howBefore == SolveResult::OKAY && howAfter == SolveResult::REDUNDANT_OKAY) ||
121
       (howBefore == SolveResult::REDUNDANT_OKAY && howAfter == SolveResult::REDUNDANT_OKAY &&
122
            rankBefore == rankAfter)) {
123
        SK.constraint.RemoveById(hc);
124
        hc = {};
125
    }
126
    return hc;
127
}
128

129
hConstraint Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
130
    return Constrain(Type::POINTS_COINCIDENT, ptA, ptB,
131
        Entity::NO_ENTITY, Entity::NO_ENTITY, /*other=*/false, /*other2=*/false);
132
}
133

134
bool Constraint::ConstrainArcLineTangent(Constraint *c, Entity *line, Entity *arc) {
135
    Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
136
           l1 = SK.GetEntity(line->point[1])->PointGetNum();
137
    Vector a1 = SK.GetEntity(arc->point[1])->PointGetNum(),
138
           a2 = SK.GetEntity(arc->point[2])->PointGetNum();
139
    if(l0.Equals(a1) || l1.Equals(a1)) {
140
        c->other = false;
141
    } else if(l0.Equals(a2) || l1.Equals(a2)) {
142
        c->other = true;
143
    } else {
144
        Error(_("The tangent arc and line segment must share an "
145
                "endpoint. Constrain them with Constrain -> "
146
                "On Point before constraining tangent."));
147
        return false;
148
    }
149
    return true;
150
}
151

152
bool Constraint::ConstrainCubicLineTangent(Constraint *c, Entity *line, Entity *cubic) {
153
    Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
154
           l1 = SK.GetEntity(line->point[1])->PointGetNum();
155
    Vector as = cubic->CubicGetStartNum(),
156
           af = cubic->CubicGetFinishNum();
157

158
    if(l0.Equals(as) || l1.Equals(as)) {
159
        c->other = false;
160
    } else if(l0.Equals(af) || l1.Equals(af)) {
161
        c->other = true;
162
    } else {
163
        Error(_("The tangent cubic and line segment must share an "
164
                "endpoint. Constrain them with Constrain -> "
165
                "On Point before constraining tangent."));
166
        return false;
167
    }
168
    return true;
169
}
170

171
bool Constraint::ConstrainCurveCurveTangent(Constraint *c, Entity *eA, Entity *eB) {
172
    Vector as = eA->EndpointStart(),
173
           af = eA->EndpointFinish(),
174
           bs = eB->EndpointStart(),
175
           bf = eB->EndpointFinish();
176
    if(as.Equals(bs)) {
177
        c->other = false;
178
        c->other2 = false;
179
    } else if(as.Equals(bf)) {
180
        c->other = false;
181
        c->other2 = true;
182
    } else if(af.Equals(bs)) {
183
        c->other = true;
184
        c->other2 = false;
185
    } else if(af.Equals(bf)) {
186
        c->other = true;
187
        c->other2 = true;
188
    } else {
189
        Error(_("The curves must share an endpoint. Constrain them "
190
                "with Constrain -> On Point before constraining "
191
                "tangent."));
192
        return false;
193
    }
194
    return true;
195
}
196

197
void Constraint::MenuConstrain(Command id) {
198
    std::vector<Constraint> newcons;
199

200
    Constraint c = {};
201
    c.group = SS.GW.activeGroup;
202
    c.workplane = SS.GW.ActiveWorkplane();
203

204
    SS.GW.GroupSelection();
205
    auto const &gs = SS.GW.gs;
206

207
    switch(id) {
208
        case Command::DISTANCE_DIA:
209
        case Command::REF_DISTANCE: {
210
            if(gs.points == 2 && gs.n == 2) {
211
                c.type = Type::PT_PT_DISTANCE;
212
                c.ptA = gs.point[0];
213
                c.ptB = gs.point[1];
214
            } else if(gs.lineSegments == 1 && gs.n == 1) {
215
                c.type = Type::PT_PT_DISTANCE;
216
                Entity *e = SK.GetEntity(gs.entity[0]);
217
                c.ptA = e->point[0];
218
                c.ptB = e->point[1];
219
            } else if(gs.vectors == 1 && gs.points == 2 && gs.n == 3) {
220
                c.type = Type::PROJ_PT_DISTANCE;
221
                c.ptA = gs.point[0];
222
                c.ptB = gs.point[1];
223
                c.entityA = gs.vector[0];
224
            } else if(gs.workplanes == 1 && gs.points == 1 && gs.n == 2) {
225
                c.type = Type::PT_PLANE_DISTANCE;
226
                c.ptA = gs.point[0];
227
                c.entityA = gs.entity[0];
228
            } else if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
229
                c.type = Type::PT_LINE_DISTANCE;
230
                c.ptA = gs.point[0];
231
                c.entityA = gs.entity[0];
232
            } else if(gs.faces == 1 && gs.points == 1 && gs.n == 2) {
233
                c.type = Type::PT_FACE_DISTANCE;
234
                c.ptA = gs.point[0];
235
                c.entityA = gs.face[0];
236
            } else if(gs.circlesOrArcs == 1 && gs.n == 1) {
237
                c.type = Type::DIAMETER;
238
                c.entityA = gs.entity[0];
239
                Entity* arc = SK.GetEntity(gs.entity[0]);
240
                if ((arc->type == EntityBase::Type::ARC_OF_CIRCLE)
241
                    && (!SS.arcDimDefaultDiameter))
242
                {
243
                  c.other = true;
244
                }
245
            } else {
246
                Error(_("Bad selection for distance / diameter constraint. This "
247
                        "constraint can apply to:\n\n"
248
                        "    * two points (distance between points)\n"
249
                        "    * a line segment (length)\n"
250
                        "    * two points and a line segment or normal (projected distance)\n"
251
                        "    * a workplane and a point (minimum distance)\n"
252
                        "    * a line segment and a point (minimum distance)\n"
253
                        "    * a plane face and a point (minimum distance)\n"
254
                        "    * a circle or an arc (diameter)\n"));
255
                return;
256
            }
257
            if(c.type == Type::PT_PT_DISTANCE || c.type == Type::PROJ_PT_DISTANCE) {
258
                Vector n = SS.GW.projRight.Cross(SS.GW.projUp);
259
                Vector a = SK.GetEntity(c.ptA)->PointGetNum();
260
                Vector b = SK.GetEntity(c.ptB)->PointGetNum();
261
                c.disp.offset = n.Cross(a.Minus(b));
262
                c.disp.offset = (c.disp.offset).WithMagnitude(50/SS.GW.scale);
263
            } else {
264
                c.disp.offset = Vector::From(0, 0, 0);
265
            }
266

267
            if(id == Command::REF_DISTANCE) {
268
                c.reference = true;
269
            }
270

271
            c.valA = 0;
272
            c.ModifyToSatisfy();
273
            AddConstraint(&c);
274
            newcons.push_back(c);
275
            break;
276
        }
277

278
        case Command::ON_ENTITY:
279
            if(gs.points >= 2 && gs.points == gs.n) {
280
                c.type = Type::POINTS_COINCIDENT;
281
                c.ptA = gs.point[0];
282
                for(int k = 1; k < gs.points; k++) {
283
                    c.ptB = gs.point[k];
284
                    newcons.push_back(c);
285
                }
286
            } else if(gs.points == 1 && gs.workplanes == 1 && gs.n == 2) {
287
                c.type = Type::PT_IN_PLANE;
288
                c.ptA = gs.point[0];
289
                c.entityA = gs.entity[0];
290
                newcons.push_back(c);
291
            } else if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) {
292
                c.type = Type::PT_ON_LINE;
293
                c.ptA = gs.point[0];
294
                c.entityA = gs.entity[0];
295
                newcons.push_back(c);
296
            } else if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.n == 2) {
297
                c.type = Type::PT_ON_CIRCLE;
298
                c.ptA = gs.point[0];
299
                c.entityA = gs.entity[0];
300
                newcons.push_back(c);
301
            } else if(gs.points == 1 && gs.faces >= 1 && gs.n == gs.points+gs.faces) {
302
                c.type = Type::PT_ON_FACE;
303
                c.ptA = gs.point[0];
304
                for (int k=0; k<gs.faces; k++) {
305
                    c.entityA = gs.face[k];
306
                    newcons.push_back(c);
307
                }
308
            } else {
309
                Error(_("Bad selection for on point / curve / plane constraint. "
310
                        "This constraint can apply to:\n\n"
311
                        "    * two or more points (points coincident)\n"
312
                        "    * a point and a workplane (point in plane)\n"
313
                        "    * a point and a line segment (point on line)\n"
314
                        "    * a point and a circle or arc (point on curve)\n"
315
                        "    * a point and one to three plane faces (point on face(s))\n"));
316
                return;
317
            }
318
            for (auto&& nc : newcons)
319
              AddConstraint(&nc);
320
            break;
321

322
        case Command::EQUAL:
323
            if(gs.lineSegments >= 2 && gs.lineSegments == gs.n) {
324
                c.type = Type::EQUAL_LENGTH_LINES;
325
                c.entityA = gs.entity[0];
326
                for (std::vector<hEntity>::size_type k = 1;k < gs.entity.size(); ++k){
327
                    c.entityB = gs.entity[k];
328
                    newcons.push_back(c);
329
                }
330
            } else if(gs.lineSegments == 2 && gs.points == 2 && gs.n == 4) {
331
                c.type = Type::EQ_PT_LN_DISTANCES;
332
                c.entityA = gs.entity[0];
333
                c.ptA = gs.point[0];
334
                c.entityB = gs.entity[1];
335
                c.ptB = gs.point[1];
336
                newcons.push_back(c);
337
            } else if(gs.lineSegments == 1 && gs.points == 2 && gs.n == 3) {
338
                // The same line segment for the distances, but different
339
                // points.
340
                c.type = Type::EQ_PT_LN_DISTANCES;
341
                c.entityA = gs.entity[0];
342
                c.ptA = gs.point[0];
343
                c.entityB = gs.entity[0];
344
                c.ptB = gs.point[1];
345
                newcons.push_back(c);
346
            } else if(gs.lineSegments == 2 && gs.points == 1 && gs.n == 3) {
347
                c.type = Type::EQ_LEN_PT_LINE_D;
348
                c.entityA = gs.entity[0];
349
                c.entityB = gs.entity[1];
350
                c.ptA = gs.point[0];
351
                newcons.push_back(c);
352
            } else  if(gs.circlesOrArcs >= 2 && gs.circlesOrArcs == gs.n) {
353
                c.type = Type::EQUAL_RADIUS;
354
                c.entityA = gs.entity[0];
355
                for (std::vector<hEntity>::size_type k = 1;k < gs.entity.size(); ++k){
356
                    c.entityB = gs.entity[k];
357
                    newcons.push_back(c);
358
                }
359
            } else if(gs.arcs == 1 && gs.lineSegments == 1 && gs.n == 2) {
360
                c.type = Type::EQUAL_LINE_ARC_LEN;
361
                if(SK.GetEntity(gs.entity[0])->type == Entity::Type::ARC_OF_CIRCLE) {
362
                    c.entityA = gs.entity[1];
363
                    c.entityB = gs.entity[0];
364
                } else {
365
                    c.entityA = gs.entity[0];
366
                    c.entityB = gs.entity[1];
367
                }
368
                newcons.push_back(c);
369
            } else {
370
                Error(_("Bad selection for equal length / radius constraint. "
371
                        "This constraint can apply to:\n\n"
372
                        "    * two or more line segments (equal length)\n"
373
                        "    * two line segments and two points "
374
                                "(equal point-line distances)\n"
375
                        "    * a line segment and two points "
376
                                "(equal point-line distances)\n"
377
                        "    * a line segment, and a point and line segment "
378
                                "(point-line distance equals length)\n"
379
                        "    * two or more circles or arcs (equal radius)\n"
380
                        "    * a line segment and an arc "
381
                                "(line segment length equals arc length)\n"));
382
                return;
383
            }
384
            SS.UndoRemember();
385
            for (auto&& nc : newcons){
386
                if(nc.type == Type::EQUAL_ANGLE) {
387
                    // Infer the nearest supplementary angle from the sketch.
388
                    Vector a1 = SK.GetEntity(c.entityA)->VectorGetNum(),
389
                            b1 = SK.GetEntity(c.entityB)->VectorGetNum(),
390
                            a2 = SK.GetEntity(c.entityC)->VectorGetNum(),
391
                            b2 = SK.GetEntity(c.entityD)->VectorGetNum();
392
                    double d1 = a1.Dot(b1), d2 = a2.Dot(b2);
393

394
                    if(d1*d2 < 0) {
395
                        nc.other = true;
396
                    }
397
                }
398
                AddConstraint(&nc, /*rememberForUndo=*/false);
399
            }
400
            break;
401

402
        case Command::RATIO:
403
            if(gs.lineSegments == 2 && gs.n == 2) {
404
                c.type = Type::LENGTH_RATIO;
405
                c.entityA = gs.entity[0];
406
                c.entityB = gs.entity[1];
407
            }
408
            else if(gs.arcs == 2 && gs.n == 2) {
409
                c.type = Type::ARC_ARC_LEN_RATIO;
410
                c.entityA = gs.entity[0];
411
                c.entityB = gs.entity[1];
412
            }
413
            else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
414
                c.type = Type::ARC_LINE_LEN_RATIO;
415
                if(SK.GetEntity(gs.entity[0])->type == Entity::Type::ARC_OF_CIRCLE) {
416
                    c.entityA = gs.entity[1];
417
                    c.entityB = gs.entity[0];
418
                } else {
419
                    c.entityA = gs.entity[0];
420
                    c.entityB = gs.entity[1];
421
                }
422
            } else {
423
                Error(_("Bad selection for length ratio constraint. This "
424
                        "constraint can apply to:\n\n"
425
                        "    * two line segments\n"
426
                        "    * two arcs\n"
427
                        "    * one arc and one line segment\n"));
428
                return;
429
            }
430

431
            c.valA = 0;
432
            c.ModifyToSatisfy();
433
            AddConstraint(&c);
434
            newcons.push_back(c);
435
            break;
436

437
        case Command::DIFFERENCE:
438
            if(gs.lineSegments == 2 && gs.n == 2) {
439
                c.type = Type::LENGTH_DIFFERENCE;
440
                c.entityA = gs.entity[0];
441
                c.entityB = gs.entity[1];
442
            }
443
            else if(gs.arcs == 2 && gs.n == 2) {
444
                c.type = Type::ARC_ARC_DIFFERENCE;
445
                c.entityA = gs.entity[0];
446
                c.entityB = gs.entity[1];
447
            }
448
            else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
449
                c.type = Type::ARC_LINE_DIFFERENCE;
450
                if(SK.GetEntity(gs.entity[0])->type == Entity::Type::ARC_OF_CIRCLE) {
451
                    c.entityA = gs.entity[1];
452
                    c.entityB = gs.entity[0];
453
                } else {
454
                    c.entityA = gs.entity[0];
455
                    c.entityB = gs.entity[1];
456
                }
457
            } else {
458
                Error(_("Bad selection for length difference constraint. This "
459
                        "constraint can apply to:\n\n"
460
                        "    * two line segments\n"
461
                        "    * two arcs\n"
462
                        "    * one arc and one line segment\n"));
463
                return;
464
            }
465

466
            c.valA = 0;
467
            c.ModifyToSatisfy();
468
            AddConstraint(&c);
469
            newcons.push_back(c);
470
            break;
471

472
        case Command::AT_MIDPOINT:
473
            if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
474
                c.type = Type::AT_MIDPOINT;
475
                c.entityA = gs.entity[0];
476
                c.ptA = gs.point[0];
477

478
                // If a point is at-midpoint, then no reason to also constrain
479
                // it on-line; so auto-remove that.  Handle as one undo group.
480
                SS.UndoRemember();
481
                DeleteAllConstraintsFor(Type::PT_ON_LINE, c.entityA, c.ptA);
482
                AddConstraint(&c, /*rememberForUndo=*/false);
483
                newcons.push_back(c);
484
                break;
485
            } else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) {
486
                c.type = Type::AT_MIDPOINT;
487
                int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
488
                c.entityA = gs.entity[i];
489
                c.entityB = gs.entity[1-i];
490
                AddConstraint(&c);
491
                newcons.push_back(c);
492
            } else {
493
                Error(_("Bad selection for at midpoint constraint. This "
494
                        "constraint can apply to:\n\n"
495
                        "    * a line segment and a point "
496
                              "(point at midpoint)\n"
497
                        "    * a line segment and a workplane "
498
                              "(line's midpoint on plane)\n"));
499
                return;
500
            }
501

502
            break;
503

504
        case Command::SYMMETRIC:
505
            if(gs.points == 2 &&
506
                                ((gs.workplanes == 1 && gs.n == 3) ||
507
                                 (gs.n == 2)))
508
            {
509
                if(gs.entities > 0)
510
                    c.entityA = gs.entity[0];
511
                c.ptA = gs.point[0];
512
                c.ptB = gs.point[1];
513
                c.type = Type::SYMMETRIC;
514
            } else if(gs.lineSegments == 1 &&
515
                                ((gs.workplanes == 1 && gs.n == 2) ||
516
                                 (gs.n == 1)))
517
            {
518
                Entity *line;
519
                if(SK.GetEntity(gs.entity[0])->IsWorkplane()) {
520
                    line = SK.GetEntity(gs.entity[1]);
521
                    c.entityA = gs.entity[0];
522
                } else {
523
                    line = SK.GetEntity(gs.entity[0]);
524
                }
525
                c.ptA = line->point[0];
526
                c.ptB = line->point[1];
527
                c.type = Type::SYMMETRIC;
528
            } else if(SS.GW.LockedInWorkplane()
529
                        && gs.lineSegments == 2 && gs.n == 2)
530
            {
531
                Entity *l0 = SK.GetEntity(gs.entity[0]),
532
                       *l1 = SK.GetEntity(gs.entity[1]);
533

534
                if((l1->group != SS.GW.activeGroup) ||
535
                   (l1->construction && !(l0->construction)))
536
                {
537
                    swap(l0, l1);
538
                }
539
                c.ptA = l1->point[0];
540
                c.ptB = l1->point[1];
541
                c.entityA = l0->h;
542
                c.type = Type::SYMMETRIC_LINE;
543
            } else if(SS.GW.LockedInWorkplane()
544
                        && gs.lineSegments == 1 && gs.points == 2 && gs.n == 3)
545
            {
546
                c.ptA = gs.point[0];
547
                c.ptB = gs.point[1];
548
                c.entityA = gs.entity[0];
549
                c.type = Type::SYMMETRIC_LINE;
550
            } else {
551
                Error(_("Bad selection for symmetric constraint. This constraint "
552
                        "can apply to:\n\n"
553
                        "    * two points or a line segment "
554
                            "(symmetric about workplane's coordinate axis)\n"
555
                        "    * line segment, and two points or a line segment "
556
                            "(symmetric about line segment)\n"
557
                        "    * workplane, and two points or a line segment "
558
                            "(symmetric about workplane)\n"));
559
                return;
560
            }
561
            // We may remove constraints so remember manually
562
            if(c.entityA == Entity::NO_ENTITY) {
563
                // Horizontal / vertical symmetry, implicit symmetry plane
564
                // normal to the workplane
565
                if(c.workplane == Entity::FREE_IN_3D) {
566
                    Error(_("A workplane must be active when constraining "
567
                            "symmetric without an explicit symmetry plane."));
568
                    return;
569
                }
570
                Vector pa = SK.GetEntity(c.ptA)->PointGetNum();
571
                Vector pb = SK.GetEntity(c.ptB)->PointGetNum();
572
                Vector dp = pa.Minus(pb);
573
                EntityBase *norm = SK.GetEntity(c.workplane)->Normal();;
574
                Vector u = norm->NormalU(), v = norm->NormalV();
575
                if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) {
576
                    c.type = Type::SYMMETRIC_HORIZ;
577
                } else {
578
                    c.type = Type::SYMMETRIC_VERT;
579
                }
580
                if(gs.lineSegments == 1) {
581
                    // If this line segment is already constrained horiz or
582
                    // vert, then auto-remove that redundant constraint.
583
                    // Handle as one undo group.
584
                    SS.UndoRemember();
585
                    DeleteAllConstraintsFor(Type::HORIZONTAL, (gs.entity[0]),
586
                        Entity::NO_ENTITY);
587
                    DeleteAllConstraintsFor(Type::VERTICAL, (gs.entity[0]),
588
                        Entity::NO_ENTITY);
589
                    AddConstraint(&c, /*rememberForUndo=*/false);
590
                    newcons.push_back(c);
591
                    break;
592
                }
593
            }
594
            AddConstraint(&c);
595
            newcons.push_back(c);
596
            break;
597

598
        case Command::VERTICAL:
599
        case Command::HORIZONTAL: {
600
            if(id == Command::HORIZONTAL) {
601
                c.type = Type::HORIZONTAL;
602
            } else {
603
                c.type = Type::VERTICAL;
604
            }
605
            if(c.workplane == Entity::FREE_IN_3D) {
606
                Error(_("Activate a workplane (with Sketch -> In Workplane) before "
607
                        "applying a horizontal or vertical constraint."));
608
                return;
609
            }
610
            if(gs.lineSegments > 0 && gs.lineSegments == gs.n) {
611
                for (auto enti : gs.entity){
612
                    c.entityA = enti;
613
                    newcons.push_back(c);
614
                }
615
            } else if(gs.points >= 2 && gs.n == gs.points) {
616
                c.ptA = gs.point[0];
617
                for (int k = 1; k<gs.points; k++) {
618
                  c.ptB = gs.point[k];
619
                  newcons.push_back(c);
620
                }
621
            } else {
622
                Error(_("Bad selection for horizontal / vertical constraint. "
623
                        "This constraint can apply to:\n\n"
624
                        "    * two or more points\n"
625
                        "    * one or more line segments\n"));
626
                return;
627
            }
628
            SS.UndoRemember();
629
            for (auto && nc: newcons)
630
                AddConstraint(&nc, /*rememberForUndo=*/false);
631
            break;
632
        }
633

634
        case Command::ORIENTED_SAME: {
635
            if(gs.anyNormals == 2 && gs.n == 2) {
636
                c.type = Type::SAME_ORIENTATION;
637
                c.entityA = gs.anyNormal[0];
638
                c.entityB = gs.anyNormal[1];
639
            } else {
640
                Error(_("Bad selection for same orientation constraint. This "
641
                        "constraint can apply to:\n\n"
642
                        "    * two normals\n"));
643
                return;
644
            }
645
            SS.UndoRemember();
646

647
            Entity *nfree = SK.GetEntity(c.entityA);
648
            Entity *nref  = SK.GetEntity(c.entityB);
649
            if(nref->group == SS.GW.activeGroup) {
650
                swap(nref, nfree);
651
            }
652
            if(nfree->group == SS.GW.activeGroup && nref->group != SS.GW.activeGroup) {
653
                // nfree is free, and nref is locked (since it came from a
654
                // previous group); so let's force nfree aligned to nref,
655
                // and make convergence easy
656
                Vector ru = nref ->NormalU(), rv = nref ->NormalV();
657
                Vector fu = nfree->NormalU(), fv = nfree->NormalV();
658

659
                if(fabs(fu.Dot(ru)) < fabs(fu.Dot(rv))) {
660
                    // There might be an odd*90 degree rotation about the
661
                    // normal vector; allow that, since the numerical
662
                    // constraint does
663
                    swap(ru, rv);
664
                }
665
                fu = fu.Dot(ru) > 0 ? ru : ru.ScaledBy(-1);
666
                fv = fv.Dot(rv) > 0 ? rv : rv.ScaledBy(-1);
667

668
                nfree->NormalForceTo(Quaternion::From(fu, fv));
669
            }
670
            AddConstraint(&c, /*rememberForUndo=*/false);
671
            newcons.push_back(c);
672
            break;
673
        }
674

675
        case Command::OTHER_ANGLE:
676
            if(gs.constraints == 1 && gs.n == 0) {
677
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
678
                if(c->type == Type::ANGLE) {
679
                    SS.UndoRemember();
680
                    c->other = !(c->other);
681
                    c->ModifyToSatisfy();
682
                    break;
683
                }
684
                if(c->type == Type::EQUAL_ANGLE) {
685
                    SS.UndoRemember();
686
                    c->other = !(c->other);
687
                    SS.MarkGroupDirty(c->group);
688
                    break;
689
                }
690
            }
691
            Error(_("Must select an angle constraint."));
692
            return;
693

694
        case Command::REFERENCE:
695
            if(gs.constraints == 1 && gs.n == 0) {
696
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
697
                if(c->HasLabel() && c->type != Type::COMMENT) {
698
                    SS.UndoRemember();
699
                    (c->reference) = !(c->reference);
700
                    SS.MarkGroupDirty(c->group, /*onlyThis=*/true);
701
                    break;
702
                }
703
            }
704
            Error(_("Must select a constraint with associated label."));
705
            return;
706

707
        case Command::ANGLE:
708
        case Command::REF_ANGLE: {
709
            if(gs.vectors == 3 && gs.n == 3) {
710
                c.type = Type::EQUAL_ANGLE;
711
                c.entityA = gs.vector[0];
712
                c.entityB = gs.vector[1];
713
                c.entityC = gs.vector[1];
714
                c.entityD = gs.vector[2];
715
            } else if(gs.vectors == 4 && gs.n == 4) {
716
                c.type = Type::EQUAL_ANGLE;
717
                c.entityA = gs.vector[0];
718
                c.entityB = gs.vector[1];
719
                c.entityC = gs.vector[2];
720
                c.entityD = gs.vector[3];
721
            } else if(gs.vectors == 2 && gs.n == 2) {
722
                c.type = Type::ANGLE;
723
                c.entityA = gs.vector[0];
724
                c.entityB = gs.vector[1];
725
                c.valA = 0;
726
            } else {
727
                Error(_("Bad selection for angle constraint. This constraint "
728
                        "can apply to:\n\n"
729
                        "Angle between:\n"
730
                        "    * two line segments\n"
731
                        "    * a line segment and a normal\n"
732
                        "    * two normals\n"
733
                        "\nEqual angles:\n"
734
                        "    * four line segments or normals "
735
                        "(equal angle between A,B and C,D)\n"
736
                        "    * three line segments or normals "
737
                        "(equal angle between A,B and B,C)\n"));
738
                return;
739
            }
740

741
            Entity *ea = SK.GetEntity(c.entityA),
742
                   *eb = SK.GetEntity(c.entityB);
743
            if(ea->type == Entity::Type::LINE_SEGMENT &&
744
               eb->type == Entity::Type::LINE_SEGMENT)
745
            {
746
                Vector a0 = SK.GetEntity(ea->point[0])->PointGetNum(),
747
                       a1 = SK.GetEntity(ea->point[1])->PointGetNum(),
748
                       b0 = SK.GetEntity(eb->point[0])->PointGetNum(),
749
                       b1 = SK.GetEntity(eb->point[1])->PointGetNum();
750
                if(a0.Equals(b0) || a1.Equals(b1)) {
751
                    // okay, vectors should be drawn in same sense
752
                } else if(a0.Equals(b1) || a1.Equals(b0)) {
753
                    // vectors are in opposite sense
754
                    c.other = true;
755
                } else {
756
                    // no shared point; not clear which intersection to draw
757
                }
758
            }
759

760
            if(id == Command::REF_ANGLE) {
761
                c.reference = true;
762
            }
763

764
            c.ModifyToSatisfy();
765
            AddConstraint(&c);
766
            newcons.push_back(c);
767
            break;
768
        }
769

770
        case Command::PARALLEL:
771
            if(gs.faces == 2 && gs.n == 2) {
772
                c.type = Type::PARALLEL;
773
                c.entityA = gs.face[0];
774
                c.entityB = gs.face[1];            
775
                newcons.push_back(c);
776
            } else if(gs.vectors > 1 && gs.vectors == gs.n) {
777
                c.type = Type::PARALLEL;
778
                c.entityA = gs.vector[0];
779
                for (std::vector<hEntity>::size_type k = 1; k < gs.vector.size();++k ){
780
                    c.entityB = gs.vector[k];
781
                    newcons.push_back(c);
782
                }
783
            } else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
784
                Entity *line = SK.GetEntity(gs.entity[0]),
785
                       *arc  = SK.GetEntity(gs.entity[1]);
786
                if(line->type == Entity::Type::ARC_OF_CIRCLE) {
787
                    swap(line, arc);
788
                }
789
                if(!ConstrainArcLineTangent(&c, line, arc)) {
790
                    return;
791
                }
792
                c.type = Type::ARC_LINE_TANGENT;
793
                c.entityA = arc->h;
794
                c.entityB = line->h;
795
                newcons.push_back(c);
796
            } else if(gs.lineSegments == 1 && gs.cubics == 1 && gs.n == 2) {
797
                Entity *line  = SK.GetEntity(gs.entity[0]),
798
                       *cubic = SK.GetEntity(gs.entity[1]);
799
                if(line->type == Entity::Type::CUBIC) {
800
                    swap(line, cubic);
801
                }
802
                if(!ConstrainCubicLineTangent(&c, line, cubic)) {
803
                    return;
804
                }
805
                c.type = Type::CUBIC_LINE_TANGENT;
806
                c.entityA = cubic->h;
807
                c.entityB = line->h;
808
                newcons.push_back(c);
809
            } else if(gs.cubics + gs.arcs == 2 && gs.n == 2) {
810
                if(!SS.GW.LockedInWorkplane()) {
811
                    Error(_("Curve-curve tangency must apply in workplane."));
812
                    return;
813
                }
814
                Entity *eA = SK.GetEntity(gs.entity[0]),
815
                       *eB = SK.GetEntity(gs.entity[1]);
816
                if(!ConstrainCurveCurveTangent(&c, eA, eB)) {
817
                    return;
818
                }
819
                c.type = Type::CURVE_CURVE_TANGENT;
820
                c.entityA = eA->h;
821
                c.entityB = eB->h;
822
                newcons.push_back(c);
823
            } else {
824
                Error(_("Bad selection for parallel / tangent constraint. This "
825
                        "constraint can apply to:\n\n"
826
                        "    * two faces\n"
827
                        "    * two or more line segments (parallel)\n"
828
                        "    * one or more line segments and one or more normals (parallel)\n"
829
                        "    * two or more normals (parallel)\n"
830
                        "    * two line segments, arcs, or beziers, that share "
831
                              "an endpoint (tangent)\n"));
832
                return;
833
            }
834
            SS.UndoRemember();
835
            for (auto&& nc:newcons)
836
                AddConstraint(&nc, /*rememberForUndo=*/false);
837
            break;
838

839
        case Command::PERPENDICULAR:
840
            if(gs.faces == 2 && gs.n == 2) {
841
                c.type = Type::PERPENDICULAR;
842
                c.entityA = gs.face[0];
843
                c.entityB = gs.face[1];            
844
            } else if(gs.vectors == 2 && gs.n == 2) {
845
                c.type = Type::PERPENDICULAR;
846
                c.entityA = gs.vector[0];
847
                c.entityB = gs.vector[1];
848
            } else {
849
                Error(_("Bad selection for perpendicular constraint. This "
850
                        "constraint can apply to:\n\n"
851
                        "    * two faces\n"
852
                        "    * two line segments\n"
853
                        "    * a line segment and a normal\n"
854
                        "    * two normals\n"));
855
                return;
856
            }
857
            AddConstraint(&c);
858
            newcons.push_back(c);
859
            break;
860

861
        case Command::WHERE_DRAGGED:
862
            if(gs.points == 1 && gs.n == 1) {
863
                c.type = Type::WHERE_DRAGGED;
864
                c.ptA = gs.point[0];
865
            } else {
866
                Error(_("Bad selection for lock point where dragged constraint. "
867
                        "This constraint can apply to:\n\n"
868
                        "    * a point\n"));
869
                return;
870
            }
871
            AddConstraint(&c);
872
            newcons.push_back(c);
873
            break;
874

875
        case Command::COMMENT:
876
            if(gs.points == 1 && gs.n == 1) {
877
                c.type = Type::COMMENT;
878
                c.ptA = gs.point[0];
879
                c.group       = SS.GW.activeGroup;
880
                c.workplane   = SS.GW.ActiveWorkplane();
881
                c.comment     = _("NEW COMMENT -- DOUBLE-CLICK TO EDIT");
882
                AddConstraint(&c);
883
                newcons.push_back(c);
884
            } else {
885
                SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
886
                SS.GW.pending.command = Command::COMMENT;
887
                SS.GW.pending.description = _("click center of comment text");
888
                SS.ScheduleShowTW();
889
            }
890
            break;
891

892
        default: ssassert(false, "Unexpected menu ID");
893
    }
894
    for (auto nc:newcons){
895
        for(const Constraint &cc : SK.constraint) {
896
            if(nc.h != cc.h && nc.Equals(cc)) {
897
                // Oops, we already have this exact constraint. Remove the one we just added.
898
                SK.constraint.RemoveById(nc.h);
899
                SS.GW.ClearSelection();
900
                // And now select the old one, to give feedback.
901
                SS.GW.MakeSelected(cc.h);
902
                return;
903
            }
904
        }
905

906
        if(SK.constraint.FindByIdNoOops(nc.h)) {
907
            Constraint *constraint = SK.GetConstraint(nc.h);
908
            if(SS.TestRankForGroup(nc.group) == SolveResult::REDUNDANT_OKAY &&
909
                    !SK.GetGroup(SS.GW.activeGroup)->allowRedundant &&
910
                    constraint->HasLabel()) {
911
                constraint->reference = true;
912
            }
913
        }
914

915
        if((id == Command::DISTANCE_DIA || id == Command::ANGLE ||
916
            id == Command::RATIO || id == Command::DIFFERENCE) &&
917
                SS.immediatelyEditDimension) {
918
            SS.GW.EditConstraint(nc.h);
919
        }
920
    }
921

922
    SS.GW.ClearSelection();
923
}
924

925
#endif /* ! LIBRARY */
926

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.