Skip to content

Commit 870b68b

Browse files
committed
update according to feedback
- Implemented new inflate function in shape.cpp to support shapes with holes - Removed inflate method from trapezoid.hpp - Added GeneralizedTrapezoid::to_shape method for converting trapezoids to shapes - Modified all calls to trapezoid.inflate in branching_scheme.cpp - Added unit tests in shape_test.cpp to verify functionality of new inflate function
1 parent 87e77e4 commit 870b68b

File tree

6 files changed

+627
-642
lines changed

6 files changed

+627
-642
lines changed

src/irregular/branching_scheme.cpp

+24-4
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,14 @@ BranchingScheme::BranchingScheme(
165165
if (cleaned_shape.elements.size() > 2) {
166166
auto trapezoids = trapezoidation(cleaned_shape);
167167
for (const GeneralizedTrapezoid& trapezoid: trapezoids) {
168+
// Convert trapezoid to shape, then use Shape's inflate function
169+
Shape trapezoid_shape = trapezoid.to_shape();
170+
Shape inflated_shape = inflate(trapezoid_shape, instance().parameters().item_bin_minimum_spacing);
171+
// Then convert the inflated shape back to trapezoid
172+
GeneralizedTrapezoid inflated_trapezoid = trapezoidation(inflated_shape)[0];
168173
UncoveredTrapezoid defect(
169174
-1,
170-
trapezoid.clean().inflate(instance().parameters().item_bin_minimum_spacing));
175+
inflated_trapezoid);
171176
bb_bin_type_x.defects.push_back(defect);
172177
}
173178
}
@@ -178,9 +183,14 @@ BranchingScheme::BranchingScheme(
178183
if (cleaned_shape.elements.size() > 2) {
179184
auto trapezoids = trapezoidation(cleaned_shape);
180185
for (const GeneralizedTrapezoid& trapezoid: trapezoids) {
186+
// Convert trapezoid to shape, then use Shape's inflate function
187+
Shape trapezoid_shape = trapezoid.to_shape();
188+
Shape inflated_shape = inflate(trapezoid_shape, instance().parameters().item_bin_minimum_spacing);
189+
// Then convert the inflated shape back to trapezoid
190+
GeneralizedTrapezoid inflated_trapezoid = trapezoidation(inflated_shape)[0];
181191
UncoveredTrapezoid defect(
182192
-1,
183-
trapezoid.clean().inflate(instance().parameters().item_bin_minimum_spacing));
193+
inflated_trapezoid);
184194
bb_bin_type_y.defects.push_back(defect);
185195
}
186196
}
@@ -204,9 +214,14 @@ BranchingScheme::BranchingScheme(
204214
cleaned_shape,
205215
cleaned_holes);
206216
for (const GeneralizedTrapezoid& trapezoid: trapezoids) {
217+
// Convert trapezoid to shape, then use Shape's inflate function
218+
Shape trapezoid_shape = trapezoid.to_shape();
219+
Shape inflated_shape = inflate(trapezoid_shape, instance().parameters().item_bin_minimum_spacing);
220+
// Then convert the inflated shape back to trapezoid
221+
GeneralizedTrapezoid inflated_trapezoid = trapezoidation(inflated_shape)[0];
207222
UncoveredTrapezoid defect(
208223
defect_id,
209-
trapezoid.clean().inflate(instance().parameters().item_bin_minimum_spacing));
224+
inflated_trapezoid);
210225
bb_bin_type_x.defects.push_back(defect);
211226
}
212227
}
@@ -227,9 +242,14 @@ BranchingScheme::BranchingScheme(
227242
cleaned_shape,
228243
cleaned_holes);
229244
for (const GeneralizedTrapezoid& trapezoid: trapezoids) {
245+
// Convert trapezoid to shape, then use Shape's inflate function
246+
Shape trapezoid_shape = trapezoid.to_shape();
247+
Shape inflated_shape = inflate(trapezoid_shape, instance().parameters().item_bin_minimum_spacing);
248+
// Then convert the inflated shape back to trapezoid
249+
GeneralizedTrapezoid inflated_trapezoid = trapezoidation(inflated_shape)[0];
230250
UncoveredTrapezoid defect(
231251
defect_id,
232-
trapezoid.clean().inflate(instance().parameters().item_bin_minimum_spacing));
252+
inflated_trapezoid);
233253
bb_bin_type_y.defects.push_back(defect);
234254
}
235255
}

src/irregular/shape.cpp

+118-82
Original file line numberDiff line numberDiff line change
@@ -371,98 +371,134 @@ std::vector<Shape> packingsolver::irregular::borders(
371371

372372
Shape irregular::inflate(
373373
const Shape& shape,
374+
const std::vector<Shape>& holes,
374375
LengthDbl value)
375376
{
376-
Shape inflated_shape;
377-
inflated_shape.elements.reserve(shape.elements.size());
377+
// 1. Inflate the outer contour
378+
Shape inflated_shape = inflate(shape, value);
379+
380+
// 2. Shrink the holes (using opposite inflation value for holes)
381+
std::vector<Shape> inflated_holes;
382+
for (const Shape& hole : holes) {
383+
// Note that holes need to use opposite value, as holes expand inward
384+
Shape inflated_hole = inflate(hole, -value);
385+
inflated_holes.push_back(inflated_hole);
386+
}
387+
388+
// 3. Set the holes for the inflated shape
389+
inflated_shape.holes = inflated_holes;
390+
391+
return inflated_shape;
392+
}
378393

394+
Shape irregular::inflate(
395+
const Shape& shape,
396+
LengthDbl value)
397+
{
398+
// If inflation value is zero, return the original shape
399+
if (equal(value, 0)) {
400+
return shape;
401+
}
402+
403+
Shape inflated_shape;
404+
inflated_shape.elements.reserve(shape.elements.size() * 2); // Pre-allocate space, considering possible arc additions
405+
406+
// 1. Calculate normal vectors for each edge and inflate the edges
379407
for (ElementPos element_pos = 0;
380408
element_pos < (ElementPos)shape.elements.size();
381409
++element_pos) {
382410
const ShapeElement& element = shape.elements[element_pos];
383-
ElementPos element_next_pos = (element_pos + 1) % shape.elements.size();
384-
const ShapeElement& element_next = shape.elements[element_next_pos];
385-
386-
// calculate the normal vector of the current edge
387-
LengthDbl dx = element.end.x - element.start.x;
388-
LengthDbl dy = element.end.y - element.start.y;
389-
LengthDbl length = std::sqrt(dx * dx + dy * dy);
390-
LengthDbl nx = dy / length;
391-
LengthDbl ny = -dx / length;
392-
393-
// calculate the normal vector of the next edge
394-
LengthDbl dx_next = element_next.end.x - element_next.start.x;
395-
LengthDbl dy_next = element_next.end.y - element_next.start.y;
396-
LengthDbl length_next = std::sqrt(dx_next * dx_next + dy_next * dy_next);
397-
LengthDbl nx_next = dy_next / length_next;
398-
LengthDbl ny_next = -dx_next / length_next;
399-
400-
// calculate the average normal vector of the vertex
401-
LengthDbl nx_avg = (nx + nx_next) / 2;
402-
LengthDbl ny_avg = (ny + ny_next) / 2;
403-
LengthDbl length_avg = std::sqrt(nx_avg * nx_avg + ny_avg * ny_avg);
404-
nx_avg /= length_avg;
405-
ny_avg /= length_avg;
406-
407-
// create a new edge
408-
ShapeElement new_element;
409-
new_element.type = element.type;
410-
new_element.start = Point{
411-
element.start.x + nx * value,
412-
element.start.y + ny * value
413-
};
414-
new_element.end = Point{
415-
element.end.x + nx * value,
416-
element.end.y + ny * value
417-
};
418-
419-
// add the arc at the vertex
420-
if (element_pos > 0) {
421-
ShapeElement arc_element;
422-
arc_element.type = ShapeElementType::CircularArc;
423-
arc_element.start = Point{
424-
element.start.x + nx_avg * value,
425-
element.start.y + ny_avg * value
411+
412+
// Get the adjacent next element
413+
ElementPos next_pos = (element_pos + 1) % shape.elements.size();
414+
const ShapeElement& next_element = shape.elements[next_pos];
415+
416+
// Only process line segment elements (currently supporting line segments, can be extended to support arcs)
417+
if (element.type == ShapeElementType::LineSegment) {
418+
// Calculate the normal vector of the current edge
419+
LengthDbl dx = element.end.x - element.start.x;
420+
LengthDbl dy = element.end.y - element.start.y;
421+
LengthDbl length = std::sqrt(dx * dx + dy * dy);
422+
423+
// Ensure length is not zero to avoid division by zero
424+
if (length < 1e-10) {
425+
continue;
426+
}
427+
428+
// Calculate unit normal vector (perpendicular to edge direction)
429+
LengthDbl nx = dy / length;
430+
LengthDbl ny = -dx / length;
431+
432+
// Create inflated edge
433+
ShapeElement new_element;
434+
new_element.type = element.type; // Keep element type unchanged
435+
436+
// Extend vertices along normal vector direction
437+
new_element.start = Point{
438+
element.start.x + nx * value,
439+
element.start.y + ny * value
440+
};
441+
442+
new_element.end = Point{
443+
element.end.x + nx * value,
444+
element.end.y + ny * value
426445
};
427-
arc_element.end = new_element.start;
428-
inflated_shape.elements.push_back(arc_element);
446+
447+
// Handle connections between adjacent edges
448+
if (next_element.type == ShapeElementType::LineSegment) {
449+
// Calculate normal vector of the next edge
450+
LengthDbl dx_next = next_element.end.x - next_element.start.x;
451+
LengthDbl dy_next = next_element.end.y - next_element.start.y;
452+
LengthDbl length_next = std::sqrt(dx_next * dx_next + dy_next * dy_next);
453+
454+
if (length_next >= 1e-10) {
455+
LengthDbl nx_next = dy_next / length_next;
456+
LengthDbl ny_next = -dx_next / length_next;
457+
458+
// Calculate average of two normal vectors for connecting corner
459+
LengthDbl nx_avg = (nx + nx_next) / 2;
460+
LengthDbl ny_avg = (ny + ny_next) / 2;
461+
LengthDbl length_avg = std::sqrt(nx_avg * nx_avg + ny_avg * ny_avg);
462+
463+
// Avoid division by zero
464+
if (length_avg >= 1e-10) {
465+
nx_avg /= length_avg;
466+
ny_avg /= length_avg;
467+
468+
// Create a more precise inflated vertex at the corner
469+
new_element.end = Point{
470+
element.end.x + nx_avg * value / (nx * nx_avg + ny * ny_avg),
471+
element.end.y + ny_avg * value / (nx * nx_avg + ny * ny_avg)
472+
};
473+
}
474+
}
475+
}
476+
477+
// Add inflated edge to new shape
478+
inflated_shape.elements.push_back(new_element);
479+
480+
// If inflation value is positive and not the last element, we can add connecting arcs
481+
if (value > 0 && element_pos < shape.elements.size() - 1) {
482+
// TODO: Arc elements can be added here for more precise inflation, especially at corners
483+
// Simplified implementation for now, using only line segments
484+
}
485+
} else if (element.type == ShapeElementType::Arc) {
486+
// TODO: Support for inflating arc elements
487+
// Simplified implementation for now, preserving the basic shape of arcs, only adjusting radius
488+
489+
ShapeElement new_element = element;
490+
// For arcs, need to adjust radius and center position
491+
// This is a simplified implementation, more precise calculations may be needed in practice
492+
493+
// Add inflated arc to new shape
494+
inflated_shape.elements.push_back(new_element);
429495
}
430-
431-
inflated_shape.elements.push_back(new_element);
432496
}
433-
434-
// add the arc at the last vertex
435-
if (!shape.elements.empty()) {
436-
const ShapeElement& first_element = shape.elements.front();
437-
const ShapeElement& last_element = shape.elements.back();
438-
439-
LengthDbl dx_first = first_element.end.x - first_element.start.x;
440-
LengthDbl dy_first = first_element.end.y - first_element.start.y;
441-
LengthDbl length_first = std::sqrt(dx_first * dx_first + dy_first * dy_first);
442-
LengthDbl nx_first = dy_first / length_first;
443-
LengthDbl ny_first = -dx_first / length_first;
444-
445-
LengthDbl dx_last = last_element.end.x - last_element.start.x;
446-
LengthDbl dy_last = last_element.end.y - last_element.start.y;
447-
LengthDbl length_last = std::sqrt(dx_last * dx_last + dy_last * dy_last);
448-
LengthDbl nx_last = dy_last / length_last;
449-
LengthDbl ny_last = -dx_last / length_last;
450-
451-
LengthDbl nx_avg = (nx_first + nx_last) / 2;
452-
LengthDbl ny_avg = (ny_first + ny_last) / 2;
453-
LengthDbl length_avg = std::sqrt(nx_avg * nx_avg + ny_avg * ny_avg);
454-
nx_avg /= length_avg;
455-
ny_avg /= length_avg;
456-
457-
ShapeElement arc_element;
458-
arc_element.type = ShapeElementType::CircularArc;
459-
arc_element.start = Point{
460-
first_element.start.x + nx_avg * value,
461-
first_element.start.y + ny_avg * value
462-
};
463-
arc_element.end = inflated_shape.elements.front().start;
464-
inflated_shape.elements.push_back(arc_element);
497+
498+
// Ensure shape is closed
499+
if (!inflated_shape.elements.empty()) {
500+
inflated_shape.elements.back().end = inflated_shape.elements.front().start;
465501
}
466-
502+
467503
return inflated_shape;
468504
}

src/irregular/shape.hpp

+20
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ bool operator==(
3030
const Shape& shape_1,
3131
const Shape& shape_2);
3232

33+
/**
34+
* Inflate a shape, creating a new shape based on the given distance value from the original shape's outline.
35+
*
36+
* @param shape The original shape to be inflated
37+
* @param holes List of hole shapes within the main shape
38+
* @param value Inflation distance, positive value for outward inflation, negative value for inward shrinking
39+
* @return The inflated shape
40+
*/
41+
Shape inflate(
42+
const Shape& shape,
43+
const std::vector<Shape>& holes,
44+
LengthDbl value);
45+
46+
/**
47+
* Inflate a shape, creating a new shape based on the given distance value from the original shape's outline.
48+
*
49+
* @param shape The original shape to be inflated
50+
* @param value Inflation distance, positive value for outward inflation, negative value for inward shrinking
51+
* @return The inflated shape
52+
*/
3353
Shape inflate(
3454
const Shape& shape,
3555
LengthDbl value);

src/irregular/trapezoid.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "irregular/trapezoid.hpp"
2+
3+
#include <iomanip>
4+
5+
using namespace packingsolver;
6+
using namespace packingsolver::irregular;
7+
8+
std::string GeneralizedTrapezoid::to_string() const
9+
{
10+
std::stringstream ss;
11+
std::streamsize precision = std::cout.precision();
12+
ss << std::setprecision(std::numeric_limits<double>::digits10 + 1)
13+
<< "y_bottom " << y_bottom()
14+
<< " y_top " << y_top()
15+
<< " x_bottom_left " << x_bottom_left()
16+
<< " x_bottom_right " << x_bottom_right()
17+
<< " x_top_left " << x_top_left()
18+
<< " x_top_right " << x_top_right()
19+
<< std::setprecision(precision);
20+
return ss.str();
21+
}
22+
23+
std::string GeneralizedTrapezoid::to_svg(
24+
const std::string& color,
25+
double factor) const
26+
{
27+
return std::string("<path d=\"")
28+
+ "M" + std::to_string(x_bottom_left() * factor) + "," + std::to_string(-y_bottom() * factor)
29+
+ "L" + std::to_string(x_bottom_right() * factor) + "," + std::to_string(-y_bottom() * factor)
30+
+ "L" + std::to_string(x_top_right() * factor) + "," + std::to_string(-y_top() * factor)
31+
+ "L" + std::to_string(x_top_left() * factor) + "," + std::to_string(-y_top() * factor)
32+
+ "Z\""
33+
+ " stroke=\"black\""
34+
+ " stroke-width=\"1\""
35+
+ " fill=\"" + color + "\""
36+
+ " fill-opacity=\"0.2\""
37+
+ "/>\n";
38+
}
39+
40+
std::ostream& packingsolver::irregular::operator<<(
41+
std::ostream &os,
42+
const GeneralizedTrapezoid& trapezoid)
43+
{
44+
os << trapezoid.to_string();
45+
return os;
46+
}

0 commit comments

Comments
 (0)