From b15b78928f0a6cd00fa3dd6dd6f628215872b26d Mon Sep 17 00:00:00 2001 From: Florian Fontan Date: Fri, 31 Jul 2026 14:00:51 +0200 Subject: [PATCH] Move arc_radius to ShapeElement::radius and use it throughout Turns the free helper (only used in basic_shapes.cpp) into a proper ShapeElement member, so it's usable everywhere a CircularArc's radius is needed and throws if called on a non-arc element instead of silently computing a meaningless distance. Replace every distance(center, start) radius computation across shape.cpp, offset.cpp, no_fit_polygon.cpp, simplification.cpp, supports.cpp, and elements_intersections.cpp with .radius(), inlining the call where the result was only used once. The squared_distance radius comparisons in elements_intersections.cpp are left as-is: they intentionally avoid a sqrt and compare at a different tolerance scale, so they aren't equivalent to .radius(). --- include/shape/shape.hpp | 8 ++++++++ src/basic_shapes.cpp | 7 +------ src/elements_intersections.cpp | 6 +++--- src/no_fit_polygon.cpp | 7 ++----- src/offset.cpp | 13 +++++-------- src/shape.cpp | 32 ++++++++++++++------------------ src/simplification.cpp | 16 ++++++---------- src/supports.cpp | 2 +- 8 files changed, 40 insertions(+), 51 deletions(-) diff --git a/include/shape/shape.hpp b/include/shape/shape.hpp index 8a09f8a..f563764 100644 --- a/include/shape/shape.hpp +++ b/include/shape/shape.hpp @@ -401,6 +401,14 @@ struct ShapeElement /** Check if a point is on the element. */ bool contains(const Point& point) const; + /** Radius of the element. The element must be a CircularArc. */ + LengthDbl radius() const + { + if (this->type != ShapeElementType::CircularArc) + throw std::invalid_argument(FUNC_SIGNATURE); + return distance(this->center, this->start); + } + /** Length of the element. */ LengthDbl length() const; diff --git a/src/basic_shapes.cpp b/src/basic_shapes.cpp index f13baeb..6bff84a 100644 --- a/src/basic_shapes.cpp +++ b/src/basic_shapes.cpp @@ -10,11 +10,6 @@ using namespace shape; namespace { -LengthDbl arc_radius(const ShapeElement& arc) -{ - return distance(arc.start, arc.center); -} - std::pair split_arc_at_midpoint(const ShapeElement& arc) { Point midpoint = arc.middle(); @@ -117,7 +112,7 @@ void cut_circular_segments( // Check whether the arc subtends >= 180°; if so the tangent lines // at the endpoints are parallel and no finite apex exists. - LengthDbl radius = arc_radius(element); + LengthDbl radius = element.radius(); LengthDbl arc_angle = element.length() / radius; bool needs_subdivision = (arc_angle >= M_PI - 1e-6) diff --git a/src/elements_intersections.cpp b/src/elements_intersections.cpp index 3ced0d5..0b7b13d 100644 --- a/src/elements_intersections.cpp +++ b/src/elements_intersections.cpp @@ -378,7 +378,7 @@ ShapeElementIntersectionsOutput compute_line_arc_intersections( //std::cout << "line " << line.to_string() << std::endl; //std::cout << "arc " << arc.to_string() << std::endl; - LengthDbl radius = distance(arc.start, arc.center); + LengthDbl radius = arc.radius(); std::vector computed_points = shape::compute_line_circle_intersections( line.start, line.end, arc.center, radius); @@ -545,8 +545,8 @@ ShapeElementIntersectionsOutput compute_arc_arc_intersections( } } - LengthDbl radius_1 = distance(arc.start, arc.center); - LengthDbl radius_2 = distance(arc_2.start, arc_2.center); + LengthDbl radius_1 = arc.radius(); + LengthDbl radius_2 = arc_2.radius(); std::vector computed_points = shape::compute_circle_circle_intersections( arc.center, radius_1, arc_2.center, radius_2); diff --git a/src/no_fit_polygon.cpp b/src/no_fit_polygon.cpp index b9fba6c..7883a96 100644 --- a/src/no_fit_polygon.cpp +++ b/src/no_fit_polygon.cpp @@ -65,8 +65,7 @@ Point point_at_tangent_direction( const ShapeElement& arc, const Point& target_direction) { - LengthDbl radius = distance(arc.center, arc.start); - return point_at_tangent_direction(arc.center, radius, arc.orientation, target_direction); + return point_at_tangent_direction(arc.center, arc.radius(), arc.orientation, target_direction); } /** @@ -454,14 +453,12 @@ void handle_arc_arc( orbiting_local_piece = orbiting.split_current_at(overlap_end); // Sum the two (local, unplaced) overlap pieces into a single arc. - LengthDbl fixed_radius = distance(fixed_local_piece.center, fixed_local_piece.start); - LengthDbl orbiting_radius = distance(orbiting_local_piece.center, orbiting_local_piece.start); Point orbiting_neg_center = {-orbiting_local_piece.center.x, -orbiting_local_piece.center.y}; Point orbiting_neg_start = {-orbiting_local_piece.start.x, -orbiting_local_piece.start.y}; Point combined_center = current_vertex + (fixed_local_piece.center - fixed_local_piece.start) + (orbiting_neg_center - orbiting_neg_start); - LengthDbl combined_radius = fixed_radius + orbiting_radius; + LengthDbl combined_radius = fixed_local_piece.radius() + orbiting_local_piece.radius(); Point combined_end = point_at_tangent_direction( combined_center, combined_radius, ShapeElementOrientation::Anticlockwise, overlap_end); ShapeElement combined = build_circular_arc( diff --git a/src/offset.cpp b/src/offset.cpp index 9e6a462..8324e3b 100644 --- a/src/offset.cpp +++ b/src/offset.cpp @@ -71,8 +71,7 @@ Shape inflate_element( } case shape::ShapeElementType::CircularArc: { if (element.orientation == shape::ShapeElementOrientation::Clockwise) return inflate_element(element.reverse(), deflate, inflate); - LengthDbl radius = distance(element.center, element.start); - if (equal(deflate, radius)) { + if (equal(deflate, element.radius())) { Point normal_start = (element.orientation == shape::ShapeElementOrientation::Anticlockwise)? element.start - element.center: element.center - element.start; @@ -223,8 +222,7 @@ ShapeWithHoles shape::inflate( if (shape.shape.is_circle()) { Shape circle = shape.shape; ShapeElement& element = circle.elements[0]; - LengthDbl radius_orig = distance(element.center, element.start); - LengthDbl radius = radius_orig + offset; + LengthDbl radius = element.radius() + offset; element.start = {element.center.x + radius, element.center.y}; element.end = element.start; union_input.push_back({circle}); @@ -286,7 +284,7 @@ ShapeWithHoles shape::inflate( if (hole.is_circle()) { Shape circle = hole; ShapeElement& element = circle.elements[0]; - LengthDbl radius_orig = distance(element.center, element.start); + LengthDbl radius_orig = element.radius(); if (strictly_greater(radius_orig, offset)) { LengthDbl radius = radius_orig - offset; element.start = {element.center.x + radius, element.center.y}; @@ -369,7 +367,7 @@ ShapeWithHoles shape::inflate( ShapeWithHoles output; Shape shape = shape_orig; ShapeElement& element = shape.elements[0]; - LengthDbl radius_orig = distance(element.center, element.start); + LengthDbl radius_orig = element.radius(); LengthDbl radius = radius_orig + offset; element.start = {element.center.x + radius, element.center.y}; element.end = element.start; @@ -554,8 +552,7 @@ std::vector shape::deflate( if (shape_orig.is_circle()) { Shape shape = shape_orig; ShapeElement& element = shape.elements[0]; - LengthDbl radius_orig = distance(element.center, element.start); - LengthDbl radius = radius_orig - offset; + LengthDbl radius = element.radius() - offset; element.start = {element.center.x + radius, element.center.y}; element.end = element.start; return {shape}; diff --git a/src/shape.cpp b/src/shape.cpp index ad3f237..a30113c 100644 --- a/src/shape.cpp +++ b/src/shape.cpp @@ -307,7 +307,7 @@ AxisAlignedBoundingBox ShapeElement::min_max() const output.y_max = (std::max)(this->start.y, this->end.y); if (this->type == ShapeElementType::CircularArc) { - LengthDbl radius = distance(this->center, this->start); + LengthDbl radius = this->radius(); Angle starting_angle = shape::angle_radian(this->start - this->center); Angle ending_angle = shape::angle_radian(this->end - this->center); if (this->orientation != ShapeElementOrientation::Anticlockwise) @@ -368,7 +368,7 @@ std::pair ShapeElement::furthest_points(Angle angle) const } if (this->type == ShapeElementType::CircularArc) { - LengthDbl radius = distance(this->center, this->start); + LengthDbl radius = this->radius(); if (this->orientation == ShapeElementOrientation::Full) { point_min.y = this->center.y - radius; point_min.x = this->center.x; @@ -522,9 +522,8 @@ bool ShapeElement::in_circular_arc_cone(const Point& point) const // already within [0, this->length()]; if it wasn't, clamping moves // this->point(l) away from it, landing on start or end instead. LengthDbl l = (std::max)(0.0, (std::min)(this->length(), this->length(point))); - LengthDbl radius = distance(this->center, this->start); LengthDbl point_distance = distance(this->center, point); - Point point_on_circle = this->center + (radius / point_distance) * (point - this->center); + Point point_on_circle = this->center + (this->radius() / point_distance) * (point - this->center); return equal(point_on_circle, this->point(l)); } @@ -575,7 +574,7 @@ LengthDbl ShapeElement::length() const case ShapeElementType::LineSegment: return distance(this->start, this->end); case ShapeElementType::CircularArc: - LengthDbl r = distance(this->center, this->start); + LengthDbl r = this->radius(); if (this->orientation == ShapeElementOrientation::Full) { return 2 * M_PI * r; } if (this->orientation == ShapeElementOrientation::Anticlockwise) { @@ -595,7 +594,7 @@ LengthDbl ShapeElement::length(const Point& point) const case ShapeElementType::LineSegment: return distance(this->start, point); case ShapeElementType::CircularArc: - LengthDbl r = distance(this->center, this->start); + LengthDbl r = this->radius(); if (this->orientation == ShapeElementOrientation::Anticlockwise || this->orientation == ShapeElementOrientation::Full) { return angle_radian(this->start - this->center, point - this->center) * r; @@ -612,7 +611,7 @@ Point ShapeElement::point(LengthDbl length) const case ShapeElementType::LineSegment: { return this->start + length / this->length() * (this->end - this->start); } case ShapeElementType::CircularArc: { - LengthDbl r = distance(this->start, this->center); + LengthDbl r = this->radius(); if (this->orientation != ShapeElementOrientation::Clockwise) { return this->start.rotate_radians( this->center, @@ -686,7 +685,7 @@ std::string ShapeElement::to_svg() const && this->orientation == ShapeElementOrientation::Full) { Point center = {this->center.x, -(this->center.y)}; Point start = {this->start.x, -(this->start.y)}; - LengthDbl radius = distance(center, start); + LengthDbl radius = this->radius(); s += std::to_string(center.x - radius) + "," + std::to_string(center.y); s += "a" + std::to_string(radius) + "," + std::to_string(radius) + ",0,1,0," @@ -702,7 +701,7 @@ std::string ShapeElement::to_svg() const if (this->type == ShapeElementType::LineSegment) { s += "L"; } else { - LengthDbl radius = distance(center, start); + LengthDbl radius = this->radius(); Angle theta = angle_radian(start - center, end - center); int large_arc_flag = (theta > M_PI)? 0: 1; int sweep_flag = (this->orientation == ShapeElementOrientation::Anticlockwise)? 0: 1; @@ -1098,14 +1097,14 @@ AreaDbl Shape::compute_area() const for (const ShapeElement& element: elements) { if (element.type == ShapeElementType::CircularArc && element.orientation == ShapeElementOrientation::Full) { - LengthDbl radius = distance(element.center, element.start); + LengthDbl radius = element.radius(); return radius * radius * M_PI; } area += cross_product(element.start, element.end); // Handle circular arcs. if (element.type == ShapeElementType::CircularArc) { - LengthDbl radius = distance(element.center, element.start); + LengthDbl radius = element.radius(); if (element.orientation == ShapeElementOrientation::Anticlockwise) { Angle theta = angle_radian(element.center - element.start, element.center - element.end); area += radius * radius * (theta - std::sin(theta)); @@ -1286,12 +1285,11 @@ bool Shape::contains( intersection_count++; } } else if (element.type == ShapeElementType::CircularArc) { - LengthDbl radius = distance(element.center, element.start); ShapeElement ray; ray.type = ShapeElementType::LineSegment; ray.start.x = point.x; ray.start.y = point.y; - ray.end.x = (std::max)(point.x, element.center.x) + 2 * radius; + ray.end.x = (std::max)(point.x, element.center.x) + 2 * element.radius(); ray.end.y = point.y; ShapeElementIntersectionsOutput intersections = compute_intersections(ray, element); @@ -1891,7 +1889,7 @@ std::string Shape::to_svg_path() const const ShapeElement& element = elements.front(); Point center = {element.center.x, -(element.center.y)}; Point start = {element.start.x, -(element.start.y)}; - LengthDbl radius = distance(center, start); + LengthDbl radius = element.radius(); s += std::to_string(center.x - radius) + "," + std::to_string(center.y); s += "a" + std::to_string(radius) + "," + std::to_string(radius) + ",0,1,0," @@ -1908,7 +1906,7 @@ std::string Shape::to_svg_path() const if (element.type == ShapeElementType::LineSegment) { s += "L"; } else { - LengthDbl radius = distance(center, start); + LengthDbl radius = element.radius(); Angle theta = angle_radian(start - center, end - center); int large_arc_flag = (theta > M_PI)? 0: 1; int sweep_flag = (element.orientation == ShapeElementOrientation::Anticlockwise)? 0: 1; @@ -2484,9 +2482,7 @@ bool shape::equal( if (!equal(element_1.center, element_2.center)) return false; if (element_1.orientation == ShapeElementOrientation::Full) { - LengthDbl radius_1 = distance(element_1.center, element_1.start); - LengthDbl radius_2 = distance(element_2.center, element_2.start); - return equal(radius_1, radius_2); + return equal(element_1.radius(), element_2.radius()); } } if (!equal(element_1.start, element_2.start)) diff --git a/src/simplification.cpp b/src/simplification.cpp index cf8bfdd..b00e4ad 100644 --- a/src/simplification.cpp +++ b/src/simplification.cpp @@ -403,7 +403,7 @@ bool is_forward_extension(const ShapeElement& element, const Point& point) 0.0); } case ShapeElementType::CircularArc: { - LengthDbl radius = distance(element.start, element.center); + LengthDbl radius = element.radius(); switch (element.orientation) { case ShapeElementOrientation::Full: return false; @@ -529,12 +529,11 @@ ExtendToIntersectionOutput shape::try_extend_to_intersection( break; } case ShapeElementType::CircularArc: { - LengthDbl radius_next = distance(element_next.start, element_next.center); candidates = compute_line_circle_intersections( element_prev.start, element_prev.end, element_next.center, - radius_next); + element_next.radius()); break; } } @@ -543,22 +542,19 @@ ExtendToIntersectionOutput shape::try_extend_to_intersection( case ShapeElementType::CircularArc: { switch (element_next.type) { case ShapeElementType::LineSegment: { - LengthDbl radius_prev = distance(element_prev.start, element_prev.center); candidates = compute_line_circle_intersections( element_next.start, element_next.end, element_prev.center, - radius_prev); + element_prev.radius()); break; } case ShapeElementType::CircularArc: { - LengthDbl radius_prev = distance(element_prev.start, element_prev.center); - LengthDbl radius_next = distance(element_next.start, element_next.center); candidates = compute_circle_circle_intersections( element_prev.center, - radius_prev, + element_prev.radius(), element_next.center, - radius_next); + element_next.radius()); break; } } @@ -629,7 +625,7 @@ SmoothArcToLineOutput shape::try_smooth_arc_to_line( if (element_prev.orientation == ShapeElementOrientation::Full) return {}; - LengthDbl radius = distance(element_prev.start, element_prev.center); + LengthDbl radius = element_prev.radius(); Point center_to_end = element_next.end - element_prev.center; LengthDbl distance_to_end = distance(element_next.end, element_prev.center); diff --git a/src/supports.cpp b/src/supports.cpp index c565baf..72f5026 100644 --- a/src/supports.cpp +++ b/src/supports.cpp @@ -21,7 +21,7 @@ ShapeSupports shape::compute_shape_supports( LengthDbl x_min_cur = element.start.x; if (element.type == ShapeElementType::CircularArc) { - LengthDbl radius = distance(element.center, element.start); + LengthDbl radius = element.radius(); Angle starting_angle = angle_radian(element.start - element.center); Angle ending_angle = angle_radian(element.end - element.center); if (element.orientation == ShapeElementOrientation::Clockwise)