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

This commit is contained in:
Jack Ha 2016-10-09 11:54:48 +02:00
commit fc0417bd45
14 changed files with 236 additions and 130 deletions

View file

@ -150,11 +150,11 @@ class QualityManager:
return [quality_type_dict[quality_type] for quality_type in quality_types]
## Fetch a more basic version of a material.
## Fetch more basic versions of a material.
#
# This tries to find a generic or basic version of the given material.
# \param material_container \type{InstanceContainer} the material
# \return \type{List[InstanceContainer]} the basic material or None if one could not be found.
# \return \type{List[InstanceContainer]} a list of the basic materials or an empty list if one could not be found.
def _getBasicMaterials(self, material_container):
base_material = material_container.getMetaDataEntry("material")
if material_container.getDefinition().getMetaDataEntry("has_machine_quality"):

View file

@ -84,11 +84,26 @@ class CuraContainerRegistry(ContainerRegistry):
if result == QMessageBox.No:
return
found_containers = []
extruder_positions = []
for instance_id in instance_ids:
containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
if containers:
found_containers.append(containers[0])
# Determine the position of the extruder of this container
extruder_id = containers[0].getMetaDataEntry("extruder", "")
if extruder_id == "":
# Global stack
extruder_positions.append(-1)
else:
extruder_containers = ContainerRegistry.getInstance().findDefinitionContainers(id=extruder_id)
if extruder_containers:
extruder_positions.append(int(extruder_containers[0].getMetaDataEntry("position", 0)))
else:
extruder_positions.append(0)
# Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...)
found_containers = [containers for (positions, containers) in sorted(zip(extruder_positions, found_containers))]
profile_writer = self._findProfileWriter(extension, description)
try:

View file

@ -205,7 +205,7 @@ class ExtruderManager(QObject):
if preferred_material_id:
search_criteria = { "type": "material", "id": preferred_material_id}
if machine_definition.getMetaDataEntry("has_machine_materials"):
search_criteria["definition"] = machine_definition.id
search_criteria["definition"] = machine_definition_id
if machine_definition.getMetaDataEntry("has_variants") and variant:
search_criteria["variant"] = variant.id

View file

@ -72,6 +72,9 @@ class MachineManager(QObject):
self._auto_materials_changed = {}
self._auto_hotends_changed = {}
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"The selected material is imcompatible with the selected machine or configuration."))
globalContainerChanged = pyqtSignal() # Emitted whenever the global stack is changed (ie: when changing between printers, changing a global profile, but not when changing a value)
activeMaterialChanged = pyqtSignal()
activeVariantChanged = pyqtSignal()
@ -549,9 +552,9 @@ class MachineManager(QObject):
material_container.nameChanged.connect(self._onMaterialNameChanged)
if material_container.getMetaDataEntry("compatible") == False:
message = Message(catalog.i18nc("@info:status",
"The selected material is imcompatible with the selected machine or configuration."))
message.show()
self._material_incompatible_message.show()
else:
self._material_incompatible_message.hide()
new_quality_id = old_quality.getId()
quality_type = old_quality.getMetaDataEntry("quality_type")
@ -829,8 +832,8 @@ class MachineManager(QObject):
# \sa getQualityVariantId
@pyqtProperty(str, notify = activeVariantChanged)
def activeQualityVariantId(self):
if self._global_container_stack:
variant = self._global_container_stack.findContainer({"type": "variant"})
if self._active_container_stack:
variant = self._active_container_stack.findContainer({"type": "variant"})
if variant:
return self.getQualityVariantId(self._global_container_stack.getBottom(), variant)
return ""

View file

@ -8,9 +8,11 @@ from UM.Math.Matrix import Matrix
from UM.Math.Vector import Vector
from UM.Scene.SceneNode import SceneNode
from UM.Scene.GroupDecorator import GroupDecorator
from UM.Math.Quaternion import Quaternion
import UM.Application
from UM.Job import Job
from UM.Math.Quaternion import Quaternion
import math
import zipfile
@ -24,113 +26,129 @@ class ThreeMFReader(MeshReader):
def __init__(self):
super().__init__()
self._supported_extensions = [".3mf"]
self._root = None
self._namespaces = {
"3mf": "http://schemas.microsoft.com/3dmanufacturing/core/2015/02",
"cura": "http://software.ultimaker.com/xml/cura/3mf/2015/10"
}
def _createNodeFromObject(self, object, name = ""):
node = SceneNode()
mesh_builder = MeshBuilder()
vertex_list = []
components = object.find(".//3mf:components", self._namespaces)
if components:
for component in components:
id = component.get("objectid")
new_object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces)
new_node = self._createNodeFromObject(new_object)
node.addChild(new_node)
transform = component.get("transform")
if transform is not None:
new_node.setTransformation(self._createMatrixFromTransformationString(transform))
# for vertex in entry.mesh.vertices.vertex:
for vertex in object.findall(".//3mf:vertex", self._namespaces):
vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])
Job.yieldThread()
# If this object has no vertices and just one child, just return the child.
if len(vertex_list) == 0 and len(node.getChildren()) == 1:
return node.getChildren()[0]
if len(node.getChildren()) > 0:
group_decorator = GroupDecorator()
node.addDecorator(group_decorator)
triangles = object.findall(".//3mf:triangle", self._namespaces)
mesh_builder.reserveFaceCount(len(triangles))
for triangle in triangles:
v1 = int(triangle.get("v1"))
v2 = int(triangle.get("v2"))
v3 = int(triangle.get("v3"))
mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2],
vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2],
vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2])
Job.yieldThread()
# Rotate the model; We use a different coordinate frame.
rotation = Matrix()
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0))
flip_matrix = Matrix()
flip_matrix._data[1, 1] = 0
flip_matrix._data[1, 2] = 1
flip_matrix._data[2, 1] = 1
flip_matrix._data[2, 2] = 0
# TODO: We currently do not check for normals and simply recalculate them.
mesh_builder.calculateNormals()
mesh_builder.setFileName(name)
mesh_data = mesh_builder.build().getTransformed(flip_matrix)
if len(mesh_data.getVertices()):
node.setMeshData(mesh_data)
node.setSelectable(True)
return node
def _createMatrixFromTransformationString(self, transformation):
splitted_transformation = transformation.split()
## Transformation is saved as:
## M00 M01 M02 0.0
## M10 M11 M12 0.0
## M20 M21 M22 0.0
## M30 M31 M32 1.0
## We switch the row & cols as that is how everyone else uses matrices!
temp_mat = Matrix()
# Rotation & Scale
temp_mat._data[0, 0] = splitted_transformation[0]
temp_mat._data[1, 0] = splitted_transformation[1]
temp_mat._data[2, 0] = splitted_transformation[2]
temp_mat._data[0, 1] = splitted_transformation[3]
temp_mat._data[1, 1] = splitted_transformation[4]
temp_mat._data[2, 1] = splitted_transformation[5]
temp_mat._data[0, 2] = splitted_transformation[6]
temp_mat._data[1, 2] = splitted_transformation[7]
temp_mat._data[2, 2] = splitted_transformation[8]
# Translation
temp_mat._data[0, 3] = splitted_transformation[9]
temp_mat._data[1, 3] = splitted_transformation[10]
temp_mat._data[2, 3] = splitted_transformation[11]
flip_matrix = Matrix()
flip_matrix._data[1, 1] = 0
flip_matrix._data[1, 2] = 1
flip_matrix._data[2, 1] = 1
flip_matrix._data[2, 2] = 0
temp_mat.multiply(flip_matrix)
return temp_mat
def read(self, file_name):
result = SceneNode()
# The base object of 3mf is a zipped archive.
archive = zipfile.ZipFile(file_name, "r")
try:
root = ET.parse(archive.open("3D/3dmodel.model"))
self._root = ET.parse(archive.open("3D/3dmodel.model"))
# There can be multiple objects, try to load all of them.
objects = root.findall("./3mf:resources/3mf:object", self._namespaces)
if len(objects) == 0:
Logger.log("w", "No objects found in 3MF file %s, either the file is corrupt or you are using an outdated format", file_name)
return None
build_items = self._root.findall("./3mf:build/3mf:item", self._namespaces)
for entry in objects:
mesh_builder = MeshBuilder()
node = SceneNode()
vertex_list = []
for build_item in build_items:
id = build_item.get("objectid")
object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces)
build_item_node = self._createNodeFromObject(object)
transform = build_item.get("transform")
if transform is not None:
build_item_node.setTransformation(self._createMatrixFromTransformationString(transform))
result.addChild(build_item_node)
# for vertex in entry.mesh.vertices.vertex:
for vertex in entry.findall(".//3mf:vertex", self._namespaces):
vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])
Job.yieldThread()
triangles = entry.findall(".//3mf:triangle", self._namespaces)
mesh_builder.reserveFaceCount(len(triangles))
for triangle in triangles:
v1 = int(triangle.get("v1"))
v2 = int(triangle.get("v2"))
v3 = int(triangle.get("v3"))
mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2],
vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2],
vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2])
Job.yieldThread()
# Rotate the model; We use a different coordinate frame.
rotation = Matrix()
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0))
# TODO: We currently do not check for normals and simply recalculate them.
mesh_builder.calculateNormals()
mesh_builder.setFileName(file_name)
mesh_data = mesh_builder.build().getTransformed(rotation)
if not len(mesh_data.getVertices()):
Logger.log("d", "One of the objects does not have vertices. Skipping it.")
continue # This object doesn't have data, so skip it.
node.setMeshData(mesh_data)
node.setSelectable(True)
transformations = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(entry.get("id")), self._namespaces)
transformation = transformations[0] if transformations else None
if transformation is not None and transformation.get("transform"):
splitted_transformation = transformation.get("transform").split()
## Transformation is saved as:
## M00 M01 M02 0.0
## M10 M11 M12 0.0
## M20 M21 M22 0.0
## M30 M31 M32 1.0
## We switch the row & cols as that is how everyone else uses matrices!
temp_mat = Matrix()
# Rotation & Scale
temp_mat._data[0,0] = splitted_transformation[0]
temp_mat._data[1,0] = splitted_transformation[1]
temp_mat._data[2,0] = splitted_transformation[2]
temp_mat._data[0,1] = splitted_transformation[3]
temp_mat._data[1,1] = splitted_transformation[4]
temp_mat._data[2,1] = splitted_transformation[5]
temp_mat._data[0,2] = splitted_transformation[6]
temp_mat._data[1,2] = splitted_transformation[7]
temp_mat._data[2,2] = splitted_transformation[8]
# Translation
temp_mat._data[0,3] = splitted_transformation[9]
temp_mat._data[1,3] = splitted_transformation[10]
temp_mat._data[2,3] = splitted_transformation[11]
node.setTransformation(temp_mat)
try:
node.getBoundingBox() # Selftest - There might be more functions that should fail
except:
Logger.log("w", "Bounding box test for object failed. Skipping this object")
continue
result.addChild(node)
Job.yieldThread()
# If there is more then one object, group them.
if len(objects) > 1:
group_decorator = GroupDecorator()
result.addDecorator(group_decorator)
elif len(objects) == 1:
if result.getChildren():
result = result.getChildren()[0] # Only one object found, return that.
else: # we failed to load any data
return None
except Exception as e:
Logger.log("e", "exception occured in 3mf reader: %s", e)
try: # Selftest - There might be more functions that should fail
@ -139,4 +157,10 @@ class ThreeMFReader(MeshReader):
except:
return None
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_container_stack:
translation = Vector(x=-global_container_stack.getProperty("machine_width", "value") / 2, y=0,
z=global_container_stack.getProperty("machine_depth", "value") / 2)
result.translate(translation, SceneNode.TransformSpace.World)
return result

View file

@ -20,10 +20,10 @@ Select an individual item in a group or merged object and edit as usual. Just Ct
Profile management is improved. You can now easily see and track changes made to your profiles.
*Improved Setting Visibility
Make multiple settings visible at once. The Visibility Overview setting indicates why a setting is not shown in the sidebar even if it is selected.
Make multiple settings visible at once. The Visibility Overview setting indicates why a setting is not shown in the sidebar even if it is enabled.
*Improved time estimation
Time estimations are more accurate. Based on our test time estimations should be within 5% accuracy.
Time estimations are more accurate. Based on our test time estimations should be within 5% accuracy for Ultimaker printers.
*Optional G-code Machine Prefix
Disable the g-code prefix in Preferences. No more UM2_ on your printer display!
@ -36,10 +36,10 @@ Configurations from older installations of Cura 2.1 are automatically imported i
*Slicing features
*Infill Improvements
We've introduced two new infill types: Tetrahedral and Cubic. They change along with the Z-axis for more uniform strength in all directions. Also, now you can change the density of the infill based on the distance from the top layers. You print get faster, use less material, and maintain the same object strength.
We've introduced two new infill types: Tetrahedral and Cubic. They change along with the Z-axis for more uniform strength in all directions. Also, now you can change the density of the infill based on the distance from the top layers. Your objects print faster, use less material, and maintain the same strength.
*Set Acceleration and Jerk by Feature
You can now set Jerk and Acceleration by feature, (infill, walls, top/bottom, etc) for more precision.
You can now set Jerk and Acceleration by feature-type (infill, walls, top/bottom, etc), for more precision.
*Outer Wall Offset
Apply an offset to where the outer wall is printed for better surface quality when the outer wall line width is smaller than the nozzle size.
@ -56,8 +56,8 @@ The Skin Overlap setting allows you to overlap the skin lines with the walls for
*Control Initial Layer Travel Speed
Set the travel speed of the initial layer(s) to reduce risk of extruder pulling the print from the bed.
*Support Bottoms
This new feature duplicates the Support Roofs feature in the places where the support rests on the model.
*Support Interface
It is now possible to print a support bottom as well as a support roof. Support bottoms are placed where the support rests on the model.
*Bug fixes & minor changes
Deleting grouped objects works as intended again.
@ -81,7 +81,7 @@ Brim is now always printed just once.
Overlap Compensation works as intended again.
A raft now produces a reasonable amount of retractions.
No more string plume on top of one-at-a-time printed objects.
Engine log can be viewed even when slicing is finished.
Engine log is now included in the application log.
Support roofs now only occur just below overhang.
Brim is now also generated under the support.
Compensate overlapping wall parts now also works for inner walls.
@ -89,9 +89,10 @@ Bed Level and Checkup procedures for UMO+ can now be done without re-adding mach
Undo and Redo now work correctly with multiple operations.
The last used folder is now remembered (instead of defaulting to home folder)
You can now adjust the speed at which the bed is lowered each layer.
Support roofs now only generate directly below the mesh.
Settings shared between skirt and brim now also activate when brim is selected.
Made it possible to add multiple Per Model Settings at once.
Support doesn't remove brim around the object any more.
Draft shield and Ooze shield get their own brim or raft.
[2.1.3]

View file

@ -3715,7 +3715,7 @@
"type": "float",
"default_value": 0.064,
"minimum_value": "0",
"maximum_value_warning": "2.0",
"maximum_value_warning": "machine_nozzle_size * 5",
"enabled": "coasting_enable",
"settable_per_mesh": false,
"settable_per_extruder": true

View file

@ -9,7 +9,7 @@ Generic Nylon profile. Serves as an example file, data in this file is not corre
<material>Nylon</material>
<color>Generic</color>
</name>
<GUID>35ebb8df-66d2-41cd-9662-b7d96b9c2cbd</GUID>
<GUID>28fb4162-db74-49e1-9008-d05f1e8bef5c</GUID>
<version>1</version>
<color_code>#3DF266</color_code>
</metadata>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic Nylon profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>Nylon</material>
<color>Black</color>
</name>
<GUID>c64c2dbe-5691-4363-a7d9-66b2dc12837f</GUID>
<version>1</version>
<color_code>#000000</color_code>
</metadata>
<properties>
<density>1.14</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">100</setting>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic Nylon profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>Nylon</material>
<color>Transparent</color>
</name>
<GUID>e256615d-a04e-4f53-b311-114b90560af9</GUID>
<version>1</version>
<color_code>#FFFFFF</color_code>
</metadata>
<properties>
<density>1.14</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">100</setting>
</settings>
</fdmmaterial>

View file

@ -36,6 +36,7 @@ Automatically generated PLA profile. Data in this file may not be not correct.
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>

View file

@ -1,5 +1,5 @@
// Copyright (c) 2016 Ultimaker B.V.
// Uranium is released under the terms of the AGPLv3 or higher.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.1
import QtQuick.Controls 1.1
@ -21,10 +21,10 @@ UM.ManagementPage
var result = { "type": "material" }
if(Cura.MachineManager.filterMaterialsByMachine)
{
result.definition = Cura.MachineManager.activeDefinitionId
result.definition = Cura.MachineManager.activeQualityDefinitionId;
if(Cura.MachineManager.hasVariants)
{
result.variant = Cura.MachineManager.activeVariantId
result.variant = Cura.MachineManager.activeQualityVariantId;
}
}
else

View file

@ -21,37 +21,45 @@ Tab
anchors.fill: parent
anchors.margins: UM.Theme.getSize("default_margin").width
TableViewColumn
Component
{
role: "label"
title: catalog.i18nc("@title:column", "Setting")
width: parent.width * 0.4
}
TableViewColumn
{
role: "profile_value"
title: catalog.i18nc("@title:column", "Profile")
width: parent.width * 0.18
delegate: Rectangle
id: itemDelegate
UM.TooltipArea
{
property var setting: qualitySettings.getItem(styleData.row)
height: childrenRect.height
color: "transparent"
width: (parent != null) ? parent.width : 0
text: (styleData.value.substr(0,1) == "=") ? styleData.value : ""
Label
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
text: styleData.value
font.weight: (setting.profile_value_source == "quality_changes") ? Font.Bold : Font.Normal
font.strikeout: quality == Cura.MachineManager.activeQualityId && setting.user_value != ""
font.strikeout: styleData.column == 1 && quality == Cura.MachineManager.activeQualityId && setting.user_value != ""
font.italic: setting.profile_value_source == "quality_changes" || (quality == Cura.MachineManager.activeQualityId && setting.user_value != "")
opacity: font.strikeout ? 0.5 : 1
color: styleData.textColor
elide: Text.ElideRight
}
}
}
TableViewColumn
{
role: "label"
title: catalog.i18nc("@title:column", "Setting")
width: parent.width * 0.4
delegate: itemDelegate
}
TableViewColumn
{
role: "profile_value"
title: catalog.i18nc("@title:column", "Profile")
width: parent.width * 0.18
delegate: itemDelegate
}
TableViewColumn
{
@ -59,12 +67,14 @@ Tab
title: catalog.i18nc("@title:column", "Current");
visible: quality == Cura.MachineManager.activeQualityId
width: parent.width * 0.18
delegate: itemDelegate
}
TableViewColumn
{
role: "unit"
title: catalog.i18nc("@title:column", "Unit")
width: parent.width * 0.14
delegate: itemDelegate
}
section.property: "category"

View file

@ -208,6 +208,8 @@ UM.ManagementPage
anchors.right: parent.right
anchors.bottom: parent.bottom
currentIndex: ExtruderManager.activeExtruderIndex + 1;
ProfileTab
{
title: catalog.i18nc("@title:tab", "Global Settings");