mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-17 03:37:48 -06:00
Merge branch 'master' of github.com:Ultimaker/Cura into feature_multi_materialsnozzles
This commit is contained in:
commit
cbbb204718
13 changed files with 324 additions and 311 deletions
|
@ -36,6 +36,7 @@ class BuildVolume(SceneNode):
|
||||||
self._disallowed_area_mesh = None
|
self._disallowed_area_mesh = None
|
||||||
|
|
||||||
self.setCalculateBoundingBox(False)
|
self.setCalculateBoundingBox(False)
|
||||||
|
self._volume_aabb = None
|
||||||
|
|
||||||
self._active_container_stack = None
|
self._active_container_stack = None
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||||
|
@ -99,7 +100,7 @@ class BuildVolume(SceneNode):
|
||||||
mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||||
mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||||
|
|
||||||
self.setMeshData(mb.getData())
|
self.setMeshData(mb.build())
|
||||||
|
|
||||||
mb = MeshBuilder()
|
mb = MeshBuilder()
|
||||||
mb.addQuad(
|
mb.addQuad(
|
||||||
|
@ -108,10 +109,10 @@ class BuildVolume(SceneNode):
|
||||||
Vector(max_w, min_h - 0.2, max_d),
|
Vector(max_w, min_h - 0.2, max_d),
|
||||||
Vector(min_w, min_h - 0.2, max_d)
|
Vector(min_w, min_h - 0.2, max_d)
|
||||||
)
|
)
|
||||||
self._grid_mesh = mb.getData()
|
|
||||||
for n in range(0, 6):
|
for n in range(0, 6):
|
||||||
v = self._grid_mesh.getVertex(n)
|
v = mb.getVertex(n)
|
||||||
self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2])
|
mb.setVertexUVCoordinates(n, v[0], v[2])
|
||||||
|
self._grid_mesh = mb.build()
|
||||||
|
|
||||||
disallowed_area_height = 0.1
|
disallowed_area_height = 0.1
|
||||||
disallowed_area_size = 0
|
disallowed_area_size = 0
|
||||||
|
@ -136,11 +137,11 @@ class BuildVolume(SceneNode):
|
||||||
size = 0
|
size = 0
|
||||||
disallowed_area_size = max(size, disallowed_area_size)
|
disallowed_area_size = max(size, disallowed_area_size)
|
||||||
|
|
||||||
self._disallowed_area_mesh = mb.getData()
|
self._disallowed_area_mesh = mb.build()
|
||||||
else:
|
else:
|
||||||
self._disallowed_area_mesh = None
|
self._disallowed_area_mesh = None
|
||||||
|
|
||||||
self._aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
|
self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
|
||||||
|
|
||||||
skirt_size = 0.0
|
skirt_size = 0.0
|
||||||
|
|
||||||
|
@ -158,6 +159,9 @@ class BuildVolume(SceneNode):
|
||||||
|
|
||||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||||
|
|
||||||
|
def getBoundingBox(self):
|
||||||
|
return self._volume_aabb
|
||||||
|
|
||||||
def _onGlobalContainerStackChanged(self):
|
def _onGlobalContainerStackChanged(self):
|
||||||
if self._active_container_stack:
|
if self._active_container_stack:
|
||||||
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||||
|
|
|
@ -1,116 +1,217 @@
|
||||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
|
|
||||||
|
from UM.Math.Polygon import Polygon
|
||||||
|
from . import ConvexHullNode
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
## The convex hull decorator is a scene node decorator that adds the convex hull functionality to a scene node.
|
## The convex hull decorator is a scene node decorator that adds the convex hull functionality to a scene node.
|
||||||
# If a scene node has a convex hull decorator, it will have a shadow in which other objects can not be printed.
|
# If a scene node has a convex hull decorator, it will have a shadow in which other objects can not be printed.
|
||||||
class ConvexHullDecorator(SceneNodeDecorator):
|
class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._convex_hull = None
|
|
||||||
|
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
|
||||||
# For one at the time this is the area without the head.
|
|
||||||
self._convex_hull_boundary = None
|
|
||||||
|
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
|
||||||
# For one at the time this is area with intersection of mirrored head
|
|
||||||
self._convex_hull_head = None
|
|
||||||
|
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
|
||||||
# For one at the time this is area with intersection of full head
|
|
||||||
self._convex_hull_head_full = None
|
|
||||||
|
|
||||||
self._convex_hull_node = None
|
self._convex_hull_node = None
|
||||||
self._convex_hull_job = None
|
self._init2DConvexHullCache()
|
||||||
|
|
||||||
# Keep track of the previous parent so we can clear its convex hull when the object is reparented
|
|
||||||
self._parent_node = None
|
|
||||||
|
|
||||||
self._global_stack = None
|
self._global_stack = None
|
||||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||||
|
Application.getInstance().getController().toolOperationStarted.connect(self._onChanged)
|
||||||
|
Application.getInstance().getController().toolOperationStopped.connect(self._onChanged)
|
||||||
|
|
||||||
self._onGlobalStackChanged()
|
self._onGlobalStackChanged()
|
||||||
#Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
|
||||||
#Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
|
|
||||||
#self._onActiveProfileChanged()
|
|
||||||
|
|
||||||
def setNode(self, node):
|
def setNode(self, node):
|
||||||
|
previous_node = self._node
|
||||||
|
if previous_node is not None and node is not previous_node:
|
||||||
|
previous_node.transformationChanged.connect(self._onChanged)
|
||||||
|
previous_node.parentChanged.connect(self._onChanged)
|
||||||
|
|
||||||
super().setNode(node)
|
super().setNode(node)
|
||||||
self._parent_node = node.getParent()
|
|
||||||
node.parentChanged.connect(self._onParentChanged)
|
self._node.transformationChanged.connect(self._onChanged)
|
||||||
|
self._node.parentChanged.connect(self._onChanged)
|
||||||
|
|
||||||
|
self._onChanged()
|
||||||
|
|
||||||
## Force that a new (empty) object is created upon copy.
|
## Force that a new (empty) object is created upon copy.
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
copy = ConvexHullDecorator()
|
return ConvexHullDecorator()
|
||||||
return copy
|
|
||||||
|
|
||||||
## Get the unmodified convex hull of the node
|
## Get the unmodified 2D projected convex hull of the node
|
||||||
def getConvexHull(self):
|
def getConvexHull(self):
|
||||||
return self._convex_hull
|
if self._node is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
hull = self._compute2DConvexHull()
|
||||||
|
if self._global_stack and self._node:
|
||||||
|
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||||
|
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon"), numpy.float32)))
|
||||||
|
return hull
|
||||||
|
|
||||||
## Get the convex hull of the node with the full head size
|
## Get the convex hull of the node with the full head size
|
||||||
def getConvexHullHeadFull(self):
|
def getConvexHullHeadFull(self):
|
||||||
if not self._convex_hull_head_full:
|
if self._node is None:
|
||||||
return self.getConvexHull()
|
return None
|
||||||
return self._convex_hull_head_full
|
|
||||||
|
return self._compute2DConvexHeadFull()
|
||||||
|
|
||||||
## Get convex hull of the object + head size
|
## Get convex hull of the object + head size
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
# In case of printing all at once this is the same as the convex hull.
|
||||||
# For one at the time this is area with intersection of mirrored head
|
# For one at the time this is area with intersection of mirrored head
|
||||||
def getConvexHullHead(self):
|
def getConvexHullHead(self):
|
||||||
if not self._convex_hull_head:
|
if self._node is None:
|
||||||
return self.getConvexHull()
|
return None
|
||||||
return self._convex_hull_head
|
|
||||||
|
if self._global_stack:
|
||||||
|
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||||
|
return self._compute2DConvexHeadMin()
|
||||||
|
return None
|
||||||
|
|
||||||
## Get convex hull of the node
|
## Get convex hull of the node
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
# In case of printing all at once this is the same as the convex hull.
|
||||||
# For one at the time this is the area without the head.
|
# For one at the time this is the area without the head.
|
||||||
def getConvexHullBoundary(self):
|
def getConvexHullBoundary(self):
|
||||||
if not self._convex_hull_boundary:
|
if self._node is None:
|
||||||
return self.getConvexHull()
|
return None
|
||||||
return self._convex_hull_boundary
|
|
||||||
|
|
||||||
def setConvexHullBoundary(self, hull):
|
if self._global_stack:
|
||||||
self._convex_hull_boundary = hull
|
if self._global_stack("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||||
|
# Printing one at a time and it's not an object in a group
|
||||||
|
return self._compute2DConvexHull()
|
||||||
|
return None
|
||||||
|
|
||||||
def setConvexHullHeadFull(self, hull):
|
def recomputeConvexHull(self):
|
||||||
self._convex_hull_head_full = hull
|
controller = Application.getInstance().getController()
|
||||||
|
root = controller.getScene().getRoot()
|
||||||
|
if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node):
|
||||||
|
if self._convex_hull_node:
|
||||||
|
self._convex_hull_node.setParent(None)
|
||||||
|
self._convex_hull_node = None
|
||||||
|
return
|
||||||
|
|
||||||
def setConvexHullHead(self, hull):
|
convex_hull = self.getConvexHull()
|
||||||
self._convex_hull_head = hull
|
if self._convex_hull_node:
|
||||||
|
if self._convex_hull_node.getHull() == convex_hull:
|
||||||
def setConvexHull(self, hull):
|
return
|
||||||
self._convex_hull = hull
|
|
||||||
if not hull and self._convex_hull_node:
|
|
||||||
self._convex_hull_node.setParent(None)
|
self._convex_hull_node.setParent(None)
|
||||||
self._convex_hull_node = None
|
hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, root)
|
||||||
|
self._convex_hull_node = hull_node
|
||||||
def getConvexHullJob(self):
|
|
||||||
return self._convex_hull_job
|
|
||||||
|
|
||||||
def setConvexHullJob(self, job):
|
|
||||||
self._convex_hull_job = job
|
|
||||||
|
|
||||||
def getConvexHullNode(self):
|
|
||||||
return self._convex_hull_node
|
|
||||||
|
|
||||||
def setConvexHullNode(self, node):
|
|
||||||
self._convex_hull_node = node
|
|
||||||
|
|
||||||
def _onSettingValueChanged(self, key, property_name):
|
def _onSettingValueChanged(self, key, property_name):
|
||||||
if key == "print_sequence" and property_name == "value":
|
if key == "print_sequence" and property_name == "value":
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
def _onChanged(self, *args):
|
def _init2DConvexHullCache(self):
|
||||||
if self._convex_hull_job:
|
# Cache for the group code path in _compute2DConvexHull()
|
||||||
self._convex_hull_job.cancel()
|
self._2d_convex_hull_group_child_polygon = None
|
||||||
self.setConvexHull(None)
|
self._2d_convex_hull_group_result = None
|
||||||
|
|
||||||
def _onParentChanged(self, node):
|
# Cache for the mesh code path in _compute2DConvexHull()
|
||||||
# Force updating the convex hull of the parent group if the object is in a group
|
self._2d_convex_hull_mesh = None
|
||||||
if self._parent_node and self._parent_node.callDecoration("isGroup"):
|
self._2d_convex_hull_mesh_world_transform = None
|
||||||
self._parent_node.callDecoration("setConvexHull", None)
|
self._2d_convex_hull_mesh_result = None
|
||||||
self._parent_node = self.getNode().getParent()
|
|
||||||
|
def _compute2DConvexHull(self):
|
||||||
|
if self._node.callDecoration("isGroup"):
|
||||||
|
points = numpy.zeros((0, 2), dtype=numpy.int32)
|
||||||
|
for child in self._node.getChildren():
|
||||||
|
child_hull = child.callDecoration("_compute2DConvexHull")
|
||||||
|
if child_hull:
|
||||||
|
points = numpy.append(points, child_hull.getPoints(), axis = 0)
|
||||||
|
|
||||||
|
if points.size < 3:
|
||||||
|
return None
|
||||||
|
child_polygon = Polygon(points)
|
||||||
|
|
||||||
|
# Check the cache
|
||||||
|
if child_polygon == self._2d_convex_hull_group_child_polygon:
|
||||||
|
return self._2d_convex_hull_group_result
|
||||||
|
|
||||||
|
# First, calculate the normal convex hull around the points
|
||||||
|
convex_hull = child_polygon.getConvexHull()
|
||||||
|
|
||||||
|
# Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
|
||||||
|
# This is done because of rounding errors.
|
||||||
|
rounded_hull = self._roundHull(convex_hull)
|
||||||
|
|
||||||
|
# Store the result in the cache
|
||||||
|
self._2d_convex_hull_group_child_polygon = child_polygon
|
||||||
|
self._2d_convex_hull_group_result = rounded_hull
|
||||||
|
|
||||||
|
return rounded_hull
|
||||||
|
|
||||||
|
else:
|
||||||
|
rounded_hull = None
|
||||||
|
if self._node.getMeshData():
|
||||||
|
mesh = self._node.getMeshData()
|
||||||
|
world_transform = self._node.getWorldTransformation()
|
||||||
|
|
||||||
|
# Check the cache
|
||||||
|
if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform:
|
||||||
|
return self._2d_convex_hull_mesh_result
|
||||||
|
|
||||||
|
vertex_data = mesh.getConvexHullTransformedVertices(world_transform)
|
||||||
|
# Don't use data below 0.
|
||||||
|
# TODO; We need a better check for this as this gives poor results for meshes with long edges.
|
||||||
|
vertex_data = vertex_data[vertex_data[:,1] >= 0]
|
||||||
|
|
||||||
|
if len(vertex_data) >= 4:
|
||||||
|
# Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
|
||||||
|
# This is done to greatly speed up further convex hull calculations as the convex hull
|
||||||
|
# becomes much less complex when dealing with highly detailed models.
|
||||||
|
vertex_data = numpy.round(vertex_data, 1)
|
||||||
|
|
||||||
|
vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D.
|
||||||
|
|
||||||
|
# Grab the set of unique points.
|
||||||
|
#
|
||||||
|
# This basically finds the unique rows in the array by treating them as opaque groups of bytes
|
||||||
|
# which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
|
||||||
|
# See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
|
||||||
|
vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(
|
||||||
|
numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1])))
|
||||||
|
_, idx = numpy.unique(vertex_byte_view, return_index=True)
|
||||||
|
vertex_data = vertex_data[idx] # Select the unique rows by index.
|
||||||
|
|
||||||
|
hull = Polygon(vertex_data)
|
||||||
|
|
||||||
|
if len(vertex_data) >= 4:
|
||||||
|
# First, calculate the normal convex hull around the points
|
||||||
|
convex_hull = hull.getConvexHull()
|
||||||
|
|
||||||
|
# Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
|
||||||
|
# This is done because of rounding errors.
|
||||||
|
rounded_hull = convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||||
|
|
||||||
|
# Store the result in the cache
|
||||||
|
self._2d_convex_hull_mesh = mesh
|
||||||
|
self._2d_convex_hull_mesh_world_transform = world_transform
|
||||||
|
self._2d_convex_hull_mesh_result = rounded_hull
|
||||||
|
|
||||||
|
return rounded_hull
|
||||||
|
|
||||||
|
def _getHeadAndFans(self):
|
||||||
|
return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon"), numpy.float32))
|
||||||
|
|
||||||
|
def _compute2DConvexHeadFull(self):
|
||||||
|
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||||
|
|
||||||
|
def _compute2DConvexHeadMin(self):
|
||||||
|
headAndFans = self._getHeadAndFans()
|
||||||
|
mirrored = headAndFans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically.
|
||||||
|
head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored)
|
||||||
|
|
||||||
|
# Min head hull is used for the push free
|
||||||
|
min_head_hull = self._compute2DConvexHull().getMinkowskiHull(head_and_fans)
|
||||||
|
return min_head_hull
|
||||||
|
|
||||||
|
def _roundHull(self, convex_hull):
|
||||||
|
return convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||||
|
|
||||||
|
def _onChanged(self, *args):
|
||||||
|
self.recomputeConvexHull()
|
||||||
|
|
||||||
def _onGlobalStackChanged(self):
|
def _onGlobalStackChanged(self):
|
||||||
if self._global_stack:
|
if self._global_stack:
|
||||||
|
@ -124,3 +225,11 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
self._global_stack.containersChanged.connect(self._onChanged)
|
self._global_stack.containersChanged.connect(self._onChanged)
|
||||||
|
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
|
## Returns true if node is a descendent or the same as the root node.
|
||||||
|
def __isDescendant(self, root, node):
|
||||||
|
if node is None:
|
||||||
|
return False
|
||||||
|
if root is node:
|
||||||
|
return True
|
||||||
|
return self.__isDescendant(root, node.getParent())
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
# Copyright (c) 2015 Ultimaker B.V.
|
|
||||||
# Cura is released under the terms of the AGPLv3 or higher.
|
|
||||||
|
|
||||||
from UM.Job import Job
|
|
||||||
from UM.Application import Application
|
|
||||||
from UM.Math.Polygon import Polygon
|
|
||||||
|
|
||||||
import numpy
|
|
||||||
import copy
|
|
||||||
from . import ConvexHullNode
|
|
||||||
|
|
||||||
## Job to async calculate the convex hull of a node.
|
|
||||||
class ConvexHullJob(Job):
|
|
||||||
def __init__(self, node):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self._node = node
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
if not self._node:
|
|
||||||
return
|
|
||||||
## If the scene node is a group, use the hull of the children to calculate its hull.
|
|
||||||
if self._node.callDecoration("isGroup"):
|
|
||||||
hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
|
|
||||||
for child in self._node.getChildren():
|
|
||||||
child_hull = child.callDecoration("getConvexHull")
|
|
||||||
if child_hull:
|
|
||||||
hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0))
|
|
||||||
|
|
||||||
if hull.getPoints().size < 3:
|
|
||||||
self._node.callDecoration("setConvexHull", None)
|
|
||||||
self._node.callDecoration("setConvexHullJob", None)
|
|
||||||
return
|
|
||||||
|
|
||||||
Job.yieldThread()
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not self._node.getMeshData():
|
|
||||||
return
|
|
||||||
mesh = self._node.getMeshData()
|
|
||||||
vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()
|
|
||||||
# Don't use data below 0.
|
|
||||||
# TODO; We need a better check for this as this gives poor results for meshes with long edges.
|
|
||||||
vertex_data = vertex_data[vertex_data[:,1] >= 0]
|
|
||||||
|
|
||||||
# Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
|
|
||||||
# This is done to greatly speed up further convex hull calculations as the convex hull
|
|
||||||
# becomes much less complex when dealing with highly detailed models.
|
|
||||||
vertex_data = numpy.round(vertex_data, 1)
|
|
||||||
|
|
||||||
vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D.
|
|
||||||
|
|
||||||
# Grab the set of unique points.
|
|
||||||
#
|
|
||||||
# This basically finds the unique rows in the array by treating them as opaque groups of bytes
|
|
||||||
# which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
|
|
||||||
# See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
|
|
||||||
vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1])))
|
|
||||||
_, idx = numpy.unique(vertex_byte_view, return_index=True)
|
|
||||||
vertex_data = vertex_data[idx] # Select the unique rows by index.
|
|
||||||
|
|
||||||
hull = Polygon(vertex_data)
|
|
||||||
|
|
||||||
# First, calculate the normal convex hull around the points
|
|
||||||
hull = hull.getConvexHull()
|
|
||||||
|
|
||||||
# Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
|
|
||||||
# This is done because of rounding errors.
|
|
||||||
hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
|
||||||
|
|
||||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
|
||||||
if global_stack:
|
|
||||||
if global_stack.getProperty("print_sequence", "value")== "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
|
||||||
# Printing one at a time and it's not an object in a group
|
|
||||||
self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
|
|
||||||
head_and_fans = Polygon(numpy.array(global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))
|
|
||||||
|
|
||||||
# Full head hull is used to actually check the order.
|
|
||||||
full_head_hull = hull.getMinkowskiHull(head_and_fans)
|
|
||||||
self._node.callDecoration("setConvexHullHeadFull", full_head_hull)
|
|
||||||
mirrored = copy.deepcopy(head_and_fans)
|
|
||||||
mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally.
|
|
||||||
mirrored.mirror([0, 0], [1, 0]) #Mirror vertically.
|
|
||||||
head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)
|
|
||||||
|
|
||||||
# Min head hull is used for the push free
|
|
||||||
min_head_hull = hull.getMinkowskiHull(head_and_fans)
|
|
||||||
self._node.callDecoration("setConvexHullHead", min_head_hull)
|
|
||||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(global_stack.getProperty("machine_head_polygon","value"),numpy.float32)))
|
|
||||||
else:
|
|
||||||
self._node.callDecoration("setConvexHullHead", None)
|
|
||||||
if self._node.getParent() is None: # Node was already deleted before job is done.
|
|
||||||
self._node.callDecoration("setConvexHullNode",None)
|
|
||||||
self._node.callDecoration("setConvexHull", None)
|
|
||||||
self._node.callDecoration("setConvexHullJob", None)
|
|
||||||
return
|
|
||||||
|
|
||||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
|
|
||||||
self._node.callDecoration("setConvexHullNode", hull_node)
|
|
||||||
self._node.callDecoration("setConvexHull", hull)
|
|
||||||
self._node.callDecoration("setConvexHullJob", None)
|
|
||||||
|
|
||||||
if self._node.getParent() and self._node.getParent().callDecoration("isGroup"):
|
|
||||||
job = self._node.getParent().callDecoration("getConvexHullJob")
|
|
||||||
if job:
|
|
||||||
job.cancel()
|
|
||||||
self._node.getParent().callDecoration("setConvexHull", None)
|
|
||||||
hull_node = self._node.getParent().callDecoration("getConvexHullNode")
|
|
||||||
if hull_node:
|
|
||||||
hull_node.setParent(None)
|
|
|
@ -9,7 +9,6 @@ from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the c
|
||||||
|
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
|
||||||
|
|
||||||
class ConvexHullNode(SceneNode):
|
class ConvexHullNode(SceneNode):
|
||||||
## Convex hull node is a special type of scene node that is used to display a 2D area, to indicate the
|
## Convex hull node is a special type of scene node that is used to display a 2D area, to indicate the
|
||||||
# location an object uses on the buildplate. This area (or area's in case of one at a time printing) is
|
# location an object uses on the buildplate. This area (or area's in case of one at a time printing) is
|
||||||
|
@ -31,21 +30,23 @@ class ConvexHullNode(SceneNode):
|
||||||
|
|
||||||
# The node this mesh is "watching"
|
# The node this mesh is "watching"
|
||||||
self._node = node
|
self._node = node
|
||||||
self._node.transformationChanged.connect(self._onNodePositionChanged)
|
|
||||||
self._node.parentChanged.connect(self._onNodeParentChanged)
|
|
||||||
self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
|
self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
|
||||||
self._onNodeDecoratorsChanged(self._node)
|
self._onNodeDecoratorsChanged(self._node)
|
||||||
|
|
||||||
self._convex_hull_head_mesh = None
|
self._convex_hull_head_mesh = None
|
||||||
self._hull = hull
|
self._hull = hull
|
||||||
|
|
||||||
hull_mesh = self.createHullMesh(self._hull.getPoints())
|
if self._hull:
|
||||||
if hull_mesh:
|
hull_mesh = self.createHullMesh(self._hull.getPoints())
|
||||||
self.setMeshData(hull_mesh)
|
if hull_mesh:
|
||||||
|
self.setMeshData(hull_mesh)
|
||||||
convex_hull_head = self._node.callDecoration("getConvexHullHead")
|
convex_hull_head = self._node.callDecoration("getConvexHullHead")
|
||||||
if convex_hull_head:
|
if convex_hull_head:
|
||||||
self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
|
self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
|
||||||
|
|
||||||
|
def getHull(self):
|
||||||
|
return self._hull
|
||||||
|
|
||||||
## Actually create the mesh from the hullpoints
|
## Actually create the mesh from the hullpoints
|
||||||
# /param hull_points list of xy values
|
# /param hull_points list of xy values
|
||||||
# /return meshData
|
# /return meshData
|
||||||
|
@ -62,7 +63,7 @@ class ConvexHullNode(SceneNode):
|
||||||
mesh_builder.addFace(point_first, point_previous, point_new, color = self._color)
|
mesh_builder.addFace(point_first, point_previous, point_new, color = self._color)
|
||||||
point_previous = point_new # Prepare point_previous for the next triangle.
|
point_previous = point_new # Prepare point_previous for the next triangle.
|
||||||
|
|
||||||
return mesh_builder.getData()
|
return mesh_builder.build()
|
||||||
|
|
||||||
def getWatchedNode(self):
|
def getWatchedNode(self):
|
||||||
return self._node
|
return self._node
|
||||||
|
@ -73,24 +74,13 @@ class ConvexHullNode(SceneNode):
|
||||||
self._shader.setUniformValue("u_color", self._color)
|
self._shader.setUniformValue("u_color", self._color)
|
||||||
|
|
||||||
if self.getParent():
|
if self.getParent():
|
||||||
renderer.queueNode(self, transparent = True, shader = self._shader, backface_cull = True, sort = -8)
|
if self.getMeshData():
|
||||||
if self._convex_hull_head_mesh:
|
renderer.queueNode(self, transparent = True, shader = self._shader, backface_cull = True, sort = -8)
|
||||||
renderer.queueNode(self, shader = self._shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
|
if self._convex_hull_head_mesh:
|
||||||
|
renderer.queueNode(self, shader = self._shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _onNodePositionChanged(self, node):
|
|
||||||
if node.callDecoration("getConvexHull"):
|
|
||||||
node.callDecoration("setConvexHull", None)
|
|
||||||
node.callDecoration("setConvexHullNode", None)
|
|
||||||
self.setParent(None) # Garbage collection should delete this node after a while.
|
|
||||||
|
|
||||||
def _onNodeParentChanged(self, node):
|
|
||||||
if node.getParent():
|
|
||||||
self.setParent(self._original_parent)
|
|
||||||
else:
|
|
||||||
self.setParent(None)
|
|
||||||
|
|
||||||
def _onNodeDecoratorsChanged(self, node):
|
def _onNodeDecoratorsChanged(self, node):
|
||||||
self._color = Color(35, 35, 35, 0.5)
|
self._color = Color(35, 35, 35, 0.5)
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENU
|
||||||
from PyQt5.QtGui import QColor, QIcon
|
from PyQt5.QtGui import QColor, QIcon
|
||||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||||
|
|
||||||
import ast #For literal eval of extruder setting types.
|
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import os.path
|
import os.path
|
||||||
|
@ -122,7 +121,8 @@ class CuraApplication(QtApplication):
|
||||||
self._i18n_catalog = None
|
self._i18n_catalog = None
|
||||||
self._previous_active_tool = None
|
self._previous_active_tool = None
|
||||||
self._platform_activity = False
|
self._platform_activity = False
|
||||||
self._scene_bounding_box = AxisAlignedBox()
|
self._scene_bounding_box = AxisAlignedBox.Null
|
||||||
|
|
||||||
self._job_name = None
|
self._job_name = None
|
||||||
self._center_after_select = False
|
self._center_after_select = False
|
||||||
self._camera_animation = None
|
self._camera_animation = None
|
||||||
|
@ -362,7 +362,8 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
|
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
|
||||||
|
|
||||||
ExtruderManager.ExtruderManager.getInstance() #Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
# Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
||||||
|
ExtruderManager.ExtruderManager.getInstance()
|
||||||
qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
|
qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
|
||||||
MachineManagerModel.createMachineManagerModel)
|
MachineManagerModel.createMachineManagerModel)
|
||||||
|
|
||||||
|
@ -468,12 +469,14 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
if not scene_bounding_box:
|
if not scene_bounding_box:
|
||||||
scene_bounding_box = copy.deepcopy(node.getBoundingBox())
|
scene_bounding_box = node.getBoundingBox()
|
||||||
else:
|
else:
|
||||||
scene_bounding_box += node.getBoundingBox()
|
other_bb = node.getBoundingBox()
|
||||||
|
if other_bb is not None:
|
||||||
|
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
|
||||||
|
|
||||||
if not scene_bounding_box:
|
if not scene_bounding_box:
|
||||||
scene_bounding_box = AxisAlignedBox()
|
scene_bounding_box = AxisAlignedBox.Null
|
||||||
|
|
||||||
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
|
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
|
||||||
self._scene_bounding_box = scene_bounding_box
|
self._scene_bounding_box = scene_bounding_box
|
||||||
|
@ -738,7 +741,6 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
# Add all individual nodes to the selection
|
# Add all individual nodes to the selection
|
||||||
Selection.add(child)
|
Selection.add(child)
|
||||||
child.callDecoration("setConvexHull", None)
|
|
||||||
|
|
||||||
op.push()
|
op.push()
|
||||||
# Note: The group removes itself from the scene once all its children have left it,
|
# Note: The group removes itself from the scene once all its children have left it,
|
||||||
|
|
|
@ -96,4 +96,4 @@ class Layer:
|
||||||
|
|
||||||
builder.addQuad(point1, point2, point3, point4, color = poly_color)
|
builder.addQuad(point1, point2, point3, point4, color = poly_color)
|
||||||
|
|
||||||
return builder.getData()
|
return builder.build()
|
||||||
|
|
|
@ -1,66 +1,25 @@
|
||||||
# Copyright (c) 2015 Ultimaker B.V.
|
# Copyright (c) 2015 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the AGPLv3 or higher.
|
# Cura is released under the terms of the AGPLv3 or higher.
|
||||||
from .Layer import Layer
|
|
||||||
from .LayerPolygon import LayerPolygon
|
|
||||||
from UM.Mesh.MeshData import MeshData
|
from UM.Mesh.MeshData import MeshData
|
||||||
|
|
||||||
import numpy
|
## Class to holds the layer mesh and information about the layers.
|
||||||
|
# Immutable, use LayerDataBuilder to create one of these.
|
||||||
|
|
||||||
class LayerData(MeshData):
|
class LayerData(MeshData):
|
||||||
def __init__(self):
|
def __init__(self, vertices = None, normals = None, indices = None, colors = None, uvs = None, file_name = None,
|
||||||
super().__init__()
|
center_position = None, layers=None, element_counts=None):
|
||||||
self._layers = {}
|
super().__init__(vertices=vertices, normals=normals, indices=indices, colors=colors, uvs=uvs,
|
||||||
self._element_counts = {}
|
file_name=file_name, center_position=center_position)
|
||||||
|
self._layers = layers
|
||||||
def addLayer(self, layer):
|
self._element_counts = element_counts
|
||||||
if layer not in self._layers:
|
|
||||||
self._layers[layer] = Layer(layer)
|
|
||||||
|
|
||||||
def addPolygon(self, layer, polygon_type, data, line_width):
|
|
||||||
if layer not in self._layers:
|
|
||||||
self.addLayer(layer)
|
|
||||||
|
|
||||||
p = LayerPolygon(self, polygon_type, data, line_width)
|
|
||||||
self._layers[layer].polygons.append(p)
|
|
||||||
|
|
||||||
def getLayer(self, layer):
|
def getLayer(self, layer):
|
||||||
if layer in self._layers:
|
if layer in self._layers:
|
||||||
return self._layers[layer]
|
return self._layers[layer]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def getLayers(self):
|
def getLayers(self):
|
||||||
return self._layers
|
return self._layers
|
||||||
|
|
||||||
def getElementCounts(self):
|
def getElementCounts(self):
|
||||||
return self._element_counts
|
return self._element_counts
|
||||||
|
|
||||||
def setLayerHeight(self, layer, height):
|
|
||||||
if layer not in self._layers:
|
|
||||||
self.addLayer(layer)
|
|
||||||
|
|
||||||
self._layers[layer].setHeight(height)
|
|
||||||
|
|
||||||
def setLayerThickness(self, layer, thickness):
|
|
||||||
if layer not in self._layers:
|
|
||||||
self.addLayer(layer)
|
|
||||||
|
|
||||||
self._layers[layer].setThickness(thickness)
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
vertex_count = 0
|
|
||||||
for layer, data in self._layers.items():
|
|
||||||
vertex_count += data.vertexCount()
|
|
||||||
|
|
||||||
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
|
||||||
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
|
||||||
indices = numpy.empty((vertex_count, 2), numpy.int32)
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
for layer, data in self._layers.items():
|
|
||||||
offset = data.build(offset, vertices, colors, indices)
|
|
||||||
self._element_counts[layer] = data.elementCount
|
|
||||||
|
|
||||||
self.clear()
|
|
||||||
self.addVertices(vertices)
|
|
||||||
self.addColors(colors)
|
|
||||||
self.addIndices(indices.flatten())
|
|
||||||
|
|
72
cura/LayerDataBuilder.py
Normal file
72
cura/LayerDataBuilder.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# Copyright (c) 2015 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the AGPLv3 or higher.
|
||||||
|
|
||||||
|
from .Layer import Layer
|
||||||
|
from .LayerPolygon import LayerPolygon
|
||||||
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
|
from .LayerData import LayerData
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
## Builder class for constructing a LayerData object
|
||||||
|
class LayerDataBuilder(MeshBuilder):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._layers = {}
|
||||||
|
self._element_counts = {}
|
||||||
|
|
||||||
|
def addLayer(self, layer):
|
||||||
|
if layer not in self._layers:
|
||||||
|
self._layers[layer] = Layer(layer)
|
||||||
|
|
||||||
|
def addPolygon(self, layer, polygon_type, data, line_width):
|
||||||
|
if layer not in self._layers:
|
||||||
|
self.addLayer(layer)
|
||||||
|
|
||||||
|
p = LayerPolygon(self, polygon_type, data, line_width)
|
||||||
|
self._layers[layer].polygons.append(p)
|
||||||
|
|
||||||
|
def getLayer(self, layer):
|
||||||
|
if layer in self._layers:
|
||||||
|
return self._layers[layer]
|
||||||
|
|
||||||
|
def getLayers(self):
|
||||||
|
return self._layers
|
||||||
|
|
||||||
|
def getElementCounts(self):
|
||||||
|
return self._element_counts
|
||||||
|
|
||||||
|
def setLayerHeight(self, layer, height):
|
||||||
|
if layer not in self._layers:
|
||||||
|
self.addLayer(layer)
|
||||||
|
|
||||||
|
self._layers[layer].setHeight(height)
|
||||||
|
|
||||||
|
def setLayerThickness(self, layer, thickness):
|
||||||
|
if layer not in self._layers:
|
||||||
|
self.addLayer(layer)
|
||||||
|
|
||||||
|
self._layers[layer].setThickness(thickness)
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
vertex_count = 0
|
||||||
|
for layer, data in self._layers.items():
|
||||||
|
vertex_count += data.vertexCount()
|
||||||
|
|
||||||
|
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
||||||
|
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
||||||
|
indices = numpy.empty((vertex_count, 2), numpy.int32)
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
for layer, data in self._layers.items():
|
||||||
|
offset = data.build(offset, vertices, colors, indices)
|
||||||
|
self._element_counts[layer] = data.elementCount
|
||||||
|
|
||||||
|
self.addVertices(vertices)
|
||||||
|
self.addColors(colors)
|
||||||
|
self.addIndices(indices.flatten())
|
||||||
|
|
||||||
|
return LayerData(vertices=self.getVertices(), normals=self.getNormals(), indices=self.getIndices(),
|
||||||
|
colors=self.getColors(), uvs=self.getUVCoordinates(), file_name=self.getFileName(),
|
||||||
|
center_position=self.getCenterPosition(), layers=self._layers,
|
||||||
|
element_counts=self._element_counts)
|
|
@ -14,7 +14,6 @@ from UM.Preferences import Preferences
|
||||||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
from cura.ConvexHullDecorator import ConvexHullDecorator
|
||||||
|
|
||||||
from . import PlatformPhysicsOperation
|
from . import PlatformPhysicsOperation
|
||||||
from . import ConvexHullJob
|
|
||||||
from . import ZOffsetDecorator
|
from . import ZOffsetDecorator
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
@ -27,7 +26,6 @@ class PlatformPhysics:
|
||||||
self._controller.toolOperationStarted.connect(self._onToolOperationStarted)
|
self._controller.toolOperationStarted.connect(self._onToolOperationStarted)
|
||||||
self._controller.toolOperationStopped.connect(self._onToolOperationStopped)
|
self._controller.toolOperationStopped.connect(self._onToolOperationStopped)
|
||||||
self._build_volume = volume
|
self._build_volume = volume
|
||||||
|
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
self._change_timer = QTimer()
|
self._change_timer = QTimer()
|
||||||
|
@ -46,16 +44,13 @@ class PlatformPhysics:
|
||||||
|
|
||||||
root = self._controller.getScene().getRoot()
|
root = self._controller.getScene().getRoot()
|
||||||
for node in BreadthFirstIterator(root):
|
for node in BreadthFirstIterator(root):
|
||||||
if node is root or type(node) is not SceneNode:
|
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
bbox = node.getBoundingBox()
|
bbox = node.getBoundingBox()
|
||||||
if not bbox or not bbox.isValid():
|
|
||||||
self._change_timer.start()
|
|
||||||
continue
|
|
||||||
|
|
||||||
build_volume_bounding_box = copy.deepcopy(self._build_volume.getBoundingBox())
|
# Ignore intersections with the bottom
|
||||||
build_volume_bounding_box.setBottom(-9001) # Ignore intersections with the bottom
|
build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001)
|
||||||
node._outside_buildarea = False
|
node._outside_buildarea = False
|
||||||
|
|
||||||
# Mark the node as outside the build volume if the bounding box test fails.
|
# Mark the node as outside the build volume if the bounding box test fails.
|
||||||
|
@ -67,9 +62,9 @@ class PlatformPhysics:
|
||||||
if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
|
if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
|
||||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||||
if bbox.bottom > 0:
|
if bbox.bottom > 0:
|
||||||
move_vector.setY(-bbox.bottom + z_offset)
|
move_vector = move_vector.set(y=-bbox.bottom + z_offset)
|
||||||
elif bbox.bottom < z_offset:
|
elif bbox.bottom < z_offset:
|
||||||
move_vector.setY((-bbox.bottom) - z_offset)
|
move_vector = move_vector.set(y=(-bbox.bottom) - z_offset)
|
||||||
|
|
||||||
#if not Float.fuzzyCompare(bbox.bottom, 0.0):
|
#if not Float.fuzzyCompare(bbox.bottom, 0.0):
|
||||||
# pass#move_vector.setY(-bbox.bottom)
|
# pass#move_vector.setY(-bbox.bottom)
|
||||||
|
@ -77,14 +72,9 @@ class PlatformPhysics:
|
||||||
# If there is no convex hull for the node, start calculating it and continue.
|
# If there is no convex hull for the node, start calculating it and continue.
|
||||||
if not node.getDecorator(ConvexHullDecorator):
|
if not node.getDecorator(ConvexHullDecorator):
|
||||||
node.addDecorator(ConvexHullDecorator())
|
node.addDecorator(ConvexHullDecorator())
|
||||||
|
node.callDecoration("recomputeConvexHull")
|
||||||
if not node.callDecoration("getConvexHull"):
|
|
||||||
if not node.callDecoration("getConvexHullJob"):
|
if Preferences.getInstance().getValue("physics/automatic_push_free"):
|
||||||
job = ConvexHullJob.ConvexHullJob(node)
|
|
||||||
job.start()
|
|
||||||
node.callDecoration("setConvexHullJob", job)
|
|
||||||
|
|
||||||
elif Preferences.getInstance().getValue("physics/automatic_push_free"):
|
|
||||||
# Check for collisions between convex hulls
|
# Check for collisions between convex hulls
|
||||||
for other_node in BreadthFirstIterator(root):
|
for other_node in BreadthFirstIterator(root):
|
||||||
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
||||||
|
@ -125,8 +115,7 @@ class PlatformPhysics:
|
||||||
|
|
||||||
if overlap is None:
|
if overlap is None:
|
||||||
continue
|
continue
|
||||||
move_vector.setX(overlap[0] * 1.1)
|
move_vector = move_vector.set(x=overlap[0] * 1.1, z=overlap[1] * 1.1)
|
||||||
move_vector.setZ(overlap[1] * 1.1)
|
|
||||||
convex_hull = node.callDecoration("getConvexHull")
|
convex_hull = node.callDecoration("getConvexHull")
|
||||||
if convex_hull:
|
if convex_hull:
|
||||||
if not convex_hull.isValid():
|
if not convex_hull.isValid():
|
||||||
|
@ -139,7 +128,7 @@ class PlatformPhysics:
|
||||||
|
|
||||||
node._outside_buildarea = True
|
node._outside_buildarea = True
|
||||||
|
|
||||||
if move_vector != Vector():
|
if not Vector.Null.equals(move_vector, epsilon=1e-5):
|
||||||
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
||||||
op.push()
|
op.push()
|
||||||
|
|
||||||
|
|
|
@ -28,4 +28,4 @@ class PlatformPhysicsOperation(Operation):
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "PlatformPhysicsOperation(t = {0})".format(self._position)
|
return "PlatformPhysicsOperation(new_position = {0})".format(self._new_position)
|
||||||
|
|
|
@ -12,7 +12,7 @@ from UM.i18n import i18nCatalog
|
||||||
|
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
|
|
||||||
from cura import LayerData
|
from cura import LayerDataBuilder
|
||||||
from cura import LayerDataDecorator
|
from cura import LayerDataDecorator
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
@ -63,7 +63,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
return
|
return
|
||||||
|
|
||||||
mesh = MeshData()
|
mesh = MeshData()
|
||||||
layer_data = LayerData.LayerData()
|
layer_data = LayerDataBuilder.LayerDataBuilder()
|
||||||
layer_count = len(self._layers)
|
layer_count = len(self._layers)
|
||||||
|
|
||||||
# Find the minimum layer number
|
# Find the minimum layer number
|
||||||
|
@ -115,7 +115,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
self._progress.setProgress(progress)
|
self._progress.setProgress(progress)
|
||||||
|
|
||||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||||
layer_data.build()
|
layer_mesh = layer_data.build()
|
||||||
|
|
||||||
if self._abort_requested:
|
if self._abort_requested:
|
||||||
if self._progress:
|
if self._progress:
|
||||||
|
@ -124,7 +124,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
|
|
||||||
# Add LayerDataDecorator to scene node to indicate that the node has layer data
|
# Add LayerDataDecorator to scene node to indicate that the node has layer data
|
||||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||||
decorator.setLayerData(layer_data)
|
decorator.setLayerData(layer_mesh)
|
||||||
new_node.addDecorator(decorator)
|
new_node.addDecorator(decorator)
|
||||||
|
|
||||||
new_node.setMeshData(mesh)
|
new_node.setMeshData(mesh)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
from UM.Mesh.MeshReader import MeshReader
|
from UM.Mesh.MeshReader import MeshReader
|
||||||
from UM.Mesh.MeshData import MeshData
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
|
@ -48,13 +48,9 @@ class ImageReader(MeshReader):
|
||||||
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert)
|
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert)
|
||||||
|
|
||||||
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert):
|
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert):
|
||||||
mesh = None # TODO: @UnusedVariable
|
|
||||||
scene_node = None # TODO: @UnusedVariable
|
|
||||||
|
|
||||||
scene_node = SceneNode()
|
scene_node = SceneNode()
|
||||||
|
|
||||||
mesh = MeshData()
|
mesh = MeshBuilder()
|
||||||
scene_node.setMeshData(mesh)
|
|
||||||
|
|
||||||
img = QImage(file_name)
|
img = QImage(file_name)
|
||||||
|
|
||||||
|
@ -76,9 +72,9 @@ class ImageReader(MeshReader):
|
||||||
scale_vector = Vector(xz_size, peak_height, xz_size)
|
scale_vector = Vector(xz_size, peak_height, xz_size)
|
||||||
|
|
||||||
if width > height:
|
if width > height:
|
||||||
scale_vector.setZ(scale_vector.z * aspect)
|
scale_vector = scale_vector.set(z=scale_vector.z * aspect)
|
||||||
elif height > width:
|
elif height > width:
|
||||||
scale_vector.setX(scale_vector.x / aspect)
|
scale_vector = scale_vector.set(x=scale_vector.x / aspect)
|
||||||
|
|
||||||
if width > max_size or height > max_size:
|
if width > max_size or height > max_size:
|
||||||
scale_factor = max_size / width
|
scale_factor = max_size / width
|
||||||
|
@ -173,8 +169,8 @@ class ImageReader(MeshReader):
|
||||||
geo_height = height_minus_one * texel_height
|
geo_height = height_minus_one * texel_height
|
||||||
|
|
||||||
# bottom
|
# bottom
|
||||||
mesh.addFace(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height)
|
mesh.addFaceByPoints(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height)
|
||||||
mesh.addFace(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0)
|
mesh.addFaceByPoints(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0)
|
||||||
|
|
||||||
# north and south walls
|
# north and south walls
|
||||||
for n in range(0, width_minus_one):
|
for n in range(0, width_minus_one):
|
||||||
|
@ -187,11 +183,11 @@ class ImageReader(MeshReader):
|
||||||
hs0 = height_data[height_minus_one, n]
|
hs0 = height_data[height_minus_one, n]
|
||||||
hs1 = height_data[height_minus_one, n + 1]
|
hs1 = height_data[height_minus_one, n + 1]
|
||||||
|
|
||||||
mesh.addFace(x, 0, 0, nx, 0, 0, nx, hn1, 0)
|
mesh.addFaceByPoints(x, 0, 0, nx, 0, 0, nx, hn1, 0)
|
||||||
mesh.addFace(nx, hn1, 0, x, hn0, 0, x, 0, 0)
|
mesh.addFaceByPoints(nx, hn1, 0, x, hn0, 0, x, 0, 0)
|
||||||
|
|
||||||
mesh.addFace(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height)
|
mesh.addFaceByPoints(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height)
|
||||||
mesh.addFace(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height)
|
mesh.addFaceByPoints(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height)
|
||||||
|
|
||||||
# west and east walls
|
# west and east walls
|
||||||
for n in range(0, height_minus_one):
|
for n in range(0, height_minus_one):
|
||||||
|
@ -204,12 +200,14 @@ class ImageReader(MeshReader):
|
||||||
he0 = height_data[n, width_minus_one]
|
he0 = height_data[n, width_minus_one]
|
||||||
he1 = height_data[n + 1, width_minus_one]
|
he1 = height_data[n + 1, width_minus_one]
|
||||||
|
|
||||||
mesh.addFace(0, 0, y, 0, 0, ny, 0, hw1, ny)
|
mesh.addFaceByPoints(0, 0, y, 0, 0, ny, 0, hw1, ny)
|
||||||
mesh.addFace(0, hw1, ny, 0, hw0, y, 0, 0, y)
|
mesh.addFaceByPoints(0, hw1, ny, 0, hw0, y, 0, 0, y)
|
||||||
|
|
||||||
mesh.addFace(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny)
|
mesh.addFaceByPoints(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny)
|
||||||
mesh.addFace(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y)
|
mesh.addFaceByPoints(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y)
|
||||||
|
|
||||||
mesh.calculateNormals(fast=True)
|
mesh.calculateNormals(fast=True)
|
||||||
|
|
||||||
|
scene_node.setMeshData(mesh.build())
|
||||||
|
|
||||||
return scene_node
|
return scene_node
|
||||||
|
|
|
@ -8,7 +8,7 @@ from UM.Event import Event, KeyEvent
|
||||||
from UM.Signal import Signal
|
from UM.Signal import Signal
|
||||||
from UM.Scene.Selection import Selection
|
from UM.Scene.Selection import Selection
|
||||||
from UM.Math.Color import Color
|
from UM.Math.Color import Color
|
||||||
from UM.Mesh.MeshData import MeshData
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ class _CreateTopLayersJob(Job):
|
||||||
if self._cancel or not layer_data:
|
if self._cancel or not layer_data:
|
||||||
return
|
return
|
||||||
|
|
||||||
layer_mesh = MeshData()
|
layer_mesh = MeshBuilder()
|
||||||
for i in range(self._solid_layers):
|
for i in range(self._solid_layers):
|
||||||
layer_number = self._layer_number - i
|
layer_number = self._layer_number - i
|
||||||
if layer_number < 0:
|
if layer_number < 0:
|
||||||
|
@ -275,7 +275,7 @@ class _CreateTopLayersJob(Job):
|
||||||
if not jump_mesh or jump_mesh.getVertices() is None:
|
if not jump_mesh or jump_mesh.getVertices() is None:
|
||||||
jump_mesh = None
|
jump_mesh = None
|
||||||
|
|
||||||
self.setResult({ "layers": layer_mesh, "jumps": jump_mesh })
|
self.setResult({ "layers": layer_mesh.build(), "jumps": jump_mesh })
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self._cancel = True
|
self._cancel = True
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue