Skip to content

Add shape level inflate method #169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a463d03
add shape level inflate method
DusKing1 Mar 19, 2025
e33c318
add holes to irregular::inflate()
DusKing1 Mar 20, 2025
81bfb84
inflate shape then trapezoidation
DusKing1 Mar 20, 2025
cc764c0
added unit test for shape inflate
DusKing1 Mar 20, 2025
05aa099
added a minkowski sum -ish inflate function
DusKing1 Mar 21, 2025
aa16967
fix compiling error
DusKing1 Mar 22, 2025
d9aa77b
use new function to remove intersections
DusKing1 Mar 22, 2025
918f356
remove outage SelfIntersectingInflate test
DusKing1 Mar 22, 2025
7cac77b
move to shape_inflate.{hpp, cpp}
DusKing1 Mar 22, 2025
601095a
translate Chinese comments into English
DusKing1 Mar 23, 2025
50a71c5
use equal(value, 0.0) instead of comparing to 1e-6
DusKing1 Mar 23, 2025
25b35a6
remove the assumption of 0.1 is small
DusKing1 Mar 23, 2025
ede694d
update is_point_strictly_inside_shape and is_point_on_line_segment
DusKing1 Mar 23, 2025
b856719
undo the changes in branching_scheme.cpp and trapezoid.hpp
DusKing1 Mar 23, 2025
bbb4215
use function in the lib to calculate angles
DusKing1 Mar 23, 2025
363db5f
update per feedback
DusKing1 Mar 24, 2025
efe3466
updated test but failed
DusKing1 Mar 24, 2025
88d915e
met precision issue when testing "IrregularShapeInflate.BasicInflate"
DusKing1 Mar 25, 2025
4039a69
fix the wrong formula when calculating the center of arc of the corne…
DusKing1 Mar 26, 2025
368b4eb
fixed all failed test
DusKing1 Mar 27, 2025
bf4fd33
remove duplicated distance() func
DusKing1 Mar 27, 2025
8701b2f
just use distance() in places where it should be used
DusKing1 Mar 27, 2025
1f5059c
Update src/irregular/shape_inflate.cpp
DusKing1 Mar 27, 2025
9e36da9
remove unused func
DusKing1 Mar 27, 2025
3987aab
Update src/irregular/shape_inflate.cpp
DusKing1 Mar 28, 2025
9514681
remove shape_closure
DusKing1 Mar 28, 2025
1ba9e56
rebase and check equality between end points
DusKing1 Mar 28, 2025
053ba2d
Update src/irregular/shape_inflate.cpp
DusKing1 Mar 28, 2025
4f84bfe
improve inflate/deflate logic on corner points
DusKing1 Mar 28, 2025
cab128c
filling the compute_intersections function to handle circular arcs
DusKing1 Mar 30, 2025
797c02a
remove duplicated gitignore
DusKing1 Apr 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions include/packingsolver/irregular/shape.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,5 +356,24 @@ bool equal(
const Shape& shape_1,
const Shape& shape_2);

// Check if a point is on a line segment
bool is_point_on_line_segment(
const Point& p,
const Point& start,
const Point& end);

// Check if a point is strictly inside a shape (excluding the boundary)
bool is_point_strictly_inside_shape(
const Point& point,
const Shape& shape);

// Check if a point is inside a shape or on its boundary
bool is_point_inside_or_on_shape(
const Point& point,
const Shape& shape);

std::vector<Shape> borders(
const Shape& shape);

}
}
1 change: 1 addition & 0 deletions src/irregular/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ target_sources(PackingSolver_irregular PRIVATE
shape_element_intersections.cpp
shape_convex_hull.cpp
shape_extract_borders.cpp
shape_inflate.cpp
shape_self_intersections_removal.cpp
shape_simplification.cpp
shape_trapezoidation.cpp
Expand Down
169 changes: 169 additions & 0 deletions src/irregular/shape.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#include "packingsolver/irregular/shape.hpp"

#include <algorithm>
#include <cmath>
#include <limits>
#include <vector>

// Define M_PI (if not provided by the system)
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

using namespace packingsolver;
using namespace packingsolver::irregular;
Expand Down Expand Up @@ -1252,3 +1260,164 @@ bool irregular::equal(

return true;
}

// Check if a point is on a line segment
bool irregular::is_point_on_line_segment(const Point& p, const Point& start, const Point& end) {
// Calculate the squared length of the line segment
LengthDbl line_length_squared = std::pow(end.x - start.x, 2) + std::pow(end.y - start.y, 2);

// If the line segment is actually a point
if (equal(line_length_squared, 0.0)) {
return equal(p.x, start.x) && equal(p.y, start.y);
}

// Calculate parameter t, representing the position of the point on the line segment (between 0 and 1 indicates on the segment)
LengthDbl t = ((p.x - start.x) * (end.x - start.x) + (p.y - start.y) * (end.y - start.y)) / line_length_squared;

// If t is outside the [0,1] range, the point is not on the line segment
if (strictly_lesser(t, 0.0) || strictly_greater(t, 1.0)) {
return false;
}

// Calculate the projection point
Point projection;
projection.x = start.x + t * (end.x - start.x);
projection.y = start.y + t * (end.y - start.y);

// Check if the distance to the projection point is small enough
LengthDbl distance_squared = std::pow(p.x - projection.x, 2) + std::pow(p.y - projection.y, 2);
return equal(distance_squared, 0.0);
}

// Check if a point is strictly inside a shape (excluding the boundary)
bool irregular::is_point_strictly_inside_shape(const Point& point, const Shape& shape) {
if (shape.elements.empty()) {
return false;
}

// First check if the point lies on any boundary
for (const ShapeElement& element : shape.elements) {
if (element.type == ShapeElementType::LineSegment) {
// Check if the point is on the line segment
if (is_point_on_line_segment(point, element.start, element.end)) {
return false; // Point is on the boundary, not strictly inside
}
} else if (element.type == ShapeElementType::CircularArc) {
// Check if the point is on the circular arc
LengthDbl dx = point.x - element.center.x;
LengthDbl dy = point.y - element.center.y;
LengthDbl distance = std::sqrt(dx * dx + dy * dy);

// Calculate the radius of the arc
LengthDbl radius = std::sqrt(
std::pow(element.start.x - element.center.x, 2) +
std::pow(element.start.y - element.center.y, 2)
);

// If the point is on the circle
if (equal(distance, radius)) {
// Calculate the point's angle
LengthDbl point_angle = angle_radian({dx, dy});

// Calculate the start and end angles of the arc
LengthDbl start_angle = angle_radian({element.start.x - element.center.x, element.start.y - element.center.y});
LengthDbl end_angle = angle_radian({element.end.x - element.center.x, element.end.y - element.center.y});

// Ensure angles are in the correct range
if (element.anticlockwise && end_angle <= start_angle) {
end_angle += 2 * M_PI;
} else if (!element.anticlockwise && start_angle <= end_angle) {
start_angle += 2 * M_PI;
}

// Check if the point's angle is within the arc range
bool in_arc_range;
if (element.anticlockwise) {
in_arc_range = (!strictly_lesser(point_angle, start_angle) && !strictly_greater(point_angle, end_angle));
} else {
in_arc_range = (!strictly_greater(point_angle, start_angle) && !strictly_lesser(point_angle, end_angle));
}

if (in_arc_range) {
return false; // Point is on the arc, not strictly inside
}
}
}
}

// Then use the ray-casting algorithm to check if the point is inside
int intersection_count = 0;

for (const ShapeElement& element : shape.elements) {
if (element.type == ShapeElementType::LineSegment) {
// Handle the special case of horizontal line segments
if (equal(element.start.y, element.end.y)) {
// Horizontal line segment: if the point's y coordinate equals the segment's y coordinate,
// and the x coordinate is within the segment range, the point is on the segment
if (equal(point.y, element.start.y) &&
(!strictly_lesser(point.x, std::min(element.start.x, element.end.x))) &&
(!strictly_greater(point.x, std::max(element.start.x, element.end.x)))) {
// Already checked in the previous section, no need to handle here
continue;
}
}

// Standard ray-casting algorithm for line segment checking
// Cast a ray to the right from the point, count intersections with segments
bool cond1 = (strictly_greater(element.start.y, point.y) != strictly_greater(element.end.y, point.y));
bool cond2 = strictly_lesser(point.x, (element.end.x - element.start.x) * (point.y - element.start.y) /
(element.end.y - element.start.y) + element.start.x);

if (cond1 && cond2) {
intersection_count++;
}
} else if (element.type == ShapeElementType::CircularArc) {
// Circular arc checking is more complex
LengthDbl dx = point.x - element.center.x;
LengthDbl dy = point.y - element.center.y;
LengthDbl distance = std::sqrt(dx * dx + dy * dy);

LengthDbl radius = std::sqrt(
std::pow(element.start.x - element.center.x, 2) +
std::pow(element.start.y - element.center.y, 2)
);

// If the point is inside the circle and to the left of the center, there may be intersections with a ray to the right
if (strictly_lesser(distance, radius) && strictly_lesser(point.x, element.center.x)) {
LengthDbl start_angle = angle_radian({element.start.x - element.center.x, element.start.y - element.center.y});
LengthDbl end_angle = angle_radian({element.end.x - element.center.x, element.end.y - element.center.y});

// Ensure angles are in the correct range
if (element.anticlockwise && end_angle <= start_angle) {
end_angle += 2 * M_PI;
} else if (!element.anticlockwise && start_angle <= end_angle) {
start_angle += 2 * M_PI;
}

// Calculate the point's line-of-sight angle (angle between the line from point to center and the horizontal)
LengthDbl point_angle = angle_radian({dx, dy});
if (strictly_lesser(point_angle, 0)) {
point_angle += 2 * M_PI; // Adjust angle to [0, 2π)
}

// Calculate the intersection angle of the ray to the right with the circle
LengthDbl ray_angle = 0; // Angle of ray to the right is 0

// Check if the ray intersects the arc
bool intersects_arc;
if (element.anticlockwise) {
intersects_arc = (!strictly_lesser(ray_angle, start_angle) && !strictly_greater(ray_angle, end_angle));
} else {
intersects_arc = (!strictly_greater(ray_angle, start_angle) && !strictly_lesser(ray_angle, end_angle));
}

if (intersects_arc) {
intersection_count++;
}
}
}
}

// If the number of intersections is odd, the point is inside the shape
return (intersection_count % 2 == 1);
}
Loading
Loading