mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-13 06:39:09 +08:00
Merge branch 'master' into recommended_sidebar
This commit is contained in:
commit
4cd940cc5e
@ -49,6 +49,7 @@ Third party plugins
|
||||
* [Barbarian Plugin](https://github.com/nallath/BarbarianPlugin): Simple scale tool for imperial to metric.
|
||||
* [X3G Writer](https://github.com/Ghostkeeper/X3GWriter): Adds support for exporting X3G files.
|
||||
* [Auto orientation](https://github.com/nallath/CuraOrientationPlugin): Calculate the optimal orientation for a model.
|
||||
* [OctoPrint Plugin](https://github.com/fieldofview/OctoPrintPlugin): Send printjobs directly to OctoPrint and monitor their progress in Cura.
|
||||
|
||||
Making profiles for other printers
|
||||
----------------------------------
|
||||
|
@ -10,6 +10,7 @@ from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Color import Color
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from UM.Math.Polygon import Polygon
|
||||
@ -22,6 +23,7 @@ catalog = i18nCatalog("cura")
|
||||
|
||||
import numpy
|
||||
import copy
|
||||
import math
|
||||
|
||||
import UM.Settings.ContainerRegistry
|
||||
|
||||
@ -45,6 +47,7 @@ class BuildVolume(SceneNode):
|
||||
self._width = 0
|
||||
self._height = 0
|
||||
self._depth = 0
|
||||
self._shape = ""
|
||||
|
||||
self._shader = None
|
||||
|
||||
@ -139,6 +142,9 @@ class BuildVolume(SceneNode):
|
||||
def setDepth(self, depth):
|
||||
if depth: self._depth = depth
|
||||
|
||||
def setShape(self, shape):
|
||||
if shape: self._shape = shape
|
||||
|
||||
def getDisallowedAreas(self):
|
||||
return self._disallowed_areas
|
||||
|
||||
@ -177,27 +183,70 @@ class BuildVolume(SceneNode):
|
||||
min_d = -self._depth / 2
|
||||
max_d = self._depth / 2
|
||||
|
||||
mb = MeshBuilder()
|
||||
z_fight_distance = 0.2 # Distance between buildplate and disallowed area meshes to prevent z-fighting
|
||||
|
||||
# Outline 'cube' of the build volume
|
||||
mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
|
||||
if self._shape != "elliptic":
|
||||
# Outline 'cube' of the build volume
|
||||
mb = MeshBuilder()
|
||||
mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor)
|
||||
|
||||
mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
|
||||
mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
|
||||
self.setMeshData(mb.build())
|
||||
self.setMeshData(mb.build())
|
||||
|
||||
mb = MeshBuilder()
|
||||
# Build plate grid mesh
|
||||
mb = MeshBuilder()
|
||||
mb.addQuad(
|
||||
Vector(min_w, min_h - z_fight_distance, min_d),
|
||||
Vector(max_w, min_h - z_fight_distance, min_d),
|
||||
Vector(max_w, min_h - z_fight_distance, max_d),
|
||||
Vector(min_w, min_h - z_fight_distance, max_d)
|
||||
)
|
||||
|
||||
for n in range(0, 6):
|
||||
v = mb.getVertex(n)
|
||||
mb.setVertexUVCoordinates(n, v[0], v[2])
|
||||
self._grid_mesh = mb.build()
|
||||
|
||||
else:
|
||||
# Bottom and top 'ellipse' of the build volume
|
||||
aspect = 1.0
|
||||
scale_matrix = Matrix()
|
||||
if self._width != 0:
|
||||
# Scale circular meshes by aspect ratio if width != height
|
||||
aspect = self._height / self._width
|
||||
scale_matrix.compose(scale = Vector(1, 1, aspect))
|
||||
mb = MeshBuilder()
|
||||
mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self.VolumeOutlineColor)
|
||||
mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self.VolumeOutlineColor)
|
||||
self.setMeshData(mb.build().getTransformed(scale_matrix))
|
||||
|
||||
# Build plate grid mesh
|
||||
mb = MeshBuilder()
|
||||
mb.addVertex(0, min_h - z_fight_distance, 0)
|
||||
mb.addArc(max_w, Vector.Unit_Y, center = Vector(0, min_h - z_fight_distance, 0))
|
||||
sections = mb.getVertexCount() - 1 # Center point is not an arc section
|
||||
indices = []
|
||||
for n in range(0, sections - 1):
|
||||
indices.append([0, n + 2, n + 1])
|
||||
mb.addIndices(numpy.asarray(indices, dtype = numpy.int32))
|
||||
mb.calculateNormals()
|
||||
|
||||
for n in range(0, mb.getVertexCount()):
|
||||
v = mb.getVertex(n)
|
||||
mb.setVertexUVCoordinates(n, v[0], v[2] * aspect)
|
||||
self._grid_mesh = mb.build().getTransformed(scale_matrix)
|
||||
|
||||
# Indication of the machine origin
|
||||
if self._global_container_stack.getProperty("machine_center_is_zero", "value"):
|
||||
@ -205,6 +254,7 @@ class BuildVolume(SceneNode):
|
||||
else:
|
||||
origin = Vector(min_w, min_h, max_d)
|
||||
|
||||
mb = MeshBuilder()
|
||||
mb.addCube(
|
||||
width = self._origin_line_length,
|
||||
height = self._origin_line_width,
|
||||
@ -228,19 +278,6 @@ class BuildVolume(SceneNode):
|
||||
)
|
||||
self._origin_mesh = mb.build()
|
||||
|
||||
mb = MeshBuilder()
|
||||
mb.addQuad(
|
||||
Vector(min_w, min_h - 0.2, min_d),
|
||||
Vector(max_w, min_h - 0.2, min_d),
|
||||
Vector(max_w, min_h - 0.2, max_d),
|
||||
Vector(min_w, min_h - 0.2, max_d)
|
||||
)
|
||||
|
||||
for n in range(0, 6):
|
||||
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
|
||||
if self._disallowed_areas:
|
||||
@ -353,6 +390,7 @@ class BuildVolume(SceneNode):
|
||||
self._height = self._global_container_stack.getProperty("machine_height", "value")
|
||||
self._build_volume_message.hide()
|
||||
self._depth = self._global_container_stack.getProperty("machine_depth", "value")
|
||||
self._shape = self._global_container_stack.getProperty("machine_shape", "value")
|
||||
|
||||
self._updateDisallowedAreas()
|
||||
self._updateRaftThickness()
|
||||
@ -581,34 +619,79 @@ class BuildVolume(SceneNode):
|
||||
bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y)
|
||||
half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
|
||||
half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2
|
||||
if border_size - left_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[-half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width, half_machine_depth],
|
||||
[-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
|
||||
[-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
|
||||
], numpy.float32)))
|
||||
if border_size + right_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[half_machine_width, half_machine_depth],
|
||||
[half_machine_width, -half_machine_depth],
|
||||
[half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
|
||||
[half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
|
||||
], numpy.float32)))
|
||||
if border_size + bottom_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[-half_machine_width, half_machine_depth],
|
||||
[half_machine_width, half_machine_depth],
|
||||
[half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
|
||||
[-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
|
||||
], numpy.float32)))
|
||||
if border_size - top_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
|
||||
[half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
|
||||
], numpy.float32)))
|
||||
|
||||
if self._shape != "elliptic":
|
||||
if border_size - left_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[-half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width, half_machine_depth],
|
||||
[-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
|
||||
[-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
|
||||
], numpy.float32)))
|
||||
if border_size + right_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[half_machine_width, half_machine_depth],
|
||||
[half_machine_width, -half_machine_depth],
|
||||
[half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
|
||||
[half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
|
||||
], numpy.float32)))
|
||||
if border_size + bottom_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[-half_machine_width, half_machine_depth],
|
||||
[half_machine_width, half_machine_depth],
|
||||
[half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border],
|
||||
[-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border]
|
||||
], numpy.float32)))
|
||||
if border_size - top_unreachable_border > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border],
|
||||
[half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border]
|
||||
], numpy.float32)))
|
||||
else:
|
||||
sections = 32
|
||||
arc_vertex = [0, half_machine_depth - border_size]
|
||||
for i in range(0, sections):
|
||||
quadrant = math.floor(4 * i / sections)
|
||||
vertices = []
|
||||
if quadrant == 0:
|
||||
vertices.append([-half_machine_width, half_machine_depth])
|
||||
elif quadrant == 1:
|
||||
vertices.append([-half_machine_width, -half_machine_depth])
|
||||
elif quadrant == 2:
|
||||
vertices.append([half_machine_width, -half_machine_depth])
|
||||
elif quadrant == 3:
|
||||
vertices.append([half_machine_width, half_machine_depth])
|
||||
vertices.append(arc_vertex)
|
||||
|
||||
angle = 2 * math.pi * (i + 1) / sections
|
||||
arc_vertex = [-(half_machine_width - border_size) * math.sin(angle), (half_machine_depth - border_size) * math.cos(angle)]
|
||||
vertices.append(arc_vertex)
|
||||
|
||||
result[extruder_id].append(Polygon(numpy.array(vertices, numpy.float32)))
|
||||
|
||||
if border_size > 0:
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[-half_machine_width, -half_machine_depth],
|
||||
[-half_machine_width, half_machine_depth],
|
||||
[-half_machine_width + border_size, 0]
|
||||
], numpy.float32)))
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[-half_machine_width, half_machine_depth],
|
||||
[ half_machine_width, half_machine_depth],
|
||||
[ 0, half_machine_depth - border_size]
|
||||
], numpy.float32)))
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[ half_machine_width, half_machine_depth],
|
||||
[ half_machine_width, -half_machine_depth],
|
||||
[ half_machine_width - border_size, 0]
|
||||
], numpy.float32)))
|
||||
result[extruder_id].append(Polygon(numpy.array([
|
||||
[ half_machine_width,-half_machine_depth],
|
||||
[-half_machine_width,-half_machine_depth],
|
||||
[ 0, -half_machine_depth + border_size]
|
||||
], numpy.float32)))
|
||||
|
||||
return result
|
||||
|
||||
|
@ -50,43 +50,12 @@ from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
import numpy
|
||||
import copy
|
||||
import urllib
|
||||
import os
|
||||
import time
|
||||
|
||||
CONFIG_LOCK_FILENAME = "cura.lock"
|
||||
|
||||
## Contextmanager to create a lock file and remove it afterwards.
|
||||
@contextmanager
|
||||
def lockFile(filename):
|
||||
try:
|
||||
with open(filename, 'w') as lock_file:
|
||||
lock_file.write("Lock file - Cura is currently writing")
|
||||
except:
|
||||
Logger.log("e", "Could not create lock file [%s]" % filename)
|
||||
yield
|
||||
try:
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
except:
|
||||
Logger.log("e", "Could not delete lock file [%s]" % filename)
|
||||
|
||||
|
||||
## Wait for a lock file to disappear
|
||||
# the maximum allowable age is settable; if the file is too old, it will be ignored too
|
||||
def waitFileDisappear(filename, max_age_seconds=10, msg=""):
|
||||
now = time.time()
|
||||
while os.path.exists(filename) and now < os.path.getmtime(filename) + max_age_seconds and now > os.path.getmtime(filename):
|
||||
if msg:
|
||||
Logger.log("d", msg)
|
||||
time.sleep(1)
|
||||
now = time.time()
|
||||
|
||||
|
||||
numpy.seterr(all="ignore")
|
||||
@ -235,10 +204,8 @@ class CuraApplication(QtApplication):
|
||||
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
# Set the filename to create if cura is writing in the config dir.
|
||||
self._config_lock_filename = os.path.join(Resources.getConfigStoragePath(), CONFIG_LOCK_FILENAME)
|
||||
self.waitConfigLockFile()
|
||||
ContainerRegistry.getInstance().load()
|
||||
with ContainerRegistry.getInstance().lockFile():
|
||||
ContainerRegistry.getInstance().load()
|
||||
|
||||
Preferences.getInstance().addPreference("cura/active_mode", "simple")
|
||||
Preferences.getInstance().addPreference("cura/recent_files", "")
|
||||
@ -264,6 +231,8 @@ class CuraApplication(QtApplication):
|
||||
shell
|
||||
wall_thickness
|
||||
top_bottom_thickness
|
||||
z_seam_x
|
||||
z_seam_y
|
||||
infill
|
||||
infill_sparse_density
|
||||
material
|
||||
@ -318,12 +287,6 @@ class CuraApplication(QtApplication):
|
||||
|
||||
self._recent_files.append(QUrl.fromLocalFile(f))
|
||||
|
||||
## Lock file check: if (another) Cura is writing in the Config dir.
|
||||
# one may not be able to read a valid set of files while writing. Not entirely fool-proof,
|
||||
# but works when you start Cura shortly after shutting down.
|
||||
def waitConfigLockFile(self):
|
||||
waitFileDisappear(self._config_lock_filename, max_age_seconds=10, msg="Waiting for Cura to finish writing in the config dir...")
|
||||
|
||||
def _onEngineCreated(self):
|
||||
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
||||
|
||||
@ -351,11 +314,8 @@ class CuraApplication(QtApplication):
|
||||
if not self._started: # Do not do saving during application start
|
||||
return
|
||||
|
||||
self.waitConfigLockFile()
|
||||
|
||||
# When starting Cura, we check for the lockFile which is created and deleted here
|
||||
with lockFile(self._config_lock_filename):
|
||||
|
||||
# Lock file for "more" atomically loading and saving to/from config dir.
|
||||
with ContainerRegistry.getInstance().lockFile():
|
||||
for instance in ContainerRegistry.getInstance().findInstanceContainers():
|
||||
if not instance.isDirty():
|
||||
continue
|
||||
@ -1000,7 +960,7 @@ class CuraApplication(QtApplication):
|
||||
|
||||
def _reloadMeshFinished(self, job):
|
||||
# TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
|
||||
mesh_data = job.getResult().getMeshData()
|
||||
mesh_data = job.getResult()[0].getMeshData()
|
||||
if mesh_data:
|
||||
job._node.setMeshData(mesh_data)
|
||||
else:
|
||||
|
@ -128,11 +128,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
return WorkspaceReader.PreReadResult.accepted
|
||||
|
||||
def read(self, file_name):
|
||||
# Load all the nodes / meshdata of the workspace
|
||||
nodes = self._3mf_mesh_reader.read(file_name)
|
||||
if nodes is None:
|
||||
nodes = []
|
||||
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
|
||||
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
||||
@ -144,8 +139,19 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
|
||||
# Copy a number of settings from the temp preferences to the global
|
||||
global_preferences = Preferences.getInstance()
|
||||
global_preferences.setValue("general/visible_settings", temp_preferences.getValue("general/visible_settings"))
|
||||
global_preferences.setValue("cura/categories_expanded", temp_preferences.getValue("cura/categories_expanded"))
|
||||
|
||||
visible_settings = temp_preferences.getValue("general/visible_settings")
|
||||
if visible_settings is None:
|
||||
Logger.log("w", "Workspace did not contain visible settings. Leaving visibility unchanged")
|
||||
else:
|
||||
global_preferences.setValue("general/visible_settings", visible_settings)
|
||||
|
||||
categories_expanded = temp_preferences.getValue("cura/categories_expanded")
|
||||
if categories_expanded is None:
|
||||
Logger.log("w", "Workspace did not contain expanded categories. Leaving them unchanged")
|
||||
else:
|
||||
global_preferences.setValue("cura/categories_expanded", categories_expanded)
|
||||
|
||||
Application.getInstance().expandedCategoriesChanged.emit() # Notify the GUI of the change
|
||||
|
||||
self._id_mapping = {}
|
||||
@ -388,6 +394,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
|
||||
# Actually change the active machine.
|
||||
Application.getInstance().setGlobalContainerStack(global_stack)
|
||||
|
||||
# Load all the nodes / meshdata of the workspace
|
||||
nodes = self._3mf_mesh_reader.read(file_name)
|
||||
if nodes is None:
|
||||
nodes = []
|
||||
return nodes
|
||||
|
||||
def _stripFileToId(self, file):
|
||||
|
@ -5,7 +5,7 @@ from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Settings.SettingRelation import RelationType
|
||||
from UM.Application import Application
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ET
|
||||
@ -78,6 +78,12 @@ class ThreeMFWriter(MeshWriter):
|
||||
model_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/3D/3dmodel.model", Id = "rel0", Type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel")
|
||||
|
||||
model = ET.Element("model", unit = "millimeter", xmlns = self._namespaces["3mf"])
|
||||
|
||||
# Add the version of Cura this was created with. As "CuraVersion" is not a recognised metadata name
|
||||
# by 3mf itself, we place it in our own namespace.
|
||||
version_metadata = ET.SubElement(model, "metadata", xmlns = self._namespaces["cura"], name = "CuraVersion")
|
||||
version_metadata.text = Application.getInstance().getVersion()
|
||||
|
||||
resources = ET.SubElement(model, "resources")
|
||||
build = ET.SubElement(model, "build")
|
||||
|
||||
|
@ -163,22 +163,17 @@ class StartSliceJob(Job):
|
||||
obj.id = id(object)
|
||||
verts = mesh_data.getVertices()
|
||||
indices = mesh_data.getIndices()
|
||||
|
||||
if indices is not None:
|
||||
#TODO: This is a very slow way of doing it! It also locks up the GUI.
|
||||
flat_vert_list = []
|
||||
for face in indices:
|
||||
for vert_index in face:
|
||||
flat_vert_list.append(verts[vert_index])
|
||||
Job.yieldThread()
|
||||
verts = numpy.array(flat_vert_list)
|
||||
flat_verts = numpy.take(verts, indices.flatten(), axis=0)
|
||||
else:
|
||||
verts = numpy.array(verts)
|
||||
flat_verts = numpy.array(verts)
|
||||
|
||||
# Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
|
||||
verts[:, [1, 2]] = verts[:, [2, 1]]
|
||||
verts[:, 1] *= -1
|
||||
flat_verts[:, [1, 2]] = flat_verts[:, [2, 1]]
|
||||
flat_verts[:, 1] *= -1
|
||||
|
||||
obj.vertices = verts
|
||||
obj.vertices = flat_verts
|
||||
|
||||
self._handlePerObjectSettings(object, obj)
|
||||
|
||||
|
@ -120,19 +120,73 @@ Cura.MachineAction
|
||||
|
||||
Column
|
||||
{
|
||||
CheckBox
|
||||
Row
|
||||
{
|
||||
id: heatedBedCheckBox
|
||||
text: catalog.i18nc("@option:check", "Heated Bed")
|
||||
checked: String(machineHeatedBedProvider.properties.value).toLowerCase() != 'false'
|
||||
onClicked: machineHeatedBedProvider.setPropertyValue("value", checked)
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Build Plate Shape")
|
||||
}
|
||||
|
||||
ComboBox
|
||||
{
|
||||
id: shapeComboBox
|
||||
model: ListModel
|
||||
{
|
||||
id: shapesModel
|
||||
Component.onCompleted:
|
||||
{
|
||||
// Options come in as a string-representation of an OrderedDict
|
||||
var options = machineShapeProvider.properties.options.match(/^OrderedDict\(\[\((.*)\)\]\)$/);
|
||||
if(options)
|
||||
{
|
||||
options = options[1].split("), (")
|
||||
for(var i = 0; i < options.length; i++)
|
||||
{
|
||||
var option = options[i].substring(1, options[i].length - 1).split("', '")
|
||||
shapesModel.append({text: option[1], value: option[0]});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
currentIndex:
|
||||
{
|
||||
var currentValue = machineShapeProvider.properties.value;
|
||||
var index = 0;
|
||||
for(var i = 0; i < shapesModel.count; i++)
|
||||
{
|
||||
if(shapesModel.get(i).value == currentValue) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
onActivated:
|
||||
{
|
||||
machineShapeProvider.setPropertyValue("value", shapesModel.get(index).value);
|
||||
manager.forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckBox
|
||||
{
|
||||
id: centerIsZeroCheckBox
|
||||
text: catalog.i18nc("@option:check", "Machine Center is Zero")
|
||||
checked: String(machineCenterIsZeroProvider.properties.value).toLowerCase() != 'false'
|
||||
onClicked: machineCenterIsZeroProvider.setPropertyValue("value", checked)
|
||||
onClicked:
|
||||
{
|
||||
machineCenterIsZeroProvider.setPropertyValue("value", checked);
|
||||
manager.forceUpdate();
|
||||
}
|
||||
}
|
||||
CheckBox
|
||||
{
|
||||
id: heatedBedCheckBox
|
||||
text: catalog.i18nc("@option:check", "Heated Bed")
|
||||
checked: String(machineHeatedBedProvider.properties.value).toLowerCase() != 'false'
|
||||
onClicked: machineHeatedBedProvider.setPropertyValue("value", checked)
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,6 +482,16 @@ Cura.MachineAction
|
||||
storeIndex: manager.containerIndex
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: machineShapeProvider
|
||||
|
||||
containerStackId: Cura.MachineManager.activeMachineId
|
||||
key: "machine_shape"
|
||||
watchedProperties: [ "value", "options" ]
|
||||
storeIndex: manager.containerIndex
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
{
|
||||
id: machineHeatedBedProvider
|
||||
|
@ -532,6 +532,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
|
||||
|
||||
Application.getInstance().showPrintMonitor.emit(True)
|
||||
self._print_finished = True
|
||||
self.writeStarted.emit(self)
|
||||
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")
|
||||
|
||||
print_information = Application.getInstance().getPrintInformation()
|
||||
|
@ -700,7 +700,11 @@ class X3DReader(MeshReader):
|
||||
if not c is None:
|
||||
pt = c.attrib.get("point")
|
||||
if pt:
|
||||
co = [float(x) for x in pt.split()]
|
||||
# allow the list of float values in 'point' attribute to
|
||||
# be separated by commas or whitespace as per spec of
|
||||
# XML encoding of X3D
|
||||
# Ref ISO/IEC 19776-1:2015 : Section 5.1.2
|
||||
co = [float(x) for vec in pt.split(',') for x in vec.split()]
|
||||
num_verts = len(co) // 3
|
||||
self.verts = numpy.empty((4, num_verts), dtype=numpy.float32)
|
||||
self.verts[3,:] = numpy.ones((num_verts), dtype=numpy.float32)
|
||||
|
@ -137,6 +137,21 @@
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_shape":
|
||||
{
|
||||
"label": "Build plate shape",
|
||||
"description": "The shape of the build plate without taking unprintable areas into account.",
|
||||
"default_value": "rectangular",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"rectangular": "Rectangular",
|
||||
"elliptic": "Elliptic"
|
||||
},
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_height":
|
||||
{
|
||||
"label": "Machine height",
|
||||
@ -946,17 +961,39 @@
|
||||
"z_seam_type":
|
||||
{
|
||||
"label": "Z Seam Alignment",
|
||||
"description": "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these at the back, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker.",
|
||||
"description": "Starting point of each path in a layer. When paths in consecutive layers start at the same point a vertical seam may show on the print. When aligning these near a user specified location, the seam is easiest to remove. When placed randomly the inaccuracies at the paths' start will be less noticeable. When taking the shortest path the print will be quicker.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"back": "Back",
|
||||
"back": "User Specified",
|
||||
"shortest": "Shortest",
|
||||
"random": "Random"
|
||||
},
|
||||
"default_value": "shortest",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"z_seam_x":
|
||||
{
|
||||
"label": "Z Seam X",
|
||||
"description": "The X coordinate of the position near where to start printing each part in a layer.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 100.0,
|
||||
"value": "machine_width / 2",
|
||||
"enabled": "z_seam_type == 'back'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"z_seam_y":
|
||||
{
|
||||
"label": "Z Seam Y",
|
||||
"description": "The Y coordinate of the position near where to start printing each part in a layer.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 100.0,
|
||||
"value": "machine_depth / 2",
|
||||
"enabled": "z_seam_type == 'back'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"skin_no_small_gaps_heuristic":
|
||||
{
|
||||
"label": "Ignore Small Z Gaps",
|
||||
@ -996,7 +1033,7 @@
|
||||
"default_value": 2,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "infill_line_width",
|
||||
"value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == 'grid' else (3 if infill_pattern == 'triangles' or infill_pattern == 'cubic' else (4 if infill_pattern == 'tetrahedral' else 1)))",
|
||||
"value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == 'grid' else (3 if infill_pattern == 'triangles' or infill_pattern == 'cubic' or infill_pattern == 'cubicsubdiv' else (4 if infill_pattern == 'tetrahedral' else 1)))",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
@ -1012,6 +1049,7 @@
|
||||
"lines": "Lines",
|
||||
"triangles": "Triangles",
|
||||
"cubic": "Cubic",
|
||||
"cubicsubdiv": "Cubic Subdivision",
|
||||
"tetrahedral": "Tetrahedral",
|
||||
"concentric": "Concentric",
|
||||
"concentric_3d": "Concentric 3D",
|
||||
@ -1022,6 +1060,32 @@
|
||||
"value": "'lines' if infill_sparse_density > 25 else 'grid'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"sub_div_rad_mult":
|
||||
{
|
||||
"label": "Cubic Subdivision Radius",
|
||||
"description": "A multiplier on the radius from the center of each cube to check for the boundary of the model, as to decide whether this cube should be subdivided. Larger values lead to more subdivisions, i.e. more small cubes.",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"default_value": 100,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "100",
|
||||
"maximum_value_warning": "200",
|
||||
"enabled": "infill_sparse_density > 0 and infill_pattern == 'cubicsubdiv'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"sub_div_rad_add":
|
||||
{
|
||||
"label": "Cubic Subdivision Shell",
|
||||
"description": "An addition to the radius from the center of each cube to check for the boundary of the model, as to decide whether this cube should be subdivided. Larger values lead to a thicker shell of small cubes near the boundary of the model.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 0.4,
|
||||
"value": "wall_line_width_x",
|
||||
"minimum_value_warning": "-1 * infill_line_distance",
|
||||
"maximum_value_warning": "5 * infill_line_distance",
|
||||
"enabled": "infill_sparse_density > 0 and infill_pattern == 'cubicsubdiv'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_overlap":
|
||||
{
|
||||
"label": "Infill Overlap Percentage",
|
||||
@ -1116,7 +1180,7 @@
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "4",
|
||||
"maximum_value": "20 - math.log(infill_line_distance) / math.log(2)",
|
||||
"enabled": "infill_sparse_density > 0",
|
||||
"enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"gradual_infill_step_height":
|
||||
@ -1129,7 +1193,7 @@
|
||||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "3 * resolveOrValue('layer_height')",
|
||||
"maximum_value_warning": "100",
|
||||
"enabled": "infill_sparse_density > 0 and gradual_infill_steps > 0",
|
||||
"enabled": "infill_sparse_density > 0 and gradual_infill_steps > 0 and infill_pattern != 'cubicsubdiv'",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_before_walls":
|
||||
@ -2434,20 +2498,6 @@
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
{
|
||||
"cool_fan_speed_0":
|
||||
{
|
||||
"label": "Initial Fan Speed",
|
||||
"description": "The speed at which the fans spin at the start of the print. In subsequent layers the fan speed is gradually increased up to the layer corresponding to Regular Fan Speed at Height.",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"value": "cool_fan_speed",
|
||||
"default_value": 100,
|
||||
"enabled": "cool_fan_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_fan_speed_min":
|
||||
{
|
||||
"label": "Regular Fan Speed",
|
||||
@ -2490,6 +2540,19 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_fan_speed_0":
|
||||
{
|
||||
"label": "Initial Fan Speed",
|
||||
"description": "The speed at which the fans spin at the start of the print. In subsequent layers the fan speed is gradually increased up to the layer corresponding to Regular Fan Speed at Height.",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 0,
|
||||
"enabled": "cool_fan_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_fan_full_at_height":
|
||||
{
|
||||
"label": "Regular Fan Speed at Height",
|
||||
@ -3616,7 +3679,6 @@
|
||||
"type": "category",
|
||||
"icon": "category_dual",
|
||||
"description": "Settings used for printing with multiple extruders.",
|
||||
"enabled": "machine_extruder_count > 1",
|
||||
"children":
|
||||
{
|
||||
"prime_tower_enable":
|
||||
@ -3742,17 +3804,6 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"multiple_mesh_overlap":
|
||||
{
|
||||
"label": "Dual Extrusion Overlap",
|
||||
"description": "Make the models printed with different extruder trains overlap a bit. This makes the different materials bond together better.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0.15,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "1.0",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"ooze_shield_enabled":
|
||||
{
|
||||
"label": "Enable Ooze Shield",
|
||||
@ -3833,6 +3884,17 @@
|
||||
"default_value": false,
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"multiple_mesh_overlap":
|
||||
{
|
||||
"label": "Merged Meshes Overlap",
|
||||
"description": "Make meshes which are touching each other overlap a bit. This makes them bond together better.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default_value": 0.15,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "1.0",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"carve_multiple_volumes":
|
||||
{
|
||||
"label": "Remove Mesh Intersection",
|
||||
@ -4106,6 +4168,7 @@
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "support_enable",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_conical_angle":
|
||||
|
@ -290,7 +290,7 @@ Item
|
||||
Action
|
||||
{
|
||||
id: loadWorkspaceAction
|
||||
text: catalog.i18nc("@action:inmenu menubar:file","&Open Workspace...");
|
||||
text: catalog.i18nc("@action:inmenu menubar:file","&Import project...");
|
||||
}
|
||||
|
||||
Action
|
||||
|
@ -110,7 +110,7 @@ UM.MainWindow
|
||||
MenuItem
|
||||
{
|
||||
id: saveWorkspaceMenu
|
||||
text: catalog.i18nc("@title:menu menubar:file","Save Workspace")
|
||||
text: catalog.i18nc("@title:menu menubar:file","Export project")
|
||||
onTriggered: UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "file_type": "workspace" });
|
||||
}
|
||||
|
||||
|
@ -166,15 +166,6 @@ Rectangle
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
|
||||
currentModeIndex:
|
||||
{
|
||||
var index = parseInt(UM.Preferences.getValue("cura/active_mode"))
|
||||
if(index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
onCurrentModeIndexChanged:
|
||||
{
|
||||
UM.Preferences.setValue("cura/active_mode", currentModeIndex);
|
||||
@ -268,7 +259,7 @@ Rectangle
|
||||
height: settingsModeSelection.height
|
||||
width: visible ? height : 0
|
||||
|
||||
visible: !monitoringPrint && modesListModel.get(base.currentModeIndex).showFilterButton
|
||||
visible: !monitoringPrint && modesListModel.get(base.currentModeIndex) != undefined && modesListModel.get(base.currentModeIndex).showFilterButton
|
||||
opacity: visible ? 1 : 0
|
||||
|
||||
onClicked: sidebarContents.currentItem.toggleFilterField()
|
||||
@ -284,7 +275,7 @@ Rectangle
|
||||
}
|
||||
label: UM.RecolorImage
|
||||
{
|
||||
anchors.verticalCenter: control.verticalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2
|
||||
|
||||
@ -420,6 +411,12 @@ Rectangle
|
||||
modesListModel.append({ text: catalog.i18nc("@title:tab", "Recommended"), item: sidebarSimple, showFilterButton: false })
|
||||
modesListModel.append({ text: catalog.i18nc("@title:tab", "Custom"), item: sidebarAdvanced, showFilterButton: true })
|
||||
sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true });
|
||||
|
||||
var index = parseInt(UM.Preferences.getValue("cura/active_mode"))
|
||||
if(index)
|
||||
{
|
||||
currentModeIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
UM.SettingPropertyProvider
|
||||
|
Loading…
x
Reference in New Issue
Block a user