This commit is contained in:
Tim Kuipers 2015-06-16 14:43:57 +02:00
commit a321bd588b
16 changed files with 543 additions and 146 deletions

21
CHANGES
View File

@ -7,6 +7,24 @@ Cura 15.06 is a new release built from the ground up on a completely new
framework called Uranium. This framework has been designed to make it easier to
extend Cura with additional functionality as well as provide a cleaner UI.
Changes since 15.05.93
----------------------
* Fixed: No shortcuts for moving up/down layers in layer view.
* Fixed: Last view layers could not be scrolled through in layer view.
* Fixed: Files provided on command line would not actually show up on the build
platform.
* Fixed: Render a ghost of the selection in Layer view to make the actual object
position clear.
* Fixed: Showing a menu would clear the selection.
* Fixed: Size and scaling factor display for scale tool.
* Fixed: Missing background for additional tool controls.
* Fixed: Loading message times out when loading large files.
* Fixed: Show recent files in the file menu.
* Fixed: Windows installer will now install MSVC 2010 redistributable, to
prevent issues with missing DLL's.
* Fixed: Collapsed/expanded state of setting categories not stored.
Changes since 15.05.91
----------------------
@ -26,7 +44,8 @@ Changes since 15.05.91
* Fixed: Camera panning now works correctly instead of doing nothing.
* Fixed: Camera would flip around center point at maximum rotation.
* Fixed: Build platform grid blocked view from below objects.
* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the window
* Fixed: Viewport on MacOSX with high-DPI screens was only taking 1/4th of the
window
Changes since 15.05.90
----------------------

View File

@ -1,7 +1,9 @@
Cura
====
This is the source code of Cura.
This is the new, shiny, unreleased frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates.
We re-worked the whole GUI code at Ultimaker, because my old code started to become an unmaintainable ball of poo.
Dependencies
------------
@ -12,3 +14,8 @@ Dependencies
This will be needed at runtime to perform the actual slicing.
* PySerial
Only required for USB printing support.
Build scripts
-------------
Please checkout [cura-build](https://github.com/Ultimaker/cura-build)

View File

@ -48,6 +48,9 @@ class ConvexHullNode(SceneNode):
self.setMeshData(mesh)
def getWatchedNode(self):
return self._node
def render(self, renderer):
if not self._material:
self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag"))

View File

@ -17,6 +17,7 @@ from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.Message import Message
from UM.PluginRegistry import PluginRegistry
from UM.JobQueue import JobQueue
from UM.Scene.BoxRenderer import BoxRenderer
from UM.Scene.Selection import Selection
@ -71,6 +72,18 @@ class CuraApplication(QtApplication):
Preferences.getInstance().addPreference("cura/active_machine", "")
Preferences.getInstance().addPreference("cura/active_mode", "simple")
Preferences.getInstance().addPreference("cura/recent_files", "")
Preferences.getInstance().addPreference("cura/categories_expanded", "")
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
self._recent_files = []
files = Preferences.getInstance().getValue("cura/recent_files").split(";")
for f in files:
if not os.path.isfile(f):
continue
self._recent_files.append(f)
## Handle loading of all plugin types (and the backend explicitly)
# \sa PluginRegistery
@ -305,6 +318,25 @@ class CuraApplication(QtApplication):
return log
recentFilesChanged = pyqtSignal()
@pyqtProperty("QStringList", notify = recentFilesChanged)
def recentFiles(self):
return self._recent_files
@pyqtSlot("QStringList")
def setExpandedCategories(self, categories):
categories = list(set(categories))
categories.sort()
joined = ";".join(categories)
if joined != Preferences.getInstance().getValue("cura/categories_expanded"):
Preferences.getInstance().setValue("cura/categories_expanded", joined)
self.expandedCategoriesChanged.emit()
expandedCategoriesChanged = pyqtSignal()
@pyqtProperty("QStringList", notify = expandedCategoriesChanged)
def expandedCategories(self):
return Preferences.getInstance().getValue("cura/categories_expanded").split(";")
outputDevicesChanged = pyqtSignal()
@pyqtProperty("QVariantMap", notify = outputDevicesChanged)
@ -460,3 +492,18 @@ class CuraApplication(QtApplication):
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
op.push()
def _onJobFinished(self, job):
if type(job) is not ReadMeshJob:
return
f = job.getFileName()
if f in self._recent_files:
self._recent_files.remove(f)
self._recent_files.insert(0, f)
if len(self._recent_files) > 10:
del self._recent_files[10]
Preferences.getInstance().setValue("cura/recent_files", ";".join(self._recent_files))
self.recentFilesChanged.emit()

View File

@ -99,6 +99,15 @@ Function LaunchLink
Exec '"$WINDIR\explorer.exe" "$SMPROGRAMS\Cura ${VERSION}\Cura ${VERSION}.lnk"'
FunctionEnd
Section "Install Visual Studio 2010 Redistributable"
SetOutPath "$INSTDIR"
File "vcredist_2010_x86.exe"
IfSilent +2
ExecWait '"$INSTDIR\vcredist_2010_x86.exe"'
SectionEnd
;Section "Install Arduino Drivers"
; ; Set output path to the driver directory.
; SetOutPath "$INSTDIR\drivers\"

View File

@ -1,6 +1,3 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: Cura.proto
@ -21,7 +18,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='Cura.proto',
package='Cura',
serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"4\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1f\n\x08polygons\x18\x02 \x03(\x0b\x32\r.Cura.Polygon\"\x9f\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\"b\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3')
serialized_pb=_b('\n\nCura.proto\x12\x04\x43ura\"+\n\nObjectList\x12\x1d\n\x07objects\x18\x01 \x03(\x0b\x32\x0c.Cura.Object\"i\n\x06Object\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08vertices\x18\x02 \x01(\x0c\x12\x0f\n\x07normals\x18\x03 \x01(\x0c\x12\x0f\n\x07indices\x18\x04 \x01(\x0c\x12\x1f\n\x08settings\x18\x05 \x03(\x0b\x32\r.Cura.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"7\n\x10SlicedObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.Cura.SlicedObject\"7\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x1b\n\x06layers\x18\x02 \x03(\x0b\x32\x0b.Cura.Layer\"W\n\x05Layer\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x11\n\tthickness\x18\x03 \x01(\x02\x12\x1f\n\x08polygons\x18\x04 \x03(\x0b\x32\r.Cura.Polygon\"\xdb\x01\n\x07Polygon\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.Cura.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\x12\x12\n\nline_width\x18\x03 \x01(\x02\"\x89\x01\n\x04Type\x12\x0c\n\x08NoneType\x10\x00\x12\x0e\n\nInset0Type\x10\x01\x12\x0e\n\nInsetXType\x10\x02\x12\x0c\n\x08SkinType\x10\x03\x12\x0f\n\x0bSupportType\x10\x04\x12\r\n\tSkirtType\x10\x05\x12\x0e\n\nInfillType\x10\x06\x12\x15\n\x11SupportInfillType\x10\x07\"&\n\nGCodeLayer\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"D\n\x0fObjectPrintTime\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04time\x18\x02 \x01(\x02\x12\x17\n\x0fmaterial_amount\x18\x03 \x01(\x02\".\n\x0bSettingList\x12\x1f\n\x08settings\x18\x01 \x03(\x0b\x32\r.Cura.Setting\"&\n\x07Setting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\"\x1b\n\x0bGCodePrefix\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x62\x06proto3')
)
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
@ -57,11 +54,19 @@ _POLYGON_TYPE = _descriptor.EnumDescriptor(
name='SkirtType', index=5, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='InfillType', index=6, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='SupportInfillType', index=7, number=7,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=430,
serialized_end=528,
serialized_start=486,
serialized_end=623,
)
_sym_db.RegisterEnumDescriptor(_POLYGON_TYPE)
@ -266,8 +271,22 @@ _LAYER = _descriptor.Descriptor(
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='polygons', full_name='Cura.Layer.polygons', index=1,
number=2, type=11, cpp_type=10, label=3,
name='height', full_name='Cura.Layer.height', index=1,
number=2, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='thickness', full_name='Cura.Layer.thickness', index=2,
number=3, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='polygons', full_name='Cura.Layer.polygons', index=3,
number=4, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
@ -284,7 +303,7 @@ _LAYER = _descriptor.Descriptor(
oneofs=[
],
serialized_start=314,
serialized_end=366,
serialized_end=401,
)
@ -309,6 +328,13 @@ _POLYGON = _descriptor.Descriptor(
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='line_width', full_name='Cura.Polygon.line_width', index=2,
number=3, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
@ -321,8 +347,8 @@ _POLYGON = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=369,
serialized_end=528,
serialized_start=404,
serialized_end=623,
)
@ -358,8 +384,8 @@ _GCODELAYER = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=530,
serialized_end=568,
serialized_start=625,
serialized_end=663,
)
@ -402,8 +428,8 @@ _OBJECTPRINTTIME = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=570,
serialized_end=638,
serialized_start=665,
serialized_end=733,
)
@ -432,8 +458,8 @@ _SETTINGLIST = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=640,
serialized_end=686,
serialized_start=735,
serialized_end=781,
)
@ -469,8 +495,8 @@ _SETTING = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=688,
serialized_end=726,
serialized_start=783,
serialized_end=821,
)
@ -499,8 +525,8 @@ _GCODEPREFIX = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=728,
serialized_end=755,
serialized_start=823,
serialized_end=850,
)
_OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT

View File

@ -2,7 +2,9 @@
# 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
@ -13,12 +15,19 @@ class LayerData(MeshData):
self._layers = {}
self._element_counts = {}
def addPolygon(self, layer, type, data):
def addLayer(self, layer):
if layer not in self._layers:
self._layers[layer] = []
self._layers[layer] = Layer(layer)
p = Polygon(self, type, data)
self._layers[layer].append(p)
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):
return self._layers[layer]
def getLayers(self):
return self._layers
@ -26,14 +35,112 @@ class LayerData(MeshData):
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):
for layer, data in self._layers.items():
if layer not in self._element_counts:
self._element_counts[layer] = []
data.build()
for polygon in data:
polygon.build()
self._element_counts[layer].append(polygon.elementCount)
self._element_counts[layer] = data.elementCount
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 build(self):
for polygon in self._polygons:
if polygon._type == Polygon.InfillType or polygon._type == Polygon.SupportInfillType:
continue
polygon.build()
self._element_count += polygon.elementCount
def createMesh(self):
builder = MeshBuilder()
for polygon in self._polygons:
poly_color = polygon.getColor()
poly_color = Color(poly_color[0], poly_color[1], poly_color[2], poly_color[3])
points = numpy.copy(polygon.data)
if polygon.type == Polygon.InfillType or polygon.type == Polygon.SkinType or polygon.type == Polygon.SupportInfillType:
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
@ -42,34 +149,26 @@ class Polygon():
SkinType = 3
SupportType = 4
SkirtType = 5
InfillType = 6
SupportInfillType = 7
def __init__(self, mesh, type, data):
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):
self._begin = self._mesh._vertex_count
self._mesh.addVertices(self._data)
self._end = self._begin + len(self._data) - 1
color = None
if self._type == self.Inset0Type:
color = [1, 0, 0, 1]
elif self._type == self.InsetXType:
color = [0, 1, 0, 1]
elif self._type == self.SkinType:
color = [1, 1, 0, 1]
elif self._type == self.SupportType:
color = [0, 1, 1, 1]
elif self._type == self.SkirtType:
color = [0, 1, 1, 1]
else:
color = [1, 1, 1, 1]
color = self.getColor()
color[3] = 2.0
colors = [color for i in range(len(self._data))]
self._mesh.addColors(numpy.array(colors, dtype=numpy.float32))
self._mesh.addColors(numpy.array(colors, dtype=numpy.float32) * 0.5)
indices = []
for i in range(self._begin, self._end):
@ -80,6 +179,24 @@ class Polygon():
indices.append(self._begin)
self._mesh.addIndices(numpy.array(indices, dtype=numpy.int32))
def getColor(self):
if self._type == self.Inset0Type:
return [1.0, 0.0, 0.0, 1.0]
elif self._type == self.InsetXType:
return [0.0, 1.0, 0.0, 1.0]
elif self._type == self.SkinType:
return [1.0, 1.0, 0.0, 1.0]
elif self._type == self.SupportType:
return [0.0, 1.0, 1.0, 1.0]
elif self._type == self.SkirtType:
return [0.0, 1.0, 1.0, 1.0]
elif self._type == self.InfillType:
return [1.0, 1.0, 0.0, 1.0]
elif self._type == self.SupportInfillType:
return [0.0, 1.0, 1.0, 1.0]
else:
return [1.0, 1.0, 1.0, 1.0]
@property
def type(self):
return self._type
@ -90,4 +207,8 @@ class Polygon():
@property
def elementCount(self):
return (self._end - self._begin) * 2 #The range of vertices multiplied by 2 since each vertex is used twice
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

View File

@ -32,22 +32,24 @@ class ProcessSlicedObjectListJob(Job):
settings = Application.getInstance().getActiveMachine()
layerHeight = settings.getSettingValueByKey("layer_height")
mesh = MeshData()
for object in self._message.objects:
try:
try:
node = objectIdMap[object.id]
except KeyError:
continue
mesh = MeshData()
layerData = LayerData.LayerData()
for layer in object.layers:
layerData.addLayer(layer.id)
layerData.setLayerHeight(layer.id, layer.height)
layerData.setLayerThickness(layer.id, layer.thickness)
for polygon in layer.polygons:
points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
points = numpy.asarray(points, dtype=numpy.float32)
points /= 1000
points = numpy.insert(points, 1, layer.id * layerHeight, axis = 1)
points = numpy.insert(points, 1, (layer.height / 1000), axis = 1)
points[:,2] *= -1
@ -55,16 +57,11 @@ class ProcessSlicedObjectListJob(Job):
center = [settings.getSettingValueByKey("machine_width") / 2, 0.0, -settings.getSettingValueByKey("machine_depth") / 2]
points -= numpy.array(center)
#points = numpy.pad(points, ((0,0), (0,1)), "constant", constant_values=(0.0, 1.0))
#inverse = node.getWorldTransformation().getInverse().getData()
#points = points.dot(inverse)
#points = points[:,0:3]
layerData.addPolygon(layer.id, polygon.type, points, polygon.line_width)
layerData.addPolygon(layer.id, polygon.type, points)
# We are done processing all the layers we got from the engine, now create a mesh out of the data
layerData.build()
mesh.layerData = layerData
# We are done processing all the layers we got from the engine, now create a mesh out of the data
layerData.build()
mesh.layerData = layerData
new_node.setMeshData(mesh)
new_node.setParent(self._scene.getRoot())

View File

@ -9,6 +9,10 @@ from UM.Event import Event, KeyEvent
from UM.Signal import Signal
from UM.Scene.Selection import Selection
from UM.Math.Color import Color
from UM.Mesh.MeshData import MeshData
from cura.ConvexHullNode import ConvexHullNode
from . import LayerViewProxy
## View used to display g-code paths.
@ -22,6 +26,9 @@ class LayerView(View):
self._controller.getScene().sceneChanged.connect(self._onSceneChanged)
self._max_layers = 10
self._current_layer_num = 10
self._current_layer_mesh = None
self._solid_layers = 5
def getCurrentLayer(self):
return self._current_layer_num
@ -45,6 +52,11 @@ class LayerView(View):
self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128))
for node in DepthFirstIterator(scene.getRoot()):
# We do not want to render ConvexHullNode as it conflicts with the bottom layers.
# However, it is somewhat relevant when the node is selected, so do render it then.
if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()):
continue
if not node.render(renderer):
if node.getMeshData() and node.isVisible():
if Selection.isSelected(node):
@ -55,19 +67,39 @@ class LayerView(View):
except AttributeError:
continue
start = 0
end = 0
# Render all layers below a certain number as line mesh instead of vertices.
if self._current_layer_num - self._solid_layers > -1:
start = 0
end = 0
element_counts = layer_data.getElementCounts()
for layer, counts in element_counts.items():
if layer + self._solid_layers > self._current_layer_num:
break
end += counts
element_counts = layer_data.getElementCounts()
for layer, counts in element_counts.items():
end += sum(counts)
## Hack to ensure the end is correct. Not quite sure what causes this
end += 2 * len(counts)
# This uses glDrawRangeElements internally to only draw a certain range of lines.
renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end)
if layer >= self._current_layer_num:
break
# We currently recreate the current "solid" layers every time a
if not self._current_layer_mesh:
self._current_layer_mesh = MeshData()
for i in range(self._solid_layers):
layer = self._current_layer_num - i
if layer < 0:
continue
renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end)
layer_mesh = layer_data.getLayer(layer).createMesh()
if not layer_mesh or layer_mesh.getVertices() is None:
continue
self._current_layer_mesh.addVertices(layer_mesh.getVertices())
# Scale layer color by a brightness factor based on the current layer number
# This will result in a range of 0.5 - 1.0 to multiply colors by.
brightness = (2.0 - (i / self._solid_layers)) / 2.0
self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness)
renderer.queueNode(node, mesh = self._current_layer_mesh, material = self._material)
def setLayer(self, value):
if self._current_layer_num != value:
@ -76,6 +108,8 @@ class LayerView(View):
self._current_layer_num = 0
if self._current_layer_num > self._max_layers:
self._current_layer_num = self._max_layers
self._current_layer_mesh = None
self.currentLayerNumChanged.emit()
currentLayerNumChanged = Signal()
@ -96,7 +130,7 @@ class LayerView(View):
except AttributeError:
continue
if new_max_layers < len(layer_data.getLayers()):
new_max_layers = len(layer_data.getLayers())
new_max_layers = len(layer_data.getLayers()) - 1
if new_max_layers > 0 and new_max_layers != self._old_max_layers:
self._max_layers = new_max_layers

View File

@ -3,7 +3,6 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.0 as UM
@ -12,48 +11,53 @@ UM.Dialog {
id: base
//: About dialog title
title: qsTr("About Cura");
title: qsTr("About Cura")
minimumWidth: 400
minimumHeight: 300
ColumnLayout {
anchors.fill: parent;
Image {
id: logo
width: parent.width * 0.75
height: width * (1/4.25)
Item {
Layout.fillWidth: true;
Layout.fillHeight: true;
}
source: UM.Theme.images.logo
Image {
Layout.alignment: Qt.AlignHCenter;
Layout.preferredWidth: parent.width * 0.75;
Layout.preferredHeight: width * (1/4.25);
sourceSize.width: width
sourceSize.height: height
anchors.centerIn: parent
anchors.verticalCenterOffset : -(height * 0.5)
}
source: UM.Theme.images.logo;
Label {
id: version
sourceSize.width: width;
sourceSize.height: height;
}
text: "Cura 15.06 Beta"
font: UM.Theme.fonts.large
anchors.horizontalCenter : logo.horizontalCenter
anchors.horizontalCenterOffset : (logo.width * 0.25)
anchors.top: logo.bottom
anchors.topMargin : 5
}
Label {
Layout.alignment: Qt.AlignHCenter;
Label {
id: description
width: parent.width
text: "Cura 15.06 Beta";
font: UM.Theme.fonts.large;
}
//: About dialog application description
text: qsTr("End-to-end solution for fused filament 3D printing.")
wrapMode: Text.WordWrap
anchors.top: version.bottom
anchors.topMargin : 10
}
Label {
//: About dialog application description
text: qsTr("End-to-end solution for fused filament 3D printing.")
}
Label {
id: author_note
width: parent.width
Label {
//: About dialog application author note
text: qsTr("Cura has been developed by Ultimaker B.V. in cooperation with the community.")
}
Item {
Layout.fillWidth: true;
Layout.fillHeight: true;
}
//: About dialog application author note
text: qsTr("Cura has been developed by Ultimaker B.V. in cooperation with the community.")
wrapMode: Text.WordWrap
anchors.top: description.bottom
}
rightButtons: Button {

View File

@ -25,6 +25,7 @@ UM.MainWindow {
window: base
Menu {
id: fileMenu
//: File menu
title: qsTr("&File");
@ -33,6 +34,19 @@ UM.MainWindow {
MenuSeparator { }
Instantiator {
model: Printer.recentFiles
MenuItem {
property url filePath: modelData;
text: (index + 1) + ". " + modelData.slice(modelData.lastIndexOf("/") + 1);
onTriggered: UM.MeshFileHandler.readLocalFile(filePath);
}
onObjectAdded: fileMenu.insertItem(index, object)
onObjectRemoved: fileMenu.removeItem(object)
}
MenuSeparator { }
MenuItem { action: actions.quit; }
}
@ -178,8 +192,8 @@ UM.MainWindow {
id: openFileButton;
iconSource: UM.Theme.icons.open;
style: UM.Theme.styles.tool_button;
style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button;
tooltip: '';
anchors {
top: parent.top;
topMargin: UM.Theme.sizes.window_margin.height;
@ -218,7 +232,7 @@ UM.MainWindow {
iconSource: UM.Theme.icons.viewmode;
style: UM.Theme.styles.tool_button;
tooltip: '';
menu: Menu {
id: viewMenu;
Instantiator {
@ -419,3 +433,4 @@ UM.MainWindow {
Component.onCompleted: UM.Theme.load(UM.Resources.getPath(UM.Resources.ThemesLocation, "cura"))
}

View File

@ -1,6 +1,13 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.0
import QtQuick.Controls 1.2
import UM 1.0 as UM
UM.SettingView { }
UM.SettingView {
expandedCategories: Printer.expandedCategories;
onExpandedCategoriesChanged: Printer.setExpandedCategories(expandedCategories);
}

View File

@ -47,7 +47,6 @@ Item {
Button {
text: model.name;
iconSource: UM.Theme.icons[model.icon];
tooltip: model.description;
checkable: true;
checked: model.active;

View File

@ -1,6 +1,6 @@
{
"id": "ultimaker_original_plus",
"name": "Ultimaker Original Plus",
"name": "Ultimaker Original+",
"icon": "icon_ultimaker.png",
"platform": "ultimaker2_platform.obj",
"platform_texture": "UltimakerPlusbackplate.png",

View File

@ -35,47 +35,42 @@ QtObject {
}
}
property Component tool_button: Component {
property Component open_file_button: Component {
ButtonStyle {
background: UM.AngledCornerRectangle {
property bool down: control.pressed || (control.checkable && control.checked);
background: Item {
implicitWidth: UM.Theme.sizes.button.width;
implicitHeight: UM.Theme.sizes.button.height;
color: {
if(!control.enabled) {
return UM.Theme.colors.button_disabled;
} else if(control.checkable && control.checked && control.hovered) {
return UM.Theme.colors.button_active_hover;
} else if(control.pressed || (control.checkable && control.checked)) {
return UM.Theme.colors.button_active;
} else if(control.hovered) {
return UM.Theme.colors.button_hover;
} else {
return UM.Theme.colors.button;
}
}
Behavior on color { ColorAnimation { duration: 50; } }
cornerSize: UM.Theme.sizes.default_margin.width;
Rectangle {
anchors.bottom: parent.top;
anchors.bottom: parent.verticalCenter;
width: parent.width;
height: control.hovered ? label.height : 0;
Behavior on height { NumberAnimation { duration: 75; } }
height: control.hovered ? parent.height / 2 + label.height : 0;
Behavior on height { NumberAnimation { duration: 100; } }
opacity: control.hovered ? 1.0 : 0.0;
Behavior on opacity { NumberAnimation { duration: 75; } }
Behavior on opacity { NumberAnimation { duration: 100; } }
Label {
id: label
id: label;
anchors.horizontalCenter: parent.horizontalCenter;
text: control.text;
text: control.text.replace("&", "");
font: UM.Theme.fonts.button_tooltip;
color: UM.Theme.colors.button_tooltip_text;
}
}
UM.AngledCornerRectangle {
anchors.fill: parent;
color: {
if(control.hovered) {
return UM.Theme.colors.button_active_hover;
} else {
return UM.Theme.colors.button_active;
}
}
Behavior on color { ColorAnimation { duration: 50; } }
cornerSize: UM.Theme.sizes.default_margin.width;
}
}
label: Item {
@ -92,6 +87,113 @@ QtObject {
}
}
property Component tool_button: Component {
ButtonStyle {
background: Item {
implicitWidth: UM.Theme.sizes.button.width;
implicitHeight: UM.Theme.sizes.button.height;
Rectangle {
anchors.bottom: parent.verticalCenter;
width: parent.width;
height: control.hovered ? parent.height / 2 + label.height : 0;
Behavior on height { NumberAnimation { duration: 100; } }
opacity: control.hovered ? 1.0 : 0.0;
Behavior on opacity { NumberAnimation { duration: 100; } }
Label {
id: label
anchors.horizontalCenter: parent.horizontalCenter;
text: control.text.replace("&", "");
font: UM.Theme.fonts.button_tooltip;
color: UM.Theme.colors.button_tooltip_text;
}
}
UM.AngledCornerRectangle {
id: buttonFace;
anchors.fill: parent;
property bool down: control.pressed || (control.checkable && control.checked);
color: {
if(!control.enabled) {
return UM.Theme.colors.button_disabled;
} else if(control.checkable && control.checked && control.hovered) {
return UM.Theme.colors.button_active_hover;
} else if(control.pressed || (control.checkable && control.checked)) {
return UM.Theme.colors.button_active;
} else if(control.hovered) {
return UM.Theme.colors.button_hover;
} else {
return UM.Theme.colors.button;
}
}
Behavior on color { ColorAnimation { duration: 50; } }
cornerSize: UM.Theme.sizes.default_margin.width;
}
}
label: Item {
Image {
anchors.centerIn: parent;
source: control.iconSource;
width: UM.Theme.sizes.button_icon.width;
height: UM.Theme.sizes.button_icon.height;
sourceSize: UM.Theme.sizes.button_icon;
}
}
}
}
property Component progressbar: Component{
ProgressBarStyle {
background: UM.AngledCornerRectangle {
anchors.fill: parent
anchors.left: parent.left
implicitWidth: UM.Theme.sizes.progressbar.width
implicitHeight: UM.Theme.sizes.progressbar.height
color: "transparent"
}
progress: UM.AngledCornerRectangle {
anchors.left: parent.left
anchors.fill: parent
cornerSize: UM.Theme.sizes.progressbar_control.height
color: UM.Theme.colors.progressbar_background
Item {
anchors.fill: parent
anchors.margins: UM.Theme.sizes.progressbar_margin.width
visible: control.indeterminate
Row {
Repeater {
UM.AngledCornerRectangle {
cornerSize: UM.Theme.sizes.progressbar_control.height
color: UM.Theme.colors.progressbar_control
width: UM.Theme.sizes.progressbar_control.width
height: UM.Theme.sizes.progressbar_control.height
}
model: 1
}
SequentialAnimation on x {
id: xAnim
property int animEndPoint: UM.Theme.sizes.progressbar.width - UM.Theme.sizes.progressbar_control.width
running: control.indeterminate
loops: Animation.Infinite
NumberAnimation { from: 0; to: xAnim.animEndPoint; duration: 2000;}
NumberAnimation { from: xAnim.animEndPoint; to: 0; duration: 2000;}
}
}
}
}
}
}
property Component sidebar_category: Component {
ButtonStyle {
background: UM.AngledCornerRectangle {

View File

@ -52,15 +52,15 @@
"border": [205, 202, 201, 255],
"secondary": [205, 202, 201, 255],
"text": [174, 174, 174, 255],
"text_inactive": [205, 202, 201, 255],
"text": [140, 144, 154, 255],
"text_inactive": [174, 174, 174, 255],
"text_hover": [35, 35, 35, 255],
"text_pressed": [12, 169, 227, 255],
"button": [205, 202, 201, 255],
"button_hover": [174, 174, 174, 255],
"button": [160, 163, 171, 255],
"button_hover": [140, 144, 154, 255],
"button_active": [12, 169, 227, 255],
"button_active_hover": [34, 150, 190, 255],
"button_active_hover": [34, 150, 199, 255],
"button_text": [255, 255, 255, 255],
"button_disabled": [245, 245, 245, 255],
"button_tooltip_text": [35, 35, 35, 255],
@ -77,7 +77,7 @@
"setting_category_active_hover": [34, 150, 190, 255],
"setting_category_text": [255, 255, 255, 255],
"setting_label": [174, 174, 174, 255],
"setting_label": [140, 144, 154, 255],
"setting_control": [255, 255, 255, 255],
"setting_control_highlight": [245, 245, 245, 255],
"setting_control_border": [174, 174, 174, 255],
@ -88,6 +88,9 @@
"setting_validation_warning": [255, 186, 15, 255],
"setting_validation_ok": [255, 255, 255, 255],
"progressbar_background": [245, 245, 245, 255],
"progressbar_control": [12, 169, 227, 255],
"slider_groove": [245, 245, 245, 255],
"slider_groove_border": [205, 202, 201, 255],
"slider_groove_fill": [205, 202, 201, 255],
@ -98,7 +101,7 @@
"checkbox_hover": [245, 245, 245, 255],
"checkbox_border": [174, 174, 174, 255],
"checkbox_mark": [35, 35, 35, 255],
"checkbox_text": [174, 174, 174, 255],
"checkbox_text": [140, 144, 154, 255],
"tooltip": [255, 225, 146, 255],
@ -133,7 +136,11 @@
"setting_unit_margin": [0.5, 0.5],
"button": [4.25, 4.25],
"button_icon": [3.57, 3.57],
"button_icon": [2.9, 2.9],
"progressbar": [26.0, 0.5],
"progressbar_control": [8.0, 0.5],
"progressbar_padding": [0.0, 1.0],
"scrollbar": [0.5, 0.5],