Browse Source

Updated comments

CURA-7951
c.lamboo 1 year ago
parent
commit
ac78de1227
1 changed files with 52 additions and 17 deletions
  1. 52 17
      cura/Arranging/GridArrange.py

+ 52 - 17
cura/Arranging/GridArrange.py

@@ -118,11 +118,40 @@ class GridArrange(Arranger):
 
             return
 
+        # If there are multiple fixed nodes, an optimal solution is not always possible
+        # We will try to find an offset that minimizes the number of grid intersections
+        # with fixed nodes. The algorithm below achieves this by utilizing a scanline
+        # algorithm. In this algorithm each axis is solved separately as offsetting
+        # is completely independent in each axis. The comments explaining the algorithm
+        # below are for the x-axis, but the same applies for the y-axis.
+        #
+        # Each node either occupies ceil((node.right - node.right) / grid_width) or
+        # ceil((node.right - node.right) / grid_width) + 1 grid squares. We will call
+        # these the node's "footprint".
+        #
+        #                      ┌────────────────┐
+        #   minimum food print │      NODE      │
+        #                      └────────────────┘
+        # │    grid 1   │    grid 2    │    grid 3    │    grid 4    |    grid 5    |
+        #                             ┌────────────────┐
+        #          maximum food print │      NODE      │
+        #                             └────────────────┘
+        #
+        # The algorithm will find the grid offset such that the number of nodes with
+        # a _minimal_ footprint is _maximized_.
+
+        # The scanline algorithm works as follows, we create events for both end points
+        # of each node's footprint. The event have two properties,
+        # - the coordinate: the amount the endpoint can move to the
+        #      left before it crosses a grid line
+        # - the change: either +1 or -1, indicating whether crossing the grid line
+        #      would result in a minimal footprint node becoming a maximal footprint
         class Event:
             def __init__(self, coord: float, change: float):
                 self.coord = coord
                 self.change = change
 
+        # create events for both the horizontal and vertical axis
         events_horizontal: List[Event] = []
         events_vertical: List[Event] = []
 
@@ -152,33 +181,39 @@ class GridArrange(Arranger):
         events_horizontal.sort(key=lambda event: event.coord)
         events_vertical.sort(key=lambda event: event.coord)
 
-        def findOptimalOffsetAxis(events: List[Event], interval: float) -> float:
-            prev_coord = events[-1].coord - interval
-
-            current_offset = 0
+        def findOptimalShiftAxis(events: List[Event], interval: float) -> float:
+            # executing the actual scanline algorithm
+            # iteratively go through events (left to right) and keep track of the
+            # current footprint. The optimal location is the one with the minimal
+            # footprint. If there are multiple locations with the same minimal
+            # footprint, the optimal location is the one with the largest range
+            # between the left and right endpoint of the footprint.
+            prev_offset = events[-1].coord - interval
+            current_minimal_footprint_count = 0
 
-            best_offset = float('inf')
-            best_coord_length = float('-inf')
-            best_coord = 0.0
+            best_minimal_footprint_count = float('inf')
+            best_offset_span = float('-inf')
+            best_offset = 0.0
 
             for event in events:
-                coord_length = event.coord - prev_coord
+                offset_span = event.coord - prev_offset
 
-                if current_offset < best_offset or (current_offset == best_offset and coord_length > best_coord_length):
-                    best_offset = current_offset
-                    best_coord_length = coord_length
-                    best_coord = event.coord
+                if current_minimal_footprint_count < best_minimal_footprint_count or (
+                        current_minimal_footprint_count == best_minimal_footprint_count and offset_span > best_offset_span):
+                    best_minimal_footprint_count = current_minimal_footprint_count
+                    best_offset_span = offset_span
+                    best_offset = event.coord
 
-                current_offset += event.change
-                prev_coord = event.coord
+                current_minimal_footprint_count += event.change
+                prev_offset = event.coord
 
-            return best_coord - best_coord_length * 0.5
+            return best_offset - best_offset_span * 0.5
 
         center_grid_x = 0.5 * self._grid_width
         center_grid_y = 0.5 * self._grid_height
 
-        optimal_center_x = self._grid_width - findOptimalOffsetAxis(events_horizontal, self._grid_width)
-        optimal_center_y = self._grid_height - findOptimalOffsetAxis(events_vertical, self._grid_height)
+        optimal_center_x = self._grid_width - findOptimalShiftAxis(events_horizontal, self._grid_width)
+        optimal_center_y = self._grid_height - findOptimalShiftAxis(events_vertical, self._grid_height)
 
         self._offset_x = optimal_center_x - center_grid_x
         self._offset_y = optimal_center_y - center_grid_y