Skip to content

Commit ee99f40

Browse files
committed
Update shapes
1 parent 5d0e70e commit ee99f40

File tree

5 files changed

+180
-61
lines changed

5 files changed

+180
-61
lines changed

include/packingsolver/irregular/shape.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ struct ShapeElement
165165

166166
ShapeElement axial_symmetry_y_axis() const;
167167

168+
/** Get the middle point on the element. */
169+
Point middle() const;
170+
171+
/** Compute the smallest and greatest x and y of the shape. */
172+
std::pair<Point, Point> min_max() const;
173+
168174
std::string to_string() const;
169175

170176
nlohmann::json to_json() const;

src/irregular/shape.cpp

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,77 @@ Angle irregular::angle_radian(
172172
return a;
173173
}
174174

175+
Point ShapeElement::middle() const
176+
{
177+
switch (type) {
178+
case ShapeElementType::LineSegment: {
179+
Point point;
180+
point.x = (this->start.x + this->end.x) / 2;
181+
point.y = (this->start.y + this->end.y) / 2;
182+
return point;
183+
} case ShapeElementType::CircularArc: {
184+
Angle angle = (this->anticlockwise)?
185+
angle_radian(
186+
this->start - this->center,
187+
this->end - this->center):
188+
angle_radian(
189+
this->end - this->center,
190+
this->start - this->center);
191+
return this->start.rotate_radians(this->center, angle);
192+
}
193+
}
194+
return {0, 0};
195+
}
196+
197+
std::pair<Point, Point> ShapeElement::min_max() const
198+
{
199+
LengthDbl x_min = (std::min)(this->start.x, this->end.x);
200+
LengthDbl x_max = (std::max)(this->start.x, this->end.x);
201+
LengthDbl y_min = (std::min)(this->start.y, this->end.y);
202+
LengthDbl y_max = (std::max)(this->start.y, this->end.y);
203+
204+
if (this->type == ShapeElementType::CircularArc) {
205+
LengthDbl radius = distance(this->center, this->start);
206+
Angle starting_angle = irregular::angle_radian(this->start - this->center);
207+
Angle ending_angle = irregular::angle_radian(this->end - this->center);
208+
if (!this->anticlockwise)
209+
std::swap(starting_angle, ending_angle);
210+
//std::cout << "starting_angle " << starting_angle << " ending_angle " << ending_angle << std::endl;
211+
if (starting_angle <= ending_angle) {
212+
if (starting_angle <= M_PI
213+
&& ending_angle >= M_PI) {
214+
x_min = std::min(x_min, this->center.x - radius);
215+
}
216+
if (starting_angle == 0)
217+
x_max = std::max(x_max, this->center.x + radius);
218+
if (starting_angle <= 3 * M_PI / 2
219+
&& ending_angle >= 3 * M_PI / 2 ) {
220+
y_min = std::min(y_min, this->center.y - radius);
221+
}
222+
if (starting_angle <= M_PI / 2
223+
&& ending_angle >= M_PI / 2) {
224+
y_max = std::max(y_max, this->center.y + radius);
225+
}
226+
} else { // starting_angle > ending_angle
227+
if (starting_angle <= M_PI
228+
|| ending_angle >= M_PI) {
229+
x_min = std::min(x_min, this->center.x - radius);
230+
}
231+
x_max = std::max(x_max, this->center.x + radius);
232+
if (starting_angle <= 3 * M_PI / 2
233+
|| ending_angle >= 3 * M_PI / 2) {
234+
y_min = std::min(y_min, this->center.y - radius);
235+
}
236+
if (starting_angle <= M_PI / 2
237+
|| ending_angle >= M_PI / 2) {
238+
y_max = std::max(y_max, this->center.y + radius);
239+
}
240+
}
241+
}
242+
243+
return {{x_min, y_min}, {x_max, y_max}};
244+
}
245+
175246
int irregular::counter_clockwise(
176247
const Point& point_1,
177248
const Point& point_2,
@@ -587,52 +658,15 @@ std::pair<Point, Point> Shape::compute_min_max(
587658
LengthDbl x_max = -std::numeric_limits<LengthDbl>::infinity();
588659
LengthDbl y_min = std::numeric_limits<LengthDbl>::infinity();
589660
LengthDbl y_max = -std::numeric_limits<LengthDbl>::infinity();
590-
for (const ShapeElement& element: elements) {
591-
Point point = (!mirror)?
592-
element.start.rotate(angle):
593-
element.start.axial_symmetry_y_axis().rotate(angle);
594-
x_min = std::min(x_min, point.x);
595-
x_max = std::max(x_max, point.x);
596-
y_min = std::min(y_min, point.y);
597-
y_max = std::max(y_max, point.y);
598-
599-
if (element.type == ShapeElementType::CircularArc) {
600-
LengthDbl radius = distance(element.center, element.start);
601-
Angle starting_angle = irregular::angle_radian(element.start - element.center);
602-
Angle ending_angle = irregular::angle_radian(element.end - element.center);
603-
if (!element.anticlockwise)
604-
std::swap(starting_angle, ending_angle);
605-
if (starting_angle <= ending_angle) {
606-
if (starting_angle <= M_PI
607-
&& M_PI <= ending_angle) {
608-
x_min = std::min(x_min, element.center.x - radius);
609-
}
610-
if (starting_angle == 0)
611-
x_max = std::max(x_max, element.center.x + radius);
612-
if (starting_angle <= 3 * M_PI / 2
613-
&& 3 * M_PI / 2 <= ending_angle) {
614-
y_min = std::min(y_min, element.center.y - radius);
615-
}
616-
if (starting_angle <= M_PI / 2
617-
&& M_PI / 2 <= ending_angle) {
618-
y_max = std::max(y_max, element.center.y + radius);
619-
}
620-
} else { // starting_angle > ending_angle
621-
if (starting_angle <= M_PI
622-
|| ending_angle <= M_PI) {
623-
x_min = std::min(x_min, element.center.x - radius);
624-
}
625-
x_max = std::max(x_max, element.center.x + radius);
626-
if (starting_angle <= 3 * M_PI / 4
627-
|| ending_angle <= 3 * M_PI / 4) {
628-
y_min = std::min(y_min, element.center.y - radius);
629-
}
630-
if (starting_angle <= M_PI / 2
631-
|| ending_angle <= M_PI / 2) {
632-
y_max = std::max(y_max, element.center.y + radius);
633-
}
634-
}
635-
}
661+
for (const ShapeElement& element_orig: elements) {
662+
ShapeElement element = (!mirror)?
663+
element_orig.rotate(angle):
664+
element_orig.axial_symmetry_y_axis().rotate(angle);
665+
auto mm = element.min_max();
666+
x_min = std::min(x_min, mm.first.x);
667+
x_max = std::max(x_max, mm.second.x);
668+
y_min = std::min(y_min, mm.first.y);
669+
y_max = std::max(y_max, mm.second.y);
636670
}
637671
return {{x_min, y_min}, {x_max, y_max}};
638672
}

src/irregular/shape_element_intersections.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,3 +749,32 @@ std::vector<Point> irregular::compute_intersections(
749749
throw std::invalid_argument("irregular::compute_intersections: Invalid element types");
750750
return {};
751751
}
752+
753+
bool irregular::intersect(
754+
const Shape& shape_1,
755+
const Shape& shape_2,
756+
bool strict)
757+
{
758+
for (const ShapeElement& element_1: shape_1.elements) {
759+
for (const ShapeElement& element_2: shape_2.elements) {
760+
auto intersections = compute_intersections(
761+
element_1,
762+
element_2,
763+
strict);
764+
if (!intersections.empty())
765+
return true;
766+
}
767+
}
768+
for (const ShapeElement& element_1: shape_1.elements) {
769+
Point middle = element_1.middle();
770+
if (shape_2.contains(middle, strict))
771+
return true;
772+
}
773+
for (const ShapeElement& element_2: shape_2.elements) {
774+
Point middle = element_2.middle();
775+
if (shape_1.contains(middle, strict))
776+
return true;
777+
}
778+
779+
return false;
780+
}

src/irregular/shape_element_intersections.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@ std::vector<Point> compute_intersections(
1212
const ShapeElement& element_2,
1313
bool strict = false);
1414

15+
bool intersect(
16+
const Shape& shape_1,
17+
const Shape& shape_2,
18+
bool strict = false);
19+
1520
}
1621
}

test/irregular/shape_test.cpp

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,75 @@
55
using namespace packingsolver;
66
using namespace packingsolver::irregular;
77

8-
struct ShapeElementLengthTestParams
8+
struct IrregularShapeElementLengthTestParams
99
{
1010
ShapeElement element;
1111
LengthDbl expected_length;
1212
};
1313

14-
class IrregularShapeElementLengthTest: public testing::TestWithParam<ShapeElementLengthTestParams> { };
14+
class IrregularShapeElementLengthTest: public testing::TestWithParam<IrregularShapeElementLengthTestParams> { };
1515

16-
TEST_P(IrregularShapeElementLengthTest, ShapeElementLength)
16+
TEST_P(IrregularShapeElementLengthTest, IrregularShapeElementLength)
1717
{
18-
ShapeElementLengthTestParams test_params = GetParam();
18+
IrregularShapeElementLengthTestParams test_params = GetParam();
1919
EXPECT_TRUE(equal(test_params.element.length(), test_params.expected_length));
2020
}
2121

2222
INSTANTIATE_TEST_SUITE_P(
2323
Irregular,
2424
IrregularShapeElementLengthTest,
25-
testing::ValuesIn(std::vector<ShapeElementLengthTestParams>{
25+
testing::ValuesIn(std::vector<IrregularShapeElementLengthTestParams>{
2626
{build_shape({{0, 0}, {0, 1}}, true).elements.front(), 1 },
2727
{build_shape({{1, 0}, {0, 0, 1}, {0, 1}}, true).elements.front(), M_PI / 2 },
2828
{build_shape({{1, 0}, {0, 0, -1}, {0, -1}}, true).elements.front(), M_PI / 2 },
2929
}));
3030

3131

32+
struct IrregularShapeElementMinMaxTestParams
33+
{
34+
ShapeElement element;
35+
LengthDbl expected_x_min;
36+
LengthDbl expected_y_min;
37+
LengthDbl expected_x_max;
38+
LengthDbl expected_y_max;
39+
};
40+
41+
class IrregularShapeElementMinMaxTest: public testing::TestWithParam<IrregularShapeElementMinMaxTestParams> { };
42+
43+
TEST_P(IrregularShapeElementMinMaxTest, IrregularShapeElementMinMax)
44+
{
45+
IrregularShapeElementMinMaxTestParams test_params = GetParam();
46+
std::cout << "element " << test_params.element.to_string() << std::endl;
47+
std::cout << "expected x_min " << test_params.expected_x_min
48+
<< " y_min " << test_params.expected_y_min
49+
<< " x_max " << test_params.expected_x_max
50+
<< " y_max " << test_params.expected_y_max << std::endl;
51+
auto mm = test_params.element.min_max();
52+
std::cout << "x_min " << mm.first.x
53+
<< " y_min " << mm.first.y
54+
<< " x_max " << mm.second.x
55+
<< " y_max " << mm.second.y << std::endl;
56+
EXPECT_TRUE(equal(mm.first.x, test_params.expected_x_min));
57+
EXPECT_TRUE(equal(mm.second.x, test_params.expected_x_max));
58+
EXPECT_TRUE(equal(mm.first.y, test_params.expected_y_min));
59+
EXPECT_TRUE(equal(mm.second.y, test_params.expected_y_max));
60+
}
61+
62+
INSTANTIATE_TEST_SUITE_P(
63+
Irregular,
64+
IrregularShapeElementMinMaxTest,
65+
testing::ValuesIn(std::vector<IrregularShapeElementMinMaxTestParams>{
66+
{build_shape({{0, 1}, {2, 3}}, true).elements.front(), 0, 1, 2, 3 },
67+
{build_shape({{1, 0}, {0, 0, 1}, {-1, 0}}, true).elements.front(), -1, 0, 1, 1 },
68+
{build_shape({{1, 0}, {0, 0, -1}, {-1, 0}}, true).elements.front(), -1, -1, 1, 0 },
69+
70+
{build_shape({{0, 1}, {0, 0, 1}, {1, 0}}, true).elements.front(), -1, -1, 1, 1 },
71+
{build_shape({{-1, 0}, {0, 0, 1}, {0, 1}}, true).elements.front(), -1, -1, 1, 1 },
72+
{build_shape({{0, -1}, {0, 0, 1}, {-1, 0}}, true).elements.front(), -1, -1, 1, 1 },
73+
{build_shape({{1, 0}, {0, 0, 1}, {0, -1}}, true).elements.front(), -1, -1, 1, 1 },
74+
}));
75+
76+
3277
struct CleanShapeTestParams
3378
{
3479
Shape shape;
@@ -61,19 +106,19 @@ INSTANTIATE_TEST_SUITE_P(
61106
}}));
62107

63108

64-
struct ShapeContainsTestParams
109+
struct IrregularShapeContainsTestParams
65110
{
66111
Shape shape;
67112
Point point;
68113
bool strict;
69114
bool expected_contained;
70115
};
71116

72-
class IrregularShapeContainsTest: public testing::TestWithParam<ShapeContainsTestParams> { };
117+
class IrregularShapeContainsTest: public testing::TestWithParam<IrregularShapeContainsTestParams> { };
73118

74-
TEST_P(IrregularShapeContainsTest, ShapeContains)
119+
TEST_P(IrregularShapeContainsTest, IrregularShapeContains)
75120
{
76-
ShapeContainsTestParams test_params = GetParam();
121+
IrregularShapeContainsTestParams test_params = GetParam();
77122
std::cout << "shape" << std::endl;
78123
std::cout << test_params.shape.to_string(0) << std::endl;
79124
std::cout << "point " << test_params.point.to_string() << std::endl;
@@ -91,7 +136,7 @@ TEST_P(IrregularShapeContainsTest, ShapeContains)
91136
INSTANTIATE_TEST_SUITE_P(
92137
Irregular,
93138
IrregularShapeContainsTest,
94-
testing::ValuesIn(std::vector<ShapeContainsTestParams>{
139+
testing::ValuesIn(std::vector<IrregularShapeContainsTestParams>{
95140
{ // Point oustide of polygon
96141
build_shape({{0, 0}, {1, 0}, {1, 1}, {0, 1}}),
97142
{2, 2},
@@ -140,19 +185,19 @@ INSTANTIATE_TEST_SUITE_P(
140185
}}));
141186

142187

143-
struct ApproximateCircularArcByLineSegmentsTestParams
188+
struct IrregularApproximateCircularArcByLineSegmentsTestParams
144189
{
145190
ShapeElement circular_arc;
146191
ElementPos number_of_line_segments;
147192
bool outer;
148193
std::vector<ShapeElement> expected_line_segments;
149194
};
150195

151-
class IrregularApproximateCircularArcByLineSegmentsTest: public testing::TestWithParam<ApproximateCircularArcByLineSegmentsTestParams> { };
196+
class IrregularApproximateCircularArcByLineSegmentsTest: public testing::TestWithParam<IrregularApproximateCircularArcByLineSegmentsTestParams> { };
152197

153-
TEST_P(IrregularApproximateCircularArcByLineSegmentsTest, ApproximateCircularArcByLineSegments)
198+
TEST_P(IrregularApproximateCircularArcByLineSegmentsTest, IrregularApproximateCircularArcByLineSegments)
154199
{
155-
ApproximateCircularArcByLineSegmentsTestParams test_params = GetParam();
200+
IrregularApproximateCircularArcByLineSegmentsTestParams test_params = GetParam();
156201
std::cout << "circular_arc" << std::endl;
157202
std::cout << test_params.circular_arc.to_string() << std::endl;
158203
std::cout << "expected_line_segments" << std::endl;
@@ -176,7 +221,7 @@ TEST_P(IrregularApproximateCircularArcByLineSegmentsTest, ApproximateCircularArc
176221
INSTANTIATE_TEST_SUITE_P(
177222
Irregular,
178223
IrregularApproximateCircularArcByLineSegmentsTest,
179-
testing::ValuesIn(std::vector<ApproximateCircularArcByLineSegmentsTestParams>{
224+
testing::ValuesIn(std::vector<IrregularApproximateCircularArcByLineSegmentsTestParams>{
180225
{
181226
build_shape({{1, 0}, {0, 0, 1}, {0, 1}}, true).elements.front(),
182227
1,

0 commit comments

Comments
 (0)