@@ -371,98 +371,134 @@ std::vector<Shape> packingsolver::irregular::borders(
371
371
372
372
Shape irregular::inflate (
373
373
const Shape& shape,
374
+ const std::vector<Shape>& holes,
374
375
LengthDbl value)
375
376
{
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
+ }
378
393
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
379
407
for (ElementPos element_pos = 0 ;
380
408
element_pos < (ElementPos)shape.elements .size ();
381
409
++element_pos) {
382
410
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
426
445
};
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);
429
495
}
430
-
431
- inflated_shape.elements .push_back (new_element);
432
496
}
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 ;
465
501
}
466
-
502
+
467
503
return inflated_shape;
468
504
}
0 commit comments