Solvespace
993 строки · 34.9 Кб
1//-----------------------------------------------------------------------------
2// The implementation of our entities in the symbolic algebra system, methods
3// to return a symbolic representation of the entity (line by its endpoints,
4// circle by center and radius, etc.).
5//
6// Copyright 2008-2013 Jonathan Westhues.
7//-----------------------------------------------------------------------------
8#include "solvespace.h"
9
10const hEntity EntityBase::FREE_IN_3D = { 0 };
11const hEntity EntityBase::NO_ENTITY = { 0 };
12
13bool EntityBase::HasVector() const {
14switch(type) {
15case Type::LINE_SEGMENT:
16case Type::NORMAL_IN_3D:
17case Type::NORMAL_IN_2D:
18case Type::NORMAL_N_COPY:
19case Type::NORMAL_N_ROT:
20case Type::NORMAL_N_ROT_AA:
21return true;
22
23default:
24return false;
25}
26}
27
28ExprVector EntityBase::VectorGetExprsInWorkplane(hEntity wrkpl) const {
29if(IsFace()) {
30return FaceGetNormalExprs();
31}
32switch(type) {
33case Type::LINE_SEGMENT:
34return (SK.GetEntity(point[0])->PointGetExprsInWorkplane(wrkpl)).Minus(
35SK.GetEntity(point[1])->PointGetExprsInWorkplane(wrkpl));
36
37case Type::NORMAL_IN_3D:
38case Type::NORMAL_IN_2D:
39case Type::NORMAL_N_COPY:
40case Type::NORMAL_N_ROT:
41case Type::NORMAL_N_ROT_AA: {
42ExprVector ev = NormalExprsN();
43if(wrkpl == EntityBase::FREE_IN_3D) {
44return ev;
45}
46// Get the offset and basis vectors for this weird exotic csys.
47EntityBase *w = SK.GetEntity(wrkpl);
48ExprVector wu = w->Normal()->NormalExprsU();
49ExprVector wv = w->Normal()->NormalExprsV();
50
51// Get our coordinates in three-space, and project them into that
52// coordinate system.
53ExprVector result;
54result.x = ev.Dot(wu);
55result.y = ev.Dot(wv);
56result.z = Expr::From(0.0);
57return result;
58}
59default: ssassert(false, "Unexpected entity type");
60}
61}
62
63ExprVector EntityBase::VectorGetExprs() const {
64return VectorGetExprsInWorkplane(EntityBase::FREE_IN_3D);
65}
66
67Vector EntityBase::VectorGetNum() const {
68if(IsFace()) {
69return FaceGetNormalNum();
70}
71switch(type) {
72case Type::LINE_SEGMENT:
73return (SK.GetEntity(point[0])->PointGetNum()).Minus(
74SK.GetEntity(point[1])->PointGetNum());
75
76case Type::NORMAL_IN_3D:
77case Type::NORMAL_IN_2D:
78case Type::NORMAL_N_COPY:
79case Type::NORMAL_N_ROT:
80case Type::NORMAL_N_ROT_AA:
81return NormalN();
82
83default: ssassert(false, "Unexpected entity type");
84}
85}
86
87Vector EntityBase::VectorGetRefPoint() const {
88if(IsFace()) {
89return FaceGetPointNum();
90}
91switch(type) {
92case Type::LINE_SEGMENT:
93return ((SK.GetEntity(point[0])->PointGetNum()).Plus(
94SK.GetEntity(point[1])->PointGetNum())).ScaledBy(0.5);
95
96case Type::NORMAL_IN_3D:
97case Type::NORMAL_IN_2D:
98case Type::NORMAL_N_COPY:
99case Type::NORMAL_N_ROT:
100case Type::NORMAL_N_ROT_AA:
101return SK.GetEntity(point[0])->PointGetNum();
102
103default: ssassert(false, "Unexpected entity type");
104}
105}
106
107Vector EntityBase::VectorGetStartPoint() const {
108switch(type) {
109case Type::LINE_SEGMENT:
110return SK.GetEntity(point[1])->PointGetNum();
111
112case Type::NORMAL_IN_3D:
113case Type::NORMAL_IN_2D:
114case Type::NORMAL_N_COPY:
115case Type::NORMAL_N_ROT:
116case Type::NORMAL_N_ROT_AA:
117return SK.GetEntity(point[0])->PointGetNum();
118
119default: ssassert(false, "Unexpected entity type");
120}
121}
122
123bool EntityBase::IsCircle() const {
124return (type == Type::CIRCLE) || (type == Type::ARC_OF_CIRCLE);
125}
126
127Expr *EntityBase::CircleGetRadiusExpr() const {
128if(type == Type::CIRCLE) {
129return SK.GetEntity(distance)->DistanceGetExpr();
130} else if(type == Type::ARC_OF_CIRCLE) {
131return Constraint::Distance(workplane, point[0], point[1]);
132} else ssassert(false, "Unexpected entity type");
133}
134
135double EntityBase::CircleGetRadiusNum() const {
136if(type == Type::CIRCLE) {
137return SK.GetEntity(distance)->DistanceGetNum();
138} else if(type == Type::ARC_OF_CIRCLE) {
139Vector c = SK.GetEntity(point[0])->PointGetNum();
140Vector pa = SK.GetEntity(point[1])->PointGetNum();
141return (pa.Minus(c)).Magnitude();
142} else ssassert(false, "Unexpected entity type");
143}
144
145void EntityBase::ArcGetAngles(double *thetaa, double *thetab, double *dtheta) const {
146ssassert(type == Type::ARC_OF_CIRCLE, "Unexpected entity type");
147
148Quaternion q = Normal()->NormalGetNum();
149Vector u = q.RotationU(), v = q.RotationV();
150
151Vector c = SK.GetEntity(point[0])->PointGetNum();
152Vector pa = SK.GetEntity(point[1])->PointGetNum();
153Vector pb = SK.GetEntity(point[2])->PointGetNum();
154
155Point2d c2 = c.Project2d(u, v);
156Point2d pa2 = (pa.Project2d(u, v)).Minus(c2);
157Point2d pb2 = (pb.Project2d(u, v)).Minus(c2);
158
159*thetaa = atan2(pa2.y, pa2.x);
160*thetab = atan2(pb2.y, pb2.x);
161*dtheta = *thetab - *thetaa;
162// If the endpoints are coincident, call it a full arc, not a zero arc;
163// useful concept to have when splitting
164while(*dtheta < 1e-6) *dtheta += 2*PI;
165while(*dtheta > (2*PI)) *dtheta -= 2*PI;
166}
167
168Vector EntityBase::CubicGetStartNum() const {
169return SK.GetEntity(point[0])->PointGetNum();
170}
171Vector EntityBase::CubicGetFinishNum() const {
172return SK.GetEntity(point[3+extraPoints])->PointGetNum();
173}
174ExprVector EntityBase::CubicGetStartTangentExprs() const {
175ExprVector pon = SK.GetEntity(point[0])->PointGetExprs(),
176poff = SK.GetEntity(point[1])->PointGetExprs();
177return (pon.Minus(poff));
178}
179ExprVector EntityBase::CubicGetFinishTangentExprs() const {
180ExprVector pon = SK.GetEntity(point[3+extraPoints])->PointGetExprs(),
181poff = SK.GetEntity(point[2+extraPoints])->PointGetExprs();
182return (pon.Minus(poff));
183}
184Vector EntityBase::CubicGetStartTangentNum() const {
185Vector pon = SK.GetEntity(point[0])->PointGetNum(),
186poff = SK.GetEntity(point[1])->PointGetNum();
187return (pon.Minus(poff));
188}
189Vector EntityBase::CubicGetFinishTangentNum() const {
190Vector pon = SK.GetEntity(point[3+extraPoints])->PointGetNum(),
191poff = SK.GetEntity(point[2+extraPoints])->PointGetNum();
192return (pon.Minus(poff));
193}
194
195bool EntityBase::IsWorkplane() const {
196return (type == Type::WORKPLANE);
197}
198
199ExprVector EntityBase::WorkplaneGetOffsetExprs() const {
200return SK.GetEntity(point[0])->PointGetExprs();
201}
202
203Vector EntityBase::WorkplaneGetOffset() const {
204return SK.GetEntity(point[0])->PointGetNum();
205}
206
207void EntityBase::WorkplaneGetPlaneExprs(ExprVector *n, Expr **dn) const {
208if(type == Type::WORKPLANE) {
209*n = Normal()->NormalExprsN();
210
211ExprVector p0 = SK.GetEntity(point[0])->PointGetExprs();
212// The plane is n dot (p - p0) = 0, or
213// n dot p - n dot p0 = 0
214// so dn = n dot p0
215*dn = p0.Dot(*n);
216} else ssassert(false, "Unexpected entity type");
217}
218
219bool EntityBase::IsDistance() const {
220return (type == Type::DISTANCE) ||
221(type == Type::DISTANCE_N_COPY);
222}
223double EntityBase::DistanceGetNum() const {
224if(type == Type::DISTANCE) {
225return SK.GetParam(param[0])->val;
226} else if(type == Type::DISTANCE_N_COPY) {
227return numDistance;
228} else ssassert(false, "Unexpected entity type");
229}
230Expr *EntityBase::DistanceGetExpr() const {
231if(type == Type::DISTANCE) {
232return Expr::From(param[0]);
233} else if(type == Type::DISTANCE_N_COPY) {
234return Expr::From(numDistance);
235} else ssassert(false, "Unexpected entity type");
236}
237void EntityBase::DistanceForceTo(double v) {
238if(type == Type::DISTANCE) {
239(SK.GetParam(param[0]))->val = v;
240} else if(type == Type::DISTANCE_N_COPY) {
241// do nothing, it's locked
242} else ssassert(false, "Unexpected entity type");
243}
244
245EntityBase *EntityBase::Normal() const {
246return SK.GetEntity(normal);
247}
248
249bool EntityBase::IsPoint() const {
250switch(type) {
251case Type::POINT_IN_3D:
252case Type::POINT_IN_2D:
253case Type::POINT_N_COPY:
254case Type::POINT_N_TRANS:
255case Type::POINT_N_ROT_TRANS:
256case Type::POINT_N_ROT_AA:
257case Type::POINT_N_ROT_AXIS_TRANS:
258return true;
259
260default:
261return false;
262}
263}
264
265bool EntityBase::IsNormal() const {
266switch(type) {
267case Type::NORMAL_IN_3D:
268case Type::NORMAL_IN_2D:
269case Type::NORMAL_N_COPY:
270case Type::NORMAL_N_ROT:
271case Type::NORMAL_N_ROT_AA:
272return true;
273
274default: return false;
275}
276}
277
278Quaternion EntityBase::NormalGetNum() const {
279Quaternion q;
280switch(type) {
281case Type::NORMAL_IN_3D:
282q = Quaternion::From(param[0], param[1], param[2], param[3]);
283break;
284
285case Type::NORMAL_IN_2D: {
286EntityBase *wrkpl = SK.GetEntity(workplane);
287EntityBase *norm = SK.GetEntity(wrkpl->normal);
288q = norm->NormalGetNum();
289break;
290}
291case Type::NORMAL_N_COPY:
292q = numNormal;
293break;
294
295case Type::NORMAL_N_ROT:
296q = Quaternion::From(param[0], param[1], param[2], param[3]);
297q = q.Times(numNormal);
298break;
299
300case Type::NORMAL_N_ROT_AA: {
301q = GetAxisAngleQuaternion(0);
302q = q.Times(numNormal);
303break;
304}
305
306default: ssassert(false, "Unexpected entity type");
307}
308return q;
309}
310
311void EntityBase::NormalForceTo(Quaternion q) {
312switch(type) {
313case Type::NORMAL_IN_3D:
314SK.GetParam(param[0])->val = q.w;
315SK.GetParam(param[1])->val = q.vx;
316SK.GetParam(param[2])->val = q.vy;
317SK.GetParam(param[3])->val = q.vz;
318break;
319
320case Type::NORMAL_IN_2D:
321case Type::NORMAL_N_COPY:
322// There's absolutely nothing to do; these are locked.
323break;
324case Type::NORMAL_N_ROT: {
325Quaternion qp = q.Times(numNormal.Inverse());
326
327SK.GetParam(param[0])->val = qp.w;
328SK.GetParam(param[1])->val = qp.vx;
329SK.GetParam(param[2])->val = qp.vy;
330SK.GetParam(param[3])->val = qp.vz;
331break;
332}
333
334case Type::NORMAL_N_ROT_AA:
335// Not sure if I'll bother implementing this one
336break;
337
338default: ssassert(false, "Unexpected entity type");
339}
340}
341
342Vector EntityBase::NormalU() const {
343return NormalGetNum().RotationU();
344}
345Vector EntityBase::NormalV() const {
346return NormalGetNum().RotationV();
347}
348Vector EntityBase::NormalN() const {
349return NormalGetNum().RotationN();
350}
351
352ExprVector EntityBase::NormalExprsU() const {
353return NormalGetExprs().RotationU();
354}
355ExprVector EntityBase::NormalExprsV() const {
356return NormalGetExprs().RotationV();
357}
358ExprVector EntityBase::NormalExprsN() const {
359return NormalGetExprs().RotationN();
360}
361
362ExprQuaternion EntityBase::NormalGetExprs() const {
363ExprQuaternion q;
364switch(type) {
365case Type::NORMAL_IN_3D:
366q = ExprQuaternion::From(param[0], param[1], param[2], param[3]);
367break;
368
369case Type::NORMAL_IN_2D: {
370EntityBase *wrkpl = SK.GetEntity(workplane);
371EntityBase *norm = SK.GetEntity(wrkpl->normal);
372q = norm->NormalGetExprs();
373break;
374}
375case Type::NORMAL_N_COPY:
376q = ExprQuaternion::From(numNormal);
377break;
378
379case Type::NORMAL_N_ROT: {
380ExprQuaternion orig = ExprQuaternion::From(numNormal);
381q = ExprQuaternion::From(param[0], param[1], param[2], param[3]);
382
383q = q.Times(orig);
384break;
385}
386
387case Type::NORMAL_N_ROT_AA: {
388ExprQuaternion orig = ExprQuaternion::From(numNormal);
389q = GetAxisAngleQuaternionExprs(0);
390q = q.Times(orig);
391break;
392}
393
394default: ssassert(false, "Unexpected entity type");
395}
396return q;
397}
398
399void EntityBase::PointForceParamTo(Vector p) {
400switch(type) {
401case Type::POINT_IN_3D:
402SK.GetParam(param[0])->val = p.x;
403SK.GetParam(param[1])->val = p.y;
404SK.GetParam(param[2])->val = p.z;
405break;
406
407case Type::POINT_IN_2D:
408SK.GetParam(param[0])->val = p.x;
409SK.GetParam(param[1])->val = p.y;
410break;
411
412default: ssassert(false, "Unexpected entity type");
413}
414}
415
416void EntityBase::PointForceTo(Vector p) {
417switch(type) {
418case Type::POINT_IN_3D:
419SK.GetParam(param[0])->val = p.x;
420SK.GetParam(param[1])->val = p.y;
421SK.GetParam(param[2])->val = p.z;
422break;
423
424case Type::POINT_IN_2D: {
425EntityBase *c = SK.GetEntity(workplane);
426p = p.Minus(c->WorkplaneGetOffset());
427SK.GetParam(param[0])->val = p.Dot(c->Normal()->NormalU());
428SK.GetParam(param[1])->val = p.Dot(c->Normal()->NormalV());
429break;
430}
431
432case Type::POINT_N_TRANS: {
433if(timesApplied == 0) break;
434Vector trans = (p.Minus(numPoint)).ScaledBy(1.0/timesApplied);
435SK.GetParam(param[0])->val = trans.x;
436SK.GetParam(param[1])->val = trans.y;
437SK.GetParam(param[2])->val = trans.z;
438break;
439}
440
441case Type::POINT_N_ROT_TRANS: {
442// Force only the translation; leave the rotation unchanged. But
443// remember that we're working with respect to the rotated
444// point.
445Vector trans = p.Minus(PointGetQuaternion().Rotate(numPoint));
446SK.GetParam(param[0])->val = trans.x;
447SK.GetParam(param[1])->val = trans.y;
448SK.GetParam(param[2])->val = trans.z;
449break;
450}
451
452case Type::POINT_N_ROT_AA: {
453// Force only the angle; the axis and center of rotation stay
454Vector offset = Vector::From(param[0], param[1], param[2]);
455Vector normal = Vector::From(param[4], param[5], param[6]);
456Vector u = normal.Normal(0), v = normal.Normal(1);
457Vector po = p.Minus(offset), numo = numPoint.Minus(offset);
458double thetap = atan2(v.Dot(po), u.Dot(po));
459double thetan = atan2(v.Dot(numo), u.Dot(numo));
460double thetaf = (thetap - thetan);
461double thetai = (SK.GetParam(param[3])->val)*timesApplied*2;
462double dtheta = thetaf - thetai;
463// Take the smallest possible change in the actual step angle,
464// in order to avoid jumps when you cross from +pi to -pi
465while(dtheta < -PI) dtheta += 2*PI;
466while(dtheta > PI) dtheta -= 2*PI;
467// this extra *2 explains the mystery *4
468SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
469break;
470}
471
472case Type::POINT_N_ROT_AXIS_TRANS: {
473if(timesApplied == 0) break;
474// is the point on the rotation axis?
475Vector offset = Vector::From(param[0], param[1], param[2]);
476Vector normal = Vector::From(param[4], param[5], param[6]).WithMagnitude(1.0);
477Vector check = numPoint.Minus(offset).Cross(normal);
478if (check.Dot(check) < LENGTH_EPS) { // if so, do extrusion style drag
479Vector trans = (p.Minus(numPoint));
480SK.GetParam(param[7])->val = trans.Dot(normal)/timesApplied;
481} else { // otherwise do rotation style
482Vector u = normal.Normal(0), v = normal.Normal(1);
483Vector po = p.Minus(offset), numo = numPoint.Minus(offset);
484double thetap = atan2(v.Dot(po), u.Dot(po));
485double thetan = atan2(v.Dot(numo), u.Dot(numo));
486double thetaf = (thetap - thetan);
487double thetai = (SK.GetParam(param[3])->val)*timesApplied*2;
488double dtheta = thetaf - thetai;
489// Take the smallest possible change in the actual step angle,
490// in order to avoid jumps when you cross from +pi to -pi
491while(dtheta < -PI) dtheta += 2*PI;
492while(dtheta > PI) dtheta -= 2*PI;
493// this extra *2 explains the mystery *4
494SK.GetParam(param[3])->val = (thetai + dtheta)/(timesApplied*2);
495}
496break;
497}
498
499case Type::POINT_N_COPY:
500// Nothing to do; it's a static copy
501break;
502
503default: ssassert(false, "Unexpected entity type");
504}
505}
506
507Vector EntityBase::PointGetNum() const {
508Vector p;
509switch(type) {
510case Type::POINT_IN_3D:
511p = Vector::From(param[0], param[1], param[2]);
512break;
513
514case Type::POINT_IN_2D: {
515EntityBase *c = SK.GetEntity(workplane);
516Vector u = c->Normal()->NormalU();
517Vector v = c->Normal()->NormalV();
518p = u.ScaledBy(SK.GetParam(param[0])->val);
519p = p.Plus(v.ScaledBy(SK.GetParam(param[1])->val));
520p = p.Plus(c->WorkplaneGetOffset());
521break;
522}
523
524case Type::POINT_N_TRANS: {
525Vector trans = Vector::From(param[0], param[1], param[2]);
526p = numPoint.Plus(trans.ScaledBy(timesApplied));
527break;
528}
529
530case Type::POINT_N_ROT_TRANS: {
531Vector offset = Vector::From(param[0], param[1], param[2]);
532Quaternion q = PointGetQuaternion();
533p = q.Rotate(numPoint);
534p = p.Plus(offset);
535break;
536}
537
538case Type::POINT_N_ROT_AA: {
539Vector offset = Vector::From(param[0], param[1], param[2]);
540Quaternion q = PointGetQuaternion();
541p = numPoint.Minus(offset);
542p = q.Rotate(p);
543p = p.Plus(offset);
544break;
545}
546
547case Type::POINT_N_ROT_AXIS_TRANS: {
548Vector offset = Vector::From(param[0], param[1], param[2]);
549Vector displace = Vector::From(param[4], param[5], param[6])
550.WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied);
551Quaternion q = PointGetQuaternion();
552p = numPoint.Minus(offset);
553p = q.Rotate(p);
554p = p.Plus(offset).Plus(displace);
555break;
556}
557
558case Type::POINT_N_COPY:
559p = numPoint;
560break;
561
562default: ssassert(false, "Unexpected entity type");
563}
564return p;
565}
566
567ExprVector EntityBase::PointGetExprs() const {
568ExprVector r;
569switch(type) {
570case Type::POINT_IN_3D:
571r = ExprVector::From(param[0], param[1], param[2]);
572break;
573
574case Type::POINT_IN_2D: {
575EntityBase *c = SK.GetEntity(workplane);
576ExprVector u = c->Normal()->NormalExprsU();
577ExprVector v = c->Normal()->NormalExprsV();
578r = c->WorkplaneGetOffsetExprs();
579r = r.Plus(u.ScaledBy(Expr::From(param[0])));
580r = r.Plus(v.ScaledBy(Expr::From(param[1])));
581break;
582}
583case Type::POINT_N_TRANS: {
584ExprVector orig = ExprVector::From(numPoint);
585ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
586r = orig.Plus(trans.ScaledBy(Expr::From(timesApplied)));
587break;
588}
589case Type::POINT_N_ROT_TRANS: {
590ExprVector orig = ExprVector::From(numPoint);
591ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
592ExprQuaternion q =
593ExprQuaternion::From(param[3], param[4], param[5], param[6]);
594orig = q.Rotate(orig);
595r = orig.Plus(trans);
596break;
597}
598case Type::POINT_N_ROT_AA: {
599ExprVector orig = ExprVector::From(numPoint);
600ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
601ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
602orig = orig.Minus(trans);
603orig = q.Rotate(orig);
604r = orig.Plus(trans);
605break;
606}
607case Type::POINT_N_ROT_AXIS_TRANS: {
608ExprVector orig = ExprVector::From(numPoint);
609ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
610ExprVector displace = ExprVector::From(param[4], param[5], param[6])
611.WithMagnitude(Expr::From(1.0)).ScaledBy(Expr::From(timesApplied)).ScaledBy(Expr::From(param[7]));
612
613ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
614orig = orig.Minus(trans);
615orig = q.Rotate(orig);
616r = orig.Plus(trans).Plus(displace);
617break;
618}
619case Type::POINT_N_COPY:
620r = ExprVector::From(numPoint);
621break;
622
623default: ssassert(false, "Unexpected entity type");
624}
625return r;
626}
627
628void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const {
629if(type == Type::POINT_IN_2D && workplane == wrkpl) {
630// They want our coordinates in the form that we've written them,
631// very nice.
632*u = Expr::From(param[0]);
633*v = Expr::From(param[1]);
634} else {
635// Get the offset and basis vectors for this weird exotic csys.
636EntityBase *w = SK.GetEntity(wrkpl);
637ExprVector wp = w->WorkplaneGetOffsetExprs();
638ExprVector wu = w->Normal()->NormalExprsU();
639ExprVector wv = w->Normal()->NormalExprsV();
640
641// Get our coordinates in three-space, and project them into that
642// coordinate system.
643ExprVector ev = PointGetExprs();
644ev = ev.Minus(wp);
645*u = ev.Dot(wu);
646*v = ev.Dot(wv);
647}
648}
649
650ExprVector EntityBase::PointGetExprsInWorkplane(hEntity wrkpl) const {
651if(wrkpl == Entity::FREE_IN_3D) {
652return PointGetExprs();
653}
654
655ExprVector r;
656PointGetExprsInWorkplane(wrkpl, &r.x, &r.y);
657r.z = Expr::From(0.0);
658return r;
659}
660
661void EntityBase::PointForceQuaternionTo(Quaternion q) {
662ssassert(type == Type::POINT_N_ROT_TRANS, "Unexpected entity type");
663
664SK.GetParam(param[3])->val = q.w;
665SK.GetParam(param[4])->val = q.vx;
666SK.GetParam(param[5])->val = q.vy;
667SK.GetParam(param[6])->val = q.vz;
668}
669
670Quaternion EntityBase::GetAxisAngleQuaternion(int param0) const {
671Quaternion q;
672double theta = timesApplied*SK.GetParam(param[param0+0])->val;
673double s = sin(theta), c = cos(theta);
674q.w = c;
675q.vx = s*SK.GetParam(param[param0+1])->val;
676q.vy = s*SK.GetParam(param[param0+2])->val;
677q.vz = s*SK.GetParam(param[param0+3])->val;
678return q;
679}
680
681ExprQuaternion EntityBase::GetAxisAngleQuaternionExprs(int param0) const {
682ExprQuaternion q;
683
684Expr *theta = Expr::From(timesApplied)->Times(
685Expr::From(param[param0+0]));
686Expr *c = theta->Cos(), *s = theta->Sin();
687q.w = c;
688q.vx = s->Times(Expr::From(param[param0+1]));
689q.vy = s->Times(Expr::From(param[param0+2]));
690q.vz = s->Times(Expr::From(param[param0+3]));
691return q;
692}
693
694Quaternion EntityBase::PointGetQuaternion() const {
695Quaternion q;
696
697if(type == Type::POINT_N_ROT_AA || type == Type::POINT_N_ROT_AXIS_TRANS) {
698q = GetAxisAngleQuaternion(3);
699} else if(type == Type::POINT_N_ROT_TRANS) {
700q = Quaternion::From(param[3], param[4], param[5], param[6]);
701} else ssassert(false, "Unexpected entity type");
702
703return q;
704}
705
706bool EntityBase::IsFace() const {
707switch(type) {
708case Type::FACE_NORMAL_PT:
709case Type::FACE_XPROD:
710case Type::FACE_N_ROT_TRANS:
711case Type::FACE_N_TRANS:
712case Type::FACE_N_ROT_AA:
713case Type::FACE_ROT_NORMAL_PT:
714case Type::FACE_N_ROT_AXIS_TRANS:
715return true;
716default:
717return false;
718}
719}
720
721ExprVector EntityBase::FaceGetNormalExprs() const {
722ExprVector r;
723if(type == Type::FACE_NORMAL_PT) {
724Vector v = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
725r = ExprVector::From(v.WithMagnitude(1));
726} else if(type == Type::FACE_XPROD) {
727ExprVector vc = ExprVector::From(param[0], param[1], param[2]);
728ExprVector vn =
729ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
730r = vc.Cross(vn);
731r = r.WithMagnitude(Expr::From(1.0));
732} else if(type == Type::FACE_N_ROT_TRANS) {
733// The numerical normal vector gets the rotation; the numerical
734// normal has magnitude one, and the rotation doesn't change that,
735// so there's no need to fix it up.
736r = ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
737ExprQuaternion q =
738ExprQuaternion::From(param[3], param[4], param[5], param[6]);
739r = q.Rotate(r);
740} else if(type == Type::FACE_N_TRANS) {
741r = ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
742} else if((type == Type::FACE_N_ROT_AA) || (type == Type::FACE_ROT_NORMAL_PT)) {
743r = ExprVector::From(numNormal.vx, numNormal.vy, numNormal.vz);
744ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
745r = q.Rotate(r);
746} else ssassert(false, "Unexpected entity type");
747return r;
748}
749
750Vector EntityBase::FaceGetNormalNum() const {
751Vector r;
752if(type == Type::FACE_NORMAL_PT) {
753r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
754} else if(type == Type::FACE_XPROD) {
755Vector vc = Vector::From(param[0], param[1], param[2]);
756Vector vn = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
757r = vc.Cross(vn);
758} else if(type == Type::FACE_N_ROT_TRANS) {
759// The numerical normal vector gets the rotation
760r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
761Quaternion q = Quaternion::From(param[3], param[4], param[5], param[6]);
762r = q.Rotate(r);
763} else if(type == Type::FACE_N_TRANS) {
764r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
765} else if((type == Type::FACE_N_ROT_AA) || (type == Type::FACE_ROT_NORMAL_PT)) {
766r = Vector::From(numNormal.vx, numNormal.vy, numNormal.vz);
767Quaternion q = GetAxisAngleQuaternion(3);
768r = q.Rotate(r);
769} else ssassert(false, "Unexpected entity type");
770return r.WithMagnitude(1);
771}
772
773ExprVector EntityBase::FaceGetPointExprs() const {
774ExprVector r;
775if((type == Type::FACE_NORMAL_PT) || (type==Type::FACE_ROT_NORMAL_PT)) {
776r = SK.GetEntity(point[0])->PointGetExprs();
777} else if(type == Type::FACE_XPROD) {
778r = ExprVector::From(numPoint);
779} else if(type == Type::FACE_N_ROT_TRANS) {
780// The numerical point gets the rotation and translation.
781ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
782ExprQuaternion q =
783ExprQuaternion::From(param[3], param[4], param[5], param[6]);
784r = ExprVector::From(numPoint);
785r = q.Rotate(r);
786r = r.Plus(trans);
787} else if(type == Type::FACE_N_ROT_AXIS_TRANS) {
788ExprVector orig = ExprVector::From(numPoint);
789ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
790ExprVector displace = ExprVector::From(param[4], param[5], param[6])
791.WithMagnitude(Expr::From(param[7])).ScaledBy(Expr::From(timesApplied));
792ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
793orig = orig.Minus(trans);
794orig = q.Rotate(orig);
795r = orig.Plus(trans).Plus(displace);
796} else if(type == Type::FACE_N_TRANS) {
797ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
798r = ExprVector::From(numPoint);
799r = r.Plus(trans.ScaledBy(Expr::From(timesApplied)));
800} else if(type == Type::FACE_N_ROT_AA) {
801ExprVector trans = ExprVector::From(param[0], param[1], param[2]);
802ExprQuaternion q = GetAxisAngleQuaternionExprs(3);
803r = ExprVector::From(numPoint);
804r = r.Minus(trans);
805r = q.Rotate(r);
806r = r.Plus(trans);
807} else ssassert(false, "Unexpected entity type");
808return r;
809}
810
811Vector EntityBase::FaceGetPointNum() const {
812Vector r;
813if((type == Type::FACE_NORMAL_PT) || (type==Type::FACE_ROT_NORMAL_PT)) {
814r = SK.GetEntity(point[0])->PointGetNum();
815} else if(type == Type::FACE_XPROD) {
816r = numPoint;
817} else if(type == Type::FACE_N_ROT_TRANS) {
818// The numerical point gets the rotation and translation.
819Vector trans = Vector::From(param[0], param[1], param[2]);
820Quaternion q = Quaternion::From(param[3], param[4], param[5], param[6]);
821r = q.Rotate(numPoint);
822r = r.Plus(trans);
823} else if(type == Type::FACE_N_ROT_AXIS_TRANS) {
824Vector offset = Vector::From(param[0], param[1], param[2]);
825Vector displace = Vector::From(param[4], param[5], param[6])
826.WithMagnitude(SK.GetParam(param[7])->val).ScaledBy(timesApplied);
827Quaternion q = PointGetQuaternion();
828r = numPoint.Minus(offset);
829r = q.Rotate(r);
830r = r.Plus(offset).Plus(displace);
831} else if(type == Type::FACE_N_TRANS) {
832Vector trans = Vector::From(param[0], param[1], param[2]);
833r = numPoint.Plus(trans.ScaledBy(timesApplied));
834} else if(type == Type::FACE_N_ROT_AA) {
835Vector trans = Vector::From(param[0], param[1], param[2]);
836Quaternion q = GetAxisAngleQuaternion(3);
837r = numPoint.Minus(trans);
838r = q.Rotate(r);
839r = r.Plus(trans);
840} else ssassert(false, "Unexpected entity type");
841return r;
842}
843
844bool EntityBase::HasEndpoints() const {
845return (type == Type::LINE_SEGMENT) ||
846(type == Type::CUBIC) ||
847(type == Type::ARC_OF_CIRCLE);
848}
849Vector EntityBase::EndpointStart() const {
850if(type == Type::LINE_SEGMENT) {
851return SK.GetEntity(point[0])->PointGetNum();
852} else if(type == Type::CUBIC) {
853return CubicGetStartNum();
854} else if(type == Type::ARC_OF_CIRCLE) {
855return SK.GetEntity(point[1])->PointGetNum();
856} else ssassert(false, "Unexpected entity type");
857}
858Vector EntityBase::EndpointFinish() const {
859if(type == Type::LINE_SEGMENT) {
860return SK.GetEntity(point[1])->PointGetNum();
861} else if(type == Type::CUBIC) {
862return CubicGetFinishNum();
863} else if(type == Type::ARC_OF_CIRCLE) {
864return SK.GetEntity(point[2])->PointGetNum();
865} else ssassert(false, "Unexpected entity type");
866}
867static bool PointInPlane(hEntity h, Vector norm, double distance) {
868Vector p = SK.GetEntity(h)->PointGetNum();
869return (fabs(norm.Dot(p) - distance) < LENGTH_EPS);
870}
871bool EntityBase::IsInPlane(Vector norm, double distance) const {
872switch(type) {
873case Type::LINE_SEGMENT: {
874return PointInPlane(point[0], norm, distance)
875&& PointInPlane(point[1], norm, distance);
876}
877case Type::CUBIC:
878case Type::CUBIC_PERIODIC: {
879bool periodic = type == Type::CUBIC_PERIODIC;
880int n = periodic ? 3 + extraPoints : extraPoints;
881int i;
882for (i=0; i<n; i++) {
883if (!PointInPlane(point[i], norm, distance)) return false;
884}
885return true;
886}
887
888case Type::CIRCLE:
889case Type::ARC_OF_CIRCLE: {
890// If it is an (arc of) a circle, check whether the normals
891// are parallel and the mid point is in the plane.
892Vector n = Normal()->NormalN();
893if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
894return PointInPlane(point[0], norm, distance);
895}
896
897case Type::TTF_TEXT: {
898Vector n = Normal()->NormalN();
899if (!norm.Equals(n) && !norm.Equals(n.Negated())) return false;
900return PointInPlane(point[0], norm, distance)
901&& PointInPlane(point[1], norm, distance);
902}
903
904default:
905return false;
906}
907}
908
909void EntityBase::RectGetPointsExprs(ExprVector *eb, ExprVector *ec) const {
910ssassert(type == Type::TTF_TEXT || type == Type::IMAGE,
911"Unexpected entity type");
912
913EntityBase *a = SK.GetEntity(point[0]);
914EntityBase *o = SK.GetEntity(point[1]);
915
916// Write equations for each point in the current workplane.
917// This reduces the complexity of resulting equations.
918ExprVector ea = a->PointGetExprsInWorkplane(workplane);
919ExprVector eo = o->PointGetExprsInWorkplane(workplane);
920
921// Take perpendicular vector and scale it by aspect ratio.
922ExprVector eu = ea.Minus(eo);
923ExprVector ev = ExprVector::From(eu.y, eu.x->Negate(), eu.z).ScaledBy(Expr::From(aspectRatio));
924
925*eb = eo.Plus(ev);
926*ec = eo.Plus(eu).Plus(ev);
927}
928
929void EntityBase::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) const {
930Equation eq;
931eq.e = expr;
932eq.h = h.equation(index);
933l->Add(&eq);
934}
935
936void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) const {
937switch(type) {
938case Type::NORMAL_IN_3D: {
939ExprQuaternion q = NormalGetExprs();
940AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0);
941break;
942}
943
944case Type::ARC_OF_CIRCLE: {
945// If this is a copied entity, with its point already fixed
946// with respect to each other, then we don't want to generate
947// the distance constraint!
948if(SK.GetEntity(point[0])->type != Type::POINT_IN_2D) break;
949
950// If the two endpoints of the arc are constrained coincident
951// (to make a complete circle), then our distance constraint
952// would be redundant and therefore overconstrain things.
953auto it = std::find_if(SK.constraint.begin(), SK.constraint.end(),
954[&](ConstraintBase const &con) {
955return (con.group == group) &&
956(con.type == Constraint::Type::POINTS_COINCIDENT) &&
957((con.ptA == point[1] && con.ptB == point[2]) ||
958(con.ptA == point[2] && con.ptB == point[1]));
959});
960if(it != SK.constraint.end()) {
961break;
962}
963
964Expr *ra = Constraint::Distance(workplane, point[0], point[1]);
965Expr *rb = Constraint::Distance(workplane, point[0], point[2]);
966AddEq(l, ra->Minus(rb), 0);
967break;
968}
969
970case Type::IMAGE:
971case Type::TTF_TEXT: {
972if(SK.GetEntity(point[0])->type != Type::POINT_IN_2D) break;
973EntityBase *b = SK.GetEntity(point[2]);
974EntityBase *c = SK.GetEntity(point[3]);
975ExprVector eb = b->PointGetExprsInWorkplane(workplane);
976ExprVector ec = c->PointGetExprsInWorkplane(workplane);
977
978ExprVector ebp, ecp;
979RectGetPointsExprs(&ebp, &ecp);
980
981ExprVector beq = eb.Minus(ebp);
982AddEq(l, beq.x, 0);
983AddEq(l, beq.y, 1);
984ExprVector ceq = ec.Minus(ecp);
985AddEq(l, ceq.x, 2);
986AddEq(l, ceq.y, 3);
987break;
988}
989
990default: // Most entities do not generate equations.
991break;
992}
993}
994