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