Solvespace

Форк
0
/
generate.cpp 
572 строки · 19.1 Кб
1
//-----------------------------------------------------------------------------
2
// Generate our model based on its parametric description, by solving each
3
// sketch, generating surfaces from the resulting entities, performing any
4
// requested surface operations (e.g. Booleans) with our model so far, and
5
// then repeating this process for each subsequent group.
6
//
7
// Copyright 2008-2013 Jonathan Westhues.
8
//-----------------------------------------------------------------------------
9
#include "solvespace.h"
10

11
void SolveSpaceUI::MarkGroupDirtyByEntity(hEntity he) {
12
    Entity *e = SK.GetEntity(he);
13
    MarkGroupDirty(e->group);
14
}
15

16
void SolveSpaceUI::MarkGroupDirty(hGroup hg, bool onlyThis) {
17
    bool go = false;
18
    for(auto const &gh : SK.groupOrder) {
19
        Group *g = SK.GetGroup(gh);
20
        if(g->h == hg) {
21
            go = true;
22
        }
23
        if(go) {
24
            g->clean = false;
25
            if(onlyThis) break;
26
        }
27
    }
28
    unsaved = true;
29
    ScheduleGenerateAll();
30
}
31

32
bool SolveSpaceUI::PruneOrphans() {
33

34
    auto r = std::find_if(SK.request.begin(), SK.request.end(),
35
                          [&](Request &r) { return !GroupExists(r.group); });
36
    if(r != SK.request.end()) {
37
        (deleted.requests)++;
38
        SK.request.RemoveById(r->h);
39
        return true;
40
    }
41

42
    auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(),
43
                          [&](Constraint &c) { return !GroupExists(c.group); });
44
    if(c != SK.constraint.end()) {
45
        (deleted.constraints)++;
46
        (deleted.nonTrivialConstraints)++;
47
        SK.constraint.RemoveById(c->h);
48
        return true;
49
    }
50
    return false;
51
}
52

53
bool SolveSpaceUI::GroupsInOrder(hGroup before, hGroup after) {
54
    if(before.v == 0) return true;
55
    if(after.v  == 0) return true;
56
    if(!GroupExists(before)) return false;
57
    if(!GroupExists(after)) return false;
58
    int beforep = SK.GetGroup(before)->order;
59
    int afterp = SK.GetGroup(after)->order;
60
    if(beforep >= afterp) return false;
61
    return true;
62
}
63

64
bool SolveSpaceUI::GroupExists(hGroup hg) {
65
    // A nonexistent group is not acceptable
66
    return SK.group.FindByIdNoOops(hg) ? true : false;
67
}
68
bool SolveSpaceUI::EntityExists(hEntity he) {
69
    // A nonexstient entity is acceptable, though, usually just means it
70
    // doesn't apply.
71
    if(he == Entity::NO_ENTITY) return true;
72
    return SK.entity.FindByIdNoOops(he) ? true : false;
73
}
74

75
bool SolveSpaceUI::PruneGroups(hGroup hg) {
76
    Group *g = SK.GetGroup(hg);
77
    if(GroupsInOrder(g->opA, hg) &&
78
       EntityExists(g->predef.origin) &&
79
       EntityExists(g->predef.entityB) &&
80
       EntityExists(g->predef.entityC))
81
    {
82
        return false;
83
    }
84
    (deleted.groups)++;
85
    SK.group.RemoveById(g->h);
86
    return true;
87
}
88

89
bool SolveSpaceUI::PruneRequests(hGroup hg) {
90
    auto e = std::find_if(SK.entity.begin(), SK.entity.end(),
91
                          [&](Entity &e) { return e.group == hg && !EntityExists(e.workplane); });
92
    if(e != SK.entity.end()) {
93
        (deleted.requests)++;
94
        SK.entity.RemoveById(e->h);
95
        return true;
96
    }
97
    return false;
98
}
99

100
bool SolveSpaceUI::PruneConstraints(hGroup hg) {
101
    auto c = std::find_if(SK.constraint.begin(), SK.constraint.end(), [&](Constraint &c) {
102
        if(c.group != hg)
103
            return false;
104

105
        if(EntityExists(c.workplane) &&
106
           EntityExists(c.ptA) &&
107
           EntityExists(c.ptB) &&
108
           EntityExists(c.entityA) &&
109
           EntityExists(c.entityB) &&
110
           EntityExists(c.entityC) &&
111
           EntityExists(c.entityD)) {
112
            return false;
113
        }
114
        return true;
115
    });
116

117
    if(c != SK.constraint.end()) {
118
        (deleted.constraints)++;
119
        if(c->type != Constraint::Type::POINTS_COINCIDENT &&
120
           c->type != Constraint::Type::HORIZONTAL &&
121
           c->type != Constraint::Type::VERTICAL) {
122
            (deleted.nonTrivialConstraints)++;
123
        }
124

125
        SK.constraint.RemoveById(c->h);
126
        return true;
127
    }
128
    return false;
129
}
130

131
void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) {
132
    int first = 0, last = 0, i;
133

134
    uint64_t startMillis = GetMilliseconds(),
135
             endMillis;
136

137
    SK.groupOrder.Clear();
138
    for(auto &g : SK.group) { SK.groupOrder.Add(&g.h); }
139
    std::sort(SK.groupOrder.begin(), SK.groupOrder.end(),
140
        [](const hGroup &ha, const hGroup &hb) {
141
            return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order;
142
        });
143

144
    switch(type) {
145
        case Generate::DIRTY: {
146
            first = INT_MAX;
147
            last  = 0;
148

149
            // Start from the first dirty group, and solve until the active group,
150
            // since all groups after the active group are hidden.
151
            // Not using range-for because we're tracking the indices.
152
            for(i = 0; i < SK.groupOrder.n; i++) {
153
                Group *g = SK.GetGroup(SK.groupOrder[i]);
154
                if((!g->clean) || !g->IsSolvedOkay()) {
155
                    first = min(first, i);
156
                }
157
                if(g->h == SS.GW.activeGroup) {
158
                    last = i;
159
                }
160
            }
161
            if(first == INT_MAX || last == 0) {
162
                // All clean; so just regenerate the entities, and don't solve anything.
163
                first = -1;
164
                last  = -1;
165
            } else {
166
                SS.nakedEdges.Clear();
167
            }
168
            break;
169
        }
170

171
        case Generate::ALL:
172
            first = 0;
173
            last  = INT_MAX;
174
            break;
175

176
        case Generate::REGEN:
177
            first = -1;
178
            last  = -1;
179
            break;
180

181
        case Generate::UNTIL_ACTIVE: {
182
            for(i = 0; i < SK.groupOrder.n; i++) {
183
                if(SK.groupOrder[i] == SS.GW.activeGroup)
184
                    break;
185
            }
186

187
            first = 0;
188
            last  = i;
189
            break;
190
        }
191
    }
192

193
    // If we're generating entities for display, first we need to find
194
    // the bounding box to turn relative chord tolerance to absolute.
195
    if(!SS.exportMode && !genForBBox) {
196
        GenerateAll(type, andFindFree, /*genForBBox=*/true);
197
        BBox box = SK.CalculateEntityBBox(/*includeInvisibles=*/true);
198
        Vector size = box.maxp.Minus(box.minp);
199
        double maxSize = std::max({ size.x, size.y, size.z });
200
        chordTolCalculated = maxSize * chordTol / 100.0;
201
    }
202

203
    // Remove any requests or constraints that refer to a nonexistent
204
    // group; can check those immediately, since we know what the list
205
    // of groups should be.
206
    while(PruneOrphans())
207
        ;
208

209
    // Don't lose our numerical guesses when we regenerate.
210
    IdList<Param,hParam> prev = {};
211
    SK.param.MoveSelfInto(&prev);
212
    SK.param.ReserveMore(prev.n);
213
    int oldEntityCount = SK.entity.n;
214
    SK.entity.Clear();
215
    SK.entity.ReserveMore(oldEntityCount);
216

217
    // Not using range-for because we're using the index inside the loop.
218
    for(i = 0; i < SK.groupOrder.n; i++) {
219
        hGroup hg = SK.groupOrder[i];
220

221
        // The group may depend on entities or other groups, to define its
222
        // workplane geometry or for its operands. Those must already exist
223
        // in a previous group, so check them before generating.
224
        if(PruneGroups(hg))
225
            goto pruned;
226

227
        int groupRequestIndex = 0;
228
        for(auto &req : SK.request) {
229
            Request *r = &req;
230
            if(r->group != hg) continue;
231
            r->groupRequestIndex = groupRequestIndex++;
232

233
            r->Generate(&(SK.entity), &(SK.param));
234
        }
235
        for(auto &con : SK.constraint) {
236
            Constraint *c = &con;
237
            if(c->group != hg) continue;
238

239
            c->Generate(&(SK.param));
240
        }
241
        SK.GetGroup(hg)->Generate(&(SK.entity), &(SK.param));
242

243
        // The requests and constraints depend on stuff in this or the
244
        // previous group, so check them after generating.
245
        if(PruneRequests(hg) || PruneConstraints(hg))
246
            goto pruned;
247

248
        // Use the previous values for params that we've seen before, as
249
        // initial guesses for the solver.
250
        for(auto &p : SK.param) {
251
            Param *newp = &p;
252
            if(newp->known) continue;
253

254
            Param *prevp = prev.FindByIdNoOops(newp->h);
255
            if(prevp) {
256
                newp->val = prevp->val;
257
                newp->free = prevp->free;
258
            }
259
        }
260

261
        if(hg == Group::HGROUP_REFERENCES) {
262
            ForceReferences();
263
            Group *g = SK.GetGroup(hg);
264
            g->solved.how = SolveResult::OKAY;
265
            g->clean = true;
266
        } else {
267
            // this i is an index in groupOrder
268
            if(i >= first && i <= last) {
269
                // The group falls inside the range, so really solve it,
270
                // and then regenerate the mesh based on the solved stuff.
271
                Group *g = SK.GetGroup(hg);
272
                if(genForBBox) {
273
                    SolveGroupAndReport(hg, andFindFree);
274
                    g->GenerateLoops();
275
                } else {
276
                    g->GenerateShellAndMesh();
277
                    g->clean = true;
278
                }
279
            } else {
280
                // The group falls outside the range, so just assume that
281
                // it's good wherever we left it. The mesh is unchanged,
282
                // and the parameters must be marked as known.
283
                for(auto &p : SK.param) {
284
                    Param *newp = &p;
285

286
                    Param *prevp = prev.FindByIdNoOops(newp->h);
287
                    if(prevp) newp->known = true;
288
                }
289
            }
290
        }
291
    }
292

293
    // And update any reference dimensions with their new values
294
    for(auto &con : SK.constraint) {
295
        Constraint *c = &con;
296
        if(c->reference) {
297
            c->ModifyToSatisfy();
298
        }
299
    }
300

301
    // Make sure the point that we're tracing exists.
302
    if(traced.point.v && !SK.entity.FindByIdNoOops(traced.point)) {
303
        traced.point = Entity::NO_ENTITY;
304
    }
305
    // And if we're tracing a point, add its new value to the path
306
    if(traced.point.v) {
307
        Entity *pt = SK.GetEntity(traced.point);
308
        traced.path.AddPoint(pt->PointGetNum());
309
    }
310

311
    prev.Clear();
312
    GW.Invalidate();
313

314
    // Remove nonexistent selection items, for same reason we waited till
315
    // the end to put up a dialog box.
316
    GW.ClearNonexistentSelectionItems();
317

318
    if(deleted.requests > 0 || deleted.constraints > 0 || deleted.groups > 0) {
319
        // All sorts of interesting things could have happened; for example,
320
        // the active group or active workplane could have been deleted. So
321
        // clear all that out.
322
        if(deleted.groups > 0) {
323
            SS.TW.ClearSuper();
324
        }
325
        ScheduleShowTW();
326
        GW.ClearSuper();
327

328
        // People get annoyed if I complain whenever they delete any request,
329
        // and I otherwise will, since those always come with pt-coincident
330
        // constraints.
331
        if(deleted.requests > 0 || deleted.nonTrivialConstraints > 0 ||
332
           deleted.groups > 0)
333
        {
334
            // Don't display any errors until we've regenerated fully. The
335
            // sketch is not necessarily in a consistent state until we've
336
            // pruned any orphaned etc. objects, and the message loop for the
337
            // messagebox could allow us to repaint and crash. But now we must
338
            // be fine.
339
            Message("Additional sketch elements were deleted, because they "
340
                    "depend on the element that was just deleted explicitly. "
341
                    "These include: \n"
342
                    "     %d request%s\n"
343
                    "     %d constraint%s\n"
344
                    "     %d group%s"
345
                    "%s",
346
                       deleted.requests, deleted.requests == 1 ? "" : "s",
347
                       deleted.constraints, deleted.constraints == 1 ? "" : "s",
348
                       deleted.groups, deleted.groups == 1 ? "" : "s",
349
                       undo.cnt > 0 ? "\n\nChoose Edit -> Undo to undelete all elements." : "");
350
        }
351

352
        deleted = {};
353
    }
354

355
    FreeAllTemporary();
356
    allConsistent = true;
357
    SS.GW.persistentDirty = true;
358
    SS.centerOfMass.dirty = true;
359

360
    endMillis = GetMilliseconds();
361

362
    if(endMillis - startMillis > 30) {
363
        const char *typeStr = "";
364
        switch(type) {
365
            case Generate::DIRTY:           typeStr = "DIRTY";        break;
366
            case Generate::ALL:             typeStr = "ALL";          break;
367
            case Generate::REGEN:           typeStr = "REGEN";        break;
368
            case Generate::UNTIL_ACTIVE:    typeStr = "UNTIL_ACTIVE"; break;
369
        }
370
        if(endMillis)
371
        dbp("Generate::%s%s took %lld ms",
372
            typeStr,
373
            (genForBBox ? " (for bounding box)" : ""),
374
            GetMilliseconds() - startMillis);
375
    }
376

377
    return;
378

379
pruned:
380
    // Restore the numerical guesses
381
    SK.param.Clear();
382
    prev.MoveSelfInto(&(SK.param));
383
    // Try again
384
    GenerateAll(type, andFindFree, genForBBox);
385
}
386

387
void SolveSpaceUI::ForceReferences() {
388
    // Force the values of the parameters that define the three reference
389
    // coordinate systems.
390
    static const struct {
391
        hRequest    hr;
392
        Quaternion  q;
393
    } Quat[] = {
394
        { Request::HREQUEST_REFERENCE_XY, { 1,    0,    0,    0,   } },
395
        { Request::HREQUEST_REFERENCE_YZ, { 0.5,  0.5,  0.5,  0.5, } },
396
        { Request::HREQUEST_REFERENCE_ZX, { 0.5, -0.5, -0.5, -0.5, } },
397
    };
398
    for(int i = 0; i < 3; i++) {
399
        hRequest hr = Quat[i].hr;
400
        Entity *wrkpl = SK.GetEntity(hr.entity(0));
401
        // The origin for our coordinate system, always zero
402
        Entity *origin = SK.GetEntity(wrkpl->point[0]);
403
        origin->PointForceTo(Vector::From(0, 0, 0));
404
        origin->construction = true;
405
        SK.GetParam(origin->param[0])->known = true;
406
        SK.GetParam(origin->param[1])->known = true;
407
        SK.GetParam(origin->param[2])->known = true;
408
        // The quaternion that defines the rotation, from the table.
409
        Entity *normal = SK.GetEntity(wrkpl->normal);
410
        normal->NormalForceTo(Quat[i].q);
411
        SK.GetParam(normal->param[0])->known = true;
412
        SK.GetParam(normal->param[1])->known = true;
413
        SK.GetParam(normal->param[2])->known = true;
414
        SK.GetParam(normal->param[3])->known = true;
415
    }
416
}
417

418
void SolveSpaceUI::UpdateCenterOfMass() {
419
    SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
420
    SS.centerOfMass.position = m->GetCenterOfMass();
421
    SS.centerOfMass.dirty = false;
422
}
423

424
void SolveSpaceUI::MarkDraggedParams() {
425
    sys.dragged.Clear();
426

427
    for(int i = -1; i < SS.GW.pending.points.n; i++) {
428
        hEntity hp;
429
        if(i == -1) {
430
            hp = SS.GW.pending.point;
431
        } else {
432
            hp = SS.GW.pending.points[i];
433
        }
434
        if(!hp.v) continue;
435

436
        // The pending point could be one in a group that has not yet
437
        // been processed, in which case the lookup will fail; but
438
        // that's not an error.
439
        Entity *pt = SK.entity.FindByIdNoOops(hp);
440
        if(pt) {
441
            switch(pt->type) {
442
                case Entity::Type::POINT_N_TRANS:
443
                case Entity::Type::POINT_IN_3D:
444
                case Entity::Type::POINT_N_ROT_AXIS_TRANS:
445
                    sys.dragged.Add(&(pt->param[0]));
446
                    sys.dragged.Add(&(pt->param[1]));
447
                    sys.dragged.Add(&(pt->param[2]));
448
                    break;
449

450
                case Entity::Type::POINT_IN_2D:
451
                    sys.dragged.Add(&(pt->param[0]));
452
                    sys.dragged.Add(&(pt->param[1]));
453
                    break;
454

455
                default: // Only the entities above can be dragged.
456
                    break;
457
            }
458
        }
459
    }
460
    if(SS.GW.pending.circle.v) {
461
        Entity *circ = SK.entity.FindByIdNoOops(SS.GW.pending.circle);
462
        if(circ) {
463
            Entity *dist = SK.GetEntity(circ->distance);
464
            switch(dist->type) {
465
                case Entity::Type::DISTANCE:
466
                    sys.dragged.Add(&(dist->param[0]));
467
                    break;
468

469
                default: // Only the entities above can be dragged.
470
                    break;
471
            }
472
        }
473
    }
474
    if(SS.GW.pending.normal.v) {
475
        Entity *norm = SK.entity.FindByIdNoOops(SS.GW.pending.normal);
476
        if(norm) {
477
            switch(norm->type) {
478
                case Entity::Type::NORMAL_IN_3D:
479
                    sys.dragged.Add(&(norm->param[0]));
480
                    sys.dragged.Add(&(norm->param[1]));
481
                    sys.dragged.Add(&(norm->param[2]));
482
                    sys.dragged.Add(&(norm->param[3]));
483
                    break;
484

485
                default: // Only the entities above can be dragged.
486
                    break;
487
            }
488
        }
489
    }
490
}
491

492
void SolveSpaceUI::SolveGroupAndReport(hGroup hg, bool andFindFree) {
493
    SolveGroup(hg, andFindFree);
494

495
    Group *g = SK.GetGroup(hg);
496
    bool isOkay = g->solved.how == SolveResult::OKAY ||
497
                  (g->allowRedundant && g->solved.how == SolveResult::REDUNDANT_OKAY);
498
    if(!isOkay || (isOkay && !g->IsSolvedOkay())) {
499
        TextWindow::ReportHowGroupSolved(g->h);
500
    }
501
}
502

503
void SolveSpaceUI::WriteEqSystemForGroup(hGroup hg) {
504
    // Clear out the system to be solved.
505
    sys.entity.Clear();
506
    sys.param.Clear();
507
    sys.eq.Clear();
508
    // And generate all the params for requests in this group
509
    for(auto &req : SK.request) {
510
        Request *r = &req;
511
        if(r->group != hg) continue;
512

513
        r->Generate(&(sys.entity), &(sys.param));
514
    }
515
    for(auto &con : SK.constraint) {
516
        Constraint *c = &con;
517
        if(c->group != hg) continue;
518

519
        c->Generate(&(sys.param));
520
    }
521
    // And for the group itself
522
    Group *g = SK.GetGroup(hg);
523
    g->Generate(&(sys.entity), &(sys.param));
524
    // Set the initial guesses for all the params
525
    for(auto &param : sys.param) {
526
        Param *p = &param;
527
        p->known = false;
528
        p->val = SK.GetParam(p->h)->val;
529
    }
530

531
    MarkDraggedParams();
532
}
533

534
void SolveSpaceUI::SolveGroup(hGroup hg, bool andFindFree) {
535
    WriteEqSystemForGroup(hg);
536
    Group *g = SK.GetGroup(hg);
537
    g->solved.remove.Clear();
538
    g->solved.findToFixTimeout = SS.timeoutRedundantConstr;
539
    SolveResult how = sys.Solve(g, NULL,
540
                                   &(g->solved.dof),
541
                                   &(g->solved.remove),
542
                                   /*andFindBad=*/!g->allowRedundant,
543
                                   /*andFindFree=*/andFindFree,
544
                                   /*forceDofCheck=*/!g->dofCheckOk);
545
    if(how == SolveResult::OKAY) {
546
        g->dofCheckOk = true;
547
    }
548
    g->solved.how = how;
549
    FreeAllTemporary();
550
}
551

552
SolveResult SolveSpaceUI::TestRankForGroup(hGroup hg, int *rank) {
553
    Group *g = SK.GetGroup(hg);
554
    // If we don't calculate dof or redundant is allowed, there is
555
    // no point to solve rank because this result is not meaningful
556
    if(g->suppressDofCalculation || g->allowRedundant) return SolveResult::OKAY;
557
    WriteEqSystemForGroup(hg);
558
    SolveResult result = sys.SolveRank(g, rank);
559
    FreeAllTemporary();
560
    return result;
561
}
562

563
bool SolveSpaceUI::ActiveGroupsOkay() {
564
    for(int i = 0; i < SK.groupOrder.n; i++) {
565
        Group *g = SK.GetGroup(SK.groupOrder[i]);
566
        if(!g->IsSolvedOkay())
567
            return false;
568
        if(g->h == SS.GW.activeGroup)
569
            break;
570
    }
571
    return true;
572
}
573

574

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

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

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

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