@@ -371,98 +371,134 @@ std::vector<Shape> packingsolver::irregular::borders(
371371
372372Shape 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}
0 commit comments