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