jaulib v1.3.8
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
geom2f.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11#ifndef JAU_MATH_GEOM_GEOM2F_HPP_
12#define JAU_MATH_GEOM_GEOM2F_HPP_
13
14#include <vector>
15#include <memory>
16
17#include <jau/math/vec2f.hpp>
20
21namespace jau::math::geom {
22
23 using namespace jau::math;
24
25 /** \addtogroup Math
26 *
27 * @{
28 */
29
30 /**
31 * Computes oriented double area of a triangle,
32 * i.e. the 2x2 determinant with b-a and c-a per column.
33 * <pre>
34 * | bx-ax, cx-ax |
35 * det = | by-ay, cy-ay |
36 * </pre>
37 * @param a first vertex
38 * @param b second vertex
39 * @param c third vertex
40 * @return area > 0 CCW, ..
41 */
42 constexpr double tri_area(const Point2f& a, const Point2f& b, const Point2f& c){
43 return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
44 }
45
46 /**
47 * Return the orientation of the given point triplet a, b and c using triArea()
48 */
49 constexpr orientation_t orientation(const Point2f& a, const Point2f& b, const Point2f& c) noexcept {
50 const double area = tri_area(a, b, c);
51 if ( jau::is_zero( area ) ) {
52 return orientation_t::COL;
53 }
54 return ( area > 0.0f ) ? orientation_t::CCW : orientation_t::CLW;
55 }
56
57 class LineSeg2f; // fwd
58
59 /**
60 * Geometric object
61 */
62 class Geom2f {
63 public:
64 virtual ~Geom2f() = default;
65
66 virtual AABBox2f box() const noexcept = 0;
67 virtual bool contains(const Point2f& o) const noexcept = 0;
68 virtual bool intersects(const LineSeg2f & o) const noexcept = 0;
69 virtual bool intersects(const AABBox2f& box) const noexcept = 0;
70 virtual bool intersects(const Geom2f& o) const noexcept = 0;
71
72 /**
73 * Return whether this object intersects with the given line segment
74 * and if intersecting, the crossing point (intersection), the normalized normal of the crossing surface and the reflection out vector.
75 */
76 virtual bool intersection(Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point, const LineSeg2f& in) const noexcept = 0;
77
78 virtual std::string toString() const noexcept = 0;
79 };
80 typedef std::shared_ptr<Geom2f> Geom2f_ref;
81 typedef std::vector<Geom2f_ref> Geom2f_list;
82
83 class LineSeg2f : public Geom2f {
84 public:
87
88 constexpr LineSeg2f() noexcept
89 : p0(), p1() {}
90
91 constexpr LineSeg2f(const Point2f& p0_, const Point2f& p1_) noexcept
92 : p0( p0_ ), p1( p1_ ) {}
93
94 /**
95 * Scale this line segment with given scale factor
96 * @param s scale factor
97 * @return this instance
98 */
99 constexpr LineSeg2f& operator*=(const float s ) noexcept {
100 p0 *= s;
101 p1 *= s;
102 return *this;
103 }
104
105 /**
106 * Return the length of this line segment, i.e. distance between both points.
107 */
108 constexpr float length() const noexcept {
109 return p1.dist(p0);
110 }
111
112 /**
113 * Return the angle of this line segment in radians
114 */
115 float angle() const noexcept {
116 return (p1 - p0).angle();
117 }
118
119 /**
120 * Return the angle between two line segments in radians
121 */
122 float angle(const LineSeg2f & o) const noexcept {
123 const Vec2f a = p1 - p0;
124 const Vec2f b = o.p1 - o.p0;
125 return a.angle(b);
126 }
127
128 void add(float length) {
129 // extend center points p0, p1 with radius in moving direction
130 const float a_move = angle();
131 Vec2f l_move_diff = Vec2f::from_length_angle(length, a_move);
132 p0 -= l_move_diff;
133 p1 += l_move_diff;
134 }
135
136 std::string toString() const noexcept override { return "L[" + p0.toString() + ", " + p1.toString() + "]"; }
137
138 /**
139 * Create an AABBox with given lineseg
140 */
141 AABBox2f box() const noexcept override {
142 return AABBox2f().resize(p0).resize(p1);
143 }
144
145 private:
146 bool is_on_line(const Point2f& p2) const noexcept {
147 // Using the perp dot product (PDP),
148 // which is the area of the parallelogram of the three points,
149 // same as the area of the triangle defined by the three points, multiplied by 2.
150 const float perpDotProduct = (p0.x - p2.x) * (p1.y - p2.y) - (p0.y - p2.y) * (p1.x - p2.x);
151 return jau::is_zero( perpDotProduct );
152
153 }
154 bool is_on_line2(const Point2f& p2) const noexcept {
155 if ( p2.x <= std::max(p0.x, p1.x) && p2.x >= std::min (p0.x, p1.x) &&
156 p2.y <= std::max(p0.y, p2.y) && p2.y >= std::min (p0.y, p1.y) )
157 {
158 return true;
159 }
160 return false;
161 }
162
163 public:
164 /**
165 * Test intersection between this line segment and the give point
166 * @return true if the line segment contains the point, otherwise false
167 */
168 bool contains(const Point2f& p2) const noexcept override {
169 if ( !( ( p0.x <= p2.x && p2.x <= p1.x ) || ( p1.x <= p2.x && p2.x <= p0.x ) ) ) {
170 // not in x-range
171 return false;
172 }
173 if ( !( ( p0.y <= p2.y && p2.y <= p1.y ) || ( p1.y <= p2.y && p2.y <= p0.y ) ) ) {
174 // not in y-range
175 return false;
176 }
177 return is_on_line(p2);
178 }
179
180 private:
181 /**
182 * See [p + t r = q + u s](https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282)
183 * and [its terse C# implementation](https://www.codeproject.com/tips/862988/find-the-intersection-point-of-two-line-segments)
184 */
185 static bool intersects(Point2f& result,
186 const Point2f& p, const Point2f& p2,
187 const Point2f& q, const Point2f& q2, const bool do_collinear=false)
188 {
189 // Operations: 11+, 8*, 2 branches without collinear case
190 constexpr const float eps = std::numeric_limits<float>::epsilon();
191 const Vec2f r = p2 - p;
192 const Vec2f s = q2 - q;
193 const float rxs = r.cross(s);
194
195 if ( jau::is_zero(rxs) ) {
196 if ( do_collinear ) {
197 const Vec2f q_p = q - p;
198 const float qpxr = q_p.cross(r);
199 if ( jau::is_zero(qpxr) ) // disabled collinear case
200 {
201 // 1) r x s = 0 and (q - p) x r = 0, the two lines are collinear.
202
203 const Point2f p_q = p - q;
204 const float qp_dot_r = q_p.dot(r);
205 const float pq_dot_s = p_q.dot(s);
206 // if ( ( 0 <= qp_dot_r && qp_dot_r <= r.dot(r) ) ||
207 // ( 0 <= pq_dot_s && pq_dot_s <= s.dot(s) ) )
208 if ( ( eps <= qp_dot_r && qp_dot_r - r.dot(r) <= eps ) ||
209 ( eps <= pq_dot_s && pq_dot_s - s.dot(s) <= eps ) )
210 {
211 // 1.1) 0 <= (q - p) · r <= r · r or 0 <= (p - q) · s <= s · s, the two lines are overlapping
212 // FIXME: result set to q2 endpoint, OK?
213 result = q2;
214 return true;
215 }
216
217 // 1.2 the two lines are collinear but disjoint.
218 return false;
219 } else {
220 // 2) r × s = 0 and (q − p) × r ≠ 0, the two lines are parallel and non-intersecting.
221 return false;
222 }
223 } else {
224 // Not considering collinear case as an intersection
225 return false;
226 }
227 } else {
228 // r x s != 0
229 const Vec2f q_p = q - p;
230 const float qpxr = q_p.cross(r);
231
232 // p + t r = q + u s
233 // (p + t r) × s = (q + u s) × s
234 // t (r × s) = (q − p) × s, with s x s = 0
235 // t = (q - p) x s / (r x s)
236 const float t = q_p.cross(s) / rxs;
237
238 // u = (p − q) × r / (s × r) = (q - p) x r / (r x s), with s × r = − r × s
239 const float u = qpxr / rxs;
240
241 // if ( (0 <= t && t <= 1) && (0 <= u && u <= 1) )
242 if ( (eps <= t && t - 1 <= eps) && (eps <= u && u - 1 <= eps) )
243 {
244 // 3) r × s ≠ 0 and 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1, the two line segments meet at the point p + t * r = q + u * s.
245 result = p + (t * r); // == q + (u * s)
246 return true;
247 }
248 }
249
250 return false;
251 }
252
253 public:
254
255 /**
256 * Compute intersection between two lines segments
257 * @param result storage for the intersection coordinates if the lines intersect, otherwise unchanged
258 * @param o the other line segment.
259 * @return true if the line segments intersect, otherwise false
260 */
261 bool intersects(Point2f& result, const LineSeg2f & o) const noexcept {
262 return intersects(result, p0, p1, o.p0, o.p1);
263 }
264
265 /**
266 * Return true if this line segment intersect with the given line segment
267 * @param o the other line segment.
268 * @return true if both intersect, otherwise false
269 */
270 bool intersects(const LineSeg2f & o) const noexcept override {
271 Point2f result;
272 return intersects(result, p0, p1, o.p0, o.p1);
273#if 0
274 const pixel::orientation_t or_1 = orientation (p0, p1, o.p0);
275 const pixel::orientation_t or_2 = orientation (p0, p1, o.p1);
276 const pixel::orientation_t or_3 = orientation(o.p0, o.p1, p0);
277 const pixel::orientation_t or_4 = orientation(o.p0, o.p1, p1);
278
279 // General case : Points are non-collinear.
280 if (or_1 != or_2 && or_3 != or_4) {
281 pixel::log_printf("i-g-0: %s with %s\n", toString().c_str(), o.toString().c_str());
282 return true;
283 }
284
285#if 0
286 // Special case : Points are collinear.
287
288 // If points p0, p1 and o.p0 are collinear, check if point o.p0 lies on this segment p0-p1
289 if (or_1 == pixel::orientation_t::COL && is_on_line (o.p0)) {
290 pixel::log_printf("i-s-1: %s with %s\n", toString().c_str(), o.toString().c_str());
291 return true;
292 }
293
294 // If points p0, p1 and o.p1 are collinear, check if point o.p1 lies on this segment p0-p1
295 if (or_2 == pixel::orientation_t::COL && is_on_line (o.p1)) {
296 pixel::log_printf("i-s-2: %s with %s\n", toString().c_str(), o.toString().c_str());
297 return true;
298 }
299
300 // If points o.p0, o.p1 and p0 are collinear, check if point p0 lies on given segment o.p0-o.p1
301 if (or_3 == pixel::orientation_t::COL && o.is_on_line (p0)) {
302 pixel::log_printf("i-s-3: %s with %s\n", toString().c_str(), o.toString().c_str());
303 return true;
304 }
305
306 // If points o.p0, o.p1 and p1 are collinear, check if point p1 lies on given segment o.p0-o.p1
307 if (or_4 == pixel::orientation_t::COL && o.is_on_line (p1)) {
308 pixel::log_printf("i-s-4: %s with %s\n", toString().c_str(), o.toString().c_str());
309 return true;
310 }
311#endif
312
313 return false;
314#endif
315 }
316
317 /**
318 * Returns minimum distance between this line segment and given point p
319 * <p>
320 * See [Shortest distance between a point and a line segment](https://stackoverflow.com/a/1501725)
321 * </p>
322 * <p>
323 * Slightly more expensive than intersects().
324 * </p>
325 */
326 float distance(Point2f p) const noexcept {
327 // Operations: 15+, 9*, 1-sqrt, 3 branches
328 const float l2 = p1.dist_sq(p0); // i.e. |p1-p0|^2 - avoid a sqrt
329 if( l2 < std::numeric_limits<float>::epsilon() ) {
330 return p.dist(p1); // p1 == p0 case
331 }
332 // Consider the line extending the segment, parameterized as p0 + t (p1 - p0).
333 // We find projection of point p onto the line.
334 // It falls where t = [(p-p0) . (p1-p0)] / |p1-p0|^2
335 // We clamp t from [0,1] to handle points outside the line segment.
336 Vec2f pv = p - p0;
337 Vec2f wv = p1 - p0;
338 const float t = std::max(0.0f, std::min(1.0f, pv.dot(wv) / l2));
339 const Vec2f projection = p0 + t * (p1 - p0); // Projection falls on the segment
340 return p.dist(projection);
341 }
342
343 bool intersects(const AABBox2f& box) const noexcept override {
344 // separating axis theorem.
345 const Vec2f d = (p1 - p0) * 0.5f; // half lineseg direction
346 const Vec2f e = (box.tr - box.bl) * 0.5f;
347 const Vec2f aabb_center = (box.bl + box.tr) * 0.5f;
348 const Vec2f lseg_center = p0 + d;
349 const Vec2f c = lseg_center - aabb_center;
350 const Vec2f ad(std::abs(d.x), std::abs(d.y));
351 if (std::abs(c.x) > e.x + ad.x) {
352 return false;
353 }
354 if (std::abs(c.y) > e.y + ad.y) {
355 return false;
356 }
357 /**
358 if (std::abs(d.y * c.z - d.z * c.y) > e.y * ad.z + e.z * ad.y + std::numeric_limits<float>::epsilon()) {
359 return false;
360 }
361 if (std::abs(d.z * c.x - d.x * c.z) > e.z * ad.x + e.x * ad.z + std::numeric_limits<float>::epsilon()) {
362 return false;
363 }
364 */
365 if (std::abs(d.x * c.y - d.y * c.x) > e.x * ad.y + e.y * ad.x + std::numeric_limits<float>::epsilon()) {
366 return false;
367 }
368 return true;
369 }
370
371 bool intersects(const Geom2f& o) const noexcept override {
372 return intersects(o.box());
373 }
374
375 bool intersection(Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point, const LineSeg2f& in) const noexcept override {
376 if( intersects(cross_point, in) ) {
377 cross_normal = (p1 - p0).normal_ccw().normalize();
378 const Vec2f v_in = cross_point - in.p0;
379 reflect_out = v_in - ( 2.0f * v_in.dot(cross_normal) * cross_normal );
380 return true;
381 }
382 return false;
383 }
384
385 bool intersection(const AABBox2f& box, Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point) const noexcept {
386 const Point2f tl(box.bl.x, box.tr.y);
387 const Point2f br(box.tr.x, box.bl.y);
388 {
389 const LineSeg2f lt(tl, box.tr);
390 const LineSeg2f lb(box.bl, br);
391 const float dt = lt.distance( p0 );
392 const float db = lb.distance( p0 );
393 if( dt < db ) {
394 if( lt.intersection(reflect_out, cross_normal, cross_point, *this) ) {
395 return true;
396 }
397 if( lb.intersection(reflect_out, cross_normal, cross_point, *this) ) {
398 return true;
399 }
400 } else {
401 if( lb.intersection(reflect_out, cross_normal, cross_point, *this) ) {
402 return true;
403 }
404 if( lt.intersection(reflect_out, cross_normal, cross_point, *this) ) {
405 return true;
406 }
407 }
408 }
409 {
410 const LineSeg2f lr(br, box.tr);
411 const LineSeg2f ll(box.bl, tl);
412 const float dr = lr.distance( p0 );
413 const float dl = ll.distance( p0 );
414 if( dr < dl ) {
415 if( lr.intersection(reflect_out, cross_normal, cross_point, *this) ) {
416 return true;
417 }
418 if( ll.intersection(reflect_out, cross_normal, cross_point, *this) ) {
419 return true;
420 }
421 } else {
422 if( ll.intersection(reflect_out, cross_normal, cross_point, *this) ) {
423 return true;
424 }
425 if( lr.intersection(reflect_out, cross_normal, cross_point, *this) ) {
426 return true;
427 }
428 }
429 }
430 return false;
431 }
432
433 };
434
435
436 /**
437 * Animated geometric object
438 * - movable
439 * - rotatable
440 * - time based mutation, i.e. tick()'able
441 */
442 class AGeom2f : public Geom2f {
443 public:
444 virtual void rotate(const float rad) noexcept = 0;
445 virtual void move_dir(const float d) noexcept = 0;
446 virtual void move(const Point2f& d) noexcept = 0;
447 virtual void move(const float dx, const float dy) noexcept = 0;
448 virtual bool tick(const float dt) noexcept { (void)dt; return true; }
449 };
450 typedef std::shared_ptr<AGeom2f> AGeom2f_ref;
451 typedef std::vector<AGeom2f_ref> AGeom2f_list;
452
453 class Disk2f : public AGeom2f {
454 public:
455 /**
456 * Imagine a circle ;-)
457 *
458 * ---------
459 * | |r |
460 * | | |
461 * | c |
462 * | |
463 * ---------
464 */
465 /** m_center */
467 float radius;
468 /** direction angle in radians */
470
471 Disk2f(const Point2f& c_, const float r_)
472 : center( c_), radius(r_), dir_angle(0.0f) {}
473
474 Disk2f(float x, float y, const float r_)
475 : center(x, y), radius(r_), dir_angle(0.0f) {}
476
477 std::string toString() const noexcept override {
478 return "disk[c " + center.toString() +
479 ", r " + std::to_string(radius) +
480 "]"; }
481
482 void set_center(const Point2f& p) {
483 center = p;
484 }
485
486 AABBox2f box() const noexcept override {
487 Point2f bl = { center.x - radius, center.y - radius};
488 Point2f tr = { center.x + radius, center.y + radius};
489 return AABBox2f(bl, tr);
490 }
491
492 bool contains(const Point2f& o) const noexcept override {
493 return center.dist(o) <= radius;
494 // return box().contains(o);
495 }
496
497 bool intersects(const LineSeg2f & o) const noexcept override {
498 return o.intersects(box());
499 }
500
501 bool intersects(const AABBox2f& o) const noexcept override {
502 return box().intersects(o);
503 }
504
505 bool intersects(const Geom2f& o) const noexcept override {
506 return box().intersects(o.box());
507 }
508
509 bool intersection(Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point, const LineSeg2f& in) const noexcept override {
510 if( !in.intersects( box() ) ) {
511 return false;
512 }
513 cross_point = center; // use center
514 const float dx = in.p1.x - in.p0.x;
515 const float dy = in.p1.y - in.p0.y;
516 cross_normal = Vec2f(-dy, dx).normalize();
517 const Vec2f v_in = in.p1 - in.p0;
518 // reflect_out = v_in - ( 2.0f * v_in.dot(cross_normal) * cross_normal ); // TODO: check if cross_normal is OK for this case
519 reflect_out = -1.0f * v_in;
520 return true;
521 }
522
523 void rotate(const float rad) noexcept override {
524 dir_angle += rad;
525 }
526
527 void move_dir(const float d) noexcept override {
528 Point2f dir { d, 0 };
529 dir.rotate(dir_angle);
530 center += dir;
531 }
532
533 void move(const Point2f& d) noexcept override {
534 center += d;
535 }
536 void move(const float dx, const float dy) noexcept override {
537 center.add(dx, dy);
538 }
539 };
540 typedef std::shared_ptr<Disk2f> Disk2f_ref;
541
542 class Rect2f : public AGeom2f {
543 public:
544 /**
545 * Unrotated, clockwise (CW):
546 *
547 * (a)-----(b)
548 * | |
549 * | |
550 * | |
551 * (c)-----(d)
552 */
553 /** Unrotated top-left */
555 /** Unrotated top-right */
557 /** Unrotated bottom-left */
559 /** Unrotated bottom_right */
562 /** direction angle in radians */
564
565 public:
566 Rect2f(const Point2f& tl_, const float width, const float height, const float radians) noexcept
567 {
568 p_a = tl_;
569 p_b = { p_a.x + width, p_a.y };
570 p_c = { p_a.x , p_a.y - height};
571 p_d = { p_a.x + width, p_a.y - height};
572 p_center = { p_a.x + width/2 , p_a.y - height/2 };
573 dir_angle = 0.0f;
574 rotate(radians);
575 }
576
577 Rect2f(const Point2f& tl_, const float width, const float height) noexcept{
578 p_a = tl_;
579 p_b = { p_a.x + width, p_a.y };
580 p_c = { p_a.x , p_a.y - height};
581 p_d = { p_a.x + width, p_a.y - height};
582 p_center = { p_a.x + width/2 , p_a.y - height/2 };
583 dir_angle = 0.0f;
584 }
585
586
587 Rect2f(const Point2f& tl_, const Point2f& tr_, const Point2f& bl_, const Point2f& br_) noexcept
588 : p_a(tl_), p_b(tr_), p_c(bl_), p_d(br_)
589 {
590 p_center = { ( p_a.x + p_b.x ) / 2.0f , ( p_a.y + p_c.y ) / 2.0f };
591 dir_angle = 0.0f;
592 }
593
594 AABBox2f box() const noexcept override {
596 }
597
598 void move_dir(const float d) noexcept override {
599 Point2f dir { d, 0 };
600 dir.rotate(dir_angle);
601 p_a += dir;
602 p_b += dir;
603 p_c += dir;
604 p_d += dir;
605 p_center += dir;
606 }
607
608 void move(const Point2f& d) noexcept override {
609 p_a += d;
610 p_b += d;
611 p_c += d;
612 p_d += d;
613 p_center += d;
614 }
615 void move(const float dx, const float dy) noexcept override {
616 p_a.add(dx, dy);
617 p_b.add(dx, dy);
618 p_c.add(dx, dy);
619 p_d.add(dx, dy);
620 p_center.add(dx, dy);
621 }
622
623 void rotate(const float radians) noexcept override {
624 rotate(radians, p_center);
625 }
626 void rotate(const float radians, const Point2f& p) noexcept {
627 const float cos = std::cos(radians);
628 const float sin = std::sin(radians);
629 p_a.rotate(sin, cos, p);
630 p_b.rotate(sin, cos, p);
631 p_c.rotate(sin, cos, p);
632 p_d.rotate(sin, cos, p);
633 dir_angle += radians;
634 }
635
636 void set_top_left(const Point2f& p) {
637 // FIXME: Since m_p_a is unknown to be top-left ...
638 const float dx = p.x - p_a.x;
639 const float dy = p.y - p_a.y;
640 move( dx, dy );
641 }
642
643 bool contains(const Point2f& o) const noexcept override {
644 return box().contains(o);
645 }
646
647 bool intersects(const LineSeg2f & o) const noexcept override {
648 return o.intersects(box());
649 }
650
651 bool intersects(const AABBox2f& o) const noexcept override {
652 return box().intersects(o);
653 }
654
655 bool intersects(const Geom2f& o) const noexcept override {
656 return box().intersects(o.box());
657 }
658
659 bool intersection(Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point, const LineSeg2f& in) const noexcept override {
660 {
661 // tl .. tr
662 const LineSeg2f l(p_a, p_b);
663 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
664 return true;
665 }
666 }
667 {
668 // bl .. br
669 const LineSeg2f l(p_c, p_d);
670 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
671 return true;
672 }
673 }
674 {
675 // br .. tr
676 const LineSeg2f l(p_d, p_b);
677 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
678 return true;
679 }
680 }
681 {
682 // bl .. tl
683 const LineSeg2f l(p_c, p_a);
684 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
685 return true;
686 }
687 }
688 return false;
689 }
690
691 bool intersection(Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point, const LineSeg2f& in, const float in_radius) const noexcept {
692 {
693 // tl .. tr
694 LineSeg2f l(p_a, p_b);
695 const Vec2f added_size = (l.p1 - l.p0).normal_ccw().normalize() * in_radius;
696 l.p0 += added_size;
697 l.p1 += added_size;
698 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
699 return true;
700 }
701 }
702 {
703 // bl .. br
704 LineSeg2f l(p_c, p_d);
705 const Vec2f added_size = (l.p1 - l.p0).normal_ccw().normalize() * in_radius;
706 l.p0 += added_size;
707 l.p1 += added_size;
708 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
709 return true;
710 }
711 }
712 {
713 // br .. tr
714 LineSeg2f l(p_d, p_b);
715 const Vec2f added_size = (l.p1 - l.p0).normal_ccw().normalize() * in_radius;
716 l.p0 += added_size;
717 l.p1 += added_size;
718 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
719 return true;
720 }
721 }
722 {
723 // bl .. tl
724 LineSeg2f l(p_c, p_a);
725 const Vec2f added_size = (l.p1 - l.p0).normal_ccw().normalize() * in_radius;
726 l.p0 += added_size;
727 l.p1 += added_size;
728 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
729 return true;
730 }
731 }
732 return false;
733 }
734
735 std::string toString() const noexcept override {
736 return "rect[a " + p_a.toString() +
737 ", b " + p_b.toString() +
738 ", c " + p_c.toString() +
739 ", d " + p_d.toString() +
740 "]";
741 }
742 };
743 typedef std::shared_ptr<Rect2f> Rect2f_ref;
744
745 /**
746 * A clockwise (CW) polyline
747 */
748 class LineStrip2f : public AGeom2f {
749 public:
750 std::vector<Point2f> p_list;
752 /** direction angle in radians */
754
755 public:
756 LineStrip2f() noexcept
757 : p_list(), p_center(), dir_angle(0.0f) {
758 }
759
760 LineStrip2f(const Point2f& center, const float angle) noexcept
761 : p_list(), p_center(center), dir_angle(angle) {
762 }
763
764 void normalize_center() noexcept {
765 Point2f c;
766 int n = 0;
767 for(size_t i=0; i<p_list.size()-1; ++i) {
768 c += p_list[i];
769 n++;
770 }
771 // skip first == last case
772 if( p_list[p_list.size()-1] != p_list[0] ) {
773 c += p_list[p_list.size()-1];
774 n++;
775 }
776 this->p_center = c / static_cast<float>(n);
777 }
778
779 AABBox2f box() const noexcept override {
781 for(const Point2f& p : p_list) {
782 box.resize(p);
783 }
784 return box;
785 }
786
787 void move_dir(const float d) noexcept override {
788 Point2f dir { d, 0 };
789 dir.rotate(dir_angle);
790 for(Point2f& p : p_list) {
791 p += dir;
792 }
793 p_center += dir;
794 }
795
796 void move(const Point2f& d) noexcept override {
797 for(Point2f& p : p_list) {
798 p += d;
799 }
800 p_center += d;
801 }
802 void move(const float dx, const float dy) noexcept override {
803 for(Point2f& p : p_list) {
804 p.add(dx, dy);
805 }
806 p_center.add(dx, dy);
807 }
808
809 void rotate(const float radians) noexcept override {
810 const float cos = std::cos(radians);
811 const float sin = std::sin(radians);
812#if 0
813 // pre-ranged loop
814 for(size_t i=0; i<p_list.size(); ++i) {
815 point2f_t& p = p_list[i];
816 p.rotate(sin, cos, p_center);
817 }
818#endif
819 for(Point2f& p : p_list) {
820 p.rotate(sin, cos, p_center);
821 }
822 dir_angle += radians;
823 }
824
825 void set_center(const Point2f& p) {
826 const float dx = p.x - p_center.x;
827 const float dy = p.y - p_center.y;
828 move( dx, dy );
829 }
830
831 bool contains(const Point2f& o) const noexcept override {
832 return box().contains(o);
833 }
834
835 bool intersects(const LineSeg2f & o) const noexcept override {
836 return o.intersects(box());
837 }
838
839 bool intersects(const AABBox2f& o) const noexcept override {
840 return box().intersects(o);
841 }
842
843 bool intersects(const Geom2f& o) const noexcept override {
844 return box().intersects(o.box());
845 }
846
847 bool intersects_lineonly(const LineSeg2f & o) const noexcept {
848 if( p_list.size() < 2 ) {
849 return false;
850 }
851 Point2f p0 = p_list[0];
852 for(size_t i=1; i<p_list.size(); ++i) {
853 const Point2f& p1 = p_list[i];
854 const LineSeg2f l(p0, p1);
855 if( l.intersects(o) ) {
856 return true;
857 }
858 p0 = p1;
859 }
860 return false;
861 }
862
863 bool intersection(Vec2f& reflect_out, Vec2f& cross_normal, Point2f& cross_point,
864 const LineSeg2f& in) const noexcept override {
865 if( p_list.size() < 2 ) {
866 return false;
867 }
868 Point2f p0 = p_list[0];
869 for(size_t i=1; i<p_list.size(); ++i) {
870 const Point2f& p1 = p_list[i];
871 const LineSeg2f l(p0, p1);
872 if( l.intersection(reflect_out, cross_normal, cross_point, in) ) {
873 return true;
874 }
875 p0 = p1;
876 }
877 return false;
878 }
879
880 std::string toString() const noexcept override {
881 return "linestrip[center " + p_center.toString() +
882 ", points " + std::to_string(p_list.size())+"]"; }
883 };
884 typedef std::shared_ptr<LineStrip2f> LineStrip2f_ref;
885
886 /**@}*/
887
888} // namespace jau::math::geom
889
890#endif /* JAU_MATH_GEOM_GEOM2F_HPP_ */
value_type x
Definition vec2f.hpp:63
constexpr value_type cross(const Vector2F &o) const noexcept
Returns cross product of this vectors and the given one, i.e.
Definition vec2f.hpp:304
constexpr_cxx26 value_type angle() const noexcept
Return the direction angle of this vector in radians.
Definition vec2f.hpp:264
value_type y
Definition vec2f.hpp:64
constexpr value_type dot(const Vector2F &o) const noexcept
Return the dot product of this vector and the given one.
Definition vec2f.hpp:293
static constexpr_cxx26 Vector2F from_length_angle(const value_type magnitude, const value_type radians) noexcept
Definition vec2f.hpp:66
Axis Aligned Bounding Box.
Definition aabbox2f.hpp:34
AABBox2f & resize(const AABBox2f &o) noexcept
Resize the AABBox to encapsulate another AABox.
Definition aabbox2f.hpp:78
Animated geometric object.
Definition geom2f.hpp:442
virtual void move(const float dx, const float dy) noexcept=0
virtual void move(const Point2f &d) noexcept=0
virtual void move_dir(const float d) noexcept=0
virtual void rotate(const float rad) noexcept=0
virtual bool tick(const float dt) noexcept
Definition geom2f.hpp:448
void move(const float dx, const float dy) noexcept override
Definition geom2f.hpp:536
void rotate(const float rad) noexcept override
Definition geom2f.hpp:523
void move(const Point2f &d) noexcept override
Definition geom2f.hpp:533
Disk2f(const Point2f &c_, const float r_)
Definition geom2f.hpp:471
Point2f center
Imagine a circle ;-)
Definition geom2f.hpp:466
std::string toString() const noexcept override
Definition geom2f.hpp:477
bool intersection(Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point, const LineSeg2f &in) const noexcept override
Return whether this object intersects with the given line segment and if intersecting,...
Definition geom2f.hpp:509
void move_dir(const float d) noexcept override
Definition geom2f.hpp:527
bool intersects(const AABBox2f &o) const noexcept override
Definition geom2f.hpp:501
bool intersects(const LineSeg2f &o) const noexcept override
Definition geom2f.hpp:497
float dir_angle
direction angle in radians
Definition geom2f.hpp:469
Disk2f(float x, float y, const float r_)
Definition geom2f.hpp:474
void set_center(const Point2f &p)
Definition geom2f.hpp:482
bool intersects(const Geom2f &o) const noexcept override
Definition geom2f.hpp:505
bool contains(const Point2f &o) const noexcept override
Definition geom2f.hpp:492
AABBox2f box() const noexcept override
Definition geom2f.hpp:486
Geometric object.
Definition geom2f.hpp:62
virtual AABBox2f box() const noexcept=0
virtual ~Geom2f()=default
virtual bool intersection(Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point, const LineSeg2f &in) const noexcept=0
Return whether this object intersects with the given line segment and if intersecting,...
virtual std::string toString() const noexcept=0
virtual bool intersects(const LineSeg2f &o) const noexcept=0
virtual bool contains(const Point2f &o) const noexcept=0
bool intersects(const LineSeg2f &o) const noexcept override
Return true if this line segment intersect with the given line segment.
Definition geom2f.hpp:270
bool contains(const Point2f &p2) const noexcept override
Test intersection between this line segment and the give point.
Definition geom2f.hpp:168
float angle() const noexcept
Return the angle of this line segment in radians.
Definition geom2f.hpp:115
constexpr LineSeg2f() noexcept
Definition geom2f.hpp:88
float angle(const LineSeg2f &o) const noexcept
Return the angle between two line segments in radians.
Definition geom2f.hpp:122
void add(float length)
Definition geom2f.hpp:128
bool intersection(const AABBox2f &box, Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point) const noexcept
Definition geom2f.hpp:385
bool intersects(const Geom2f &o) const noexcept override
Definition geom2f.hpp:371
constexpr float length() const noexcept
Return the length of this line segment, i.e.
Definition geom2f.hpp:108
bool intersection(Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point, const LineSeg2f &in) const noexcept override
Return whether this object intersects with the given line segment and if intersecting,...
Definition geom2f.hpp:375
std::string toString() const noexcept override
Definition geom2f.hpp:136
bool intersects(Point2f &result, const LineSeg2f &o) const noexcept
Compute intersection between two lines segments.
Definition geom2f.hpp:261
constexpr LineSeg2f(const Point2f &p0_, const Point2f &p1_) noexcept
Definition geom2f.hpp:91
constexpr LineSeg2f & operator*=(const float s) noexcept
Scale this line segment with given scale factor.
Definition geom2f.hpp:99
AABBox2f box() const noexcept override
Create an AABBox with given lineseg.
Definition geom2f.hpp:141
bool intersects(const AABBox2f &box) const noexcept override
Definition geom2f.hpp:343
float distance(Point2f p) const noexcept
Returns minimum distance between this line segment and given point p.
Definition geom2f.hpp:326
void rotate(const float radians) noexcept override
Definition geom2f.hpp:809
bool intersection(Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point, const LineSeg2f &in) const noexcept override
Return whether this object intersects with the given line segment and if intersecting,...
Definition geom2f.hpp:863
bool intersects(const AABBox2f &o) const noexcept override
Definition geom2f.hpp:839
void normalize_center() noexcept
Definition geom2f.hpp:764
bool intersects_lineonly(const LineSeg2f &o) const noexcept
Definition geom2f.hpp:847
void move_dir(const float d) noexcept override
Definition geom2f.hpp:787
void move(const Point2f &d) noexcept override
Definition geom2f.hpp:796
bool contains(const Point2f &o) const noexcept override
Definition geom2f.hpp:831
void move(const float dx, const float dy) noexcept override
Definition geom2f.hpp:802
std::string toString() const noexcept override
Definition geom2f.hpp:880
float dir_angle
direction angle in radians
Definition geom2f.hpp:753
AABBox2f box() const noexcept override
Definition geom2f.hpp:779
bool intersects(const Geom2f &o) const noexcept override
Definition geom2f.hpp:843
void set_center(const Point2f &p)
Definition geom2f.hpp:825
std::vector< Point2f > p_list
Definition geom2f.hpp:750
bool intersects(const LineSeg2f &o) const noexcept override
Definition geom2f.hpp:835
LineStrip2f(const Point2f &center, const float angle) noexcept
Definition geom2f.hpp:760
bool contains(const Point2f &o) const noexcept override
Definition geom2f.hpp:643
float dir_angle
direction angle in radians
Definition geom2f.hpp:563
void move(const Point2f &d) noexcept override
Definition geom2f.hpp:608
Rect2f(const Point2f &tl_, const float width, const float height, const float radians) noexcept
Definition geom2f.hpp:566
bool intersects(const AABBox2f &o) const noexcept override
Definition geom2f.hpp:651
bool intersection(Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point, const LineSeg2f &in, const float in_radius) const noexcept
Definition geom2f.hpp:691
AABBox2f box() const noexcept override
Definition geom2f.hpp:594
bool intersects(const LineSeg2f &o) const noexcept override
Definition geom2f.hpp:647
bool intersection(Vec2f &reflect_out, Vec2f &cross_normal, Point2f &cross_point, const LineSeg2f &in) const noexcept override
Return whether this object intersects with the given line segment and if intersecting,...
Definition geom2f.hpp:659
Rect2f(const Point2f &tl_, const Point2f &tr_, const Point2f &bl_, const Point2f &br_) noexcept
Definition geom2f.hpp:587
std::string toString() const noexcept override
Definition geom2f.hpp:735
bool intersects(const Geom2f &o) const noexcept override
Definition geom2f.hpp:655
Point2f p_c
Unrotated bottom-left.
Definition geom2f.hpp:558
void move(const float dx, const float dy) noexcept override
Definition geom2f.hpp:615
void move_dir(const float d) noexcept override
Definition geom2f.hpp:598
Point2f p_b
Unrotated top-right.
Definition geom2f.hpp:556
void rotate(const float radians, const Point2f &p) noexcept
Definition geom2f.hpp:626
Point2f p_d
Unrotated bottom_right.
Definition geom2f.hpp:560
Point2f p_a
Unrotated, clockwise (CW):
Definition geom2f.hpp:554
Rect2f(const Point2f &tl_, const float width, const float height) noexcept
Definition geom2f.hpp:577
void set_top_left(const Point2f &p)
Definition geom2f.hpp:636
void rotate(const float radians) noexcept override
Definition geom2f.hpp:623
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
std::shared_ptr< Geom2f > Geom2f_ref
Definition geom2f.hpp:80
std::vector< AGeom2f_ref > AGeom2f_list
Definition geom2f.hpp:451
std::vector< Geom2f_ref > Geom2f_list
Definition geom2f.hpp:81
constexpr orientation_t orientation(const Point2f &a, const Point2f &b, const Point2f &c) noexcept
Return the orientation of the given point triplet a, b and c using triArea()
Definition geom2f.hpp:49
std::shared_ptr< Disk2f > Disk2f_ref
Definition geom2f.hpp:540
std::shared_ptr< AGeom2f > AGeom2f_ref
Definition geom2f.hpp:450
std::shared_ptr< LineStrip2f > LineStrip2f_ref
Definition geom2f.hpp:884
Vector2F< float > Vec2f
Definition vec2f.hpp:416
Point2F< float > Point2f
Definition vec2f.hpp:431
constexpr double tri_area(const Point2f &a, const Point2f &b, const Point2f &c)
Computes oriented double area of a triangle, i.e.
Definition geom2f.hpp:42
std::shared_ptr< Rect2f > Rect2f_ref
Definition geom2f.hpp:743
@ CCW
Counter-Clockwise.
Definition geom.hpp:27
STL namespace.