grid placement available for elliptical buildplates

Co-authored-by: Casper Lamboo <c.lamboo@ultimaker.com>

CURA-7951
This commit is contained in:
saumya.jain 2023-08-22 15:06:17 +02:00
parent b662da732e
commit 4096fc864b
5 changed files with 78 additions and 35 deletions

View file

@ -15,7 +15,7 @@ i18n_catalog = i18nCatalog("cura")
class ArrangeObjectsJob(Job):
def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset=8,
def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8,
grid_arrange: bool = False) -> None:
super().__init__()
self._nodes = nodes

View file

@ -18,10 +18,12 @@ class GridArrange:
_nodes_to_arrange: List["SceneNode"]
_fixed_nodes: List["SceneNode"]
_build_volume_bounding_box = AxisAlignedBox
_build_volume: "BuildVolume"
_build_volume_bounding_box: AxisAlignedBox
def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []):
self._nodes_to_arrange = nodes_to_arrange
self._build_volume = build_volume
self._build_volume_bounding_box = build_volume.getBoundingBox()
self._fixed_nodes = fixed_nodes
@ -39,7 +41,6 @@ class GridArrange:
self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y)
def arrange(self)-> bool:
grouped_operation, not_fit_count = self.createGroupOperationForArrange()
grouped_operation.push()
return not_fit_count == 0
@ -51,6 +52,11 @@ class GridArrange:
fixed_nodes_grid_ids = fixed_nodes_grid_ids.union(self.intersectingGridIdxInclusive(node.getBoundingBox()))
build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box)
# Filter out the corner grid squares if the build plate shape is elliptic
if self._build_volume.getShape() == "elliptic":
build_plate_grid_ids = set(filter(lambda grid_id: self.checkGridUnderDiscSpace(grid_id[0], grid_id[1]), build_plate_grid_ids))
allowed_grid_idx = build_plate_grid_ids.difference(fixed_nodes_grid_ids)
# Find the sequence in which items are placed
@ -86,7 +92,6 @@ class GridArrange:
operation = self.moveNodeOnGrid(node, self._initial_leftover_grid_x, left_over_grid_y)
grouped_operation.addOperation(operation)
left_over_grid_y = left_over_grid_y - 1
return grouped_operation, len(leftover_nodes)
def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation":
@ -138,6 +143,35 @@ class GridArrange:
coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self.offset_y)
return coord_x, coord_y
def checkGridUnderDiscSpace(self, grid_x: int, grid_y: int) -> bool:
left, back = self.gridSpaceToCoordSpace(grid_x, grid_y)
right, front = self.gridSpaceToCoordSpace(grid_x + 1, grid_y + 1)
corners = [(left, back), (right, back), (right, front), (left, front)]
return all([self.checkPointUnderDiscSpace(x, y) for x, y in corners])
def checkPointUnderDiscSpace(self, x: float, y: float) -> bool:
disc_x, disc_y = self.coordSpaceToDiscSpace(x, y)
distance_to_center_squared = disc_x ** 2 + disc_y ** 2
return distance_to_center_squared <= 1.0
def coordSpaceToDiscSpace(self, x: float, y: float) -> Tuple[float, float]:
# Transform coordinate system to
#
# coord_build_plate_left = -1
# | coord_build_plate_right = 1
# v (0,1) v
# ┌───────┬───────┐ < coord_build_plate_back = -1
# │ │ │
# │ │(0,0) │
# (-1,0)│───────o───────┤(1,0)
# │ │ │
# │ │ │
# └───────┴───────┘ < coord_build_plate_front = +1
# (0,-1)
disc_x = ((x - self._build_volume_bounding_box.left) / self._build_volume_bounding_box.width) * 2.0 - 1.0
disc_y = ((y - self._build_volume_bounding_box.back) / self._build_volume_bounding_box.depth) * 2.0 - 1.0
return disc_x, disc_y
def drawDebugSvg(self):
with open("Builvolume_test.svg", "w") as f:
build_volume_bounding_box = self._build_volume_bounding_box
@ -145,6 +179,19 @@ class GridArrange:
f.write(
f"<svg xmlns='http://www.w3.org/2000/svg' viewBox='{build_volume_bounding_box.left - 100} {build_volume_bounding_box.back - 100} {build_volume_bounding_box.width + 200} {build_volume_bounding_box.depth + 200}'>\n")
ellipse = True
if ellipse:
f.write(
f"""
<ellipse
cx='{(build_volume_bounding_box.left + build_volume_bounding_box.right) * 0.5}'
cy='{(build_volume_bounding_box.back + build_volume_bounding_box.front) * 0.5}'
rx='{build_volume_bounding_box.width * 0.5}'
ry='{build_volume_bounding_box.depth * 0.5}'
fill=\"blue\"
/>
""")
else:
f.write(
f"""
<rect
@ -156,14 +203,15 @@ class GridArrange:
/>
""")
for grid_x in range(-10, 10):
for grid_y in range(-10, 10):
for grid_x in range(0, 100):
for grid_y in range(0, 100):
# if (grid_x, grid_y) in intersecting_grid_idx:
# fill_color = "red"
# elif (grid_x, grid_y) in build_plate_grid_idx:
# fill_color = "green"
# else:
# fill_color = "orange"
coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y)
f.write(
f"""
@ -172,12 +220,13 @@ class GridArrange:
y="{coord_grid_y}"
width="{self._grid_width}"
height="{self._grid_height}"
fill="green"
fill="#ff00ff88"
stroke="black"
/>
""")
f.write(f"""
<text
font-size="8"
x="{coord_grid_x + self._grid_width * 0.5}"
y="{coord_grid_y + self._grid_height * 0.5}"
>
@ -208,4 +257,11 @@ class GridArrange:
stroke-width="3"
/>
""")
for x in range(math.floor(self._build_volume_bounding_box.left), math.floor(self._build_volume_bounding_box.right), 50):
for y in range(math.floor(self._build_volume_bounding_box.back), math.floor(self._build_volume_bounding_box.front), 50):
color = "green" if self.checkPointUnderDiscSpace(x, y) else "red"
f.write(f"""
<circle cx="{x}" cy="{y}" r="10" fill="{color}" />
""")
f.write(f"</svg>")

View file

@ -203,6 +203,9 @@ class BuildVolume(SceneNode):
if shape:
self._shape = shape
def getShape(self) -> str:
return self._shape
def getDiagonalSize(self) -> float:
"""Get the length of the 3D diagonal through the build volume.

View file

@ -80,7 +80,7 @@ class CuraActions(QObject):
center_y = 0
# Move the object so that it's bottom is on to of the buildplate
center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position=True)
center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position = True)
operation.addOperation(center_operation)
operation.push()
@pyqtSlot(int)

View file

@ -42,8 +42,6 @@ Item
property alias reloadAll: reloadAllAction
property alias arrangeAll: arrangeAllAction
property alias arrangeAllGrid: arrangeAllGridAction
property alias arrangeSelection: arrangeSelectionAction
property alias arrangeSelectionLock: arrangeSelectionLockAction
property alias resetAllTranslation: resetAllTranslationAction
property alias resetAll: resetAllAction
@ -470,20 +468,6 @@ Item
shortcut: "Shift+Ctrl+R"
}
Action
{
id: arrangeSelectionAction
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection")
onTriggered: Printer.arrangeSelection(false)
}
Action
{
id: arrangeSelectionLockAction
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection Without Rotation")
onTriggered: Printer.arrangeSelection(true)
}
Action
{
id: resetAllTranslationAction