Skip to content

Commit 8cdd9ef

Browse files
committed
Implement shape supports
1 parent 9bba8e6 commit 8cdd9ef

File tree

5 files changed

+324
-0
lines changed

5 files changed

+324
-0
lines changed

src/irregular/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ target_sources(PackingSolver_irregular PRIVATE
1212
shape_self_intersections_removal.cpp
1313
shape_simplification.cpp
1414
shape_trapezoidation.cpp
15+
shape_supports.cpp
1516
branching_scheme.cpp)
1617
target_include_directories(PackingSolver_irregular PUBLIC
1718
${PROJECT_SOURCE_DIR}/include)

src/irregular/shape_supports.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include "irregular/shape_supports.hpp"
2+
3+
using namespace packingsolver;
4+
using namespace packingsolver::irregular;
5+
6+
ShapeSupports irregular::compute_shape_supports(
7+
const Shape& shape,
8+
bool is_hole)
9+
{
10+
ShapeSupports supports;
11+
12+
// Find a starting element.
13+
ElementPos element_start_pos = -1;
14+
LengthDbl x_min = std::numeric_limits<LengthDbl>::infinity();
15+
ElementPos element_prev_pos = shape.elements.size() - 1;
16+
for (ElementPos element_pos = 0;
17+
element_pos < (ElementPos)shape.elements.size();
18+
++element_pos) {
19+
const ShapeElement& element = shape.elements[element_pos];
20+
21+
LengthDbl x_min_cur = element.start.x;
22+
if (element.type == ShapeElementType::CircularArc) {
23+
LengthDbl radius = distance(element.center, element.start);
24+
Angle starting_angle = irregular::angle_radian(element.start - element.center);
25+
Angle ending_angle = irregular::angle_radian(element.end - element.center);
26+
if (!element.anticlockwise)
27+
std::swap(starting_angle, ending_angle);
28+
if (starting_angle <= ending_angle) {
29+
if (starting_angle <= M_PI
30+
&& M_PI <= ending_angle) {
31+
x_min_cur = std::min(x_min_cur, element.center.x - radius);
32+
}
33+
} else { // starting_angle > ending_angle
34+
if (starting_angle <= M_PI
35+
|| ending_angle <= M_PI) {
36+
x_min_cur = std::min(x_min_cur, element.center.x - radius);
37+
}
38+
}
39+
}
40+
41+
if (x_min > x_min_cur) {
42+
x_min = x_min_cur;
43+
element_start_pos = element_pos;
44+
}
45+
}
46+
47+
int current_status = 0;
48+
Shape current_support;
49+
for (ElementPos p = 0;
50+
p < (ElementPos)shape.elements.size();
51+
++p) {
52+
ElementPos element_pos = (element_start_pos + p) % shape.elements.size();
53+
const ShapeElement& element = shape.elements[element_pos];
54+
//std::cout << "p " << p
55+
// << " element_pos " << element_pos
56+
// << " " << element.to_string()
57+
// << " current_status " << current_status
58+
// << " current_support.size() " << current_support.elements.size()
59+
// << std::endl;
60+
if (current_status == 0) {
61+
switch (element.type) {
62+
case ShapeElementType::LineSegment: {
63+
if (element.end.x < element.start.x) {
64+
current_status = 1;
65+
current_support.elements = {element};
66+
} else if (element.end.x > element.start.x) {
67+
current_status = -1;
68+
current_support.elements = {element};
69+
}
70+
break;
71+
} case ShapeElementType::CircularArc: {
72+
// TODO
73+
throw std::invalid_argument("");
74+
break;
75+
}
76+
}
77+
} else if (current_status == 1) {
78+
switch (element.type) {
79+
case ShapeElementType::LineSegment: {
80+
if (element.end.x >= element.start.x) {
81+
if (!current_support.elements.empty()) {
82+
if (!is_hole) {
83+
supports.supporting_parts.push_back(current_support);
84+
} else {
85+
supports.supported_parts.push_back(current_support);
86+
}
87+
}
88+
if (element.end.x == element.start.x) {
89+
current_status = 0;
90+
current_support.elements.clear();
91+
} else {
92+
current_status = -1;
93+
current_support.elements = {element};
94+
}
95+
} else {
96+
current_support.elements.push_back(element);
97+
}
98+
break;
99+
} case ShapeElementType::CircularArc: {
100+
// TODO
101+
throw std::invalid_argument("");
102+
break;
103+
}
104+
}
105+
} else if (current_status == -1) {
106+
switch (element.type) {
107+
case ShapeElementType::LineSegment: {
108+
if (element.end.x <= element.start.x) {
109+
if (!current_support.elements.empty()) {
110+
if (!is_hole) {
111+
supports.supported_parts.push_back(current_support);
112+
} else {
113+
supports.supporting_parts.push_back(current_support);
114+
}
115+
}
116+
if (element.end.x == element.start.x) {
117+
current_status = 0;
118+
current_support.elements.clear();
119+
} else {
120+
current_status = 1;
121+
current_support.elements = {element};
122+
}
123+
} else {
124+
current_support.elements.push_back(element);
125+
}
126+
break;
127+
} case ShapeElementType::CircularArc: {
128+
// TODO
129+
throw std::invalid_argument("");
130+
break;
131+
}
132+
}
133+
}
134+
}
135+
136+
if (!current_support.elements.empty()) {
137+
if ((current_status == 1 && !is_hole)
138+
|| (current_status == -1 && is_hole)) {
139+
supports.supporting_parts.push_back(current_support);
140+
} else if ((current_status == -1 && !is_hole)
141+
|| (current_status == 1 && is_hole)) {
142+
supports.supported_parts.push_back(current_support);
143+
}
144+
}
145+
146+
if (!is_hole) {
147+
for (Shape& support: supports.supporting_parts)
148+
support = support.reverse();
149+
} else {
150+
for (Shape& support: supports.supported_parts)
151+
support = support.reverse();
152+
}
153+
154+
return supports;
155+
}
156+
157+
ShapeSupports irregular::compute_shape_supports(
158+
const Shape& shape,
159+
const std::vector<Shape>& holes)
160+
{
161+
ShapeSupports supports = compute_shape_supports(shape, false);
162+
for (const Shape& hole: holes) {
163+
ShapeSupports hole_supports = compute_shape_supports(hole, true);
164+
supports.supported_parts.insert(
165+
supports.supported_parts.end(),
166+
hole_supports.supported_parts.begin(),
167+
hole_supports.supported_parts.end());
168+
supports.supporting_parts.insert(
169+
supports.supporting_parts.end(),
170+
hole_supports.supporting_parts.begin(),
171+
hole_supports.supporting_parts.end());
172+
}
173+
return supports;
174+
}

src/irregular/shape_supports.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include "packingsolver/irregular/shape.hpp"
4+
5+
namespace packingsolver
6+
{
7+
namespace irregular
8+
{
9+
10+
struct ShapeSupports
11+
{
12+
std::vector<Shape> supporting_parts;
13+
14+
std::vector<Shape> supported_parts;
15+
};
16+
17+
ShapeSupports compute_shape_supports(
18+
const Shape& shape,
19+
bool is_hole);
20+
21+
ShapeSupports compute_shape_supports(
22+
const Shape& shape,
23+
const std::vector<Shape>& holes);
24+
25+
}
26+
}

test/irregular/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ target_sources(PackingSolver_irregular_test PRIVATE
66
shape_extract_borders_test.cpp
77
shape_self_intersections_removal_test.cpp
88
shape_trapezoidation_test.cpp
9+
shape_supports_test.cpp
910
irregular_test.cpp)
1011
target_include_directories(PackingSolver_irregular_test PRIVATE
1112
${PROJECT_SOURCE_DIR}/src)
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "irregular/shape_supports.hpp"
2+
3+
#include <gtest/gtest.h>
4+
5+
using namespace packingsolver;
6+
using namespace packingsolver::irregular;
7+
8+
struct ComputeShapeSupportsTestParams
9+
{
10+
Shape shape;
11+
std::vector<Shape> holes;
12+
std::vector<Shape> expected_supporting_parts;
13+
std::vector<Shape> expected_supported_parts;
14+
};
15+
16+
class IrregularComputeShapeSupportsTest: public testing::TestWithParam<ComputeShapeSupportsTestParams> { };
17+
18+
TEST_P(IrregularComputeShapeSupportsTest, ComputeShapeSupports)
19+
{
20+
ComputeShapeSupportsTestParams test_params = GetParam();
21+
std::cout << "shape:" << std::endl;
22+
std::cout << "- " << test_params.shape.to_string(0) << std::endl;
23+
std::cout << "holes:" << std::endl;
24+
for (const Shape& hole: test_params.holes)
25+
std::cout << "- " << hole.to_string(2) << std::endl;
26+
27+
ShapeSupports supports = compute_shape_supports(test_params.shape, test_params.holes);
28+
std::cout << "supporting_parts:" << std::endl;
29+
for (const auto& support: supports.supporting_parts) {
30+
for (ElementPos element_pos = 0;
31+
element_pos < (ElementPos)support.elements.size();
32+
++element_pos) {
33+
const ShapeElement& element = support.elements[element_pos];
34+
if (element_pos == 0) {
35+
std::cout << "- " << element.to_string() << std::endl;
36+
} else {
37+
std::cout << " " << element.to_string() << std::endl;
38+
}
39+
}
40+
}
41+
std::cout << "supported_parts:" << std::endl;
42+
for (const auto& support: supports.supported_parts) {
43+
for (ElementPos element_pos = 0;
44+
element_pos < (ElementPos)support.elements.size();
45+
++element_pos) {
46+
const ShapeElement& element = support.elements[element_pos];
47+
if (element_pos == 0) {
48+
std::cout << "- " << element.to_string() << std::endl;
49+
} else {
50+
std::cout << " " << element.to_string() << std::endl;
51+
}
52+
}
53+
}
54+
55+
ASSERT_EQ(supports.supporting_parts.size(), test_params.expected_supporting_parts.size());
56+
ASSERT_EQ(supports.supported_parts.size(), test_params.expected_supported_parts.size());
57+
for (ElementPos pos = 0;
58+
pos < (ElementPos)supports.supporting_parts.size();
59+
++pos) {
60+
Shape support = supports.supporting_parts[pos];
61+
Shape expected_support = test_params.expected_supporting_parts[pos];
62+
ASSERT_EQ(supports.supported_parts.size(), test_params.expected_supported_parts.size());
63+
for (ElementPos element_pos = 0;
64+
element_pos < (ElementPos)support.elements.size();
65+
++element_pos) {
66+
EXPECT_EQ(support.elements[element_pos], expected_support.elements[element_pos]);
67+
}
68+
}
69+
for (ElementPos pos = 0;
70+
pos < (ElementPos)supports.supported_parts.size();
71+
++pos) {
72+
Shape support = supports.supported_parts[pos];
73+
Shape expected_support = test_params.expected_supported_parts[pos];
74+
ASSERT_EQ(supports.supported_parts.size(), test_params.expected_supported_parts.size());
75+
for (ElementPos element_pos = 0;
76+
element_pos < (ElementPos)support.elements.size();
77+
++element_pos) {
78+
EXPECT_EQ(support.elements[element_pos], expected_support.elements[element_pos]);
79+
}
80+
}
81+
}
82+
83+
INSTANTIATE_TEST_SUITE_P(
84+
Irregular,
85+
IrregularComputeShapeSupportsTest,
86+
testing::ValuesIn(std::vector<ComputeShapeSupportsTestParams>{
87+
{
88+
build_shape({{0, 0}, {1, 0}, {1, 1}, {0, 1}}),
89+
{},
90+
{build_shape({{0, 1}, {1, 1}}, true)},
91+
{build_shape({{0, 0}, {1, 0}}, true)},
92+
}, {
93+
build_shape({{0, 0}, {1, 0}, {2, 1}, {1, 1}}),
94+
{},
95+
{build_shape({{0, 0}, {1, 1}, {2, 1}}, true)},
96+
{build_shape({{0, 0}, {1, 0}, {2, 1}}, true)},
97+
}, {
98+
build_shape({{0, 0}, {7, 0}, {6, 2}, {5, 2}, {4, 1}, {3, 1}, {2, 2}, {1, 2}}),
99+
{},
100+
{build_shape({{0, 0}, {1, 2}, {2, 2}, {3, 1}, {4, 1}, {5, 2}, {6, 2}, {7, 0}}, true)},
101+
{build_shape({{0, 0}, {7, 0}}, true)},
102+
}, {
103+
build_shape({{0, 0}, {2, 0}, {2, 1}, {1, 1}, {1, 2}, {2, 2}, {2, 3}, {0, 3}}),
104+
{},
105+
{
106+
build_shape({{1, 1}, {2, 1}}, true),
107+
build_shape({{0, 3}, {2, 3}}, true),
108+
}, {
109+
build_shape({{0, 0}, {2, 0}}, true),
110+
build_shape({{1, 2}, {2, 2}}, true),
111+
},
112+
}, {
113+
build_shape({{0, 0}, {3, 0}, {3, 3}, {0, 3}}),
114+
{build_shape({{1, 1}, {2, 1}, {2, 2}, {1, 2}}),},
115+
{
116+
build_shape({{0, 3}, {3, 3}}, true),
117+
build_shape({{1, 1}, {2, 1}}, true),
118+
}, {
119+
build_shape({{0, 0}, {3, 0}}, true),
120+
build_shape({{1, 2}, {2, 2}}, true),
121+
},
122+
}}));

0 commit comments

Comments
 (0)