Take disallowed areas into account when arranging

CURA-7440
This commit is contained in:
Jaime van Kessel 2020-09-30 10:34:35 +02:00
parent 73289b2a77
commit e0e65b91b5
No known key found for this signature in database
GPG key ID: 3710727397403C91

View file

@ -1,10 +1,12 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import numpy
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from UM.Application import Application from UM.Application import Application
from UM.Job import Job from UM.Job import Job
from UM.Math.Matrix import Matrix from UM.Math.Matrix import Matrix
from UM.Math.Polygon import Polygon
from UM.Math.Quaternion import Quaternion from UM.Math.Quaternion import Quaternion
from UM.Operations.RotateOperation import RotateOperation from UM.Operations.RotateOperation import RotateOperation
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
@ -41,11 +43,10 @@ class ArrangeObjectsJob(Job):
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = Application.getInstance().getGlobalContainerStack()
machine_width = global_container_stack.getProperty("machine_width", "value") machine_width = global_container_stack.getProperty("machine_width", "value")
machine_depth = global_container_stack.getProperty("machine_depth", "value") machine_depth = global_container_stack.getProperty("machine_depth", "value")
factor = 10000 factor = 10000
build_plate_bounding_box = Box(machine_width * factor, machine_depth * factor)
build_plate_bounding_box = Box(machine_width * factor, machine_depth * factor ) # Add all the items we want to arrange
node_items = [] node_items = []
for node in self._nodes: for node in self._nodes:
hull_polygon = node.callDecoration("getConvexHull") hull_polygon = node.callDecoration("getConvexHull")
@ -55,9 +56,42 @@ class ArrangeObjectsJob(Job):
item = Item(converted_points) item = Item(converted_points)
node_items.append(item) node_items.append(item)
# Use a tiny margin for the build_plate_polygon (the nesting doesn't like overlapping disallowed areas)
half_machine_width = 0.5 * machine_width - 1
half_machine_depth = 0.5 * machine_depth - 1
build_plate_polygon = Polygon(numpy.array([
[half_machine_width, -half_machine_depth],
[-half_machine_width, -half_machine_depth],
[-half_machine_width, half_machine_depth],
[half_machine_width, half_machine_depth]
], numpy.float32))
build_volume = Application.getInstance().getBuildVolume()
disallowed_areas = build_volume.getDisallowedAreas()
num_disallowed_areas_added = 0
for area in disallowed_areas:
converted_points = []
# Clip the disallowed areas so that they don't overlap the bounding box (The arranger chokes otherwise)
clipped_area = area.intersectionConvexHulls(build_plate_polygon)
for point in clipped_area.getPoints():
converted_points.append(Point(point[0] * factor, point[1] * factor))
disallowed_area = Item(converted_points)
disallowed_area.markAsFixedInBin(0)
node_items.append(disallowed_area)
num_disallowed_areas_added += 1
config = NfpConfig() config = NfpConfig()
config.accuracy = 1.0 config.accuracy = 1.0
num_bins = nest(node_items, build_plate_bounding_box, 1, config)
num_bins = nest(node_items, build_plate_bounding_box, 10000, config)
# Strip the disallowed areas from the results again
if num_disallowed_areas_added != 0:
node_items = node_items[:-num_disallowed_areas_added]
found_solution_for_all = num_bins == 1 found_solution_for_all = num_bins == 1
not_fit_count = 0 not_fit_count = 0
grouped_operation = GroupedOperation() grouped_operation = GroupedOperation()
@ -66,7 +100,6 @@ class ArrangeObjectsJob(Job):
# We found a spot for it # We found a spot for it
rotation_matrix = Matrix() rotation_matrix = Matrix()
rotation_matrix.setByRotationAxis(node_item.rotation(),Vector(0, -1, 0)) rotation_matrix.setByRotationAxis(node_item.rotation(),Vector(0, -1, 0))
grouped_operation.addOperation(RotateOperation(node, Quaternion.fromMatrix(rotation_matrix))) grouped_operation.addOperation(RotateOperation(node, Quaternion.fromMatrix(rotation_matrix)))
grouped_operation.addOperation(TranslateOperation(node, Vector(node_item.translation().x() / factor, 0, node_item.translation().y() / factor))) grouped_operation.addOperation(TranslateOperation(node, Vector(node_item.translation().x() / factor, 0, node_item.translation().y() / factor)))
else: else:
@ -74,12 +107,9 @@ class ArrangeObjectsJob(Job):
grouped_operation.addOperation(TranslateOperation(node, Vector(200, 0, -not_fit_count * 20), set_position=True)) grouped_operation.addOperation(TranslateOperation(node, Vector(200, 0, -not_fit_count * 20), set_position=True))
not_fit_count += 1 not_fit_count += 1
grouped_operation.push() grouped_operation.push()
status_message.hide() status_message.hide()
if not found_solution_for_all: if not found_solution_for_all:
no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"),
title = i18n_catalog.i18nc("@info:title", "Can't Find Location")) title = i18n_catalog.i18nc("@info:title", "Can't Find Location"))
no_full_solution_message.show() no_full_solution_message.show()
self.finished.emit(self) self.finished.emit(self)