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