Cura/cura/LayerData.py
Arjen Hiemstra 751f58fb02 Merge branch '15.10'
* 15.10: (39 commits)
  Remove unused import in StartSliceJob
  conforming to code style
  fix typo's..
  Adjust initial view to be slightly from the side
  uses a different method to check whether a machine name excists
  Sets the languageComboBox to the default language
  Remove per-group settings for now
  Make sure to send all settings when an object overrides the profile
  Properly emit writeStarted in RemovableDriveOutputDevice
  Add xy_offset setting to list of settings that trigger a disallowed area update
  Properly trigger a reslice when the active instance is changed
  Wizardpages without hack
  Only hides the window when there are no more pages
  Only add layer data node after all processing
  Also account for "xy_offset" setting for the disallowed areas
  JSON: workaround for stutter in spiralize vase: set travel speed to printing speed
  Adds a color for the error-messages
  Shows an error message when a user tries to add a printer with a name that already excists.
  JSON: support bottom stair step height defaults changed so that the bottom distance to the model isn't violated too much
  Try to use Protobuf CPP implementation if it is available
  ...
2015-11-13 11:31:29 +01:00

260 lines
8.2 KiB
Python

# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Mesh.MeshData import MeshData
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Math.Color import Color
from UM.Math.Vector import Vector
import numpy
import math
import copy
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, type, data, line_width):
if layer not in self._layers:
self.addLayer(layer)
p = Polygon(self, 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.clear()
self.addVertices(vertices)
self.addColors(colors)
self.addIndices(indices.flatten())
class Layer():
def __init__(self, id):
self._id = id
self._height = 0.0
self._thickness = 0.0
self._polygons = []
self._element_count = 0
@property
def height(self):
return self._height
@property
def thickness(self):
return self._thickness
@property
def polygons(self):
return self._polygons
@property
def elementCount(self):
return self._element_count
def setHeight(self, height):
self._height = height
def setThickness(self, thickness):
self._thickness = thickness
def vertexCount(self):
result = 0
for polygon in self._polygons:
result += polygon.vertexCount()
return result
def build(self, offset, vertices, colors, indices):
result = offset
for polygon in self._polygons:
if polygon.type == Polygon.InfillType or polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
continue
polygon.build(result, vertices, colors, indices)
result += polygon.vertexCount()
self._element_count += polygon.elementCount
return result
def createMesh(self):
return self.createMeshOrJumps(True)
def createJumps(self):
return self.createMeshOrJumps(False)
def createMeshOrJumps(self, make_mesh):
builder = MeshBuilder()
for polygon in self._polygons:
if make_mesh and (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
continue
if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
continue
poly_color = polygon.getColor()
points = numpy.copy(polygon.data)
if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType:
points[:,1] -= 0.01
if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
points[:,1] += 0.01
# Calculate normals for the entire polygon using numpy.
normals = numpy.copy(points)
normals[:,1] = 0.0 # We are only interested in 2D normals
# Calculate the edges between points.
# The call to numpy.roll shifts the entire array by one so that
# we end up subtracting each next point from the current, wrapping
# around. This gives us the edges from the next point to the current
# point.
normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0)
# Calculate the length of each edge using standard Pythagoras
lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2)
# The normal of a 2D vector is equal to its x and y coordinates swapped
# and then x inverted. This code does that.
normals[:,[0, 2]] = normals[:,[2, 0]]
normals[:,0] *= -1
# Normalize the normals.
normals[:,0] /= lengths
normals[:,2] /= lengths
# Scale all by the line width of the polygon so we can easily offset.
normals *= (polygon.lineWidth / 2)
#TODO: Use numpy magic to perform the vertex creation to speed up things.
for i in range(len(points)):
start = points[i - 1]
end = points[i]
normal = normals[i - 1]
point1 = Vector(data = start - normal)
point2 = Vector(data = start + normal)
point3 = Vector(data = end + normal)
point4 = Vector(data = end - normal)
builder.addQuad(point1, point2, point3, point4, color = poly_color)
return builder.getData()
class Polygon():
NoneType = 0
Inset0Type = 1
InsetXType = 2
SkinType = 3
SupportType = 4
SkirtType = 5
InfillType = 6
SupportInfillType = 7
MoveCombingType = 8
MoveRetractionType = 9
def __init__(self, mesh, type, data, line_width):
super().__init__()
self._mesh = mesh
self._type = type
self._data = data
self._line_width = line_width / 1000
def build(self, offset, vertices, colors, indices):
self._begin = offset
self._end = self._begin + len(self._data) - 1
color = self.getColor()
color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)
color = numpy.array([color.r, color.g, color.b, color.a], numpy.float32)
vertices[self._begin:self._end + 1, :] = self._data[:, :]
colors[self._begin:self._end + 1, :] = color
for i in range(self._begin, self._end):
indices[i, 0] = i
indices[i, 1] = i + 1
indices[self._end, 0] = self._end
indices[self._end, 1] = self._begin
def getColor(self):
if self._type == self.Inset0Type:
return Color(1.0, 0.0, 0.0, 1.0)
elif self._type == self.InsetXType:
return Color(0.0, 1.0, 0.0, 1.0)
elif self._type == self.SkinType:
return Color(1.0, 1.0, 0.0, 1.0)
elif self._type == self.SupportType:
return Color(0.0, 1.0, 1.0, 1.0)
elif self._type == self.SkirtType:
return Color(0.0, 1.0, 1.0, 1.0)
elif self._type == self.InfillType:
return Color(1.0, 0.74, 0.0, 1.0)
elif self._type == self.SupportInfillType:
return Color(0.0, 1.0, 1.0, 1.0)
elif self._type == self.MoveCombingType:
return Color(0.0, 0.0, 1.0, 1.0)
elif self._type == self.MoveRetractionType:
return Color(0.5, 0.5, 1.0, 1.0)
else:
return Color(1.0, 1.0, 1.0, 1.0)
def vertexCount(self):
return len(self._data)
@property
def type(self):
return self._type
@property
def data(self):
return self._data
@property
def elementCount(self):
return ((self._end - self._begin) + 1) * 2 #The range of vertices multiplied by 2 since each vertex is used twice
@property
def lineWidth(self):
return self._line_width