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