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