Solvespace
1072 строки · 29.4 Кб
1//-----------------------------------------------------------------------------
2// Utility functions, mostly various kinds of vector math (working on real
3// numbers, not working on quantities in the symbolic algebra system).
4//
5// Copyright 2008-2013 Jonathan Westhues.
6//-----------------------------------------------------------------------------
7#include "solvespace.h"8
9void SolveSpace::AssertFailure(const char *file, unsigned line, const char *function,10const char *condition, const char *message) {11std::string formattedMsg;12formattedMsg += ssprintf("File %s, line %u, function %s:\n", file, line, function);13formattedMsg += ssprintf("Assertion failed: %s.\n", condition);14formattedMsg += ssprintf("Message: %s.\n", message);15SolveSpace::Platform::FatalError(formattedMsg);16}
17
18std::string SolveSpace::ssprintf(const char *fmt, ...)19{
20va_list va;21
22va_start(va, fmt);23int size = vsnprintf(NULL, 0, fmt, va);24ssassert(size >= 0, "vsnprintf could not encode string");25va_end(va);26
27std::string result;28result.resize(size + 1);29
30va_start(va, fmt);31vsnprintf(&result[0], size + 1, fmt, va);32va_end(va);33
34result.resize(size);35return result;36}
37
38char32_t utf8_iterator::operator*()39{
40const uint8_t *it = (const uint8_t*) this->p;41char32_t result = *it;42
43if((result & 0x80) != 0) {44unsigned int mask = 0x40;45
46do {47result <<= 6;48unsigned int c = (*++it);49mask <<= 5;50result += c - 0x80;51} while((result & mask) != 0);52
53result &= mask - 1;54}55
56this->n = (const char*) (it + 1);57return result;58}
59
60int64_t SolveSpace::GetMilliseconds()61{
62auto timestamp = std::chrono::steady_clock::now().time_since_epoch();63return std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count();64}
65
66void SolveSpace::MakeMatrix(double *mat,67double a11, double a12, double a13, double a14,68double a21, double a22, double a23, double a24,69double a31, double a32, double a33, double a34,70double a41, double a42, double a43, double a44)71{
72mat[ 0] = a11;73mat[ 1] = a21;74mat[ 2] = a31;75mat[ 3] = a41;76mat[ 4] = a12;77mat[ 5] = a22;78mat[ 6] = a32;79mat[ 7] = a42;80mat[ 8] = a13;81mat[ 9] = a23;82mat[10] = a33;83mat[11] = a43;84mat[12] = a14;85mat[13] = a24;86mat[14] = a34;87mat[15] = a44;88}
89
90void SolveSpace::MultMatrix(double *mata, double *matb, double *matr) {91for(int i = 0; i < 4; i++) {92for(int j = 0; j < 4; j++) {93double s = 0.0;94for(int k = 0; k < 4; k++) {95s += mata[k * 4 + j] * matb[i * 4 + k];96}97matr[i * 4 + j] = s;98}99}100}
101
102//-----------------------------------------------------------------------------
103// Format the string for our message box appropriately, and then display
104// that string.
105//-----------------------------------------------------------------------------
106static void MessageBox(const char *fmt, va_list va, bool error,107std::function<void()> onDismiss = std::function<void()>())108{
109#ifndef LIBRARY110va_list va_size;111va_copy(va_size, va);112int size = vsnprintf(NULL, 0, fmt, va_size);113ssassert(size >= 0, "vsnprintf could not encode string");114va_end(va_size);115
116std::string text;117text.resize(size);118
119vsnprintf(&text[0], size + 1, fmt, va);120
121// Split message text using a heuristic for better presentation.122size_t separatorAt = 0;123while(separatorAt != std::string::npos) {124size_t dotAt = text.find('.', separatorAt + 1),125colonAt = text.find(':', separatorAt + 1);126separatorAt = min(dotAt, colonAt);127if(separatorAt == std::string::npos ||128(separatorAt + 1 < text.size() && isspace(text[separatorAt + 1]))) {129break;130}131}132std::string message = text;133std::string description;134if(separatorAt != std::string::npos) {135message = text.substr(0, separatorAt + 1);136if(separatorAt + 1 < text.size()) {137description = text.substr(separatorAt + 1);138}139}140
141if(description.length() > 0) {142std::string::iterator it = description.begin();143while(isspace(*it)) it++;144description = description.substr(it - description.begin());145}146
147Platform::MessageDialogRef dialog = CreateMessageDialog(SS.GW.window);148if (!dialog) {149if (error) {150fprintf(stderr, "Error: %s\n", message.c_str());151} else {152fprintf(stderr, "Message: %s\n", message.c_str());153}154if(onDismiss) {155onDismiss();156}157return;158}159using Platform::MessageDialog;160if(error) {161dialog->SetType(MessageDialog::Type::ERROR);162} else {163dialog->SetType(MessageDialog::Type::INFORMATION);164}165dialog->SetTitle(error ? C_("title", "Error") : C_("title", "Message"));166dialog->SetMessage(message);167if(!description.empty()) {168dialog->SetDescription(description);169}170dialog->AddButton(C_("button", "&OK"), MessageDialog::Response::OK,171/*isDefault=*/true);172
173dialog->onResponse = [=](MessageDialog::Response _response) {174if(onDismiss) {175onDismiss();176}177};178dialog->ShowModal();179#endif180}
181void SolveSpace::Error(const char *fmt, ...)182{
183va_list f;184va_start(f, fmt);185MessageBox(fmt, f, /*error=*/true);186va_end(f);187}
188void SolveSpace::Message(const char *fmt, ...)189{
190va_list f;191va_start(f, fmt);192MessageBox(fmt, f, /*error=*/false);193va_end(f);194}
195void SolveSpace::MessageAndRun(std::function<void()> onDismiss, const char *fmt, ...)196{
197va_list f;198va_start(f, fmt);199MessageBox(fmt, f, /*error=*/false, onDismiss);200va_end(f);201}
202
203//-----------------------------------------------------------------------------
204// Solve a mostly banded matrix. In a given row, there are LEFT_OF_DIAG
205// elements to the left of the diagonal element, and RIGHT_OF_DIAG elements to
206// the right (so that the total band width is LEFT_OF_DIAG + RIGHT_OF_DIAG + 1).
207// There also may be elements in the last two columns of any row. We solve
208// without pivoting.
209//-----------------------------------------------------------------------------
210void BandedMatrix::Solve() {211int i, ip, j, jp;212double temp;213
214// Reduce the matrix to upper triangular form.215for(i = 0; i < n; i++) {216for(ip = i+1; ip < n && ip <= (i + LEFT_OF_DIAG); ip++) {217temp = A[ip][i]/A[i][i];218
219for(jp = i; jp < (n - 2) && jp <= (i + RIGHT_OF_DIAG); jp++) {220A[ip][jp] -= temp*(A[i][jp]);221}222A[ip][n-2] -= temp*(A[i][n-2]);223A[ip][n-1] -= temp*(A[i][n-1]);224
225B[ip] -= temp*B[i];226}227}228
229// And back-substitute.230for(i = n - 1; i >= 0; i--) {231temp = B[i];232
233if(i < n-1) temp -= X[n-1]*A[i][n-1];234if(i < n-2) temp -= X[n-2]*A[i][n-2];235
236for(j = min(n - 3, i + RIGHT_OF_DIAG); j > i; j--) {237temp -= X[j]*A[i][j];238}239X[i] = temp / A[i][i];240}241}
242
243const Quaternion Quaternion::IDENTITY = { 1, 0, 0, 0 };244
245Quaternion Quaternion::From(double w, double vx, double vy, double vz) {246Quaternion q;247q.w = w;248q.vx = vx;249q.vy = vy;250q.vz = vz;251return q;252}
253
254Quaternion Quaternion::From(hParam w, hParam vx, hParam vy, hParam vz) {255Quaternion q;256q.w = SK.GetParam(w )->val;257q.vx = SK.GetParam(vx)->val;258q.vy = SK.GetParam(vy)->val;259q.vz = SK.GetParam(vz)->val;260return q;261}
262
263Quaternion Quaternion::From(Vector axis, double dtheta) {264Quaternion q;265double c = cos(dtheta / 2), s = sin(dtheta / 2);266axis = axis.WithMagnitude(s);267q.w = c;268q.vx = axis.x;269q.vy = axis.y;270q.vz = axis.z;271return q;272}
273
274Quaternion Quaternion::From(Vector u, Vector v)275{
276Vector n = u.Cross(v);277
278Quaternion q;279double s, tr = 1 + u.x + v.y + n.z;280if(tr > 1e-4) {281s = 2*sqrt(tr);282q.w = s/4;283q.vx = (v.z - n.y)/s;284q.vy = (n.x - u.z)/s;285q.vz = (u.y - v.x)/s;286} else {287if(u.x > v.y && u.x > n.z) {288s = 2*sqrt(1 + u.x - v.y - n.z);289q.w = (v.z - n.y)/s;290q.vx = s/4;291q.vy = (u.y + v.x)/s;292q.vz = (n.x + u.z)/s;293} else if(v.y > n.z) {294s = 2*sqrt(1 - u.x + v.y - n.z);295q.w = (n.x - u.z)/s;296q.vx = (u.y + v.x)/s;297q.vy = s/4;298q.vz = (v.z + n.y)/s;299} else {300s = 2*sqrt(1 - u.x - v.y + n.z);301q.w = (u.y - v.x)/s;302q.vx = (n.x + u.z)/s;303q.vy = (v.z + n.y)/s;304q.vz = s/4;305}306}307
308return q.WithMagnitude(1);309}
310
311Quaternion Quaternion::Plus(Quaternion b) const {312Quaternion q;313q.w = w + b.w;314q.vx = vx + b.vx;315q.vy = vy + b.vy;316q.vz = vz + b.vz;317return q;318}
319
320Quaternion Quaternion::Minus(Quaternion b) const {321Quaternion q;322q.w = w - b.w;323q.vx = vx - b.vx;324q.vy = vy - b.vy;325q.vz = vz - b.vz;326return q;327}
328
329Quaternion Quaternion::ScaledBy(double s) const {330Quaternion q;331q.w = w*s;332q.vx = vx*s;333q.vy = vy*s;334q.vz = vz*s;335return q;336}
337
338double Quaternion::Magnitude() const {339return sqrt(w*w + vx*vx + vy*vy + vz*vz);340}
341
342Quaternion Quaternion::WithMagnitude(double s) const {343return ScaledBy(s/Magnitude());344}
345
346Vector Quaternion::RotationU() const {347Vector v;348v.x = w*w + vx*vx - vy*vy - vz*vz;349v.y = 2*w *vz + 2*vx*vy;350v.z = 2*vx*vz - 2*w *vy;351return v;352}
353
354Vector Quaternion::RotationV() const {355Vector v;356v.x = 2*vx*vy - 2*w*vz;357v.y = w*w - vx*vx + vy*vy - vz*vz;358v.z = 2*w*vx + 2*vy*vz;359return v;360}
361
362Vector Quaternion::RotationN() const {363Vector v;364v.x = 2*w*vy + 2*vx*vz;365v.y = 2*vy*vz - 2*w*vx;366v.z = w*w - vx*vx - vy*vy + vz*vz;367return v;368}
369
370Vector Quaternion::Rotate(Vector p) const {371// Express the point in the new basis372return (RotationU().ScaledBy(p.x)).Plus(373RotationV().ScaledBy(p.y)).Plus(374RotationN().ScaledBy(p.z));375}
376
377Quaternion Quaternion::Inverse() const {378Quaternion r;379r.w = w;380r.vx = -vx;381r.vy = -vy;382r.vz = -vz;383return r.WithMagnitude(1); // not that the normalize should be reqd384}
385
386Quaternion Quaternion::ToThe(double p) const {387// Avoid division by zero, or arccos of something not in its domain388if(w >= (1 - 1e-6)) {389return From(1, 0, 0, 0);390} else if(w <= (-1 + 1e-6)) {391return From(-1, 0, 0, 0);392}393
394Quaternion r;395Vector axis = Vector::From(vx, vy, vz);396double theta = acos(w); // okay, since magnitude is 1, so -1 <= w <= 1397theta *= p;398r.w = cos(theta);399axis = axis.WithMagnitude(sin(theta));400r.vx = axis.x;401r.vy = axis.y;402r.vz = axis.z;403return r;404}
405
406Quaternion Quaternion::Times(Quaternion b) const {407double sa = w, sb = b.w;408Vector va = { vx, vy, vz };409Vector vb = { b.vx, b.vy, b.vz };410
411Quaternion r;412r.w = sa*sb - va.Dot(vb);413Vector vr = vb.ScaledBy(sa).Plus(414va.ScaledBy(sb).Plus(415va.Cross(vb)));416r.vx = vr.x;417r.vy = vr.y;418r.vz = vr.z;419return r;420}
421
422Quaternion Quaternion::Mirror() const {423Vector u = RotationU(),424v = RotationV();425u = u.ScaledBy(-1);426v = v.ScaledBy(-1);427return Quaternion::From(u, v);428}
429
430
431Vector Vector::From(hParam x, hParam y, hParam z) {432Vector v;433v.x = SK.GetParam(x)->val;434v.y = SK.GetParam(y)->val;435v.z = SK.GetParam(z)->val;436return v;437}
438
439bool Vector::EqualsExactly(Vector v) const {440return EXACT(x == v.x &&441y == v.y &&442z == v.z);443}
444
445double Vector::DirectionCosineWith(Vector b) const {446Vector a = this->WithMagnitude(1);447b = b.WithMagnitude(1);448return a.Dot(b);449}
450
451Vector Vector::Normal(int which) const {452Vector n;453
454// Arbitrarily choose one vector that's normal to us, pivoting455// appropriately.456double xa = fabs(x), ya = fabs(y), za = fabs(z);457if(this->Equals(Vector::From(0, 0, 1))) {458// Make DXFs exported in the XY plane work nicely...459n = Vector::From(1, 0, 0);460} else if(xa < ya && xa < za) {461n.x = 0;462n.y = z;463n.z = -y;464} else if(ya < za) {465n.x = -z;466n.y = 0;467n.z = x;468} else {469n.x = y;470n.y = -x;471n.z = 0;472}473
474if(which == 0) {475// That's the vector we return.476} else if(which == 1) {477n = this->Cross(n);478} else ssassert(false, "Unexpected vector normal index");479
480n = n.WithMagnitude(1);481
482return n;483}
484
485Vector Vector::RotatedAbout(Vector orig, Vector axis, double theta) const {486Vector r = this->Minus(orig);487r = r.RotatedAbout(axis, theta);488return r.Plus(orig);489}
490
491Vector Vector::RotatedAbout(Vector axis, double theta) const {492double c = cos(theta);493double s = sin(theta);494
495axis = axis.WithMagnitude(1);496
497Vector r;498
499r.x = (x)*(c + (1 - c)*(axis.x)*(axis.x)) +500(y)*((1 - c)*(axis.x)*(axis.y) - s*(axis.z)) +501(z)*((1 - c)*(axis.x)*(axis.z) + s*(axis.y));502
503r.y = (x)*((1 - c)*(axis.y)*(axis.x) + s*(axis.z)) +504(y)*(c + (1 - c)*(axis.y)*(axis.y)) +505(z)*((1 - c)*(axis.y)*(axis.z) - s*(axis.x));506
507r.z = (x)*((1 - c)*(axis.z)*(axis.x) - s*(axis.y)) +508(y)*((1 - c)*(axis.z)*(axis.y) + s*(axis.x)) +509(z)*(c + (1 - c)*(axis.z)*(axis.z));510
511return r;512}
513
514Vector Vector::DotInToCsys(Vector u, Vector v, Vector n) const {515Vector r = {516this->Dot(u),517this->Dot(v),518this->Dot(n)519};520return r;521}
522
523Vector Vector::ScaleOutOfCsys(Vector u, Vector v, Vector n) const {524Vector r = u.ScaledBy(x).Plus(525v.ScaledBy(y).Plus(526n.ScaledBy(z)));527return r;528}
529
530Vector Vector::InPerspective(Vector u, Vector v, Vector n,531Vector origin, double cameraTan) const532{
533Vector r = this->Minus(origin);534r = r.DotInToCsys(u, v, n);535// yes, minus; we are assuming a csys where u cross v equals n, backwards536// from the display stuff537double w = (1 - r.z*cameraTan);538r = r.ScaledBy(1/w);539
540return r;541}
542
543double Vector::DistanceToLine(Vector p0, Vector dp) const {544double m = dp.Magnitude();545return ((this->Minus(p0)).Cross(dp)).Magnitude() / m;546}
547
548double Vector::DistanceToPlane(Vector normal, Vector origin) const {549return this->Dot(normal) - origin.Dot(normal);550}
551
552bool Vector::OnLineSegment(Vector a, Vector b, double tol) const {553if(this->Equals(a, tol) || this->Equals(b, tol)) return true;554
555Vector d = b.Minus(a);556
557double m = d.MagSquared();558double distsq = ((this->Minus(a)).Cross(d)).MagSquared() / m;559
560if(distsq >= tol*tol) return false;561
562double t = (this->Minus(a)).DivProjected(d);563// On-endpoint already tested564if(t < 0 || t > 1) return false;565return true;566}
567
568Vector Vector::ClosestPointOnLine(Vector p0, Vector dp) const {569dp = dp.WithMagnitude(1);570// this, p0, and (p0+dp) define a plane; the min distance is in571// that plane, so calculate its normal572Vector pn = (this->Minus(p0)).Cross(dp);573// The minimum distance line is in that plane, perpendicular574// to the line575Vector n = pn.Cross(dp);576
577// Calculate the actual distance578double d = (dp.Cross(p0.Minus(*this))).Magnitude();579return this->Plus(n.WithMagnitude(d));580}
581
582Vector Vector::WithMagnitude(double v) const {583double m = Magnitude();584if(EXACT(m == 0)) {585// We can do a zero vector with zero magnitude, but not any other cases.586if(fabs(v) > 1e-100) {587dbp("Vector::WithMagnitude(%g) of zero vector!", v);588}589return From(0, 0, 0);590} else {591return ScaledBy(v/m);592}593}
594
595Vector Vector::ProjectVectorInto(hEntity wrkpl) const {596EntityBase *w = SK.GetEntity(wrkpl);597Vector u = w->Normal()->NormalU();598Vector v = w->Normal()->NormalV();599
600double up = this->Dot(u);601double vp = this->Dot(v);602
603return (u.ScaledBy(up)).Plus(v.ScaledBy(vp));604}
605
606Vector Vector::ProjectInto(hEntity wrkpl) const {607EntityBase *w = SK.GetEntity(wrkpl);608Vector p0 = w->WorkplaneGetOffset();609
610Vector f = this->Minus(p0);611
612return p0.Plus(f.ProjectVectorInto(wrkpl));613}
614
615Point2d Vector::Project2d(Vector u, Vector v) const {616Point2d p;617p.x = this->Dot(u);618p.y = this->Dot(v);619return p;620}
621
622Point2d Vector::ProjectXy() const {623Point2d p;624p.x = x;625p.y = y;626return p;627}
628
629Vector4 Vector::Project4d() const {630return Vector4::From(1, x, y, z);631}
632
633double Vector::DivProjected(Vector delta) const {634return (x*delta.x + y*delta.y + z*delta.z)635/ (delta.x*delta.x + delta.y*delta.y + delta.z*delta.z);636}
637
638Vector Vector::ClosestOrtho() const {639double mx = fabs(x), my = fabs(y), mz = fabs(z);640
641if(mx > my && mx > mz) {642return From((x > 0) ? 1 : -1, 0, 0);643} else if(my > mz) {644return From(0, (y > 0) ? 1 : -1, 0);645} else {646return From(0, 0, (z > 0) ? 1 : -1);647}648}
649
650Vector Vector::ClampWithin(double minv, double maxv) const {651Vector ret = *this;652
653if(ret.x < minv) ret.x = minv;654if(ret.y < minv) ret.y = minv;655if(ret.z < minv) ret.z = minv;656
657if(ret.x > maxv) ret.x = maxv;658if(ret.y > maxv) ret.y = maxv;659if(ret.z > maxv) ret.z = maxv;660
661return ret;662}
663
664bool Vector::OutsideAndNotOn(Vector maxv, Vector minv) const {665return (x > maxv.x + LENGTH_EPS) || (x < minv.x - LENGTH_EPS) ||666(y > maxv.y + LENGTH_EPS) || (y < minv.y - LENGTH_EPS) ||667(z > maxv.z + LENGTH_EPS) || (z < minv.z - LENGTH_EPS);668}
669
670bool Vector::BoundingBoxesDisjoint(Vector amax, Vector amin,671Vector bmax, Vector bmin)672{
673int i;674for(i = 0; i < 3; i++) {675if(amax.Element(i) < bmin.Element(i) - LENGTH_EPS) return true;676if(amin.Element(i) > bmax.Element(i) + LENGTH_EPS) return true;677}678return false;679}
680
681bool Vector::BoundingBoxIntersectsLine(Vector amax, Vector amin,682Vector p0, Vector p1, bool asSegment)683{
684Vector dp = p1.Minus(p0);685double lp = dp.Magnitude();686dp = dp.ScaledBy(1.0/lp);687
688int i, a;689for(i = 0; i < 3; i++) {690int j = WRAP(i+1, 3), k = WRAP(i+2, 3);691if(lp*fabs(dp.Element(i)) < LENGTH_EPS) continue; // parallel to plane692
693for(a = 0; a < 2; a++) {694double d = (a == 0) ? amax.Element(i) : amin.Element(i);695// n dot (p0 + t*dp) = d696// (n dot p0) + t * (n dot dp) = d697double t = (d - p0.Element(i)) / dp.Element(i);698Vector p = p0.Plus(dp.ScaledBy(t));699
700if(asSegment && (t < -LENGTH_EPS || t > (lp+LENGTH_EPS))) continue;701
702if(p.Element(j) > amax.Element(j) + LENGTH_EPS) continue;703if(p.Element(k) > amax.Element(k) + LENGTH_EPS) continue;704
705if(p.Element(j) < amin.Element(j) - LENGTH_EPS) continue;706if(p.Element(k) < amin.Element(k) - LENGTH_EPS) continue;707
708return true;709}710}711
712return false;713}
714
715Vector Vector::AtIntersectionOfPlanes(Vector n1, double d1,716Vector n2, double d2)717{
718double det = (n1.Dot(n1))*(n2.Dot(n2)) -719(n1.Dot(n2))*(n1.Dot(n2));720double c1 = (d1*n2.Dot(n2) - d2*n1.Dot(n2))/det;721double c2 = (d2*n1.Dot(n1) - d1*n1.Dot(n2))/det;722
723return (n1.ScaledBy(c1)).Plus(n2.ScaledBy(c2));724}
725
726void Vector::ClosestPointBetweenLines(Vector a0, Vector da,727Vector b0, Vector db,728double *ta, double *tb)729{
730// Make a semi-orthogonal coordinate system from those directions;731// note that dna and dnb need not be perpendicular.732Vector dn = da.Cross(db); // normal to both733Vector dna = dn.Cross(da); // normal to da734Vector dnb = dn.Cross(db); // normal to db735
736// At the intersection of the lines737// a0 + pa*da = b0 + pb*db (where pa, pb are scalar params)738// So dot this equation against dna and dnb to get two equations739// to solve for da and db740*tb = ((a0.Minus(b0)).Dot(dna))/(db.Dot(dna));741*ta = -((a0.Minus(b0)).Dot(dnb))/(da.Dot(dnb));742}
743
744Vector Vector::AtIntersectionOfLines(Vector a0, Vector a1,745Vector b0, Vector b1,746bool *skew,747double *parama, double *paramb)748{
749Vector da = a1.Minus(a0), db = b1.Minus(b0);750
751double pa, pb;752Vector::ClosestPointBetweenLines(a0, da, b0, db, &pa, &pb);753
754if(parama) *parama = pa;755if(paramb) *paramb = pb;756
757// And from either of those, we get the intersection point.758Vector pi = a0.Plus(da.ScaledBy(pa));759
760if(skew) {761// Check if the intersection points on each line are actually762// coincident...763if(pi.Equals(b0.Plus(db.ScaledBy(pb)))) {764*skew = false;765} else {766*skew = true;767}768}769return pi;770}
771
772Vector Vector::AtIntersectionOfPlaneAndLine(Vector n, double d,773Vector p0, Vector p1,774bool *parallel)775{
776Vector dp = p1.Minus(p0);777
778if(fabs(n.Dot(dp)) < LENGTH_EPS) {779if(parallel) *parallel = true;780return Vector::From(0, 0, 0);781}782
783if(parallel) *parallel = false;784
785// n dot (p0 + t*dp) = d786// (n dot p0) + t * (n dot dp) = d787double t = (d - n.Dot(p0)) / (n.Dot(dp));788
789return p0.Plus(dp.ScaledBy(t));790}
791
792static double det2(double a1, double b1,793double a2, double b2)794{
795return (a1*b2) - (b1*a2);796}
797static double det3(double a1, double b1, double c1,798double a2, double b2, double c2,799double a3, double b3, double c3)800{
801return a1*det2(b2, c2, b3, c3) -802b1*det2(a2, c2, a3, c3) +803c1*det2(a2, b2, a3, b3);804}
805Vector Vector::AtIntersectionOfPlanes(Vector na, double da,806Vector nb, double db,807Vector nc, double dc,808bool *parallel)809{
810double det = det3(na.x, na.y, na.z,811nb.x, nb.y, nb.z,812nc.x, nc.y, nc.z);813if(fabs(det) < 1e-10) { // arbitrary tolerance, not so good814*parallel = true;815return Vector::From(0, 0, 0);816}817*parallel = false;818
819double detx = det3(da, na.y, na.z,820db, nb.y, nb.z,821dc, nc.y, nc.z);822
823double dety = det3(na.x, da, na.z,824nb.x, db, nb.z,825nc.x, dc, nc.z);826
827double detz = det3(na.x, na.y, da,828nb.x, nb.y, db,829nc.x, nc.y, dc );830
831return Vector::From(detx/det, dety/det, detz/det);832}
833
834size_t VectorHash::operator()(const Vector &v) const {835const size_t size = (size_t)pow(std::numeric_limits<size_t>::max(), 1.0 / 3.0) - 1;836const double eps = 4.0 * LENGTH_EPS;837
838double x = fabs(v.x) / eps;839double y = fabs(v.y) / eps;840double z = fabs(v.y) / eps;841
842size_t xs = size_t(fmod(x, (double)size));843size_t ys = size_t(fmod(y, (double)size));844size_t zs = size_t(fmod(z, (double)size));845
846return (zs * size + ys) * size + xs;847}
848
849bool VectorPred::operator()(Vector a, Vector b) const {850return a.Equals(b, LENGTH_EPS);851}
852
853Vector4 Vector4::From(double w, double x, double y, double z) {854Vector4 ret;855ret.w = w;856ret.x = x;857ret.y = y;858ret.z = z;859return ret;860}
861
862Vector4 Vector4::From(double w, Vector v) {863return Vector4::From(w, w*v.x, w*v.y, w*v.z);864}
865
866Vector4 Vector4::Blend(Vector4 a, Vector4 b, double t) {867return (a.ScaledBy(1 - t)).Plus(b.ScaledBy(t));868}
869
870Vector4 Vector4::Plus(Vector4 b) const {871return Vector4::From(w + b.w, x + b.x, y + b.y, z + b.z);872}
873
874Vector4 Vector4::Minus(Vector4 b) const {875return Vector4::From(w - b.w, x - b.x, y - b.y, z - b.z);876}
877
878Vector4 Vector4::ScaledBy(double s) const {879return Vector4::From(w*s, x*s, y*s, z*s);880}
881
882Vector Vector4::PerspectiveProject() const {883return Vector::From(x / w, y / w, z / w);884}
885
886Point2d Point2d::From(double x, double y) {887return { x, y };888}
889
890Point2d Point2d::FromPolar(double r, double a) {891return { r * cos(a), r * sin(a) };892}
893
894double Point2d::Angle() const {895double a = atan2(y, x);896return M_PI + remainder(a - M_PI, 2 * M_PI);897}
898
899double Point2d::AngleTo(const Point2d &p) const {900return p.Minus(*this).Angle();901}
902
903Point2d Point2d::Plus(const Point2d &b) const {904return { x + b.x, y + b.y };905}
906
907Point2d Point2d::Minus(const Point2d &b) const {908return { x - b.x, y - b.y };909}
910
911Point2d Point2d::ScaledBy(double s) const {912return { x * s, y * s };913}
914
915double Point2d::DivProjected(Point2d delta) const {916return (x*delta.x + y*delta.y) / (delta.x*delta.x + delta.y*delta.y);917}
918
919double Point2d::MagSquared() const {920return x*x + y*y;921}
922
923double Point2d::Magnitude() const {924return sqrt(x*x + y*y);925}
926
927Point2d Point2d::WithMagnitude(double v) const {928double m = Magnitude();929if(m < 1e-20) {930dbp("!!! WithMagnitude() of zero vector");931return { v, 0 };932}933return { x * v / m, y * v / m };934}
935
936double Point2d::DistanceTo(const Point2d &p) const {937double dx = x - p.x;938double dy = y - p.y;939return sqrt(dx*dx + dy*dy);940}
941
942double Point2d::Dot(Point2d p) const {943return x*p.x + y*p.y;944}
945
946double Point2d::DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const {947double m = dp.x*dp.x + dp.y*dp.y;948if(m < LENGTH_EPS*LENGTH_EPS) return VERY_POSITIVE;949
950// Let our line be p = p0 + t*dp, for a scalar t from 0 to 1951double t = (dp.x*(x - p0.x) + dp.y*(y - p0.y))/m;952
953if(asSegment) {954if(t < 0.0) return DistanceTo(p0);955if(t > 1.0) return DistanceTo(p0.Plus(dp));956}957Point2d closest = p0.Plus(dp.ScaledBy(t));958return DistanceTo(closest);959}
960
961double Point2d::DistanceToLineSigned(const Point2d &p0, const Point2d &dp, bool asSegment) const {962double m = dp.x*dp.x + dp.y*dp.y;963if(m < LENGTH_EPS*LENGTH_EPS) return VERY_POSITIVE;964
965Point2d n = dp.Normal().WithMagnitude(1.0);966double dist = n.Dot(*this) - n.Dot(p0);967if(asSegment) {968// Let our line be p = p0 + t*dp, for a scalar t from 0 to 1969double t = (dp.x*(x - p0.x) + dp.y*(y - p0.y))/m;970double sign = (dist > 0.0) ? 1.0 : -1.0;971if(t < 0.0) return DistanceTo(p0) * sign;972if(t > 1.0) return DistanceTo(p0.Plus(dp)) * sign;973}974
975return dist;976}
977
978Point2d Point2d::Normal() const {979return { y, -x };980}
981
982bool Point2d::Equals(Point2d v, double tol) const {983double dx = v.x - x; if(dx < -tol || dx > tol) return false;984double dy = v.y - y; if(dy < -tol || dy > tol) return false;985
986return (this->Minus(v)).MagSquared() < tol*tol;987}
988
989BBox BBox::From(const Vector &p0, const Vector &p1) {990BBox bbox;991bbox.minp.x = min(p0.x, p1.x);992bbox.minp.y = min(p0.y, p1.y);993bbox.minp.z = min(p0.z, p1.z);994
995bbox.maxp.x = max(p0.x, p1.x);996bbox.maxp.y = max(p0.y, p1.y);997bbox.maxp.z = max(p0.z, p1.z);998return bbox;999}
1000
1001Vector BBox::GetOrigin() const { return minp.Plus(maxp.Minus(minp).ScaledBy(0.5)); }1002Vector BBox::GetExtents() const { return maxp.Minus(minp).ScaledBy(0.5); }1003
1004void BBox::Include(const Vector &v, double r) {1005minp.x = min(minp.x, v.x - r);1006minp.y = min(minp.y, v.y - r);1007minp.z = min(minp.z, v.z - r);1008
1009maxp.x = max(maxp.x, v.x + r);1010maxp.y = max(maxp.y, v.y + r);1011maxp.z = max(maxp.z, v.z + r);1012}
1013
1014bool BBox::Overlaps(const BBox &b1) const {1015Vector t = b1.GetOrigin().Minus(GetOrigin());1016Vector e = b1.GetExtents().Plus(GetExtents());1017
1018return fabs(t.x) < e.x && fabs(t.y) < e.y && fabs(t.z) < e.z;1019}
1020
1021bool BBox::Contains(const Point2d &p, double r) const {1022return p.x >= (minp.x - r) &&1023p.y >= (minp.y - r) &&1024p.x <= (maxp.x + r) &&1025p.y <= (maxp.y + r);1026}
1027
1028const std::vector<double>& SolveSpace::StipplePatternDashes(StipplePattern pattern) {1029static bool initialized;1030static std::vector<double> dashes[(size_t)StipplePattern::LAST + 1];1031if(!initialized) {1032// Inkscape ignores all elements that are exactly zero instead of drawing1033// them as dots, so set those to 1e-6.1034dashes[(size_t)StipplePattern::CONTINUOUS] =1035{};1036dashes[(size_t)StipplePattern::SHORT_DASH] =1037{ 1.0, 2.0 };1038dashes[(size_t)StipplePattern::DASH] =1039{ 1.0, 1.0 };1040dashes[(size_t)StipplePattern::DASH_DOT] =1041{ 1.0, 0.5, 1e-6, 0.5 };1042dashes[(size_t)StipplePattern::DASH_DOT_DOT] =1043{ 1.0, 0.5, 1e-6, 0.5, 1e-6, 0.5 };1044dashes[(size_t)StipplePattern::DOT] =1045{ 1e-6, 0.5 };1046dashes[(size_t)StipplePattern::LONG_DASH] =1047{ 2.0, 0.5 };1048dashes[(size_t)StipplePattern::FREEHAND] =1049{ 1.0, 2.0 };1050dashes[(size_t)StipplePattern::ZIGZAG] =1051{ 1.0, 2.0 };1052}1053
1054return dashes[(size_t)pattern];1055}
1056
1057double SolveSpace::StipplePatternLength(StipplePattern pattern) {1058static bool initialized;1059static double lengths[(size_t)StipplePattern::LAST + 1];1060if(!initialized) {1061for(size_t i = 0; i < (size_t)StipplePattern::LAST; i++) {1062const std::vector<double> &dashes = StipplePatternDashes((StipplePattern)i);1063double length = 0.0;1064for(double dash : dashes) {1065length += dash;1066}1067lengths[i] = length;1068}1069}1070
1071return lengths[(size_t)pattern];1072}
1073