Merge branch 'master' of github.com:Ultimaker/Cura into feature_multi_materialsnozzles

This commit is contained in:
Jaime van Kessel 2016-06-22 16:32:37 +02:00
commit cbbb204718
13 changed files with 324 additions and 311 deletions

View file

@ -36,6 +36,7 @@ class BuildVolume(SceneNode):
self._disallowed_area_mesh = None
self.setCalculateBoundingBox(False)
self._volume_aabb = None
self._active_container_stack = None
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(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.addQuad(
@ -108,10 +109,10 @@ class BuildVolume(SceneNode):
Vector(max_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):
v = self._grid_mesh.getVertex(n)
self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2])
v = mb.getVertex(n)
mb.setVertexUVCoordinates(n, v[0], v[2])
self._grid_mesh = mb.build()
disallowed_area_height = 0.1
disallowed_area_size = 0
@ -136,11 +137,11 @@ class BuildVolume(SceneNode):
size = 0
disallowed_area_size = max(size, disallowed_area_size)
self._disallowed_area_mesh = mb.getData()
self._disallowed_area_mesh = mb.build()
else:
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
@ -158,6 +159,9 @@ class BuildVolume(SceneNode):
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
def getBoundingBox(self):
return self._volume_aabb
def _onGlobalContainerStackChanged(self):
if self._active_container_stack:
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)

View file

@ -1,116 +1,217 @@
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
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.
# 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):
def __init__(self):
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_job = None
# Keep track of the previous parent so we can clear its convex hull when the object is reparented
self._parent_node = None
self._init2DConvexHullCache()
self._global_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
Application.getInstance().getController().toolOperationStarted.connect(self._onChanged)
Application.getInstance().getController().toolOperationStopped.connect(self._onChanged)
self._onGlobalStackChanged()
#Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
#Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
#self._onActiveProfileChanged()
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)
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.
def __deepcopy__(self, memo):
copy = ConvexHullDecorator()
return copy
return ConvexHullDecorator()
## Get the unmodified convex hull of the node
## Get the unmodified 2D projected convex hull of the node
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
def getConvexHullHeadFull(self):
if not self._convex_hull_head_full:
return self.getConvexHull()
return self._convex_hull_head_full
if self._node is None:
return None
return self._compute2DConvexHeadFull()
## Get convex hull of the object + head size
# 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
def getConvexHullHead(self):
if not self._convex_hull_head:
return self.getConvexHull()
return self._convex_hull_head
if self._node is None:
return None
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
# 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.
def getConvexHullBoundary(self):
if not self._convex_hull_boundary:
return self.getConvexHull()
return self._convex_hull_boundary
if self._node is None:
return None
def setConvexHullBoundary(self, hull):
self._convex_hull_boundary = hull
if self._global_stack:
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):
self._convex_hull_head_full = hull
def recomputeConvexHull(self):
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):
self._convex_hull_head = hull
def setConvexHull(self, hull):
self._convex_hull = hull
if not hull and self._convex_hull_node:
convex_hull = self.getConvexHull()
if self._convex_hull_node:
if self._convex_hull_node.getHull() == convex_hull:
return
self._convex_hull_node.setParent(None)
self._convex_hull_node = None
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
hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, root)
self._convex_hull_node = hull_node
def _onSettingValueChanged(self, key, property_name):
if key == "print_sequence" and property_name == "value":
self._onChanged()
def _onChanged(self, *args):
if self._convex_hull_job:
self._convex_hull_job.cancel()
self.setConvexHull(None)
def _init2DConvexHullCache(self):
# Cache for the group code path in _compute2DConvexHull()
self._2d_convex_hull_group_child_polygon = None
self._2d_convex_hull_group_result = None
def _onParentChanged(self, node):
# Force updating the convex hull of the parent group if the object is in a group
if self._parent_node and self._parent_node.callDecoration("isGroup"):
self._parent_node.callDecoration("setConvexHull", None)
self._parent_node = self.getNode().getParent()
# Cache for the mesh code path in _compute2DConvexHull()
self._2d_convex_hull_mesh = None
self._2d_convex_hull_mesh_world_transform = None
self._2d_convex_hull_mesh_result = None
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):
if self._global_stack:
@ -124,3 +225,11 @@ class ConvexHullDecorator(SceneNodeDecorator):
self._global_stack.containersChanged.connect(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())

View file

@ -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)

View file

@ -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
class ConvexHullNode(SceneNode):
## 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
@ -31,21 +30,23 @@ class ConvexHullNode(SceneNode):
# The node this mesh is "watching"
self._node = node
self._node.transformationChanged.connect(self._onNodePositionChanged)
self._node.parentChanged.connect(self._onNodeParentChanged)
self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
self._onNodeDecoratorsChanged(self._node)
self._convex_hull_head_mesh = None
self._hull = hull
hull_mesh = self.createHullMesh(self._hull.getPoints())
if hull_mesh:
self.setMeshData(hull_mesh)
if self._hull:
hull_mesh = self.createHullMesh(self._hull.getPoints())
if hull_mesh:
self.setMeshData(hull_mesh)
convex_hull_head = self._node.callDecoration("getConvexHullHead")
if convex_hull_head:
self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
def getHull(self):
return self._hull
## Actually create the mesh from the hullpoints
# /param hull_points list of xy values
# /return meshData
@ -62,7 +63,7 @@ class ConvexHullNode(SceneNode):
mesh_builder.addFace(point_first, point_previous, point_new, color = self._color)
point_previous = point_new # Prepare point_previous for the next triangle.
return mesh_builder.getData()
return mesh_builder.build()
def getWatchedNode(self):
return self._node
@ -73,24 +74,13 @@ class ConvexHullNode(SceneNode):
self._shader.setUniformValue("u_color", self._color)
if self.getParent():
renderer.queueNode(self, transparent = True, shader = self._shader, 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)
if self.getMeshData():
renderer.queueNode(self, transparent = True, shader = self._shader, 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
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):
self._color = Color(35, 35, 35, 0.5)

View file

@ -49,7 +49,6 @@ from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENU
from PyQt5.QtGui import QColor, QIcon
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
import ast #For literal eval of extruder setting types.
import platform
import sys
import os.path
@ -122,7 +121,8 @@ class CuraApplication(QtApplication):
self._i18n_catalog = None
self._previous_active_tool = None
self._platform_activity = False
self._scene_bounding_box = AxisAlignedBox()
self._scene_bounding_box = AxisAlignedBox.Null
self._job_name = None
self._center_after_select = False
self._camera_animation = None
@ -362,7 +362,8 @@ class CuraApplication(QtApplication):
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",
MachineManagerModel.createMachineManagerModel)
@ -468,12 +469,14 @@ class CuraApplication(QtApplication):
count += 1
if not scene_bounding_box:
scene_bounding_box = copy.deepcopy(node.getBoundingBox())
scene_bounding_box = node.getBoundingBox()
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:
scene_bounding_box = AxisAlignedBox()
scene_bounding_box = AxisAlignedBox.Null
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
self._scene_bounding_box = scene_bounding_box
@ -738,7 +741,6 @@ class CuraApplication(QtApplication):
# Add all individual nodes to the selection
Selection.add(child)
child.callDecoration("setConvexHull", None)
op.push()
# Note: The group removes itself from the scene once all its children have left it,

View file

@ -96,4 +96,4 @@ class Layer:
builder.addQuad(point1, point2, point3, point4, color = poly_color)
return builder.getData()
return builder.build()

View file

@ -1,66 +1,25 @@
# 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.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):
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 __init__(self, vertices = None, normals = None, indices = None, colors = None, uvs = None, file_name = None,
center_position = None, layers=None, element_counts=None):
super().__init__(vertices=vertices, normals=normals, indices=indices, colors=colors, uvs=uvs,
file_name=file_name, center_position=center_position)
self._layers = layers
self._element_counts = element_counts
def getLayer(self, layer):
if layer in self._layers:
return self._layers[layer]
else:
return None
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.clear()
self.addVertices(vertices)
self.addColors(colors)
self.addIndices(indices.flatten())

72
cura/LayerDataBuilder.py Normal file
View 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)

View file

@ -14,7 +14,6 @@ from UM.Preferences import Preferences
from cura.ConvexHullDecorator import ConvexHullDecorator
from . import PlatformPhysicsOperation
from . import ConvexHullJob
from . import ZOffsetDecorator
import copy
@ -27,7 +26,6 @@ class PlatformPhysics:
self._controller.toolOperationStarted.connect(self._onToolOperationStarted)
self._controller.toolOperationStopped.connect(self._onToolOperationStopped)
self._build_volume = volume
self._enabled = True
self._change_timer = QTimer()
@ -46,16 +44,13 @@ class PlatformPhysics:
root = self._controller.getScene().getRoot()
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
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())
build_volume_bounding_box.setBottom(-9001) # Ignore intersections with the bottom
# Ignore intersections with the bottom
build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001)
node._outside_buildarea = False
# 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
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 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:
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):
# 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 not node.getDecorator(ConvexHullDecorator):
node.addDecorator(ConvexHullDecorator())
if not node.callDecoration("getConvexHull"):
if not node.callDecoration("getConvexHullJob"):
job = ConvexHullJob.ConvexHullJob(node)
job.start()
node.callDecoration("setConvexHullJob", job)
elif Preferences.getInstance().getValue("physics/automatic_push_free"):
node.callDecoration("recomputeConvexHull")
if Preferences.getInstance().getValue("physics/automatic_push_free"):
# Check for collisions between convex hulls
for other_node in BreadthFirstIterator(root):
# Ignore root, ourselves and anything that is not a normal SceneNode.
@ -125,8 +115,7 @@ class PlatformPhysics:
if overlap is None:
continue
move_vector.setX(overlap[0] * 1.1)
move_vector.setZ(overlap[1] * 1.1)
move_vector = move_vector.set(x=overlap[0] * 1.1, z=overlap[1] * 1.1)
convex_hull = node.callDecoration("getConvexHull")
if convex_hull:
if not convex_hull.isValid():
@ -139,7 +128,7 @@ class PlatformPhysics:
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.push()

View file

@ -28,4 +28,4 @@ class PlatformPhysicsOperation(Operation):
return group
def __repr__(self):
return "PlatformPhysicsOperation(t = {0})".format(self._position)
return "PlatformPhysicsOperation(new_position = {0})".format(self._new_position)