Solvespace

Форк
0
/
drawentity.cpp 
863 строки · 30.1 Кб
1
//-----------------------------------------------------------------------------
2
// Draw a representation of an entity on-screen, in the case of curves up
3
// to our chord tolerance, or return the distance from the user's mouse pointer
4
// to the entity for selection.
5
//
6
// Copyright 2008-2013 Jonathan Westhues.
7
//-----------------------------------------------------------------------------
8
#include "solvespace.h"
9

10
std::string Entity::DescriptionString() const {
11
    if(h.isFromRequest()) {
12
        Request *r = SK.GetRequest(h.request());
13
        return r->DescriptionString();
14
    } else {
15
        Group *g = SK.GetGroup(h.group());
16
        return g->DescriptionString();
17
    }
18
}
19

20
void Entity::GenerateEdges(SEdgeList *el) {
21
    SBezierList *sbl = GetOrGenerateBezierCurves();
22

23
    for(int i = 0; i < sbl->l.n; i++) {
24
        SBezier *sb = &(sbl->l[i]);
25

26
        List<Vector> lv = {};
27
        sb->MakePwlInto(&lv);
28
        for(int j = 1; j < lv.n; j++) {
29
            el->AddEdge(lv[j-1], lv[j], Style::ForEntity(h).v, i);
30
        }
31
        lv.Clear();
32
    }
33
}
34

35
SBezierList *Entity::GetOrGenerateBezierCurves() {
36
    if(beziers.l.IsEmpty())
37
        GenerateBezierCurves(&beziers);
38
    return &beziers;
39
}
40

41
SEdgeList *Entity::GetOrGenerateEdges() {
42
    if(!edges.l.IsEmpty()) {
43
        if(EXACT(edgesChordTol == SS.ChordTolMm()))
44
            return &edges;
45
        edges.l.Clear();
46
    }
47
    if(edges.l.IsEmpty())
48
        GenerateEdges(&edges);
49
    edgesChordTol = SS.ChordTolMm();
50
    return &edges;
51
}
52

53
BBox Entity::GetOrGenerateScreenBBox(bool *hasBBox) {
54
    SBezierList *sbl = GetOrGenerateBezierCurves();
55

56
    // We don't bother with bounding boxes for workplanes, etc.
57
    *hasBBox = (IsPoint() || IsNormal() || !sbl->l.IsEmpty());
58
    if(!*hasBBox) return {};
59

60
    if(screenBBoxValid)
61
        return screenBBox;
62

63
    if(IsPoint()) {
64
        Vector proj = SS.GW.ProjectPoint3(PointGetNum());
65
        screenBBox = BBox::From(proj, proj);
66
    } else if(IsNormal()) {
67
        Vector proj = SS.GW.ProjectPoint3(SK.GetEntity(point[0])->PointGetNum());
68
        screenBBox = BBox::From(proj, proj);
69
    } else if(!sbl->l.IsEmpty()) {
70
        Vector first = SS.GW.ProjectPoint3(sbl->l[0].ctrl[0]);
71
        screenBBox = BBox::From(first, first);
72
        for(auto &sb : sbl->l) {
73
            for(int i = 0; i <= sb.deg; ++i) { screenBBox.Include(SS.GW.ProjectPoint3(sb.ctrl[i])); }
74
        }
75
    } else
76
        ssassert(false, "Expected entity to be a point or have beziers");
77

78
    screenBBoxValid = true;
79
    return screenBBox;
80
}
81

82
void Entity::GetReferencePoints(std::vector<Vector> *refs) {
83
    switch(type) {
84
        case Type::POINT_N_COPY:
85
        case Type::POINT_N_TRANS:
86
        case Type::POINT_N_ROT_TRANS:
87
        case Type::POINT_N_ROT_AA:
88
        case Type::POINT_N_ROT_AXIS_TRANS:
89
        case Type::POINT_IN_3D:
90
        case Type::POINT_IN_2D:
91
            refs->push_back(PointGetDrawNum());
92
            break;
93

94
        case Type::NORMAL_N_COPY:
95
        case Type::NORMAL_N_ROT:
96
        case Type::NORMAL_N_ROT_AA:
97
        case Type::NORMAL_IN_3D:
98
        case Type::NORMAL_IN_2D:
99
        case Type::WORKPLANE:
100
        case Type::CIRCLE:
101
        case Type::ARC_OF_CIRCLE:
102
        case Type::CUBIC:
103
        case Type::CUBIC_PERIODIC:
104
        case Type::TTF_TEXT:
105
        case Type::IMAGE:
106
            refs->push_back(SK.GetEntity(point[0])->PointGetDrawNum());
107
            break;
108

109
        case Type::LINE_SEGMENT: {
110
            Vector a = SK.GetEntity(point[0])->PointGetDrawNum(),
111
                   b = SK.GetEntity(point[1])->PointGetDrawNum();
112
            refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
113
            break;
114
        }
115

116
        case Type::DISTANCE:
117
        case Type::DISTANCE_N_COPY:
118
        case Type::FACE_NORMAL_PT:
119
        case Type::FACE_XPROD:
120
        case Type::FACE_N_ROT_TRANS:
121
        case Type::FACE_N_TRANS:
122
        case Type::FACE_N_ROT_AA:
123
        case Type::FACE_ROT_NORMAL_PT:
124
        case Type::FACE_N_ROT_AXIS_TRANS:
125
            break;
126
    }
127
}
128

129
int Entity::GetPositionOfPoint(const Camera &camera, Point2d p) {
130
    int position;
131

132
    ObjectPicker canvas = {};
133
    canvas.camera      = camera;
134
    canvas.point       = p;
135
    canvas.minDistance = 1e12;
136
    Draw(DrawAs::DEFAULT, &canvas);
137
    position = canvas.position;
138
    canvas.Clear();
139

140
    return position;
141
}
142

143
bool Entity::IsStylable() const {
144
    if(IsPoint()) return false;
145
    if(IsWorkplane()) return false;
146
    if(IsNormal()) return false;
147
    return true;
148
}
149

150
bool Entity::IsVisible() const {
151
    Group *g = SK.GetGroup(group);
152

153
    if(g->h == Group::HGROUP_REFERENCES && IsNormal()) {
154
        // The reference normals are always shown
155
        return true;
156
    }
157
    if(!(g->IsVisible())) return false;
158

159
    if(IsPoint() && !SS.GW.showPoints) return false;
160
    if(IsNormal() && !SS.GW.showNormals) return false;
161
    if(construction && !SS.GW.showConstruction) return false;
162

163
    if(!SS.GW.showWorkplanes) {
164
        if(IsWorkplane() && !h.isFromRequest()) {
165
            if(g->h != SS.GW.activeGroup) {
166
                // The group-associated workplanes are hidden outside
167
                // their group.
168
                return false;
169
            }
170
        }
171
    }
172

173
    if(style.v) {
174
        Style *s = Style::Get(style);
175
        if(!s->visible) return false;
176
    }
177

178
    if(forceHidden) return false;
179

180
    return true;
181
}
182

183
static bool PtCanDrag(hEntity pt) {
184
    Entity* p = SK.GetEntity(pt);
185
    // a numeric copy can not move
186
    if(p->type == Entity::Type::POINT_N_COPY) return false;
187
    // these transforms applied zero times can not be moved
188
    if(((p->type == Entity::Type::POINT_N_TRANS) ||
189
       (p->type == Entity::Type::POINT_N_ROT_AA) ||
190
       (p->type == Entity::Type::POINT_N_ROT_AXIS_TRANS))
191
        && (p->timesApplied == 0)) return false;
192
    return true;
193
}
194

195
// entities that were created via some copy types will not be
196
// draggable with the mouse. We identify the undraggables here
197
bool Entity::CanBeDragged() const {
198
    if(IsPoint()) {
199
        if(!PtCanDrag(h))
200
            return false;
201
        // are we constrained pt-on-point from a previous group?
202
        for(const Constraint &cc : SK.constraint) {
203
            if(cc.group == group && cc.type == ConstraintBase::Type::POINTS_COINCIDENT) {
204
                if(cc.ptA == h) {
205
                    if((SK.GetEntity(cc.ptB)->group < group)
206
                      || (!PtCanDrag(cc.ptB)))
207
                      return false;
208
                }
209
                if(cc.ptB == h) {
210
                    if((SK.GetEntity(cc.ptA)->group < group)
211
                      || (!PtCanDrag(cc.ptA)))
212
                      return false;
213
                }
214
            }
215
        }
216
    }
217
    // for these types of entities the first point will indicate draggability
218
    if(HasEndpoints() || type == Entity::Type::CIRCLE) {
219
        return PtCanDrag(point[0]);
220
    }
221
    // if we're not certain it can't be dragged then default to true
222
    return true;
223
}
224

225
void Entity::CalculateNumerical(bool forExport) {
226
    if(IsPoint()) actPoint = PointGetNum();
227
    if(IsNormal()) actNormal = NormalGetNum();
228
    if(type == Type::DISTANCE || type == Type::DISTANCE_N_COPY) {
229
        actDistance = DistanceGetNum();
230
    }
231
    if(IsFace()) {
232
        actPoint  = FaceGetPointNum();
233
        Vector n = FaceGetNormalNum();
234
        actNormal = Quaternion::From(0, n.x, n.y, n.z);
235
    }
236
    if(forExport) {
237
        // Visibility in copied linked entities follows source file
238
        actVisible = IsVisible();
239
    } else {
240
        // Copied entities within a file are always visible
241
        actVisible = true;
242
    }
243
}
244

245
//-----------------------------------------------------------------------------
246
// Compute a cubic, second derivative continuous, interpolating spline. Same
247
// routine for periodic splines (in a loop) or open splines (with specified
248
// end tangents).
249
//-----------------------------------------------------------------------------
250
void Entity::ComputeInterpolatingSpline(SBezierList *sbl, bool periodic) const {
251
    static const int MAX_N = BandedMatrix::MAX_UNKNOWNS;
252
    int ep = extraPoints;
253

254
    // The number of unknowns to solve for.
255
    int n   = periodic ? 3 + ep : ep;
256
    ssassert(n < MAX_N, "Too many unknowns");
257
    // The number of on-curve points, one more than the number of segments.
258
    int pts = periodic ? 4 + ep : 2 + ep;
259

260
    int i, j, a;
261

262
    // The starting and finishing control points that define our end tangents
263
    // (if the spline isn't periodic), and the on-curve points.
264
    Vector ctrl_s = Vector::From(0, 0, 0);
265
    Vector ctrl_f = Vector::From(0, 0, 0);
266
    Vector pt[MAX_N+4];
267
    if(periodic) {
268
        for(i = 0; i < ep + 3; i++) {
269
            pt[i] = SK.GetEntity(point[i])->PointGetNum();
270
        }
271
        pt[i++] = SK.GetEntity(point[0])->PointGetNum();
272
    } else {
273
        ctrl_s = SK.GetEntity(point[1])->PointGetNum();
274
        ctrl_f = SK.GetEntity(point[ep+2])->PointGetNum();
275
        j = 0;
276
        pt[j++] = SK.GetEntity(point[0])->PointGetNum();
277
        for(i = 2; i <= ep + 1; i++) {
278
            pt[j++] = SK.GetEntity(point[i])->PointGetNum();
279
        }
280
        pt[j++] = SK.GetEntity(point[ep+3])->PointGetNum();
281
    }
282

283
    // The unknowns that we will be solving for, a set for each coordinate.
284
    double Xx[MAX_N], Xy[MAX_N], Xz[MAX_N];
285
    // For a cubic Bezier section f(t) as t goes from 0 to 1,
286
    //    f' (0) = 3*(P1 - P0)
287
    //    f' (1) = 3*(P3 - P2)
288
    //    f''(0) = 6*(P0 - 2*P1 + P2)
289
    //    f''(1) = 6*(P3 - 2*P2 + P1)
290
    for(a = 0; a < 3; a++) {
291
        BandedMatrix bm = {};
292
        bm.n = n;
293

294
        for(i = 0; i < n; i++) {
295
            int im, it, ip;
296
            if(periodic) {
297
                im = WRAP(i - 1, n);
298
                it = i;
299
                ip = WRAP(i + 1, n);
300
            } else {
301
                im = i;
302
                it = i + 1;
303
                ip = i + 2;
304
            }
305
            // All of these are expressed in terms of a constant part, and
306
            // of X[i-1], X[i], and X[i+1]; so let these be the four
307
            // components of that vector;
308
            Vector4 A, B, C, D, E;
309
            // The on-curve interpolated point
310
            C = Vector4::From((pt[it]).Element(a), 0, 0, 0);
311
            // control point one back, C - X[i]
312
            B = C.Plus(Vector4::From(0, 0, -1, 0));
313
            // control point one forward, C + X[i]
314
            D = C.Plus(Vector4::From(0, 0, 1, 0));
315
            // control point two back
316
            if(i == 0 && !periodic) {
317
                A = Vector4::From(ctrl_s.Element(a), 0, 0, 0);
318
            } else {
319
                // pt[im] + X[i-1]
320
                A = Vector4::From(pt[im].Element(a), 1, 0, 0);
321
            }
322
            // control point two forward
323
            if(i == (n - 1) && !periodic) {
324
                E = Vector4::From(ctrl_f.Element(a), 0, 0, 0);
325
            } else {
326
                // pt[ip] - X[i+1]
327
                E = Vector4::From((pt[ip]).Element(a), 0, 0, -1);
328
            }
329
            // Write the second derivatives of each segment, dropping constant
330
            Vector4 fprev_pp = (C.Minus(B.ScaledBy(2))).Plus(A),
331
                    fnext_pp = (C.Minus(D.ScaledBy(2))).Plus(E),
332
                    eq       = fprev_pp.Minus(fnext_pp);
333

334
            bm.B[i] = -eq.w;
335
            if(periodic) {
336
                bm.A[i][WRAP(i-2, n)] = eq.x;
337
                bm.A[i][WRAP(i-1, n)] = eq.y;
338
                bm.A[i][i]            = eq.z;
339
            } else {
340
                // The wrapping would work, except when n = 1 and everything
341
                // wraps to zero...
342
                if(i > 0) {
343
                    bm.A[i][i - 1] = eq.x;
344
                }
345
                bm.A[i][i] = eq.y;
346
                if(i < (n-1)) {
347
                    bm.A[i][i + 1] = eq.z;
348
                }
349
            }
350
        }
351
        bm.Solve();
352
        double *X = (a == 0) ? Xx :
353
                    (a == 1) ? Xy :
354
                               Xz;
355
        memcpy(X, bm.X, n*sizeof(double));
356
    }
357

358
    for(i = 0; i < pts - 1; i++) {
359
        Vector p0, p1, p2, p3;
360
        if(periodic) {
361
            p0 = pt[i];
362
            int iw = WRAP(i - 1, n);
363
            p1 = p0.Plus(Vector::From(Xx[iw], Xy[iw], Xz[iw]));
364
        } else if(i == 0) {
365
            p0 = pt[0];
366
            p1 = ctrl_s;
367
        } else {
368
            p0 = pt[i];
369
            p1 = p0.Plus(Vector::From(Xx[i-1], Xy[i-1], Xz[i-1]));
370
        }
371
        if(periodic) {
372
            p3 = pt[i+1];
373
            int iw = WRAP(i, n);
374
            p2 = p3.Minus(Vector::From(Xx[iw], Xy[iw], Xz[iw]));
375
        } else if(i == (pts - 2)) {
376
            p3 = pt[pts-1];
377
            p2 = ctrl_f;
378
        } else {
379
            p3 = pt[i+1];
380
            p2 = p3.Minus(Vector::From(Xx[i], Xy[i], Xz[i]));
381
        }
382
        SBezier sb = SBezier::From(p0, p1, p2, p3);
383
        sbl->l.Add(&sb);
384
    }
385
}
386

387
void Entity::GenerateBezierCurves(SBezierList *sbl) const {
388
    SBezier sb;
389

390
    int i = sbl->l.n;
391

392
    switch(type) {
393
        case Type::LINE_SEGMENT: {
394
            Vector a = SK.GetEntity(point[0])->PointGetNum();
395
            Vector b = SK.GetEntity(point[1])->PointGetNum();
396
            sb = SBezier::From(a, b);
397
            sb.entity = h.v;
398
            sbl->l.Add(&sb);
399
            break;
400
        }
401
        case Type::CUBIC:
402
            ComputeInterpolatingSpline(sbl, /*periodic=*/false);
403
            break;
404

405
        case Type::CUBIC_PERIODIC:
406
            ComputeInterpolatingSpline(sbl, /*periodic=*/true);
407
            break;
408

409
        case Type::CIRCLE:
410
        case Type::ARC_OF_CIRCLE: {
411
            Vector center = SK.GetEntity(point[0])->PointGetNum();
412
            Quaternion q = SK.GetEntity(normal)->NormalGetNum();
413
            Vector u = q.RotationU(), v = q.RotationV();
414
            double r = CircleGetRadiusNum();
415
            double thetaa, thetab, dtheta;
416

417
            if(r < LENGTH_EPS) {
418
                // If a circle or an arc gets dragged through zero radius,
419
                // then we just don't generate anything.
420
                break;
421
            }
422

423
            if(type == Type::CIRCLE) {
424
                thetaa = 0;
425
                thetab = 2*PI;
426
                dtheta = 2*PI;
427
            } else {
428
                ArcGetAngles(&thetaa, &thetab, &dtheta);
429
            }
430
            int i, n;
431
            if(dtheta > (3*PI/2 + 0.01)) {
432
                n = 4;
433
            } else if(dtheta > (PI + 0.01)) {
434
                n = 3;
435
            } else if(dtheta > (PI/2 + 0.01)) {
436
                n = 2;
437
            } else {
438
                n = 1;
439
            }
440
            dtheta /= n;
441

442
            for(i = 0; i < n; i++) {
443
                double s, c;
444

445
                c = cos(thetaa);
446
                s = sin(thetaa);
447
                // The start point of the curve, and the tangent vector at
448
                // that start point.
449
                Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
450
                       t0 =             u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
451

452
                thetaa += dtheta;
453

454
                c = cos(thetaa);
455
                s = sin(thetaa);
456
                Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
457
                       t2 =             u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
458

459
                // The control point must lie on both tangents.
460
                Vector p1 = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
461
                                                          p2, p2.Plus(t2),
462
                                                          NULL);
463

464
                SBezier sb = SBezier::From(p0, p1, p2);
465
                sb.weight[1] = cos(dtheta/2);
466
                sbl->l.Add(&sb);
467
            }
468
            break;
469
        }
470

471
        case Type::TTF_TEXT: {
472
            Vector topLeft = SK.GetEntity(point[0])->PointGetNum();
473
            Vector botLeft = SK.GetEntity(point[1])->PointGetNum();
474
            Vector n = Normal()->NormalN();
475
            Vector v = topLeft.Minus(botLeft);
476
            Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
477

478
            // `extraPoints` is storing kerning boolean
479
            SS.fonts.PlotString(font, str, sbl, extraPoints, botLeft, u, v);
480
            break;
481
        }
482

483
        default:
484
            // Not a problem, points and normals and such don't generate curves
485
            break;
486
    }
487

488
    // Record our style for all of the Beziers that we just created.
489
    for(; i < sbl->l.n; i++) {
490
        sbl->l[i].auxA = Style::ForEntity(h).v;
491
    }
492
}
493

494
bool Entity::ShouldDrawExploded() const {
495
    return SK.GetGroup(group)->ShouldDrawExploded();
496
}
497

498
Vector Entity::ExplodeOffset() const {
499
    if(ShouldDrawExploded() && workplane.v != 0) {
500
        int requestIdx = SK.GetRequest(h.request())->groupRequestIndex;
501
        double offset = SS.explodeDistance * (requestIdx + 1);
502
        return SK.GetEntity(workplane)->Normal()->NormalN().ScaledBy(offset);
503
    } else {
504
        return Vector::From(0, 0, 0);
505
    }
506
}
507

508
Vector Entity::PointGetDrawNum() const {
509
    // As per EntityBase::PointGetNum but specifically for when drawing/rendering the point
510
    // (and not when solving), so we can potentially draw it somewhere different
511
    return PointGetNum().Plus(ExplodeOffset());
512
}
513

514
void Entity::Draw(DrawAs how, Canvas *canvas) {
515
    if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
516
       !IsVisible()) return;
517

518
    int zIndex;
519
    if(IsPoint()) {
520
        zIndex = 6;
521
    } else if(how == DrawAs::HIDDEN) {
522
        zIndex = 2;
523
    } else if(group != SS.GW.activeGroup) {
524
        zIndex = 3;
525
    } else {
526
        zIndex = 5;
527
    }
528

529
    hStyle hs;
530
    if(IsPoint()) {
531
        hs.v = Style::DATUM;
532
    } else if(IsNormal() || type == Type::WORKPLANE) {
533
        hs.v = Style::NORMALS;
534
    } else {
535
        hs = Style::ForEntity(h);
536
        if (hs.v == Style::CONSTRUCTION) {
537
            zIndex = 4;
538
        }
539
    }
540

541
    Canvas::Stroke stroke = Style::Stroke(hs);
542
    switch(how) {
543
        case DrawAs::DEFAULT:
544
            stroke.layer = Canvas::Layer::NORMAL;
545
            break;
546

547
        case DrawAs::OVERLAY:
548
            stroke.layer = Canvas::Layer::FRONT;
549
            break;
550

551
        case DrawAs::HIDDEN:
552
            stroke.layer = Canvas::Layer::OCCLUDED;
553
            stroke.stipplePattern = Style::PatternType({ Style::HIDDEN_EDGE });
554
            stroke.stippleScale   = Style::Get({ Style::HIDDEN_EDGE })->stippleScale;
555
            break;
556

557
        case DrawAs::HOVERED:
558
            stroke.layer = Canvas::Layer::FRONT;
559
            stroke.color = Style::Color(Style::HOVERED);
560
            break;
561

562
        case DrawAs::SELECTED:
563
            stroke.layer = Canvas::Layer::FRONT;
564
            stroke.color = Style::Color(Style::SELECTED);
565
            break;
566
    }
567
    stroke.zIndex = zIndex;
568
    Canvas::hStroke hcs = canvas->GetStroke(stroke);
569

570
    switch(type) {
571
        case Type::POINT_N_COPY:
572
        case Type::POINT_N_TRANS:
573
        case Type::POINT_N_ROT_TRANS:
574
        case Type::POINT_N_ROT_AA:
575
        case Type::POINT_N_ROT_AXIS_TRANS:
576
        case Type::POINT_IN_3D:
577
        case Type::POINT_IN_2D: {
578
            if(how == DrawAs::HIDDEN) return;
579

580
            // If we're analyzing the sketch to show the degrees of freedom,
581
            // then we draw big colored squares over the points that are
582
            // free to move.
583
            bool free = false;
584
            if(type == Type::POINT_IN_3D) {
585
                Param *px = SK.GetParam(param[0]),
586
                      *py = SK.GetParam(param[1]),
587
                      *pz = SK.GetParam(param[2]);
588

589
                free = px->free || py->free || pz->free;
590
            } else if(type == Type::POINT_IN_2D) {
591
                Param *pu = SK.GetParam(param[0]),
592
                      *pv = SK.GetParam(param[1]);
593

594
                free = pu->free || pv->free;
595
            }
596

597
            Canvas::Stroke pointStroke = {};
598
            pointStroke.layer  = (free) ? Canvas::Layer::FRONT : stroke.layer;
599
            pointStroke.zIndex = stroke.zIndex;
600
            pointStroke.color  = stroke.color;
601
            pointStroke.width  = 7.0;
602
            pointStroke.unit   = Canvas::Unit::PX;
603
            Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
604

605
            Vector p = PointGetDrawNum();
606
            if(free) {
607
                Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
608
                analyzeStroke.width = 14.0;
609
                analyzeStroke.layer = Canvas::Layer::FRONT;
610
                Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
611

612
                canvas->DrawPoint(p, hcsAnalyze);
613
            }
614

615
            canvas->DrawPoint(p, hcsPoint);
616
            return;
617
        }
618

619
        case Type::NORMAL_N_COPY:
620
        case Type::NORMAL_N_ROT:
621
        case Type::NORMAL_N_ROT_AA:
622
        case Type::NORMAL_IN_3D:
623
        case Type::NORMAL_IN_2D: {
624
            const Camera &camera = canvas->GetCamera();
625

626
            if(how == DrawAs::HIDDEN) return;
627

628
            for(int i = 0; i < 2; i++) {
629
                bool asReference = (i == 1);
630
                if(asReference) {
631
                    if(!h.request().IsFromReferences()) continue;
632
                } else {
633
                    if(!SK.GetGroup(group)->IsVisible() || !SS.GW.showNormals) continue;
634
                }
635

636
                stroke.layer = (asReference) ? Canvas::Layer::FRONT : Canvas::Layer::NORMAL;
637
                if(how != DrawAs::HOVERED && how != DrawAs::SELECTED) {
638
                    // Always draw the x, y, and z axes in red, green, and blue;
639
                    // brighter for the ones at the bottom left of the screen,
640
                    // dimmer for the ones at the model origin.
641
                    hRequest hr   = h.request();
642
                    uint8_t  luma = (asReference) ? 255 : 100;
643
                    if(hr == Request::HREQUEST_REFERENCE_XY) {
644
                        stroke.color = RgbaColor::From(0, 0, luma);
645
                    } else if(hr == Request::HREQUEST_REFERENCE_YZ) {
646
                        stroke.color = RgbaColor::From(luma, 0, 0);
647
                    } else if(hr == Request::HREQUEST_REFERENCE_ZX) {
648
                        stroke.color = RgbaColor::From(0, luma, 0);
649
                    }
650
                }
651

652
                Quaternion q = NormalGetNum();
653
                Vector tail;
654
                if(asReference) {
655
                    // Draw an extra copy of the x, y, and z axes, that's
656
                    // always in the corner of the view and at the front.
657
                    // So those are always available, perhaps useful.
658
                    stroke.width = 2;
659
                    double s = camera.scale;
660
                    double h = 60 - camera.height / 2.0;
661
                    double w = 60 - camera.width  / 2.0;
662
                    // Shift the axis to the right if they would overlap with the toolbar.
663
                    if(SS.showToolbar) {
664
                        if(h + 30 > -(32*18 + 3*16 + 8) / 2)
665
                            w += 60;
666
                    }
667
                    tail = camera.projRight.ScaledBy(w/s).Plus(
668
                           camera.projUp.   ScaledBy(h/s)).Minus(camera.offset);
669
                } else {
670
                    tail = SK.GetEntity(point[0])->PointGetDrawNum();
671
                }
672
                tail = camera.AlignToPixelGrid(tail);
673

674
                hcs = canvas->GetStroke(stroke);
675
                Vector v = (q.RotationN()).WithMagnitude(50.0 / camera.scale);
676
                Vector tip = tail.Plus(v);
677
                canvas->DrawLine(tail, tip, hcs);
678

679
                v = v.WithMagnitude(12.0 / camera.scale);
680
                Vector axis = q.RotationV();
681
                canvas->DrawLine(tip, tip.Minus(v.RotatedAbout(axis,  0.6)), hcs);
682
                canvas->DrawLine(tip, tip.Minus(v.RotatedAbout(axis, -0.6)), hcs);
683

684
                if(type == Type::NORMAL_IN_3D) {
685
                    Param *nw = SK.GetParam(param[0]),
686
                          *nx = SK.GetParam(param[1]),
687
                          *ny = SK.GetParam(param[2]),
688
                          *nz = SK.GetParam(param[3]);
689

690
                    if(nw->free || nx->free || ny->free || nz->free) {
691
                        Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
692
                        analyzeStroke.layer = Canvas::Layer::FRONT;
693
                        Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
694
                        canvas->DrawLine(tail, tip, hcsAnalyze);
695
                    }
696
                }
697
            }
698
            return;
699
        }
700

701
        case Type::DISTANCE:
702
        case Type::DISTANCE_N_COPY:
703
            // These are used only as data structures, nothing to display.
704
            return;
705

706
        case Type::WORKPLANE: {
707
            const Camera &camera = canvas->GetCamera();
708

709
            Vector p = SK.GetEntity(point[0])->PointGetNum();
710
            p = camera.AlignToPixelGrid(p);
711

712
            Vector u = Normal()->NormalU();
713
            Vector v = Normal()->NormalV();
714

715
            double s = (std::min(camera.width, camera.height)) * 0.45 / camera.scale;
716

717
            Vector us = u.ScaledBy(s);
718
            Vector vs = v.ScaledBy(s);
719

720
            Vector pp = p.Plus (us).Plus (vs);
721
            Vector pm = p.Plus (us).Minus(vs);
722
            Vector mm = p.Minus(us).Minus(vs), mm2 = mm;
723
            Vector mp = p.Minus(us).Plus (vs);
724

725
            Canvas::Stroke strokeBorder = stroke;
726
            strokeBorder.zIndex        -= 3;
727
            strokeBorder.stipplePattern = StipplePattern::SHORT_DASH;
728
            strokeBorder.stippleScale   = 8.0;
729
            Canvas::hStroke hcsBorder = canvas->GetStroke(strokeBorder);
730

731
            double textHeight = Style::TextHeight(hs) / camera.scale;
732

733
            if(!h.isFromRequest()) {
734
                mm = mm.Plus(v.ScaledBy(textHeight * 4.7));
735
                mm2 = mm2.Plus(u.ScaledBy(textHeight * 4.7));
736
                canvas->DrawLine(mm2, mm, hcsBorder);
737
            }
738
            canvas->DrawLine(pp,  pm, hcsBorder);
739
            canvas->DrawLine(mm2, pm, hcsBorder);
740
            canvas->DrawLine(mm,  mp, hcsBorder);
741
            canvas->DrawLine(pp,  mp, hcsBorder);
742

743
            Vector o = mm2.Plus(u.ScaledBy(3.0 / camera.scale)).Plus(
744
                                v.ScaledBy(3.0 / camera.scale));
745
            std::string shortDesc = DescriptionString().substr(5);
746
            canvas->DrawVectorText(shortDesc, textHeight, o, u, v, hcs);
747
            return;
748
        }
749

750
        case Type::LINE_SEGMENT:
751
        case Type::CIRCLE:
752
        case Type::ARC_OF_CIRCLE:
753
        case Type::CUBIC:
754
        case Type::CUBIC_PERIODIC:
755
        case Type::TTF_TEXT: {
756
            // Generate the rational polynomial curves, then piecewise linearize
757
            // them, and display those.
758
            // Calculating the draw offset, if necessary.
759
            const bool shouldExplode = ShouldDrawExploded();
760
            Vector explodeOffset;
761
            SBezierList offsetBeziers = {};
762
            SBezierList *beziers = GetOrGenerateBezierCurves();
763
            if(shouldExplode) {
764
                explodeOffset = ExplodeOffset();
765
                for(const SBezier& b : beziers->l) {
766
                    SBezier offset = b.TransformedBy(explodeOffset, Quaternion::IDENTITY, 1.0);
767
                    offsetBeziers.l.Add(&offset);
768
                }
769
                beziers = &offsetBeziers;
770
            }
771

772
            SEdgeList *edges = nullptr;
773
            SEdgeList offsetEdges = {};
774

775
            if(!canvas->DrawBeziers(*beziers, hcs)) {
776
                edges = GetOrGenerateEdges();
777
                if(shouldExplode) {
778
                    for(const SEdge &e : edges->l) {
779
                        offsetEdges.AddEdge(e.a.Plus(explodeOffset), e.b.Plus(explodeOffset), e.auxA, e.auxB, e.tag);
780
                    }
781
                    edges = &offsetEdges;
782
                }
783
                canvas->DrawEdges(*edges, hcs);
784
            }
785
            if(type == Type::CIRCLE) {
786
                Entity *dist = SK.GetEntity(distance);
787
                if(dist->type == Type::DISTANCE) {
788
                    Param *p = SK.GetParam(dist->param[0]);
789
                    if(p->free) {
790
                        Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
791
                        analyzeStroke.layer = Canvas::Layer::FRONT;
792
                        Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
793
                        if(!canvas->DrawBeziers(*beziers, hcsAnalyze)) {
794
                            canvas->DrawEdges(*edges, hcsAnalyze);
795
                        }
796
                    }
797
                }
798
            }
799
            offsetBeziers.Clear();
800
            offsetEdges.Clear();
801
            return;
802
        }
803
        case Type::IMAGE: {
804
            Canvas::Fill fill = {};
805
            std::shared_ptr<Pixmap> pixmap;
806
            switch(how) {
807
                case DrawAs::HIDDEN: return;
808

809
                case DrawAs::HOVERED: {
810
                    fill.color   = Style::Color(Style::HOVERED).WithAlpha(180);
811
                    fill.pattern = Canvas::FillPattern::CHECKERED_A;
812
                    fill.zIndex  = 2;
813
                    break;
814
                }
815

816
                case DrawAs::SELECTED: {
817
                    fill.color   = Style::Color(Style::SELECTED).WithAlpha(180);
818
                    fill.pattern = Canvas::FillPattern::CHECKERED_B;
819
                    fill.zIndex  = 1;
820
                    break;
821
                }
822

823
                default:
824
                    fill.color   = RgbaColor::FromFloat(1.0f, 1.0f, 1.0f);
825
                    pixmap       = SS.images[file];
826
                    break;
827
            }
828

829
            Canvas::hFill hf = canvas->GetFill(fill);
830
            Vector v[4] = {};
831
            for(int i = 0; i < 4; i++) {
832
                v[i] = SK.GetEntity(point[i])->PointGetDrawNum();
833
            }
834
            Vector iu = v[3].Minus(v[0]);
835
            Vector iv = v[1].Minus(v[0]);
836

837
            if(how == DrawAs::DEFAULT && pixmap == NULL) {
838
                Canvas::Stroke stroke = Style::Stroke(Style::DRAW_ERROR);
839
                stroke.color = stroke.color.WithAlpha(50);
840
                Canvas::hStroke hs = canvas->GetStroke(stroke);
841
                canvas->DrawLine(v[0], v[2], hs);
842
                canvas->DrawLine(v[1], v[3], hs);
843
                for(int i = 0; i < 4; i++) {
844
                    canvas->DrawLine(v[i], v[(i + 1) % 4], hs);
845
                }
846
            } else {
847
                canvas->DrawPixmap(pixmap, v[0], iu, iv,
848
                                   Point2d::From(0.0, 0.0), Point2d::From(1.0, 1.0), hf);
849
            }
850
        }
851

852
        case Type::FACE_NORMAL_PT:
853
        case Type::FACE_XPROD:
854
        case Type::FACE_N_ROT_TRANS:
855
        case Type::FACE_N_TRANS:
856
        case Type::FACE_N_ROT_AA:
857
        case Type::FACE_ROT_NORMAL_PT:
858
        case Type::FACE_N_ROT_AXIS_TRANS:
859
            // Do nothing; these are drawn with the triangle mesh
860
            return;
861
    }
862
    ssassert(false, "Unexpected entity type");
863
}
864

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

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

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

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