PDF SDK Documentation

Comprehensive Guide for Developers: Features, Integration, and API Reference

Loading...
Searching...
No Matches
math.h
1// Copyright (c) 2009-2025 Avanquest Software. All rights reserved.
2
3#ifndef PDFSDK_CXX_MATH_H_INCLUDED_
4#define PDFSDK_CXX_MATH_H_INCLUDED_
5
6#include <algorithm>
7#include <cassert>
8#include <cfloat>
9#include <cmath>
10#include <limits>
11#include <optional>
12#include <vector>
13#include <list>
14
15#include <pdfsdk/cxx/exception.h>
16#include <pdfsdk/math_types.h>
17
18namespace PDF {
19
20namespace Math {
21
22inline constexpr float EPSILON = 0.0001f;
23
24inline constexpr bool FloatLt(float a, float b, float epsilon = EPSILON) {
25 return a < b - epsilon;
26}
27inline constexpr bool FloatGt(float a, float b, float epsilon = EPSILON) {
28 return a > b + epsilon;
29}
30inline constexpr bool FloatEq(float a, float b, float epsilon = EPSILON) {
31 return a >= b - epsilon && a <= b + epsilon;
32}
33inline constexpr bool FloatInRange(float value, float lowerBound, float upperBound, float epsilon = EPSILON) {
34 return !FloatLt(value, lowerBound, epsilon) && !FloatGt(value, upperBound, epsilon);
35}
36
37inline constexpr double PI = 3.14159265358979323846;
38
39inline constexpr float RadianToDegree(float radians) {
40 return (radians * static_cast<float>(180.0 / PI));
41}
42inline constexpr double RadianToDegree(double radians) {
43 return (radians * (180.0 / PI));
44}
45
46inline constexpr float DegreeToRadian(float degrees) {
47 return (degrees * static_cast<float>(PI / 180.0));
48}
49inline constexpr double DegreeToRadian(double degrees) {
50 return (degrees * (PI / 180.0));
51}
52}
53
54struct PointI : public PDPointI {
55 PointI() {
56 x = 0;
57 y = 0;
58 }
59
60 PointI(int X_, int Y_) {
61 x = X_;
62 y = Y_;
63 }
64
65 template<typename XT, typename YT>
66 PointI(XT X_,
67 YT Y_,
68 const typename std::enable_if<std::is_integral<XT>::value && std::is_integral<YT>::value>::type* const
69 dummy = nullptr) {
70 x = static_cast<int>(X_);
71 y = static_cast<int>(Y_);
72 }
73
74 PointI(const PDPointI& that) {
75 x = that.x;
76 y = that.y;
77 }
78
79 bool Equals(const PointI& that) const { return x == that.x && y == that.y; }
80
81 bool operator==(const PointI& that) const { return Equals(that); }
82 bool operator!=(const PointI& that) const { return !Equals(that); }
83
84 PointI operator-() const { return PointI(-x, -y); }
85
86 void Offset(int dx, int dy) {
87 x += dx;
88 y += dy;
89 }
90
91 void Mul(int num) {
92 x *= num;
93 y *= num;
94 }
95
96 void Div(int num) {
97 x /= num;
98 y /= num;
99 }
100
101 PointI& operator+=(const PointI& that) {
102 Offset(that.x, that.y);
103 return *this;
104 }
105
106 PointI& operator-=(const PointI& that) {
107 Offset(-that.x, -that.y);
108 return *this;
109 }
110
111 PointI& operator*=(int num) {
112 Mul(num);
113 return *this;
114 }
115
116 PointI& operator/=(int num) {
117 Div(num);
118 return *this;
119 }
120
121 PointI operator+(const PointI& that) { return PointI(x + that.x, y + that.y); }
122 PointI operator-(const PointI& that) { return PointI(x - that.x, y - that.y); }
123
124 PointI operator*(int num) const { return PointI(x * num, y * num); }
125 friend PointI operator*(int num, const PointI& point) { return point * num; }
126
127 PointI operator/(int num) const { return PointI(x / num, y / num); }
128
129 static int Distance(const PointI& a, const PointI& b) { return std::abs(b.x - a.x) + std::abs(b.y - a.y); }
130};
131
132struct PointF : public PDPointF {
133 PointF() {
134 x = 0.f;
135 y = 0.f;
136 }
137
138 PointF(float X_, float Y_) {
139 x = X_;
140 y = Y_;
141 }
142
143 PointF(const PDPointF& that) {
144 x = that.x;
145 y = that.y;
146 }
147
148 PointF(const PDPointI& pointi) {
149 x = static_cast<float>(pointi.x);
150 y = static_cast<float>(pointi.y);
151 }
152
153 PointI Round() const { return PointI(static_cast<int>(std::round(x)), static_cast<int>(std::round(y))); }
154 PointI Floor() const { return PointI(static_cast<int>(std::floor(x)), static_cast<int>(std::floor(y))); }
155 PointI Ceil() const { return PointI(static_cast<int>(std::ceil(x)), static_cast<int>(std::ceil(y))); }
156
157 bool Equals(const PointF& that) const { return Math::FloatEq(x, that.x) && Math::FloatEq(y, that.y); }
158
159 bool operator==(const PointF& that) const { return Equals(that); }
160 bool operator!=(const PointF& that) const { return !Equals(that); }
161
162 PointF operator-() const { return PointF{-x, -y}; }
163
164 void Offset(float dx, float dy) {
165 x += dx;
166 y += dy;
167 }
168
169 static PointF Offset(const PointF& point, float dx, float dy) {
170 PointF result = point;
171 result.Offset(dx, dy);
172 return result;
173 }
174
175 void Mul(float num) {
176 x *= num;
177 y *= num;
178 }
179
180 void Div(float num) {
181 x /= num;
182 y /= num;
183 }
184
185 PointF& operator+=(const PointF& that) {
186 Offset(that.x, that.y);
187 return *this;
188 }
189
190 PointF& operator-=(const PointF& that) {
191 Offset(-that.x, -that.y);
192 return *this;
193 }
194
195 PointF& operator*=(float num) {
196 Mul(num);
197 return *this;
198 }
199
200 PointF& operator/=(float num) {
201 Div(num);
202 return *this;
203 }
204
205 PointF operator+(const PointF& that) const { return PointF(x + that.x, y + that.y); }
206 PointF operator-(const PointF& that) const { return PointF(x - that.x, y - that.y); }
207
208 PointF operator*(float num) const { return PointF(x * num, y * num); }
209 friend PointF operator*(float num, const PointF& point) { return point * num; }
210
211 PointF operator/(float num) const { return PointF(x / num, y / num); }
212
213 static float DistanceSquared(const PointF& a, const PointF& b) { return (b - a).VectorMagnitudeSquared(); }
214 static float Distance(const PointF& a, const PointF& b) { return (b - a).VectorMagnitude(); }
215
216 static PointF AtCenter(const PointF& a, const PointF& b) { return (a + b) / 2.0f; }
217
218 float DistanceToEdge(const PointF& a, const PointF& b) const {
219 PointF p = *this;
220 float d = DistanceSquared(a, b);
221 if (Math::FloatEq(d, 0.f))
222 return Distance(p, a);
223 float t = VectorDotProduct(p - a, b - a) / d;
224 if (Math::FloatLt(t, 0.f))
225 return Distance(p, a);
226 if (Math::FloatGt(t, 1.f))
227 return Distance(p, b);
228 return Distance(p, a + t * (b - a));
229 }
230
231 float VectorMagnitudeSquared() const { return x * x + y * y; }
232 float VectorMagnitude() const { return std::sqrt(VectorMagnitudeSquared()); }
233
240 float TestSide(const PointF& a, const PointF& b) const { return VectorCrossProduct(b - a, *this - a); }
241
242 static float VectorDotProduct(const PointF& a, const PointF& b) { return a.x * b.x + a.y * b.y; }
243 static float VectorCrossProduct(const PointF& a, const PointF& b) { return a.x * b.y - b.x * a.y; }
244
245 static float VectorAngleBetween(const PointF& a, const PointF& b) {
246 return std::atan2(b.y, b.x) - std::atan2(a.y, a.x);
247 }
248
249 PointF& VectorNormalize() {
250 float mag = VectorMagnitude();
251 if (!Math::FloatEq(mag, 0.f))
252 Div(mag);
253 return *this;
254 }
255
256 static PointF VectorNormalTo(const PointF& v) { return PointF(-v.y, v.x); }
257};
258
259namespace Math {
260inline bool EdgeIntersectsEdge(const PointF& a0, const PointF& a1, const PointF& b0, const PointF& b1) {
261 auto va = a1 - a0;
262 auto vb = b1 - b0;
263 float d = PointF::VectorCrossProduct(va, vb);
264 if (Math::FloatEq(d, 0.f))
265 return false;
266
267 auto vc = a0 - b0;
268 float r = PointF::VectorCrossProduct(vb, vc) / d;
269 if (Math::FloatLt(r, 0.f) || Math::FloatGt(r, 1.f))
270 return false;
271 float s = PointF::VectorCrossProduct(va, vc) / d;
272 if (Math::FloatLt(s, 0.f) || Math::FloatGt(s, 1.f))
273 return false;
274
275 return true;
276}
277}
278
279struct SizeI : public PDSizeI {
280 SizeI() {
281 width = 0;
282 height = 0;
283 }
284
285 SizeI(int width_, int height_) {
286 width = width_;
287 height = height_;
288 }
289
290 template<typename WT, typename HT>
291 SizeI(WT width_,
292 HT height_,
293 const typename std::enable_if<std::is_integral<WT>::value && std::is_integral<HT>::value>::type* const
294 dummy = nullptr) {
295 width = static_cast<int>(width_);
296 height = static_cast<int>(height_);
297 }
298
299 SizeI(const PDSizeI& sz) {
300 width = sz.width;
301 height = sz.height;
302 }
303
304 bool Equals(const SizeI& that) const { return width == that.width && height == that.height; }
305
306 bool operator==(const SizeI& that) const { return Equals(that); }
307 bool operator!=(const SizeI& that) const { return !Equals(that); }
308
309 int GetArea() const { return width * height; }
310 bool IsAreaEmpty() const { return width == 0 || height == 0; }
311};
312
313struct SizeF : public PDSizeF {
314 SizeF() {
315 width = 0.f;
316 height = 0.f;
317 }
318
319 SizeF(float width_, float height_) {
320 width = width_;
321 height = height_;
322 }
323
324 SizeF(const PDSizeF& that) {
325 width = that.width;
326 height = that.height;
327 }
328
329 SizeF(const PDSizeI& sizei) {
330 width = static_cast<float>(sizei.width);
331 height = static_cast<float>(sizei.height);
332 }
333
334 SizeI Round() const { return SizeI(static_cast<int>(std::round(width)), static_cast<int>(std::round(height))); }
335 SizeI Floor() const { return SizeI(static_cast<int>(std::floor(width)), static_cast<int>(std::floor(height))); }
336 SizeI Ceil() const { return SizeI(static_cast<int>(std::ceil(width)), static_cast<int>(std::ceil(height))); }
337
338 bool Equals(const SizeF& that) const { return width == that.width && height == that.height; }
339
340 bool operator==(const SizeF& that) const { return Equals(that); }
341 bool operator!=(const SizeF& that) const { return !Equals(that); }
342
343 float GetArea() const { return width * height; }
344 bool IsAreaEmpty() const { return width == 0.f || height == 0.f; }
345};
346
347struct RectI : public PDRectI {
348 RectI() {
349 left = 0;
350 top = 0;
351 right = 0;
352 bottom = 0;
353 }
354
355 RectI(int left_, int top_, int right_, int bottom_) {
356 left = left_;
357 top = top_;
358 right = right_;
359 bottom = bottom_;
360 }
361
362 explicit RectI(const SizeI& size) {
363 left = 0;
364 top = 0;
365 right = size.width;
366 bottom = size.height;
367 }
368
369 RectI(const PointI& origin, const SizeI& size) {
370 left = origin.x;
371 top = origin.y;
372 right = origin.x + size.width;
373 bottom = origin.y + size.height;
374 }
375
376 RectI(const PointI& leftTop, const PointI& rightBottom) {
377 left = leftTop.x;
378 top = leftTop.y;
379 right = rightBottom.x;
380 bottom = rightBottom.y;
381 }
382
383 RectI(const PDRectI& that) {
384 left = that.left;
385 top = that.top;
386 right = that.right;
387 bottom = that.bottom;
388 }
389
390 PointI LeftTop() const { return PointI(left, top); }
391 PointI LeftBottom() const { return PointI(left, bottom); }
392 PointI RightTop() const { return PointI(right, top); }
393 PointI RightBottom() const { return PointI(right, bottom); }
394
395 void SortForPageSpace() {
396 if (left > right)
397 std::swap(left, right);
398 if (bottom > top)
399 std::swap(top, bottom);
400 }
401
402 void SortForDeviceSpace() {
403 if (left > right)
404 std::swap(left, right);
405 if (top > bottom)
406 std::swap(top, bottom);
407 }
408
409#ifndef SWIG
410 int& MinX() { return (left <= right ? left : right); }
411 int& MaxX() { return (left > right ? left : right); }
412 int& MinY() { return (top <= bottom ? top : bottom); }
413 int& MaxY() { return (top > bottom ? top : bottom); }
414#endif
415
416 int MinX() const { return (std::min)(left, right); }
417 int MaxX() const { return (std::max)(left, right); }
418 int MinY() const { return (std::min)(top, bottom); }
419 int MaxY() const { return (std::max)(top, bottom); }
420
421 bool Equals(const RectI& that) const {
422 if (this == &that)
423 return true;
424 return MinX() == that.MinX() && MaxX() == that.MaxX() && MinY() == that.MinY() && MaxY() == that.MaxY();
425 }
426
427 bool operator==(const RectI& that) const { return Equals(that); }
428 bool operator!=(const RectI& that) const { return !Equals(that); }
429
430 int GetWidth() const { return std::abs(right - left); }
431 int GetHeight() const { return std::abs(top - bottom); }
432 SizeI GetSize() const { return SizeI(GetWidth(), GetHeight()); }
433 PointI GetOrigin() const { return PointI(MinX(), MinY()); }
434 PointI GetCenter() const { return PointI(MinX() + GetWidth() / 2, MinY() + GetHeight() / 2); }
435
436 int GetArea() const { return GetWidth() * GetHeight(); }
437 bool IsAreaEmpty() const { return left == right || top == bottom; }
438
439 void Offset(int dx, int dy) {
440 left += dx;
441 top += dy;
442 right += dx;
443 bottom += dy;
444 }
445
446 void Offset(const PointI& delta) { Offset(delta.x, delta.y); }
447
448 static RectI Offset(const RectI& rect, int dx, int dy) {
449 RectI result = rect;
450 result.Offset(dx, dy);
451 return result;
452 }
453
454 static RectI Offset(const RectI& rect, const PointI& delta) { return Offset(rect, delta.x, delta.y); }
455
456 RectI& operator+=(const PointI& point) {
457 Offset(point);
458 return *this;
459 }
460
461 RectI& operator-=(const PointI& point) {
462 Offset(-point);
463 return *this;
464 }
465
466 RectI operator+(const PointI& point) const { return Offset(*this, point); }
467 RectI operator-(const PointI& point) const { return Offset(*this, -point); }
468
469 void Inflate(int dx, int dy) {
470 auto& minX = MinX();
471 auto& minY = MinY();
472 auto& maxX = MaxX();
473 auto& maxY = MaxY();
474 minX -= dx;
475 maxX += dx;
476 minY -= dy;
477 maxY += dy;
478 }
479
480 void Inflate(int delta) { return Inflate(delta, delta); }
481
482 static RectI Inflate(const RectI& rect, int dx, int dy) {
483 RectI result = rect;
484 result.Inflate(dx, dy);
485 return result;
486 }
487
488 static RectI Inflate(const RectI& rect, int delta) { return Inflate(rect, delta, delta); }
489
490 void Extend(const PointI& point) {
491 MinX() = (std::min)(MinX(), point.x);
492 MinY() = (std::min)(MinY(), point.y);
493 MaxX() = (std::max)(MaxX(), point.x);
494 MaxY() = (std::max)(MaxY(), point.y);
495 }
496
497 RectI Extend(const PointI& point) const {
498 RectI result = *this;
499 result.Extend(point);
500 return result;
501 }
502
503 bool Contains(const PointI& point) const {
504 return point.x >= MinX() && point.x < MaxX() && point.y >= MinY() && point.y < MaxY();
505 }
506
507 bool Contains(const RectI& rect) const {
508 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
509 }
510
511 bool HasIntersection(const RectI& rect) const {
512 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
513 }
514
515 static RectI Intersect(const RectI& a, const RectI& b) {
516 if (!a.HasIntersection(b))
517 return RectI();
518
519 RectI i = a;
520 i.MinX() = (std::max)(a.MinX(), b.MinX());
521 i.MinY() = (std::max)(a.MinY(), b.MinY());
522 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
523 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
524 return i;
525 }
526
527 static RectI Union(const RectI& a, const RectI& b) {
528 RectI u = a;
529 u.MinX() = (std::min)(a.MinX(), b.MinX());
530 u.MinY() = (std::min)(a.MinY(), b.MinY());
531 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
532 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
533 return u;
534 }
535
536 static RectI UnionNonEmpty(const RectI& a, const RectI& b) {
537 if (a.IsAreaEmpty())
538 return b;
539 if (b.IsAreaEmpty())
540 return a;
541 return Union(a, b);
542 }
543};
544
545struct RectF : public PDRectF {
546 RectF() {
547 left = 0.f;
548 top = 0.f;
549 right = 0.f;
550 bottom = 0.f;
551 }
552
553 RectF(float left_, float top_, float right_, float bottom_) {
554 left = left_;
555 top = top_;
556 right = right_;
557 bottom = bottom_;
558 }
559
560 RectF(const PDRectF& rect) {
561 left = rect.left;
562 top = rect.top;
563 right = rect.right;
564 bottom = rect.bottom;
565 }
566
567 explicit RectF(const SizeF& size) {
568 left = 0.f;
569 top = size.height;
570 right = size.width;
571 bottom = 0.f;
572 }
573
574 RectF(const PointF& origin, const SizeF& size) {
575 left = origin.x;
576 top = origin.y + size.height;
577 right = origin.x + size.width;
578 bottom = origin.y;
579 }
580
581 RectF(const PointF& leftTop, const PointF& rightBottom) {
582 left = leftTop.x;
583 top = leftTop.y;
584 right = rightBottom.x;
585 bottom = rightBottom.y;
586 }
587
588 RectF(const RectI& recti) {
589 left = static_cast<float>(recti.left);
590 top = static_cast<float>(recti.top);
591 right = static_cast<float>(recti.right);
592 bottom = static_cast<float>(recti.bottom);
593 }
594
595#ifndef SWIG
596 template<class PointsIter>
597 static RectF EnclosingPoints(const PointsIter& pointsBegin, const PointsIter& pointsEnd) {
598 RectF rect;
599 auto it = pointsBegin;
600 if (it != pointsEnd) {
601 rect = RectF(*it, *it);
602 while (++it != pointsEnd)
603 rect.Extend(*it);
604 }
605 return rect;
606 }
607
608 template<class PointsContainer>
609 static RectF EnclosingPoints(const PointsContainer& points) {
610 return EnclosingPoints(std::begin(points), std::end(points));
611 }
612
613 static RectF EnclosingPoints(const std::initializer_list<PointF>& points) {
614 return EnclosingPoints(std::begin(points), std::end(points));
615 }
616#endif
617
618 RectI Round() const {
619 return RectI(static_cast<int>(std::round(MinX())),
620 static_cast<int>(std::round(MinY())),
621 static_cast<int>(std::round(MaxX())),
622 static_cast<int>(std::round(MaxY())));
623 }
624
625 RectI Floor() const {
626 return RectI(static_cast<int>(std::ceil(MinX())),
627 static_cast<int>(std::ceil(MinY())),
628 static_cast<int>(std::floor(MaxX())),
629 static_cast<int>(std::floor(MaxY())));
630 }
631
632 RectI Ceil() const {
633 return RectI(static_cast<int>(std::floor(MinX())),
634 static_cast<int>(std::floor(MinY())),
635 static_cast<int>(std::ceil(MaxX())),
636 static_cast<int>(std::ceil(MaxY())));
637 }
638
639 PointF LeftTop() const { return PointF(left, top); }
640 PointF LeftBottom() const { return PointF(left, bottom); }
641 PointF RightTop() const { return PointF(right, top); }
642 PointF RightBottom() const { return PointF(right, bottom); }
643
644 static RectF SortForPageSpace(const RectF& rect) {
645 RectF result = rect;
646 result.SortForPageSpace();
647 return result;
648 }
649 void SortForPageSpace() {
650 if (left > right)
651 std::swap(left, right);
652 if (bottom > top)
653 std::swap(top, bottom);
654 }
655
656 static RectF SortForDeviceSpace(const RectF& rect) {
657 RectF result = rect;
658 result.SortForDeviceSpace();
659 return result;
660 }
661 void SortForDeviceSpace() {
662 if (left > right)
663 std::swap(left, right);
664 if (top > bottom)
665 std::swap(top, bottom);
666 }
667
668#ifndef SWIG
669 float& MinX() { return (left <= right ? left : right); }
670 float& MaxX() { return (left > right ? left : right); }
671 float& MinY() { return (top <= bottom ? top : bottom); }
672 float& MaxY() { return (top > bottom ? top : bottom); }
673#endif
674
675 float MinX() const { return (std::min)(left, right); }
676 float MaxX() const { return (std::max)(left, right); }
677 float MinY() const { return (std::min)(top, bottom); }
678 float MaxY() const { return (std::max)(top, bottom); }
679
680 bool Equals(const RectF& that) const {
681 if (this == &that)
682 return true;
683 return MinX() == that.MinX() && MaxX() == that.MaxX() && MinY() == that.MinY() && MaxY() == that.MaxY();
684 }
685
686 bool operator==(const RectF& that) const { return Equals(that); }
687 bool operator!=(const RectF& that) const { return !Equals(that); }
688
689 float GetWidth() const { return std::abs(right - left); }
690 float GetHeight() const { return std::abs(top - bottom); }
691 SizeF GetSize() const { return SizeF(GetWidth(), GetHeight()); }
692 PointF GetOrigin() const { return PointF(MinX(), MinY()); }
693 PointF GetCenter() const { return PointF(MinX() + GetWidth() / 2.f, MinY() + GetHeight() / 2.f); }
694
695 PointF LeftCenter() const { return PointF::AtCenter(LeftBottom(), LeftTop()); }
696 PointF CenterTop() const { return PointF::AtCenter(LeftTop(), RightTop()); }
697 PointF RightCenter() const { return PointF::AtCenter(RightBottom(), RightTop()); }
698 PointF CenterBottom() const { return PointF::AtCenter(LeftBottom(), RightBottom()); }
699
700 float GetArea() const { return GetWidth() * GetHeight(); }
701 bool IsAreaEmpty() const { return Math::FloatEq(left, right) || Math::FloatEq(top, bottom); }
702
703 void Offset(float dx, float dy) {
704 left += dx;
705 top += dy;
706 right += dx;
707 bottom += dy;
708 }
709 void Offset(const PointF& delta) { Offset(delta.x, delta.y); }
710 static RectF Offset(const RectF& rect, float dx, float dy) {
711 RectF result = rect;
712 result.Offset(dx, dy);
713 return result;
714 }
715 static RectF Offset(const RectF& rect, const PointF& delta) { return Offset(rect, delta.x, delta.y); }
716
717 void MoveTo(float x, float y) { Offset(x - MinX(), y - MinY()); }
718 void MoveTo(const PointF& point) { MoveTo(point.x, point.y); }
719
720 static RectF MoveTo(const RectF& rect, float x, float y) {
721 RectF result = rect;
722 result.MoveTo(x, y);
723 return result;
724 }
725 static RectF MoveTo(const RectF& rect, const PointF& point) { return MoveTo(rect, point.x, point.y); }
726
727 RectF& operator+=(const PointF& point) {
728 Offset(point);
729 return *this;
730 }
731 RectF& operator-=(const PointF& point) {
732 Offset(-point);
733 return *this;
734 }
735 RectF operator+(const PointF& point) const { return (RectF(*this) += point); }
736 RectF operator-(const PointF& point) const { return (RectF(*this) -= point); }
737
738 void Inflate(float dx, float dy) {
739 auto& minX = MinX();
740 auto& minY = MinY();
741 auto& maxX = MaxX();
742 auto& maxY = MaxY();
743 minX -= dx;
744 maxX += dx;
745 minY -= dy;
746 maxY += dy;
747 }
748 void Inflate(float delta) { Inflate(delta, delta); }
749 static RectF Inflate(const RectF& rect, float dx, float dy) {
750 RectF result = rect;
751 result.Inflate(dx, dy);
752 return result;
753 }
754 static RectF Inflate(const RectF& rect, float delta) { return Inflate(rect, delta, delta); }
755
756 void Extend(const PointF& point) {
757 float& minX = MinX();
758 float& minY = MinY();
759 float& maxX = MaxX();
760 float& maxY = MaxY();
761 minX = (std::min)(minX, point.x);
762 minY = (std::min)(minY, point.y);
763 maxX = (std::max)(maxX, point.x);
764 maxY = (std::max)(maxY, point.y);
765 }
766 static RectF Extend(const RectF& rect, const PointF& point) {
767 RectF result = rect;
768 result.Extend(point);
769 return result;
770 }
771
772 bool Contains(const PointF& point) const {
773 return point.x >= MinX() && point.x <= MaxX() && point.y >= MinY() && point.y <= MaxY();
774 }
775 bool Contains(const RectF& rect) const {
776 return MinX() <= rect.MinX() && MaxX() >= rect.MaxX() && MinY() <= rect.MinY() && MaxY() >= rect.MaxY();
777 }
778
779 bool HasIntersectionWithEdge(const PointF& a, const PointF& b) const {
780 return (Contains(a) ||
781 Contains(b) ||
782 Math::EdgeIntersectsEdge(LeftTop(), RightTop(), a, b) ||
783 Math::EdgeIntersectsEdge(RightTop(), RightBottom(), a, b) ||
784 Math::EdgeIntersectsEdge(RightBottom(), LeftBottom(), a, b) ||
785 Math::EdgeIntersectsEdge(LeftBottom(), LeftTop(), a, b));
786 }
787 bool HasIntersection(const RectF& rect) const {
788 return MinX() < rect.MaxX() && MinY() < rect.MaxY() && MaxX() > rect.MinX() && MaxY() > rect.MinY();
789 }
790
791 static RectF Intersect(const RectF& a, const RectF& b) {
792 if (!a.HasIntersection(b))
793 return RectF();
794
795 RectF i = a;
796 i.MinX() = (std::max)(a.MinX(), b.MinX());
797 i.MinY() = (std::max)(a.MinY(), b.MinY());
798 i.MaxX() = (std::min)(a.MaxX(), b.MaxX());
799 i.MaxY() = (std::min)(a.MaxY(), b.MaxY());
800 return i;
801 }
802
803 static RectF Union(const RectF& a, const RectF& b) {
804 RectF u = a;
805 u.MinX() = (std::min)(a.MinX(), b.MinX());
806 u.MinY() = (std::min)(a.MinY(), b.MinY());
807 u.MaxX() = (std::max)(a.MaxX(), b.MaxX());
808 u.MaxY() = (std::max)(a.MaxY(), b.MaxY());
809 return u;
810 }
811
812 static RectF UnionNonEmpty(const RectF& a, const RectF& b) {
813 if (a.IsAreaEmpty())
814 return b;
815 if (b.IsAreaEmpty())
816 return a;
817 return Union(a, b);
818 }
819
820 static std::list<RectF> Subtract(const RectF& minuend, const RectF& subtrahend) {
821 if (!minuend.HasIntersection(subtrahend)) {
822 return {minuend}; // no intersection, return original minuend as is
823 }
824 RectF intersection = RectF::Intersect(minuend, subtrahend);
825 std::list<RectF> result;
826 if (minuend.MinY() < intersection.MinY())
827 result.emplace_back(RectF(minuend.MinX(), minuend.MinY(), minuend.MaxX(), intersection.MinY()));
828 if (minuend.MaxY() > intersection.MaxY())
829 result.emplace_back(RectF(minuend.MinX(), intersection.MaxY(), minuend.MaxX(), minuend.MaxY()));
830 if (minuend.MinX() < intersection.MinX())
831 result.emplace_back(RectF(minuend.MinX(), intersection.MinY(), intersection.MinX(), intersection.MaxY()));
832 if (minuend.MaxX() > intersection.MaxX())
833 result.emplace_back(RectF(intersection.MaxX(), intersection.MinY(), minuend.MaxX(), intersection.MaxY()));
834 return result;
835 }
836};
837
838struct Quad : public PDQuad {
839 Quad() {
840 topleft = PointF();
841 topright = PointF();
842 botleft = PointF();
843 botright = PointF();
844 }
845
846 Quad(const PDQuad& that) {
847 topleft = that.topleft;
848 topright = that.topright;
849 botleft = that.botleft;
850 botright = that.botright;
851 }
852
853 Quad(const PointF& leftTop_, const PointF& rightTop_, const PointF& leftBottom_, const PointF& rightBottom_) {
854 topleft = leftTop_;
855 topright = rightTop_;
856 botleft = leftBottom_;
857 botright = rightBottom_;
858 }
859
860 explicit Quad(const PDRectF& rect) {
861 topleft = PointF(rect.left, rect.top);
862 topright = PointF(rect.right, rect.top);
863 botleft = PointF(rect.left, rect.bottom);
864 botright = PointF(rect.right, rect.bottom);
865 }
866
867 PointF LeftTop() const { return topleft; }
868 PointF RightTop() const { return topright; }
869 PointF LeftBottom() const { return botleft; }
870 PointF RightBottom() const { return botright; }
871
872 PointF CenterTop() const { return PointF::AtCenter(topleft, topright); }
873 PointF LeftCenter() const { return PointF::AtCenter(topleft, botleft); }
874 PointF CenterBottom() const { return PointF::AtCenter(botleft, botright); }
875 PointF RightCenter() const { return PointF::AtCenter(botright, topright); }
876
877 PointF GetCenter() const { return PointF::AtCenter(PointF::AtCenter(topleft, botright), PointF::AtCenter(topright, botleft)); }
878 RectF GetBound() const {
879 auto [minx, maxx] = std::minmax({topleft.x, topright.x, botleft.x, botright.x});
880 auto [miny, maxy] = std::minmax({topleft.y, topright.y, botleft.y, botright.y});
881 return RectF(minx, maxy, maxx, miny);
882 }
883
884 float GetRotationAngle() const { return std::atan2(topright.y - topleft.y, topright.x - topleft.x); }
885
886 bool IsRectangle() const {
887 return (std::min)(topleft.x, botright.x) == (std::min)(botleft.x, topright.x) &&
888 (std::max)(topleft.x, botright.x) == (std::max)(botleft.x, topright.x) &&
889 (std::min)(topleft.y, botright.y) == (std::min)(botleft.y, topright.y) &&
890 (std::max)(topleft.y, botright.y) == (std::max)(botleft.y, topright.y);
891 }
892
893 bool Equals(const Quad& that) const {
894 if (this == &that)
895 return true;
896 return (LeftTop() == that.LeftTop()) &&
897 (RightTop() == that.RightTop()) &&
898 (LeftBottom() == that.LeftBottom()) &&
899 (RightBottom() == that.RightBottom());
900 }
901
902 bool operator==(const Quad& that) const { return Equals(that); }
903 bool operator!=(const Quad& that) const { return !Equals(that); }
904
905 void Offset(float dx, float dy) {
906 topleft.x += dx;
907 topleft.y += dy;
908 topright.x += dx;
909 topright.y += dy;
910 botleft.x += dx;
911 botleft.y += dy;
912 botright.x += dx;
913 botright.y += dy;
914 }
915
916 void Offset(const PointF& point) { Offset(point.x, point.y); }
917
918 static Quad Offset(const Quad& quad, float dx, float dy) {
919 Quad result = quad;
920 result.Offset(dx, dy);
921 return result;
922 }
923
924 static Quad Offset(const Quad& quad, const PointF& delta) { return Offset(quad, delta.x, delta.y); }
925
926 Quad& operator+=(const PointF& point) {
927 Offset(point);
928 return *this;
929 }
930
931 Quad& operator-=(const PointF& point) {
932 Offset(-point);
933 return *this;
934 }
935
936 Quad operator+(const PointF& point) const { return (Quad(*this) += point); }
937 Quad operator-(const PointF& point) const { return (Quad(*this) -= point); }
938
939 bool Contains(const PointF& point) const {
940 RectF bound = GetBound();
941 if (!bound.Contains(point))
942 return false;
943
944 if (point == topleft || point == topright || point == botleft || point == botright)
945 return true;
946
947 if (Math::FloatEq(0.f, point.DistanceToEdge(topleft, topright)) ||
948 Math::FloatEq(0.f, point.DistanceToEdge(topright, botright)) ||
949 Math::FloatEq(0.f, point.DistanceToEdge(botright, botleft)) ||
950 Math::FloatEq(0.f, point.DistanceToEdge(botleft, topleft)))
951 return true;
952
953 int winding = 0;
954 winding += (point.TestSide(topleft, topright) > 0) ? 1 : -1;
955 winding += (point.TestSide(topright, botright) > 0) ? 1 : -1;
956 winding += (point.TestSide(botright, botleft) > 0) ? 1 : -1;
957 winding += (point.TestSide(botleft, topleft) > 0) ? 1 : -1;
958 return winding != 0;
959 }
960
961 bool Contains(const Quad& that) const {
962 return Contains(that.LeftTop()) &&
963 Contains(that.RightTop()) &&
964 Contains(that.LeftBottom()) &&
965 Contains(that.RightBottom());
966 }
967
968 bool HasIntersectionWithEdge(const PointF& a, const PointF& b) const {
969 return (Contains(a) ||
970 Contains(b) ||
971 Math::EdgeIntersectsEdge(LeftTop(), RightTop(), a, b) ||
972 Math::EdgeIntersectsEdge(RightTop(), RightBottom(), a, b) ||
973 Math::EdgeIntersectsEdge(RightBottom(), LeftBottom(), a, b) ||
974 Math::EdgeIntersectsEdge(LeftBottom(), LeftTop(), a, b));
975 }
976
977 bool HasIntersection(const RectF& rect) const {
978 return HasIntersection(Quad{rect});
979 }
980
981 bool HasIntersection(const Quad& quad) const {
982 return (Contains(quad.LeftTop()) ||
983 Contains(quad.RightTop()) ||
984 Contains(quad.RightBottom()) ||
985 Contains(quad.LeftBottom()) ||
986 quad.Contains(LeftTop()) ||
987 quad.Contains(RightTop()) ||
988 quad.Contains(RightBottom()) ||
989 quad.Contains(LeftBottom()) ||
990 HasIntersectionWithEdge(quad.LeftTop(), quad.RightTop()) ||
991 HasIntersectionWithEdge(quad.RightTop(), quad.RightBottom()) ||
992 HasIntersectionWithEdge(quad.RightBottom(), quad.LeftBottom()) ||
993 HasIntersectionWithEdge(quad.LeftBottom(), quad.LeftTop()));
994 }
995};
996
998 explicit QuadPoints(std::vector<Quad> Quads_)
999 : quads(std::move(Quads_)) {
1000 }
1001
1002 RectF GetBound() const {
1003 RectF rect;
1004 if (!quads.empty()) {
1005 rect = quads[0].GetBound();
1006 for (const auto& quad : quads)
1007 rect = RectF::Union(rect, quad.GetBound());
1008 }
1009 return rect;
1010 }
1011
1012 QuadPoints Optimize() const {
1013 std::vector<Quad> merged;
1014
1015 static constexpr auto areLinesCollinear = [](const PointF& p0, const PointF& p1, const PointF& p2) {
1016 return Math::FloatEq(PointF::VectorCrossProduct(p1 - p0, p2 - p0), 0.f);
1017 };
1018
1019 Quad quad;
1020 bool initialized = false;
1021 for (size_t i = 0; i <= quads.size(); ++i) {
1022 if (i == quads.size()) {
1023 merged.emplace_back(quad);
1024 break;
1025 }
1026
1027 auto& newQuad = quads[i];
1028 if (!initialized) {
1029 quad = newQuad;
1030 initialized = true;
1031 } else {
1032 if (areLinesCollinear(quad.LeftBottom(), quad.RightBottom(), newQuad.LeftBottom()) &&
1033 areLinesCollinear(quad.LeftTop(), quad.RightTop(), newQuad.LeftTop()) &&
1034 quad.HasIntersection(newQuad)) {
1035 quad.topright = newQuad.RightTop();
1036 quad.botright = newQuad.RightBottom();
1037 } else {
1038 merged.emplace_back(quad);
1039 quad = newQuad;
1040 }
1041 }
1042 }
1043
1044 return QuadPoints(merged);
1045 }
1046
1047 std::vector<Quad> quads;
1048};
1049
1053struct Matrix : public PDMatrix {
1054 Matrix() {
1055 a = 1.f;
1056 b = 0.f;
1057 c = 0.f;
1058 d = 1.f;
1059 e = 0.f;
1060 f = 0.f;
1061 }
1062
1063 Matrix(float a_, float b_, float c_, float d_, float e_, float f_) {
1064 a = a_;
1065 b = b_;
1066 c = c_;
1067 d = d_;
1068 e = e_;
1069 f = f_;
1070 }
1071
1072 Matrix(const PDMatrix& that) {
1073 a = that.a;
1074 b = that.b;
1075 c = that.c;
1076 d = that.d;
1077 e = that.e;
1078 f = that.f;
1079 }
1080
1081 bool Equals(const Matrix& that) const {
1082 if (this == &that)
1083 return true;
1084
1085 return (Math::FloatEq(a, that.a) && Math::FloatEq(b, that.b) && Math::FloatEq(c, that.c) &&
1086 Math::FloatEq(d, that.d) && Math::FloatEq(e, that.e) && Math::FloatEq(f, that.f));
1087 }
1088
1089 bool operator==(const Matrix& that) const { return Equals(that); }
1090 bool operator!=(const Matrix& that) const { return !Equals(that); }
1091
1092 bool IsIdentity() const { return *this == Matrix{}; }
1093
1094 static Matrix Concat(const Matrix& lhs, const Matrix& rhs) {
1095 return Matrix(lhs.a * rhs.a + lhs.b * rhs.c,
1096 lhs.a * rhs.b + lhs.b * rhs.d,
1097 lhs.c * rhs.a + lhs.d * rhs.c,
1098 lhs.c * rhs.b + lhs.d * rhs.d,
1099 lhs.e * rhs.a + lhs.f * rhs.c + rhs.e,
1100 lhs.e * rhs.b + lhs.f * rhs.d + rhs.f);
1101 }
1102
1103 Matrix operator*(const Matrix& that) const { return Concat(*this, that); }
1104 Matrix& operator*=(const Matrix& that) { return (*this = *this * that); }
1105
1106 PointF MapPoint(const PointF& point) const {
1107 return PointF(a * point.x + c * point.y + e, b * point.x + d * point.y + f);
1108 }
1109
1110 SizeF MapSize(const SizeF& size) const {
1111 return SizeF(a * size.width + c * size.height, b * size.width + d * size.height);
1112 }
1113
1114 RectF MapRect(const RectF& rect) const {
1115 return MapQuad(Quad{rect}).GetBound();
1116 }
1117
1118 Quad MapQuad(const Quad& quad) const {
1119 return Quad(MapPoint(quad.LeftTop()),
1120 MapPoint(quad.RightTop()),
1121 MapPoint(quad.LeftBottom()),
1122 MapPoint(quad.RightBottom()));
1123 }
1124
1125 float GetScalingX() const {
1126 return std::sqrt(a * a + c * c);
1127 }
1128
1129 float GetScalingY() const {
1130 return std::sqrt(b * b + d * d);
1131 }
1132
1133 bool HasRotation() const {
1134 return !Math::FloatEq(b, 0.f) || !Math::FloatEq(c, 0.f);
1135 }
1136
1137 float GetRotation() const {
1138 return std::atan2(b, a);
1139 }
1140
1141 float GetRotationDegrees() const {
1142 return Math::RadianToDegree(GetRotation());
1143 }
1144
1145 double Determinant() const { return static_cast<double>(a) * d - static_cast<double>(b) * c; }
1146
1147 static Matrix Scaling(float value) { return Scaling(value, value); }
1148 static Matrix Scaling(float x, float y) { return Matrix(x, 0.f, 0.f, y, 0.f, 0.f); }
1149 static Matrix Scaling(const SizeF& scaling) { return Scaling(scaling.width, scaling.height); }
1150 static Matrix Scaling(float value, const PointF& origin) { return Scaling(value, value, origin); }
1151 static Matrix Scaling(float x, float y, const PointF& origin) { return Matrix(x, 0.f, 0.f, y, (1.f - x) * origin.x, (1.f - y) * origin.y); }
1152 static Matrix Scaling(const SizeF& scaling, const PointF& origin) { return Scaling(scaling.width, scaling.height, origin); }
1153
1154 void Scale(float value) { *this = Scaling(value) * *this; }
1155 void Scale(float x, float y) { *this = Scaling(x, y) * *this; }
1156 void Scale(const SizeF& scaling) { *this = Scaling(scaling) * *this; }
1157 void Scale(float value, const PointF& origin) { *this = Scaling(value, origin) * *this; }
1158 void Scale(float x, float y, const PointF& origin) { *this = Scaling(x, y, origin) * *this; }
1159 void Scale(const SizeF& scaling, const PointF& origin) { *this = Scaling(scaling, origin) * *this; }
1160
1161 static Matrix Rotation(double radians) {
1162 float sina = static_cast<float>(std::sin(radians));
1163 float cosa = static_cast<float>(std::cos(radians));
1164 return Matrix(cosa, sina, -sina, cosa, 0.f, 0.f);
1165 }
1166
1167 static Matrix Rotation(double radians, const PointF& origin) {
1168 float sina = static_cast<float>(std::sin(radians));
1169 float cosa = static_cast<float>(std::cos(radians));
1170 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1171 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1172 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1173 }
1174
1175 void Rotate(double radians) { *this = Rotation(radians) * *this; }
1176 void Rotate(double radians, const PointF& origin) { *this = Rotation(radians, origin) * *this; }
1177
1178 static Matrix RotationDegree(double degree) {
1179 PointF zero(0.f, 0.f);
1180 return RotationDegree(degree, zero);
1181 }
1182
1183 static Matrix RotationDegree(double degree, const PointF& origin) {
1184 float sina;
1185 float cosa;
1186 if (degree == 0.0) {
1187 sina = 0.f;
1188 cosa = 1.f;
1189 } else if (degree == 90.0) {
1190 sina = 1.f;
1191 cosa = 0.f;
1192 } else if (degree == 180.0) {
1193 sina = 0.f;
1194 cosa = -1.f;
1195 } else if (degree == 270.0) {
1196 sina = -1.f;
1197 cosa = 0.f;
1198 } else {
1199 double radians = Math::DegreeToRadian(degree);
1200 sina = static_cast<float>(std::sin(radians));
1201 cosa = static_cast<float>(std::cos(radians));
1202 }
1203
1204 float dx = origin.x * (1.f - cosa) + origin.y * sina;
1205 float dy = origin.y * (1.f - cosa) - origin.x * sina;
1206 return Matrix(cosa, sina, -sina, cosa, dx, dy);
1207 }
1208
1209 void RotateDegree(double degree) { *this = RotationDegree(degree) * *this; }
1210 void RotateDegree(double degree, const PointF& origin) { *this = RotationDegree(degree, origin) * *this; }
1211
1212 static Matrix ReflectionX(float width = 0.f) { return Matrix(-1.f, 0.f, 0.f, 1.f, 0.f, width); }
1213 static Matrix ReflectionY(float height = 0.f) { return Matrix(1.f, 0.f, 0.f, -1.f, 0.f, height); }
1214
1215 void ReflectX(float width = 0.f) { *this = ReflectionX(width) * *this; }
1216 void ReflectY(float height = 0.f) { *this = ReflectionY(height) * *this; }
1217
1218 bool IsReflected() const {
1219 return a * d < b * c;
1220 }
1221
1222 static Matrix Translation(float x, float y) { return Matrix(1.f, 0.f, 0.f, 1.f, x, y); }
1223 static Matrix Translation(const PointF& v) { return Translation(v.x, v.y); }
1224
1225 void Translate(float x, float y) { *this = Translation(x, y) * *this; }
1226 void Translate(const PointF& v) { *this = Translation(v) * *this; }
1227
1228 bool IsInvertible() const {
1229 double det = Determinant();
1230 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1231 return (det < -EPS || det > EPS);
1232 }
1233
1234#ifndef SWIG
1235 std::optional<Matrix> Inverse() const {
1236 double det = Determinant();
1237
1238 static constexpr double EPS = std::numeric_limits<double>::epsilon();
1239 if (det >= -EPS && det <= EPS)
1240 return std::nullopt;
1241
1242 double invdet = 1.0 / det;
1243 float A = static_cast<float>(d * invdet);
1244 float B = static_cast<float>(-b * invdet);
1245 float C = static_cast<float>(-c * invdet);
1246 float D = static_cast<float>(a * invdet);
1247 float E = static_cast<float>((f * c - e * d) * invdet);
1248 float F = static_cast<float>((e * b - f * a) * invdet);
1249 return Matrix(A, B, C, D, E, F);
1250 }
1251#endif
1252
1253 Matrix InverseOrIdentity() const {
1254 return Inverse().value_or(Matrix{});
1255 }
1256
1257 static Matrix RectToRect(const RectF& source, const RectF& dest) {
1258 float sx = dest.GetWidth() / source.GetWidth();
1259 float sy = dest.GetHeight() / source.GetHeight();
1260 float dx = dest.MinX() - sx * source.MinX();
1261 float dy = dest.MinY() - sy * source.MinY();
1262 return Matrix(sx, 0.f, 0.f, sy, dx, dy);
1263 }
1264
1265 static Matrix RectToRectProportional(const RectF& source, const RectF& dest) {
1266 float sx = dest.GetWidth() / source.GetWidth();
1267 float sy = dest.GetHeight() / source.GetHeight();
1268 float scale = (std::min)(sx, sy);
1269 float dx = dest.MinX() - scale * source.MinX() + (dest.GetWidth() - scale * source.GetWidth()) / 2.0f;
1270 float dy = dest.MinY() - scale * source.MinY() + (dest.GetHeight() - scale * source.GetHeight()) / 2.0f;
1271 return Matrix(scale, 0.f, 0.f, scale, dx, dy);
1272 }
1273
1274 bool DoesPreserveRects() const {
1275 bool A = !Math::FloatEq(a, 0.0f);
1276 bool B = !Math::FloatEq(b, 0.0f);
1277 bool C = !Math::FloatEq(c, 0.0f);
1278 bool D = !Math::FloatEq(d, 0.0f);
1279 return (A == D && B == C && A != B && C != D);
1280 }
1281};
1282
1283} // namespace PDF
1284
1285#endif // PDFSDK_CXX_MATH_H_INCLUDED_
Math types.
Definition math.h:1053
Definition math.h:132
float TestSide(const PointF &a, const PointF &b) const
Test which side of the line AB this point lies.
Definition math.h:240
Definition math.h:54
Definition math.h:838
Definition math.h:997
Definition math.h:545
Definition math.h:347
Definition math.h:313
Definition math.h:279
Definition math_types.h:60
Definition math_types.h:12
Definition math_types.h:18
Definition math_types.h:52
Definition math_types.h:30
Definition math_types.h:44
Definition math_types.h:24
Definition math_types.h:38