Nighty3912 commited on
Commit
5738da0
·
verified ·
1 Parent(s): 0d20dea

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +11 -434
app.py CHANGED
@@ -54,6 +54,9 @@ class TextOverlapError(Exception):
54
  """Raised when the text overlaps with the inner contours (with a margin of 0.75).Please provide larger value for boundary length and width."""
55
  pass
56
 
 
 
 
57
  # ---------------------
58
  # Global Model Initialization with caching and print statements
59
  # ---------------------
@@ -243,420 +246,6 @@ def exclude_scaling_box(image: np.ndarray, bbox: np.ndarray, orig_size: tuple, p
243
  image[expanded_y_min:expanded_y_max, expanded_x_min:expanded_x_max] = 0
244
  return image
245
 
246
- # def resample_contour(contour):
247
- # num_points = 1000
248
- # smoothing_factor = 5
249
- # spline_degree = 3
250
- # if len(contour) < spline_degree + 1:
251
- # raise ValueError(f"Contour must have at least {spline_degree + 1} points, but has {len(contour)} points.")
252
- # contour = contour[:, 0, :]
253
- # tck, _ = splprep([contour[:, 0], contour[:, 1]], s=smoothing_factor)
254
- # u = np.linspace(0, 1, num_points)
255
- # resampled_points = splev(u, tck)
256
- # smoothed_x = gaussian_filter1d(resampled_points[0], sigma=1)
257
- # smoothed_y = gaussian_filter1d(resampled_points[1], sigma=1)
258
- # return np.array([smoothed_x, smoothed_y]).T
259
-
260
- # # ---------------------
261
- # # Add the missing extract_outlines function
262
- # # ---------------------
263
- # def extract_outlines(binary_image: np.ndarray) -> (np.ndarray, list):
264
- # contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
265
- # outline_image = np.zeros_like(binary_image)
266
- # cv2.drawContours(outline_image, contours, -1, (255), thickness=2)
267
- # return cv2.bitwise_not(outline_image), contours
268
-
269
- # # # ---------------------
270
- # # # Functions for Finger Cut Clearance
271
- # # # ---------------------
272
- # # def union_tool_and_circle(tool_polygon: Polygon, center_inch, circle_diameter=1.0):
273
- # # radius = circle_diameter / 2.0
274
- # # circle_poly = Point(center_inch).buffer(radius, resolution=64)
275
- # # union_poly = tool_polygon.union(circle_poly)
276
- # # return union_poly
277
-
278
-
279
-
280
-
281
- # # def build_tool_polygon(points_inch):
282
- # # return Polygon(points_inch)
283
-
284
- # # def polygon_to_exterior_coords(poly: Polygon):
285
- # # if poly.geom_type == "MultiPolygon":
286
- # # biggest = max(poly.geoms, key=lambda g: g.area)
287
- # # poly = biggest
288
- # # if not poly.exterior:
289
- # # return []
290
- # # return list(poly.exterior.coords)
291
-
292
-
293
- # # from shapely.geometry import Point, Polygon
294
- # # import numpy as np
295
- # # import random
296
-
297
- # # from shapely.geometry import Point, Polygon
298
- # # import numpy as np
299
- # # import random
300
-
301
- # # def place_finger_cut_adjusted(
302
- # # tool_polygon: Polygon,
303
- # # points_inch: list,
304
- # # existing_centers: list,
305
- # # all_polygons: list,
306
- # # circle_diameter: float = 1.0,
307
- # # min_gap: float = 0.5,
308
- # # max_attempts: int = 100
309
- # # ) -> (Polygon, tuple):
310
-
311
- # # needed_center_distance = circle_diameter + min_gap
312
- # # radius = circle_diameter / 2.0
313
- # # attempts = 0
314
- # # timeout_secs = 10 # 100s timeout
315
- # # start_time = time.perf_counter()
316
- # # fallback_triggered = False
317
-
318
- # # # Randomize candidate points order.
319
- # # indices = list(range(len(points_inch)))
320
- # # random.shuffle(indices)
321
-
322
- # # while attempts < max_attempts and not fallback_triggered:
323
- # # for i in indices:
324
- # # if time.perf_counter() - start_time >= timeout_secs:
325
- # # fallback_triggered = True
326
- # # break
327
- # # cx, cy = points_inch[i]
328
- # # # Try small adjustments around the candidate point.
329
- # # for dx in np.linspace(-0.3, 0.3, 7): # adjust by ±0.3 inches in 7 steps
330
- # # for dy in np.linspace(-0.3, 0.3, 7):
331
- # # if time.perf_counter() - start_time >= timeout_secs:
332
- # # fallback_triggered = True
333
- # # break
334
- # # candidate_center = (cx + dx, cy + dy)
335
-
336
- # # # Ensure candidate center is not too close to any already placed centers.
337
- # # if any(np.hypot(candidate_center[0] - ex, candidate_center[1] - ey) < needed_center_distance
338
- # # for ex, ey in existing_centers):
339
- # # continue
340
-
341
- # # # Create candidate circle with a high resolution.
342
- # # candidate_circle = Point(candidate_center).buffer(radius, resolution=64)
343
-
344
- # # # Reject candidate if circle is completely inside the tool polygon.
345
- # # if tool_polygon.contains(candidate_circle):
346
- # # continue
347
-
348
- # # # Also reject candidate if circle does not intersect the tool at all.
349
- # # if not candidate_circle.intersects(tool_polygon):
350
- # # continue
351
-
352
-
353
- # # # Ensure that the candidate circle crosses the tool boundary.
354
- # # inter_area = candidate_circle.intersection(tool_polygon).area
355
- # # if inter_area <= 0 or inter_area >= candidate_circle.area:
356
- # # continue
357
-
358
- # # # Verify candidate circle is not too close to any neighboring tool polygons.
359
- # # too_close = False
360
- # # for other_poly in all_polygons:
361
- # # if other_poly.equals(tool_polygon):
362
- # # continue
363
- # # if candidate_circle.buffer(0.1).intersects(other_poly):
364
- # # too_close = True
365
- # # if other_poly.distance(candidate_circle) < min_gap:
366
- # # too_close = True
367
- # # break
368
- # # if too_close:
369
- # # continue
370
-
371
- # # # Attempt the union, using a buffering trick to fix potential geometry problems.
372
- # # try:
373
- # # union_poly = tool_polygon.union(candidate_circle)
374
- # # except Exception:
375
- # # union_poly = tool_polygon.buffer(0).union(candidate_circle.buffer(0))
376
-
377
- # # # Verify that the union is a single contiguous polygon.
378
- # # if union_poly.geom_type == "MultiPolygon" and len(union_poly.geoms) > 1:
379
- # # continue
380
-
381
- # # # If the union did not change the polygon (no effective union), skip candidate.
382
- # # if union_poly.equals(tool_polygon):
383
- # # continue
384
-
385
- # # # We have found a valid candidate.
386
- # # existing_centers.append(candidate_center)
387
- # # return union_poly, candidate_center
388
- # # if fallback_triggered:
389
- # # break
390
- # # attempts += 1
391
- # # if fallback_triggered:
392
- # # print("In fallback block")
393
-
394
- # # # Fallback: If no candidate was found after max_attempts, force a candidate from median of points.
395
- # # candidate_center = points_inch[len(points_inch) // 2]
396
- # # candidate_circle = Point(candidate_center).buffer(radius, resolution=64)
397
- # # try:
398
- # # too_close= False
399
- # # for other_poly in all_polygons:
400
-
401
- # # if other_poly.equals(tool_polygon):
402
- # # continue
403
- # # if candidate_circle.buffer(0.1).intersects(other_poly):
404
- # # too_close = True
405
- # # if other_poly.distance(candidate_circle) < min_gap:
406
- # # too_close = True
407
- # # if too_close:
408
- # # continue
409
- # # union_poly = tool_polygon.union(candidate_circle)
410
- # # except Exception:
411
- # # too_close= False
412
- # # for other_poly in all_polygons:
413
-
414
- # # if other_poly.equals(tool_polygon):
415
- # # continue
416
- # # if candidate_circle.buffer(0.1).intersects(other_poly):
417
- # # too_close = True
418
- # # if other_poly.distance(candidate_circle) < min_gap:
419
- # # too_close = True
420
- # # if too_close:
421
- # # continue
422
- # # union_poly = tool_polygon.buffer(0).union(candidate_circle.buffer(0))
423
- # # existing_centers.append(candidate_center)
424
- # # return union_poly, candidate_center
425
-
426
- # # # ---------------------
427
- # # # DXF Spline and Boundary Functions
428
- # # # ---------------------
429
- # # def save_dxf_spline(inflated_contours, scaling_factor, height, finger_clearance=False):
430
- # degree = 3
431
- # closed = True
432
- # doc = ezdxf.new(units=0)
433
- # doc.units = ezdxf.units.IN
434
- # doc.header["$INSUNITS"] = ezdxf.units.IN
435
- # msp = doc.modelspace()
436
- # finger_cut_centers = []
437
- # final_polygons_inch = []
438
- # for contour in inflated_contours:
439
- # try:
440
- # resampled_contour = resample_contour(contour)
441
- # points_inch = [(x * scaling_factor, (height - y) * scaling_factor) for x, y in resampled_contour]
442
- # if len(points_inch) < 3:
443
- # continue
444
- # if np.linalg.norm(np.array(points_inch[0]) - np.array(points_inch[-1])) > 1e-2:
445
- # points_inch.append(points_inch[0])
446
- # tool_polygon = build_tool_polygon(points_inch)
447
- # if finger_clearance:
448
- # union_poly, center = place_finger_cut_adjusted(tool_polygon, points_inch, finger_cut_centers, final_polygons_inch, circle_diameter=1.0, min_gap=0.25, max_attempts=100)
449
- # if union_poly is not None:
450
- # tool_polygon = union_poly
451
- # exterior_coords = polygon_to_exterior_coords(tool_polygon)
452
- # if len(exterior_coords) < 3:
453
- # continue
454
- # msp.add_spline(exterior_coords, degree=degree, dxfattribs={"layer": "TOOLS"})
455
- # final_polygons_inch.append(tool_polygon)
456
- # except ValueError as e:
457
- # print(f"Skipping contour: {e}")
458
- # return doc, final_polygons_inch
459
-
460
- # import random
461
- # import time
462
- # import numpy as np
463
- # from shapely.geometry import Point, Polygon
464
-
465
- # # ---------------------
466
- # # Utility functions
467
- # # ---------------------
468
- # def union_tool_and_circle(tool_polygon: Polygon, center_inch, circle_diameter=1.0):
469
- # radius = circle_diameter / 2.0
470
- # circle_poly = Point(center_inch).buffer(radius, resolution=64)
471
- # union_poly = tool_polygon.union(circle_poly)
472
- # return union_poly
473
-
474
- # def build_tool_polygon(points_inch):
475
- # return Polygon(points_inch)
476
-
477
- # def polygon_to_exterior_coords(poly: Polygon):
478
- # if poly.geom_type == "MultiPolygon":
479
- # biggest = max(poly.geoms, key=lambda g: g.area)
480
- # poly = biggest
481
- # if not poly.exterior:
482
- # return []
483
- # return list(poly.exterior.coords)
484
-
485
- # ---------------------
486
- # Main candidate placement function
487
- # ---------------------
488
- # def place_finger_cut_adjusted(
489
- # tool_polygon1: Polygon,
490
- # points_inch: list,
491
- # existing_centers: list,
492
- # all_polygons: list,
493
- # circle_diameter: float = 1.0,
494
- # min_gap: float = 0.5,
495
- # max_attempts: int = 100
496
- # ) -> (Polygon, tuple):
497
- # """
498
- # Adjust and union a candidate circle (finger cut) with the tool_polygon.
499
- # If a candidate meeting all conditions is found, update existing_centers
500
- # and return the union and candidate_center.
501
-
502
- # If no candidate is found after max_attempts (or if a timeout is reached),
503
- # use a fallback candidate (the median point from points_inch).
504
- # """
505
- # needed_center_distance = circle_diameter + min_gap
506
- # radius = circle_diameter / 2.0
507
- # attempts = 0
508
- # timeout_secs = 0.1 # 100ms timeout
509
- # start_time = time.perf_counter()
510
- # fallback_triggered = False
511
- # tool_polygon= tool_polygon1
512
-
513
- # # Randomize candidate points order.
514
- # indices = list(range(len(points_inch)))
515
- # random.shuffle(indices)
516
-
517
- # while attempts < max_attempts and not fallback_triggered:
518
- # for i in indices:
519
- # if time.perf_counter() - start_time >= timeout_secs:
520
- # fallback_triggered = True
521
- # break
522
- # cx, cy = points_inch[i]
523
- # # Try small adjustments around the candidate point.
524
- # for dx in np.linspace(-0.3, 0.3, 7):
525
- # for dy in np.linspace(-0.3, 0.3, 7):
526
- # if time.perf_counter() - start_time >= timeout_secs:
527
- # fallback_triggered = True
528
- # break
529
- # candidate_center = (cx + dx, cy + dy)
530
-
531
- # # Ensure candidate center is not too close to any already placed centers.
532
- # if any(np.hypot(candidate_center[0] - ex, candidate_center[1] - ey) < needed_center_distance
533
- # for ex, ey in existing_centers):
534
- # continue
535
-
536
- # # Create candidate circle with high resolution.
537
- # candidate_circle = Point(candidate_center).buffer(radius, resolution=64)
538
-
539
- # # Reject candidate if circle is completely inside the tool polygon.
540
- # if tool_polygon.contains(candidate_circle):
541
- # continue
542
- # # Reject candidate if circle does not intersect the tool at all.
543
- # if not candidate_circle.intersects(tool_polygon):
544
- # continue
545
- # # Ensure that the candidate circle crosses the tool boundary.
546
- # inter_area = candidate_circle.intersection(tool_polygon).area
547
- # if inter_area <= 0 or inter_area >= candidate_circle.area:
548
- # continue
549
-
550
- # # Verify candidate circle is not too close to any neighboring tool polygons.
551
- # too_close = False
552
- # for other_poly in all_polygons:
553
- # if other_poly.equals(tool_polygon):
554
- # continue
555
- # # Use a small buffer around the circle for safety.
556
- # if candidate_circle.buffer(0.1).intersects(other_poly):
557
- # too_close = True
558
- # if other_poly.distance(candidate_circle) < min_gap:
559
- # too_close = True
560
- # break
561
- # if too_close:
562
- # continue
563
-
564
- # # Attempt the union, using buffering to fix any potential geometry issues.
565
- # try:
566
- # union_poly = tool_polygon.union(candidate_circle)
567
- # except Exception:
568
- # union_poly = tool_polygon.buffer(0).union(candidate_circle.buffer(0))
569
-
570
- # # Clean the unioned polygon.
571
- # union_poly = union_poly.buffer(0)
572
-
573
- # # Verify that the union is a single contiguous polygon.
574
- # if union_poly.geom_type == "MultiPolygon" and len(union_poly.geoms) > 1:
575
- # continue
576
-
577
- # # If the union did not change the tool polygon (no effective union), skip candidate.
578
- # if union_poly.equals(tool_polygon):
579
- # continue
580
-
581
- # # We have found a valid candidate. Update the centers list.
582
- # existing_centers.append(candidate_center)
583
- # return tool_polygon1, existing_centers[-1]
584
- # if fallback_triggered:
585
- # break
586
- # attempts += 1
587
-
588
- # # Fallback: If no candidate is found (or timeout reached), use a fallback candidate.
589
- # if fallback_triggered:
590
- # print("Fallback triggered")
591
- # # Use a fallback candidate – here the median point is used.
592
- # xs = [p[0] for p in points_inch]
593
- # ys = [p[1] for p in points_inch]
594
- # candidate_center = (np.median(xs), np.median(ys))
595
- # candidate_circle = Point(candidate_center).buffer(radius, resolution=64)
596
- # try:
597
- # union_poly = tool_polygon.union(candidate_circle)
598
- # except Exception:
599
- # union_poly = tool_polygon.buffer(0).union(candidate_circle.buffer(0))
600
- # union_poly = union_poly.buffer(0)
601
- # # Add the fallback center to avoid duplicate placements later.
602
- # existing_centers.append(candidate_center)
603
- # return tool_polygon1, existing_centers[-1]
604
-
605
- # ---------------------
606
- # DXF Spline and Boundary Functions
607
- # ---------------------
608
- # def save_dxf_spline(inflated_contours, scaling_factor, height, finger_clearance=False):
609
- # import ezdxf # assuming ezdxf is installed
610
- # degree = 3
611
- # closed = True
612
- # doc = ezdxf.new(units=0)
613
- # doc.units = ezdxf.units.IN
614
- # doc.header["$INSUNITS"] = ezdxf.units.IN
615
- # msp = doc.modelspace()
616
-
617
- # # Global shared lists for finger cut centers and final tool polygons.
618
- # finger_cut_centers = []
619
- # final_polygons_inch = []
620
-
621
- # for contour in inflated_contours:
622
- # try:
623
- # # resample_contour should be defined elsewhere;
624
- # # here it returns a list of (x, y) points.
625
- # resampled_contour = resample_contour(contour)
626
- # # Scale and flip Y coordinate according to height.
627
- # points_inch = [(x * scaling_factor, (height - y) * scaling_factor) for x, y in resampled_contour]
628
-
629
- # if len(points_inch) < 3:
630
- # continue
631
-
632
- # # Ensure the polygon is closed.
633
- # if np.linalg.norm(np.array(points_inch[0]) - np.array(points_inch[-1])) > 1e-2:
634
- # points_inch.append(points_inch[0])
635
-
636
- # tool_polygon = build_tool_polygon(points_inch)
637
-
638
- # # Add finger clearance cuts if needed.
639
- # if finger_clearance:
640
- # tool, center = place_finger_cut_adjusted(
641
- # tool_polygon, points_inch, finger_cut_centers, final_polygons_inch,
642
- # circle_diameter=1.0, min_gap=0.25, max_attempts=100
643
- # )
644
- # union_poly=union_tool_and_circle(tool,center)
645
- # if union_poly is not None:
646
- # tool_polygon = union_poly
647
-
648
- # exterior_coords = polygon_to_exterior_coords(tool_polygon)
649
- # if len(exterior_coords) < 3:
650
- # continue
651
-
652
- # # Add the tool geometry to the DXF document as a spline.
653
- # msp.add_spline(exterior_coords, degree=degree, dxfattribs={"layer": "TOOLS"})
654
- # final_polygons_inch.append(tool_polygon)
655
- # except ValueError as e:
656
- # print(f"Skipping contour: {e}")
657
-
658
- # return doc, final_polygons_inch
659
-
660
  import logging
661
  import time
662
  import signal
@@ -782,26 +371,6 @@ def build_tool_polygon(points_inch):
782
  logger.error(f"Error in build_tool_polygon: {e}")
783
  raise
784
 
785
- # def polygon_to_exterior_coords(poly: Polygon):
786
- # logger.info(f"Starting polygon_to_exterior_coords with polygon type {poly.geom_type}")
787
-
788
- # try:
789
- # if poly.geom_type == "MultiPolygon":
790
- # logger.debug("Converting MultiPolygon to single Polygon")
791
- # biggest = max(poly.geoms, key=lambda g: g.area)
792
- # poly = biggest
793
-
794
- # if not poly.exterior:
795
- # logger.warning("Polygon has no exterior")
796
- # return []
797
-
798
- # coords = list(poly.exterior.coords)
799
- # logger.info(f"Completed polygon_to_exterior_coords with {len(coords)} coordinates")
800
- # return coords
801
- # except Exception as e:
802
- # logger.error(f"Error in polygon_to_exterior_coords: {e}")
803
- # raise
804
-
805
  def polygon_to_exterior_coords(poly):
806
  logger.info(f"Starting polygon_to_exterior_coords with polygon type {poly.geom_type}")
807
 
@@ -1034,6 +603,14 @@ def save_dxf_spline(offset_value,inflated_contours, scaling_factor, height, fing
1034
  if len(exterior_coords) < 3:
1035
  logger.warning(f"Skipping contour {idx}: insufficient exterior points ({len(exterior_coords)})")
1036
  continue
 
 
 
 
 
 
 
 
1037
 
1038
  msp.add_spline(exterior_coords, degree=degree, dxfattribs={"layer": "TOOLS"})
1039
  final_polygons_inch.append(tool_polygon)
 
54
  """Raised when the text overlaps with the inner contours (with a margin of 0.75).Please provide larger value for boundary length and width."""
55
  pass
56
 
57
+ class FingerCutOverlapError(Exception):
58
+ """There was an overlap with fingercuts... Please try again to generate dxf."""
59
+ pass
60
  # ---------------------
61
  # Global Model Initialization with caching and print statements
62
  # ---------------------
 
246
  image[expanded_y_min:expanded_y_max, expanded_x_min:expanded_x_max] = 0
247
  return image
248
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  import logging
250
  import time
251
  import signal
 
371
  logger.error(f"Error in build_tool_polygon: {e}")
372
  raise
373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  def polygon_to_exterior_coords(poly):
375
  logger.info(f"Starting polygon_to_exterior_coords with polygon type {poly.geom_type}")
376
 
 
603
  if len(exterior_coords) < 3:
604
  logger.warning(f"Skipping contour {idx}: insufficient exterior points ({len(exterior_coords)})")
605
  continue
606
+ for existing_poly in final_polygons_inch:
607
+ if tool_polygon.intersects(existing_poly):
608
+ # Check if the intersection is more than just touching points
609
+ intersection = tool_polygon.intersection(existing_poly)
610
+ # If the intersection has ANY area (not just points touching)
611
+ if intersection.area > 0: # Zero tolerance for overlap
612
+ logger.error(f"Polygon {idx} overlaps with an existing polygon")
613
+ raise FingerCutOverlapError("There was an overlap with fingercuts... Please try again to generate dxf.")
614
 
615
  msp.add_spline(exterior_coords, degree=degree, dxfattribs={"layer": "TOOLS"})
616
  final_polygons_inch.append(tool_polygon)