mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-15 01:35:59 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
7a9e19555a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
*.pyc
|
||||
*kdev*
|
||||
*.kate-swp
|
||||
__pycache__
|
||||
docs/html
|
||||
*.lprof
|
||||
|
@ -9,6 +9,20 @@ set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY
|
||||
set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
|
||||
|
||||
# Macro needed to list all sub-directory of a directory.
|
||||
# There is no function in cmake as far as I know.
|
||||
# Found at: http://stackoverflow.com/a/7788165
|
||||
MACRO(SUBDIRLIST result curdir)
|
||||
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
|
||||
SET(dirlist "")
|
||||
FOREACH(child ${children})
|
||||
IF(IS_DIRECTORY ${curdir}/${child})
|
||||
LIST(APPEND dirlist ${child})
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
SET(${result} ${dirlist})
|
||||
ENDMACRO()
|
||||
|
||||
if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
|
||||
# Extract Strings
|
||||
add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura)
|
||||
@ -24,36 +38,16 @@ if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
|
||||
# build directory to the source resources directory. This is mostly a convenience
|
||||
# during development, normally you want to simply use the install target to install
|
||||
# the files along side the rest of the application.
|
||||
add_custom_target(copy-translations)
|
||||
|
||||
#TODO: Properly install the built files. This should be done after we move the applications out of the Uranium repo.
|
||||
set(languages
|
||||
en
|
||||
x-test
|
||||
ru
|
||||
fr
|
||||
de
|
||||
it
|
||||
es
|
||||
fi
|
||||
pl
|
||||
cs
|
||||
bg
|
||||
)
|
||||
SUBDIRLIST(languages ${CMAKE_SOURCE_DIR}/resources/i18n/)
|
||||
foreach(lang ${languages})
|
||||
file(GLOB po_files resources/i18n/${lang}/*.po)
|
||||
foreach(file ${po_files})
|
||||
string(REGEX REPLACE ".*/(.*).po" "${lang}/\\1.mo" mofile ${file})
|
||||
add_custom_command(TARGET translations POST_BUILD COMMAND mkdir ARGS -p ${lang} COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ARGS ${file} -o ${mofile})
|
||||
file(GLOB po_files ${CMAKE_SOURCE_DIR}/resources/i18n/${lang}/*.po)
|
||||
foreach(po_file ${po_files})
|
||||
string(REGEX REPLACE ".*/(.*).po" "${CMAKE_BINARY_DIR}/resources/i18n/${lang}/LC_MESSAGES/\\1.mo" mo_file ${po_file})
|
||||
add_custom_command(TARGET translations POST_BUILD COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/resources/i18n/${lang}/LC_MESSAGES/ COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ARGS ${po_file} -o ${mo_file} -f)
|
||||
endforeach()
|
||||
|
||||
file(GLOB mo_files ${CMAKE_BINARY_DIR}/${lang}/*.mo)
|
||||
foreach(file ${mo_files})
|
||||
add_custom_command(TARGET copy-translations POST_BUILD COMMAND mkdir ARGS -p ${CMAKE_SOURCE_DIR}/resources/i18n/${lang}/LC_MESSAGES/ COMMAND cp ARGS ${file} ${CMAKE_SOURCE_DIR}/resources/i18n/${lang}/LC_MESSAGES/ COMMENT "Copying ${file}...")
|
||||
endforeach()
|
||||
|
||||
install(FILES ${mo_files} DESTINATION ${CMAKE_INSTALL_DATADIR}/uranium/resources/i18n/${lang}/LC_MESSAGES/)
|
||||
endforeach()
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -66,6 +60,9 @@ if(NOT APPLE AND NOT WIN32)
|
||||
install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages FILES_MATCHING PATTERN *.py)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages/cura)
|
||||
install(FILES cura.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||
install(FILES cura.sharedmimeinfo
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/
|
||||
RENAME cura.xml )
|
||||
else()
|
||||
install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages FILES_MATCHING PATTERN *.py)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura)
|
||||
|
@ -1,13 +1,15 @@
|
||||
[Desktop Entry]
|
||||
Version=15.06.01
|
||||
Name=Cura
|
||||
Name[de]=Cura
|
||||
GenericName=3D Printing Software
|
||||
GenericName[de]=3D-Druck-Software
|
||||
Comment=
|
||||
Exec=/usr/bin/cura_app.py
|
||||
TryExec=/usr/bin/cura_app.py
|
||||
Exec=/usr/bin/cura_app.py %f
|
||||
TryExec=/usr/bin/cura_app.py %f
|
||||
Icon=/usr/share/cura/resources/images/cura-icon.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=application/sla
|
||||
MimeType=application/sla;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png
|
||||
Categories=Graphics;
|
||||
Keywords=3D;Printing;
|
||||
|
22
cura.sharedmimeinfo
Normal file
22
cura.sharedmimeinfo
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/vnd.ms-3mfdocument">
|
||||
<comment>3D Manufacturing Format Document</comment>
|
||||
<icon name="unknown"/>
|
||||
<glob-deleteall/>
|
||||
<glob pattern="*.3mf"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/sla">
|
||||
<comment>Computer-aided design and manufacturing format</comment>
|
||||
<icon name="unknown"/>
|
||||
<glob-deleteall/>
|
||||
<glob pattern="*.stl"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/prs.wavefront-obj">
|
||||
<sub-class-of type="text/plain"/>
|
||||
<comment>Wavefront 3D Object file</comment>
|
||||
<icon name="unknown"/>
|
||||
<glob-deleteall/>
|
||||
<glob pattern="*.obj"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
@ -129,9 +129,13 @@ class BuildVolume(SceneNode):
|
||||
new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d))
|
||||
mb.addFace(first, previous_point, new_point, color = color)
|
||||
previous_point = new_point
|
||||
|
||||
# Find the largest disallowed area to exclude it from the maximum scale bounds
|
||||
size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1]))
|
||||
# Find the largest disallowed area to exclude it from the maximum scale bounds.
|
||||
# This is a very nasty hack. This pretty much only works for UM machines. This disallowed area_size needs
|
||||
# A -lot- of rework at some point in the future: TODO
|
||||
if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area.
|
||||
size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1]))
|
||||
else:
|
||||
size = 0
|
||||
disallowed_area_size = max(size, disallowed_area_size)
|
||||
|
||||
self._disallowed_area_mesh = mb.getData()
|
||||
@ -142,13 +146,16 @@ class BuildVolume(SceneNode):
|
||||
|
||||
skirt_size = 0.0
|
||||
|
||||
profile = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
skirt_size = self._getSkirtSize(profile)
|
||||
|
||||
# As this works better for UM machines, we only add the dissallowed_area_size for the z direction.
|
||||
# This is probably wrong in all other cases. TODO!
|
||||
# The +1 and -1 is added as there is always a bit of extra room required to work properly.
|
||||
scale_to_max_bounds = AxisAlignedBox(
|
||||
minimum = Vector(min_w + skirt_size, min_h, min_d + skirt_size + disallowed_area_size),
|
||||
maximum = Vector(max_w - skirt_size, max_h, max_d - skirt_size - disallowed_area_size)
|
||||
minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1),
|
||||
maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1)
|
||||
)
|
||||
|
||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
@ -169,7 +176,7 @@ class BuildVolume(SceneNode):
|
||||
if self._active_profile:
|
||||
self._active_profile.settingValueChanged.disconnect(self._onSettingValueChanged)
|
||||
|
||||
self._active_profile = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
self._active_profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if self._active_profile:
|
||||
self._active_profile.settingValueChanged.connect(self._onSettingValueChanged)
|
||||
self._updateDisallowedAreas()
|
||||
@ -192,6 +199,7 @@ class BuildVolume(SceneNode):
|
||||
skirt_size = self._getSkirtSize(self._active_profile)
|
||||
|
||||
if disallowed_areas:
|
||||
# Extend every area already in the disallowed_areas with the skirt size.
|
||||
for area in disallowed_areas:
|
||||
poly = Polygon(numpy.array(area, numpy.float32))
|
||||
poly = poly.getMinkowskiHull(Polygon(numpy.array([
|
||||
@ -207,6 +215,7 @@ class BuildVolume(SceneNode):
|
||||
|
||||
areas.append(poly)
|
||||
|
||||
# Add the skirt areas arround the borders of the build plate.
|
||||
if skirt_size > 0:
|
||||
half_machine_width = self._active_instance.getMachineSettingValue("machine_width") / 2
|
||||
half_machine_depth = self._active_instance.getMachineSettingValue("machine_depth") / 2
|
||||
@ -257,7 +266,8 @@ class BuildVolume(SceneNode):
|
||||
if profile.getSettingValue("draft_shield_enabled"):
|
||||
skirt_size += profile.getSettingValue("draft_shield_dist")
|
||||
|
||||
skirt_size += profile.getSettingValue("xy_offset")
|
||||
if profile.getSettingValue("xy_offset"):
|
||||
skirt_size += profile.getSettingValue("xy_offset")
|
||||
|
||||
return skirt_size
|
||||
|
||||
|
@ -9,19 +9,32 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
# In case of printing all at once this is the same as the convex hull. For one at the time this is the area without the head.
|
||||
self._convex_hull_boundary = None
|
||||
|
||||
# In case of printing all at once this is the same as the convex hull. For one at the time this is area with full head
|
||||
# In case of printing all at once this is the same as the convex hull. For one at the time this is area with intersection of mirrored head
|
||||
self._convex_hull_head = None
|
||||
# In case of printing all at once this is the same as the convex hull. For one at the time this is area with intersection of full head
|
||||
self._convex_hull_head_full = None
|
||||
|
||||
self._convex_hull_node = None
|
||||
self._convex_hull_job = None
|
||||
|
||||
self._profile = None
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
|
||||
self._onActiveProfileChanged()
|
||||
|
||||
|
||||
## Force that a new (empty) object is created upon copy.
|
||||
def __deepcopy__(self, memo):
|
||||
copy = ConvexHullDecorator()
|
||||
return copy
|
||||
|
||||
def getConvexHull(self):
|
||||
return self._convex_hull
|
||||
|
||||
def getConvexHullHeadFull(self):
|
||||
if not self._convex_hull_head_full:
|
||||
return self.getConvexHull()
|
||||
return self._convex_hull_head_full
|
||||
|
||||
def getConvexHullHead(self):
|
||||
if not self._convex_hull_head:
|
||||
return self.getConvexHull()
|
||||
@ -34,7 +47,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
|
||||
def setConvexHullBoundary(self, hull):
|
||||
self._convex_hull_boundary = hull
|
||||
|
||||
|
||||
def setConvexHullHeadFull(self, hull):
|
||||
self._convex_hull_head_full = hull
|
||||
|
||||
def setConvexHullHead(self, hull):
|
||||
self._convex_hull_head = hull
|
||||
|
||||
@ -57,11 +73,19 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._onSettingValueChanged)
|
||||
|
||||
self._profile = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
self._profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.connect(self._onSettingValueChanged)
|
||||
|
||||
def _onActiveMachineInstanceChanged(self):
|
||||
if self._convex_hull_job:
|
||||
self._convex_hull_job.cancel()
|
||||
self.setConvexHull(None)
|
||||
if self._convex_hull_node:
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
|
||||
def _onSettingValueChanged(self, setting):
|
||||
if setting == "print_sequence":
|
||||
if self._convex_hull_job:
|
||||
|
@ -39,7 +39,7 @@ class ConvexHullJob(Job):
|
||||
mesh = self._node.getMeshData()
|
||||
vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()
|
||||
# Don't use data below 0. TODO; We need a better check for this as this gives poor results for meshes with long edges.
|
||||
vertex_data = vertex_data[vertex_data[:,1]>0]
|
||||
vertex_data = vertex_data[vertex_data[:,1] >= 0]
|
||||
hull = Polygon(numpy.rint(vertex_data[:, [0, 2]]).astype(int))
|
||||
|
||||
# First, calculate the normal convex hull around the points
|
||||
@ -49,22 +49,36 @@ class ConvexHullJob(Job):
|
||||
# This is done because of rounding errors.
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32)))
|
||||
|
||||
profile = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if profile:
|
||||
if profile.getSettingValue("print_sequence") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
# Printing one at a time and it's not an object in a group
|
||||
self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
|
||||
head_hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"),numpy.float32)))
|
||||
self._node.callDecoration("setConvexHullHead", head_hull)
|
||||
head_and_fans = Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32))
|
||||
# Full head hull is used to actually check the order.
|
||||
full_head_hull = hull.getMinkowskiHull(head_and_fans)
|
||||
self._node.callDecoration("setConvexHullHeadFull", full_head_hull)
|
||||
mirrored = copy.deepcopy(head_and_fans)
|
||||
mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally.
|
||||
mirrored.mirror([0, 0], [1, 0]) #Mirror vertically.
|
||||
head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)
|
||||
# Min head hull is used for the push free
|
||||
min_head_hull = hull.getMinkowskiHull(head_and_fans)
|
||||
self._node.callDecoration("setConvexHullHead", min_head_hull)
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32)))
|
||||
else:
|
||||
self._node.callDecoration("setConvexHullHead", None)
|
||||
if self._node.getParent() is None: #Node was already deleted before job is done.
|
||||
self._node.callDecoration("setConvexHullNode",None)
|
||||
self._node.callDecoration("setConvexHull", None)
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
return
|
||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
|
||||
self._node.callDecoration("setConvexHullNode", hull_node)
|
||||
self._node.callDecoration("setConvexHull", hull)
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
|
||||
if self._node.getParent().callDecoration("isGroup"):
|
||||
if self._node.getParent() and self._node.getParent().callDecoration("isGroup"):
|
||||
job = self._node.getParent().callDecoration("getConvexHullJob")
|
||||
if job:
|
||||
job.cancel()
|
||||
|
@ -5,7 +5,7 @@ from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Resources import Resources
|
||||
from UM.Math.Color import Color
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder #To create a mesh to display the convex hull with.
|
||||
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
@ -25,6 +25,7 @@ class ConvexHullNode(SceneNode):
|
||||
self._inherit_scale = False
|
||||
|
||||
self._color = Color(35, 35, 35, 128)
|
||||
self._mesh_height = 0.1 #The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting.
|
||||
|
||||
self._node = node
|
||||
self._node.transformationChanged.connect(self._onNodePositionChanged)
|
||||
@ -43,22 +44,19 @@ class ConvexHullNode(SceneNode):
|
||||
self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
|
||||
|
||||
def createHullMesh(self, hull_points):
|
||||
mesh = MeshData()
|
||||
if len(hull_points) > 3:
|
||||
center = (hull_points.min(0) + hull_points.max(0)) / 2.0
|
||||
mesh.addVertex(center[0], -0.1, center[1])
|
||||
else:
|
||||
#Input checking.
|
||||
if len(hull_points) < 3:
|
||||
return None
|
||||
for point in hull_points:
|
||||
mesh.addVertex(point[0], -0.1, point[1])
|
||||
indices = []
|
||||
for i in range(len(hull_points) - 1):
|
||||
indices.append([0, i + 1, i + 2])
|
||||
|
||||
indices.append([0, mesh.getVertexCount() - 1, 1])
|
||||
mesh_builder = MeshBuilder()
|
||||
point_first = Vector(hull_points[0][0], self._mesh_height, hull_points[0][1])
|
||||
point_previous = Vector(hull_points[1][0], self._mesh_height, hull_points[1][1])
|
||||
for point in hull_points[2:]: #Add the faces in the order of a triangle fan.
|
||||
point_new = Vector(point[0], self._mesh_height, point[1])
|
||||
mesh_builder.addFace(point_first, point_previous, point_new, color = self._color)
|
||||
point_previous = point_new #Prepare point_previous for the next triangle.
|
||||
|
||||
mesh.addIndices(numpy.array(indices, numpy.int32))
|
||||
return mesh
|
||||
return mesh_builder.getData()
|
||||
|
||||
def getWatchedNode(self):
|
||||
return self._node
|
||||
|
@ -57,8 +57,10 @@ numpy.seterr(all="ignore")
|
||||
|
||||
if platform.system() == "Linux": # Needed for platform.linux_distribution, which is not available on Windows and OSX
|
||||
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
|
||||
if platform.linux_distribution()[0] in ("Ubuntu", ): # Just in case it also happens on Debian, so it can be added
|
||||
from OpenGL import GL
|
||||
if platform.linux_distribution()[0] in ("Ubuntu", ): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
|
||||
import ctypes
|
||||
from ctypes.util import find_library
|
||||
ctypes.CDLL(find_library('GL'), ctypes.RTLD_GLOBAL)
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraVersion
|
||||
@ -76,6 +78,8 @@ class CuraApplication(QtApplication):
|
||||
if not hasattr(sys, "frozen"):
|
||||
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
|
||||
|
||||
self._open_file_queue = [] #Files to open when plug-ins are loaded.
|
||||
|
||||
super().__init__(name = "cura", version = CuraVersion)
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
@ -100,10 +104,12 @@ class CuraApplication(QtApplication):
|
||||
self._platform_activity = False
|
||||
self._scene_boundingbox = AxisAlignedBox()
|
||||
self._job_name = None
|
||||
self._center_after_select = False
|
||||
|
||||
self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged)
|
||||
self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested)
|
||||
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
|
||||
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
|
||||
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
||||
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
||||
@ -114,6 +120,7 @@ class CuraApplication(QtApplication):
|
||||
Preferences.getInstance().addPreference("cura/categories_expanded", "")
|
||||
Preferences.getInstance().addPreference("view/center_on_select", True)
|
||||
Preferences.getInstance().addPreference("mesh/scale_to_fit", True)
|
||||
Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode")
|
||||
|
||||
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
|
||||
|
||||
@ -124,6 +131,10 @@ class CuraApplication(QtApplication):
|
||||
continue
|
||||
|
||||
self._recent_files.append(QUrl.fromLocalFile(f))
|
||||
|
||||
@pyqtSlot(result = QUrl)
|
||||
def getDefaultPath(self):
|
||||
return QUrl.fromLocalFile(os.path.expanduser("~/"))
|
||||
|
||||
## Handle loading of all plugin types (and the backend explicitly)
|
||||
# \sa PluginRegistery
|
||||
@ -132,21 +143,21 @@ class CuraApplication(QtApplication):
|
||||
if not hasattr(sys, "frozen"):
|
||||
self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))
|
||||
self._plugin_registry.loadPlugin("ConsoleLogger")
|
||||
self._plugin_registry.loadPlugin("CuraEngineBackend")
|
||||
|
||||
self._plugin_registry.loadPlugins()
|
||||
|
||||
if self.getBackend() == None:
|
||||
raise RuntimeError("Could not load the backend plugin!")
|
||||
|
||||
self._plugins_loaded = True
|
||||
|
||||
def addCommandLineOptions(self, parser):
|
||||
super().addCommandLineOptions(parser)
|
||||
parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
|
||||
parser.add_argument("--debug", dest="debug-mode", action="store_true", default=False, help="Enable detailed crash reports.")
|
||||
|
||||
def run(self):
|
||||
if "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" not in os.environ or os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] != "cpp":
|
||||
Logger.log("w", "Using Python implementation of Protobuf, expect bad performance!")
|
||||
|
||||
self._i18n_catalog = i18nCatalog("cura");
|
||||
|
||||
i18nCatalog.setTagReplacements({
|
||||
@ -198,13 +209,18 @@ class CuraApplication(QtApplication):
|
||||
|
||||
for file in self.getCommandLineOption("file", []):
|
||||
self._openFile(file)
|
||||
for file_name in self._open_file_queue: #Open all the files that were queued up while plug-ins were loading.
|
||||
self._openFile(file_name)
|
||||
|
||||
self.exec_()
|
||||
|
||||
# Handle Qt events
|
||||
def event(self, event):
|
||||
if event.type() == QEvent.FileOpen:
|
||||
self._openFile(event.file())
|
||||
if self._plugins_loaded:
|
||||
self._openFile(event.file())
|
||||
else:
|
||||
self._open_file_queue.append(event.file())
|
||||
|
||||
return super().event(event)
|
||||
|
||||
@ -229,9 +245,7 @@ class CuraApplication(QtApplication):
|
||||
else:
|
||||
self.getController().setActiveTool("TranslateTool")
|
||||
if Preferences.getInstance().getValue("view/center_on_select"):
|
||||
self._camera_animation.setStart(self.getController().getTool("CameraTool").getOrigin())
|
||||
self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
|
||||
self._camera_animation.start()
|
||||
self._center_after_select = True
|
||||
else:
|
||||
if self.getController().getActiveTool():
|
||||
self._previous_active_tool = self.getController().getActiveTool().getPluginId()
|
||||
@ -239,6 +253,13 @@ class CuraApplication(QtApplication):
|
||||
else:
|
||||
self._previous_active_tool = None
|
||||
|
||||
def _onToolOperationStopped(self, event):
|
||||
if self._center_after_select:
|
||||
self._center_after_select = False
|
||||
self._camera_animation.setStart(self.getController().getTool("CameraTool").getOrigin())
|
||||
self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
|
||||
self._camera_animation.start()
|
||||
|
||||
requestAddPrinter = pyqtSignal()
|
||||
activityChanged = pyqtSignal()
|
||||
sceneBoundingBoxChanged = pyqtSignal()
|
||||
@ -249,17 +270,23 @@ class CuraApplication(QtApplication):
|
||||
|
||||
@pyqtProperty(str, notify = sceneBoundingBoxChanged)
|
||||
def getSceneBoundingBoxString(self):
|
||||
return self._i18n_catalog.i18nc("@info", "%.1f x %.1f x %.1f mm") % (self._scene_boundingbox.width.item(), self._scene_boundingbox.depth.item(), self._scene_boundingbox.height.item())
|
||||
return self._i18n_catalog.i18nc("@info", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_boundingbox.width.item(), 'depth': self._scene_boundingbox.depth.item(), 'height' : self._scene_boundingbox.height.item()}
|
||||
|
||||
def updatePlatformActivity(self, node = None):
|
||||
count = 0
|
||||
scene_boundingbox = AxisAlignedBox()
|
||||
scene_boundingbox = None
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode or not node.getMeshData():
|
||||
continue
|
||||
|
||||
count += 1
|
||||
scene_boundingbox += node.getBoundingBox()
|
||||
if not scene_boundingbox:
|
||||
scene_boundingbox = copy.deepcopy(node.getBoundingBox())
|
||||
else:
|
||||
scene_boundingbox += node.getBoundingBox()
|
||||
|
||||
if not scene_boundingbox:
|
||||
scene_boundingbox = AxisAlignedBox()
|
||||
|
||||
if repr(self._scene_boundingbox) != repr(scene_boundingbox):
|
||||
self._scene_boundingbox = scene_boundingbox
|
||||
@ -270,6 +297,7 @@ class CuraApplication(QtApplication):
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setJobName(self, name):
|
||||
name = os.path.splitext(name)[0] #when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its extension. This cuts the extension off if nescessary.
|
||||
if self._job_name != name:
|
||||
self._job_name = name
|
||||
self.jobNameChanged.emit()
|
||||
@ -280,9 +308,28 @@ class CuraApplication(QtApplication):
|
||||
def jobName(self):
|
||||
return self._job_name
|
||||
|
||||
## Remove an object from the scene
|
||||
# Remove all selected objects from the scene.
|
||||
@pyqtSlot()
|
||||
def deleteSelection(self):
|
||||
if not self.getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
op = GroupedOperation()
|
||||
nodes = Selection.getAllSelectedObjects()
|
||||
for node in nodes:
|
||||
op.addOperation(RemoveSceneNodeOperation(node))
|
||||
|
||||
op.push()
|
||||
|
||||
pass
|
||||
|
||||
## Remove an object from the scene.
|
||||
# Note that this only removes an object if it is selected.
|
||||
@pyqtSlot("quint64")
|
||||
def deleteObject(self, object_id):
|
||||
if not self.getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
node = self.getController().getScene().findObject(object_id)
|
||||
|
||||
if not node and object_id != 0: #Workaround for tool handles overlapping the selected object
|
||||
@ -342,6 +389,9 @@ class CuraApplication(QtApplication):
|
||||
## Delete all mesh data on the scene.
|
||||
@pyqtSlot()
|
||||
def deleteAll(self):
|
||||
if not self.getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if type(node) is not SceneNode:
|
||||
@ -462,18 +512,18 @@ class CuraApplication(QtApplication):
|
||||
|
||||
@pyqtSlot(str, result = "QVariant")
|
||||
def getSettingValue(self, key):
|
||||
if not self.getMachineManager().getActiveProfile():
|
||||
if not self.getMachineManager().getWorkingProfile():
|
||||
return None
|
||||
return self.getMachineManager().getActiveProfile().getSettingValue(key)
|
||||
return self.getMachineManager().getWorkingProfile().getSettingValue(key)
|
||||
#return self.getActiveMachine().getSettingValueByKey(key)
|
||||
|
||||
## Change setting by key value pair
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setSettingValue(self, key, value):
|
||||
if not self.getMachineManager().getActiveProfile():
|
||||
if not self.getMachineManager().getWorkingProfile():
|
||||
return
|
||||
|
||||
self.getMachineManager().getActiveProfile().setSettingValue(key, value)
|
||||
self.getMachineManager().getWorkingProfile().setSettingValue(key, value)
|
||||
|
||||
@pyqtSlot()
|
||||
def mergeSelected(self):
|
||||
@ -499,6 +549,7 @@ class CuraApplication(QtApplication):
|
||||
group_decorator = GroupDecorator()
|
||||
group_node.addDecorator(group_decorator)
|
||||
group_node.setParent(self.getController().getScene().getRoot())
|
||||
group_node.setSelectable(True)
|
||||
center = Selection.getSelectionCenter()
|
||||
group_node.setPosition(center)
|
||||
group_node.setCenterPosition(center)
|
||||
@ -570,9 +621,9 @@ class CuraApplication(QtApplication):
|
||||
def _onFileLoaded(self, job):
|
||||
node = job.getResult()
|
||||
if node != None:
|
||||
self.setJobName(os.path.basename(job.getFileName()))
|
||||
node.setSelectable(True)
|
||||
node.setName(os.path.basename(job.getFileName()))
|
||||
|
||||
op = AddSceneNodeOperation(node, self.getController().getScene().getRoot())
|
||||
op.push()
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QPixmap, QColor, QFont
|
||||
from PyQt5.QtCore import Qt, QCoreApplication
|
||||
from PyQt5.QtGui import QPixmap, QColor, QFont, QFontMetrics
|
||||
from PyQt5.QtWidgets import QSplashScreen
|
||||
|
||||
from UM.Resources import Resources
|
||||
@ -11,7 +11,10 @@ from UM.Application import Application
|
||||
class CuraSplashScreen(QSplashScreen):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setPixmap(QPixmap(Resources.getPath(Resources.Images, "cura.png")))
|
||||
self._scale = round(QFontMetrics(QCoreApplication.instance().font()).ascent() / 12)
|
||||
|
||||
splash_image = QPixmap(Resources.getPath(Resources.Images, "cura.png"))
|
||||
self.setPixmap(splash_image.scaled(splash_image.size() * self._scale))
|
||||
|
||||
def drawContents(self, painter):
|
||||
painter.save()
|
||||
@ -19,11 +22,11 @@ class CuraSplashScreen(QSplashScreen):
|
||||
|
||||
version = Application.getInstance().getVersion().split("-")
|
||||
|
||||
painter.setFont(QFont("Proxima Nova Rg", 20))
|
||||
painter.drawText(0, 0, 203, 230, Qt.AlignRight | Qt.AlignBottom, version[0])
|
||||
painter.setFont(QFont("Proxima Nova Rg", 20 ))
|
||||
painter.drawText(0, 0, 330 * self._scale, 230 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[0])
|
||||
if len(version) > 1:
|
||||
painter.setFont(QFont("Proxima Nova Rg", 12))
|
||||
painter.drawText(0, 0, 203, 255, Qt.AlignRight | Qt.AlignBottom, version[1])
|
||||
painter.setFont(QFont("Proxima Nova Rg", 12 ))
|
||||
painter.drawText(0, 0, 330 * self._scale, 255 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[1])
|
||||
|
||||
painter.restore()
|
||||
super().drawContents(painter)
|
||||
|
@ -131,7 +131,7 @@ class Layer():
|
||||
continue
|
||||
if not make_mesh and not (polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType):
|
||||
continue
|
||||
|
||||
|
||||
poly_color = polygon.getColor()
|
||||
|
||||
points = numpy.copy(polygon.data)
|
||||
@ -140,26 +140,7 @@ class Layer():
|
||||
if polygon.type == Polygon.MoveCombingType or polygon.type == Polygon.MoveRetractionType:
|
||||
points[:,1] += 0.01
|
||||
|
||||
# Calculate normals for the entire polygon using numpy.
|
||||
normals = numpy.copy(points)
|
||||
normals[:,1] = 0.0 # We are only interested in 2D normals
|
||||
|
||||
# Calculate the edges between points.
|
||||
# The call to numpy.roll shifts the entire array by one so that
|
||||
# we end up subtracting each next point from the current, wrapping
|
||||
# around. This gives us the edges from the next point to the current
|
||||
# point.
|
||||
normals[:] = normals[:] - numpy.roll(normals, -1, axis = 0)
|
||||
# Calculate the length of each edge using standard Pythagoras
|
||||
lengths = numpy.sqrt(normals[:,0] ** 2 + normals[:,2] ** 2)
|
||||
# The normal of a 2D vector is equal to its x and y coordinates swapped
|
||||
# and then x inverted. This code does that.
|
||||
normals[:,[0, 2]] = normals[:,[2, 0]]
|
||||
normals[:,0] *= -1
|
||||
|
||||
# Normalize the normals.
|
||||
normals[:,0] /= lengths
|
||||
normals[:,2] /= lengths
|
||||
normals = polygon.getNormals()
|
||||
|
||||
# Scale all by the line width of the polygon so we can easily offset.
|
||||
normals *= (polygon.lineWidth / 2)
|
||||
@ -193,22 +174,19 @@ class Polygon():
|
||||
MoveRetractionType = 9
|
||||
|
||||
def __init__(self, mesh, type, data, line_width):
|
||||
super().__init__()
|
||||
self._mesh = mesh
|
||||
self._type = type
|
||||
self._data = data
|
||||
self._line_width = line_width / 1000
|
||||
|
||||
self._color = self.__color_map[type]
|
||||
|
||||
def build(self, offset, vertices, colors, indices):
|
||||
self._begin = offset
|
||||
self._end = self._begin + len(self._data) - 1
|
||||
|
||||
color = self.getColor()
|
||||
color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)
|
||||
color = numpy.array([color.r, color.g, color.b, color.a], numpy.float32)
|
||||
|
||||
vertices[self._begin:self._end + 1, :] = self._data[:, :]
|
||||
colors[self._begin:self._end + 1, :] = color
|
||||
colors[self._begin:self._end + 1, :] = numpy.array([self._color.r * 0.5, self._color.g * 0.5, self._color.b * 0.5, self._color.a], numpy.float32)
|
||||
|
||||
for i in range(self._begin, self._end):
|
||||
indices[i, 0] = i
|
||||
@ -218,26 +196,7 @@ class Polygon():
|
||||
indices[self._end, 1] = self._begin
|
||||
|
||||
def getColor(self):
|
||||
if self._type == self.Inset0Type:
|
||||
return Color(1.0, 0.0, 0.0, 1.0)
|
||||
elif self._type == self.InsetXType:
|
||||
return Color(0.0, 1.0, 0.0, 1.0)
|
||||
elif self._type == self.SkinType:
|
||||
return Color(1.0, 1.0, 0.0, 1.0)
|
||||
elif self._type == self.SupportType:
|
||||
return Color(0.0, 1.0, 1.0, 1.0)
|
||||
elif self._type == self.SkirtType:
|
||||
return Color(0.0, 1.0, 1.0, 1.0)
|
||||
elif self._type == self.InfillType:
|
||||
return Color(1.0, 0.74, 0.0, 1.0)
|
||||
elif self._type == self.SupportInfillType:
|
||||
return Color(0.0, 1.0, 1.0, 1.0)
|
||||
elif self._type == self.MoveCombingType:
|
||||
return Color(0.0, 0.0, 1.0, 1.0)
|
||||
elif self._type == self.MoveRetractionType:
|
||||
return Color(0.5, 0.5, 1.0, 1.0)
|
||||
else:
|
||||
return Color(1.0, 1.0, 1.0, 1.0)
|
||||
return self._color
|
||||
|
||||
def vertexCount(self):
|
||||
return len(self._data)
|
||||
@ -257,3 +216,40 @@ class Polygon():
|
||||
@property
|
||||
def lineWidth(self):
|
||||
return self._line_width
|
||||
|
||||
# Calculate normals for the entire polygon using numpy.
|
||||
def getNormals(self):
|
||||
normals = numpy.copy(self._data)
|
||||
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
|
||||
|
||||
return normals
|
||||
|
||||
__color_map = {
|
||||
NoneType: Color(1.0, 1.0, 1.0, 1.0),
|
||||
Inset0Type: Color(1.0, 0.0, 0.0, 1.0),
|
||||
InsetXType: Color(0.0, 1.0, 0.0, 1.0),
|
||||
SkinType: Color(1.0, 1.0, 0.0, 1.0),
|
||||
SupportType: Color(0.0, 1.0, 1.0, 1.0),
|
||||
SkirtType: Color(0.0, 1.0, 1.0, 1.0),
|
||||
InfillType: Color(1.0, 0.74, 0.0, 1.0),
|
||||
SupportInfillType: Color(0.0, 1.0, 1.0, 1.0),
|
||||
MoveCombingType: Color(0.0, 0.0, 1.0, 1.0),
|
||||
MoveRetractionType: Color(0.5, 0.5, 1.0, 1.0),
|
||||
}
|
||||
|
@ -21,26 +21,27 @@ class OneAtATimeIterator(Iterator.Iterator):
|
||||
if not type(node) is SceneNode:
|
||||
continue
|
||||
|
||||
if node.getBoundingBox().height > Application.getInstance().getMachineManager().getActiveProfile().getSettingValue("gantry_height"):
|
||||
if node.getBoundingBox().height > Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("gantry_height"):
|
||||
return
|
||||
if node.callDecoration("getConvexHull"):
|
||||
node_list.append(node)
|
||||
|
||||
|
||||
if len(node_list) < 2:
|
||||
self._node_stack = node_list[:]
|
||||
return
|
||||
|
||||
|
||||
# Copy the list
|
||||
self._original_node_list = node_list[:]
|
||||
|
||||
|
||||
## Initialise the hit map (pre-compute all hits between all objects)
|
||||
self._hit_map = [[self._checkHit(j,i) for i in node_list] for j in node_list]
|
||||
|
||||
self._hit_map = [[self._checkHit(i,j) for i in node_list] for j in node_list]
|
||||
|
||||
# Check if we have to files that block eachother. If this is the case, there is no solution!
|
||||
for a in range(0,len(node_list)):
|
||||
for b in range(0,len(node_list)):
|
||||
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return
|
||||
|
||||
|
||||
# Sort the original list so that items that block the most other objects are at the beginning.
|
||||
# This does not decrease the worst case running time, but should improve it in most cases.
|
||||
sorted(node_list, key = cmp_to_key(self._calculateScore))
|
||||
@ -59,44 +60,46 @@ class OneAtATimeIterator(Iterator.Iterator):
|
||||
# We have no more nodes to check, so quit looking.
|
||||
todo_node_list = None
|
||||
self._node_stack = new_order
|
||||
|
||||
|
||||
return
|
||||
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
||||
self._node_stack = [] #No result found!
|
||||
self._node_stack = [] #No result found!
|
||||
|
||||
|
||||
|
||||
# Check if first object can be printed before the provided list (using the hit map)
|
||||
def _checkHitMultiple(self, node, other_nodes):
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
if self._hit_map[node_index][self._original_node_list.index(other_node)]:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[node_index][other_node_index]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _checkBlockMultiple(self, node, other_nodes):
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
if self._hit_map[self._original_node_list.index(other_node)][node_index] and node_index != self._original_node_list.index(other_node):
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
## Calculate score simply sums the number of other objects it 'blocks'
|
||||
def _calculateScore(self, a, b):
|
||||
score_a = sum(self._hit_map[self._original_node_list.index(a)])
|
||||
score_b = sum(self._hit_map[self._original_node_list.index(b)])
|
||||
return score_a - score_b
|
||||
|
||||
|
||||
# Checks if A can be printed before B
|
||||
def _checkHit(self, a, b):
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHead"))
|
||||
|
||||
overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHeadFull"))
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
## Internal object used to keep track of a possible order in which to print objects.
|
||||
class _ObjectOrder():
|
||||
@ -107,4 +110,4 @@ class _ObjectOrder():
|
||||
"""
|
||||
self.order = order
|
||||
self.todo = todo
|
||||
|
||||
|
||||
|
@ -60,12 +60,16 @@ class PlatformPhysics:
|
||||
|
||||
build_volume_bounding_box = copy.deepcopy(self._build_volume.getBoundingBox())
|
||||
build_volume_bounding_box.setBottom(-9001) # Ignore intersections with the bottom
|
||||
node._outside_buildarea = False
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
node._outside_buildarea = True
|
||||
else:
|
||||
node._outside_buildarea = False
|
||||
# When printing one at a time too high objects are not printable.
|
||||
if Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("print_sequence") == "one_at_a_time":
|
||||
if node.getBoundingBox().height > Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("gantry_height"):
|
||||
node._outside_buildarea = True
|
||||
|
||||
# Move it downwards if bottom is above platform
|
||||
move_vector = Vector()
|
||||
|
@ -4,7 +4,6 @@
|
||||
from PyQt5.QtCore import QObject, QDateTime, QTimer, pyqtSignal, pyqtSlot, pyqtProperty
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Settings.MachineSettings import MachineSettings
|
||||
from UM.Resources import Resources
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Qt.Duration import Duration
|
||||
@ -66,6 +65,6 @@ class PrintInformation(QObject):
|
||||
self.currentPrintTimeChanged.emit()
|
||||
|
||||
# Material amount is sent as an amount of mm^3, so calculate length from that
|
||||
r = Application.getInstance().getMachineManager().getActiveProfile().getSettingValue("material_diameter") / 2
|
||||
r = Application.getInstance().getMachineManager().getWorkingProfile().getSettingValue("material_diameter") / 2
|
||||
self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2)
|
||||
self.materialAmountChanged.emit()
|
||||
|
@ -6,7 +6,6 @@ class ZOffsetDecorator(SceneNodeDecorator):
|
||||
self._z_offset = 0
|
||||
|
||||
def setZOffset(self, offset):
|
||||
print("setZOffset", offset)
|
||||
self._z_offset = offset
|
||||
|
||||
def getZOffset(self):
|
||||
|
15
cura_app.py
15
cura_app.py
@ -12,15 +12,12 @@ def exceptHook(type, value, traceback):
|
||||
|
||||
sys.excepthook = exceptHook
|
||||
|
||||
try:
|
||||
from google.protobuf.pyext import _message
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp"
|
||||
|
||||
if True: # To make the code style checker stop complaining
|
||||
import cura.CuraApplication
|
||||
# Workaround for a race condition on certain systems where there
|
||||
# is a race condition between Arcus and PyQt. Importing Arcus
|
||||
# first seems to prevent Sip from going into a state where it
|
||||
# tries to create PyQt objects on a non-main thread.
|
||||
import Arcus
|
||||
import cura.CuraApplication
|
||||
|
||||
if sys.platform == "win32" and hasattr(sys, "frozen"):
|
||||
import os
|
||||
|
@ -24,7 +24,7 @@ import xml.etree.ElementTree as ET
|
||||
class ThreeMFReader(MeshReader):
|
||||
def __init__(self):
|
||||
super(ThreeMFReader, self).__init__()
|
||||
self._supported_extension = ".3mf"
|
||||
self._supported_extensions = [".3mf"]
|
||||
|
||||
self._namespaces = {
|
||||
"3mf": "http://schemas.microsoft.com/3dmanufacturing/core/2015/02",
|
||||
@ -33,102 +33,102 @@ class ThreeMFReader(MeshReader):
|
||||
|
||||
def read(self, file_name):
|
||||
result = None
|
||||
extension = os.path.splitext(file_name)[1]
|
||||
if extension.lower() == self._supported_extension:
|
||||
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"))
|
||||
|
||||
# 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
|
||||
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"))
|
||||
|
||||
for object in objects:
|
||||
mesh = MeshData()
|
||||
node = SceneNode()
|
||||
vertex_list = []
|
||||
#for vertex in object.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()
|
||||
|
||||
triangles = object.findall(".//3mf:triangle", self._namespaces)
|
||||
|
||||
mesh.reserveFaceCount(len(triangles))
|
||||
|
||||
#for triangle in object.mesh.triangles.triangle:
|
||||
for triangle in triangles:
|
||||
v1 = int(triangle.get("v1"))
|
||||
v2 = int(triangle.get("v2"))
|
||||
v3 = int(triangle.get("v3"))
|
||||
mesh.addFace(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()
|
||||
|
||||
#TODO: We currently do not check for normals and simply recalculate them.
|
||||
mesh.calculateNormals()
|
||||
node.setMeshData(mesh)
|
||||
node.setSelectable(True)
|
||||
|
||||
transformation = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(object.get("id")), self._namespaces)
|
||||
if transformation:
|
||||
transformation = transformation[0]
|
||||
|
||||
if 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.setPosition(Vector(temp_mat.at(0,3), temp_mat.at(1,3), temp_mat.at(2,3)))
|
||||
|
||||
temp_quaternion = Quaternion()
|
||||
temp_quaternion.setByMatrix(temp_mat)
|
||||
node.setOrientation(temp_quaternion)
|
||||
|
||||
# Magical scale extraction
|
||||
scale = temp_mat.getTransposed().multiply(temp_mat)
|
||||
scale_x = math.sqrt(scale.at(0,0))
|
||||
scale_y = math.sqrt(scale.at(1,1))
|
||||
scale_z = math.sqrt(scale.at(2,2))
|
||||
node.setScale(Vector(scale_x,scale_y,scale_z))
|
||||
|
||||
# We use a different coordinate frame, so rotate.
|
||||
#rotation = Quaternion.fromAngleAxis(-0.5 * math.pi, Vector(1,0,0))
|
||||
#node.rotate(rotation)
|
||||
result.addChild(node)
|
||||
# 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
|
||||
|
||||
for object in objects:
|
||||
mesh = MeshData()
|
||||
node = SceneNode()
|
||||
vertex_list = []
|
||||
#for vertex in object.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 there is more then one object, group them.
|
||||
try:
|
||||
if len(objects) > 1:
|
||||
group_decorator = GroupDecorator()
|
||||
result.addDecorator(group_decorator)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
Logger.log("e" ,"exception occured in 3mf reader: %s" , e)
|
||||
triangles = object.findall(".//3mf:triangle", self._namespaces)
|
||||
|
||||
mesh.reserveFaceCount(len(triangles))
|
||||
|
||||
#for triangle in object.mesh.triangles.triangle:
|
||||
for triangle in triangles:
|
||||
v1 = int(triangle.get("v1"))
|
||||
v2 = int(triangle.get("v2"))
|
||||
v3 = int(triangle.get("v3"))
|
||||
mesh.addFace(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()
|
||||
|
||||
#TODO: We currently do not check for normals and simply recalculate them.
|
||||
mesh.calculateNormals()
|
||||
node.setMeshData(mesh)
|
||||
node.setSelectable(True)
|
||||
|
||||
transformation = root.findall("./3mf:build/3mf:item[@objectid='{0}']".format(object.get("id")), self._namespaces)
|
||||
if transformation:
|
||||
transformation = transformation[0]
|
||||
|
||||
if 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.setPosition(Vector(temp_mat.at(0,3), temp_mat.at(1,3), temp_mat.at(2,3)))
|
||||
|
||||
temp_quaternion = Quaternion()
|
||||
temp_quaternion.setByMatrix(temp_mat)
|
||||
node.setOrientation(temp_quaternion)
|
||||
|
||||
# Magical scale extraction
|
||||
scale = temp_mat.getTransposed().multiply(temp_mat)
|
||||
scale_x = math.sqrt(scale.at(0,0))
|
||||
scale_y = math.sqrt(scale.at(1,1))
|
||||
scale_z = math.sqrt(scale.at(2,2))
|
||||
node.setScale(Vector(scale_x,scale_y,scale_z))
|
||||
|
||||
# We use a different coordinate frame, so rotate.
|
||||
#rotation = Quaternion.fromAngleAxis(-0.5 * math.pi, Vector(1,0,0))
|
||||
#node.rotate(rotation)
|
||||
result.addChild(node)
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
#If there is more then one object, group them.
|
||||
try:
|
||||
if len(objects) > 1:
|
||||
group_decorator = GroupDecorator()
|
||||
result.addDecorator(group_decorator)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
Logger.log("e" ,"exception occured in 3mf reader: %s" , e)
|
||||
|
||||
return result
|
||||
|
@ -15,10 +15,12 @@ def getMetaData():
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for reading 3MF files."),
|
||||
"api": 2
|
||||
},
|
||||
"mesh_reader": {
|
||||
"extension": "3mf",
|
||||
"description": catalog.i18nc("@item:inlistbox", "3MF File")
|
||||
}
|
||||
"mesh_reader": [
|
||||
{
|
||||
"extension": "3mf",
|
||||
"description": catalog.i18nc("@item:inlistbox", "3MF File")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
|
61
plugins/AutoSave/AutoSave.py
Normal file
61
plugins/AutoSave/AutoSave.py
Normal file
@ -0,0 +1,61 @@
|
||||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
from UM.Extension import Extension
|
||||
from UM.Preferences import Preferences
|
||||
from UM.Application import Application
|
||||
from UM.Resources import Resources
|
||||
from UM.Logger import Logger
|
||||
|
||||
class AutoSave(Extension):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._triggerTimer)
|
||||
|
||||
machine_manager = Application.getInstance().getMachineManager()
|
||||
|
||||
self._profile = None
|
||||
machine_manager.activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
machine_manager.profileNameChanged.connect(self._triggerTimer)
|
||||
machine_manager.profilesChanged.connect(self._triggerTimer)
|
||||
machine_manager.machineInstanceNameChanged.connect(self._triggerTimer)
|
||||
machine_manager.machineInstancesChanged.connect(self._triggerTimer)
|
||||
self._onActiveProfileChanged()
|
||||
|
||||
Preferences.getInstance().addPreference("cura/autosave_delay", 1000 * 10)
|
||||
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(Preferences.getInstance().getValue("cura/autosave_delay"))
|
||||
self._change_timer.setSingleShot(True)
|
||||
self._change_timer.timeout.connect(self._onTimeout)
|
||||
|
||||
self._saving = False
|
||||
|
||||
def _triggerTimer(self, *args):
|
||||
if not self._saving:
|
||||
self._change_timer.start()
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._triggerTimer)
|
||||
|
||||
self._profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.connect(self._triggerTimer)
|
||||
|
||||
def _onTimeout(self):
|
||||
self._saving = True # To prevent the save process from triggering another autosave.
|
||||
Logger.log("d", "Autosaving preferences, instances and profiles")
|
||||
|
||||
machine_manager = Application.getInstance().getMachineManager()
|
||||
|
||||
machine_manager.saveVisibility()
|
||||
machine_manager.saveMachineInstances()
|
||||
machine_manager.saveProfiles()
|
||||
Preferences.getInstance().writeToFile(Resources.getStoragePath(Resources.Preferences, Application.getInstance().getApplicationName() + ".cfg"))
|
||||
|
||||
self._saving = False
|
21
plugins/AutoSave/__init__.py
Normal file
21
plugins/AutoSave/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import AutoSave
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Auto Save"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Automatically saves Preferences, Machines and Profiles after changes."),
|
||||
"api": 2
|
||||
},
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "extension": AutoSave.AutoSave() }
|
104
plugins/CuraEngineBackend/Cura.proto
Normal file
104
plugins/CuraEngineBackend/Cura.proto
Normal file
@ -0,0 +1,104 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package cura.proto;
|
||||
|
||||
|
||||
message ObjectList
|
||||
{
|
||||
repeated Object objects = 1;
|
||||
repeated Setting settings = 2;
|
||||
}
|
||||
|
||||
// typeid 1
|
||||
message Slice
|
||||
{
|
||||
repeated ObjectList object_lists = 1;
|
||||
}
|
||||
|
||||
message Object
|
||||
{
|
||||
int64 id = 1;
|
||||
bytes vertices = 2; //An array of 3 floats.
|
||||
bytes normals = 3; //An array of 3 floats.
|
||||
bytes indices = 4; //An array of ints.
|
||||
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
|
||||
}
|
||||
|
||||
// typeid 3
|
||||
message Progress
|
||||
{
|
||||
float amount = 1;
|
||||
}
|
||||
|
||||
// typeid 2
|
||||
message SlicedObjectList
|
||||
{
|
||||
repeated SlicedObject objects = 1;
|
||||
}
|
||||
|
||||
message SlicedObject
|
||||
{
|
||||
int64 id = 1;
|
||||
|
||||
repeated Layer layers = 2;
|
||||
}
|
||||
|
||||
message Layer {
|
||||
int32 id = 1;
|
||||
|
||||
float height = 2;
|
||||
float thickness = 3;
|
||||
|
||||
repeated Polygon polygons = 4;
|
||||
}
|
||||
|
||||
message Polygon {
|
||||
enum Type {
|
||||
NoneType = 0;
|
||||
Inset0Type = 1;
|
||||
InsetXType = 2;
|
||||
SkinType = 3;
|
||||
SupportType = 4;
|
||||
SkirtType = 5;
|
||||
InfillType = 6;
|
||||
SupportInfillType = 7;
|
||||
MoveCombingType = 8;
|
||||
MoveRetractionType = 9;
|
||||
}
|
||||
Type type = 1;
|
||||
bytes points = 2;
|
||||
float line_width = 3;
|
||||
}
|
||||
|
||||
// typeid 4
|
||||
message GCodeLayer {
|
||||
int64 id = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// typeid 5
|
||||
message ObjectPrintTime {
|
||||
int64 id = 1;
|
||||
float time = 2;
|
||||
float material_amount = 3;
|
||||
}
|
||||
|
||||
// typeid 6
|
||||
message SettingList {
|
||||
repeated Setting settings = 1;
|
||||
}
|
||||
|
||||
message Setting {
|
||||
string name = 1;
|
||||
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
// typeid 7
|
||||
message GCodePrefix {
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// typeid 8
|
||||
message SlicingFinished {
|
||||
}
|
@ -9,12 +9,13 @@ from UM.Preferences import Preferences
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Signal import Signal
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.Bindings.BackendProxy import BackendState #To determine the state of the slicing job.
|
||||
from UM.Resources import Resources
|
||||
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from . import Cura_pb2
|
||||
from . import ProcessSlicedObjectListJob
|
||||
from . import ProcessGCodeJob
|
||||
from . import StartSliceJob
|
||||
@ -25,6 +26,8 @@ import numpy
|
||||
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
import Arcus
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
@ -61,19 +64,23 @@ class CuraEngineBackend(Backend):
|
||||
self._change_timer.setSingleShot(True)
|
||||
self._change_timer.timeout.connect(self.slice)
|
||||
|
||||
self._message_handlers[Cura_pb2.SlicedObjectList] = self._onSlicedObjectListMessage
|
||||
self._message_handlers[Cura_pb2.Progress] = self._onProgressMessage
|
||||
self._message_handlers[Cura_pb2.GCodeLayer] = self._onGCodeLayerMessage
|
||||
self._message_handlers[Cura_pb2.GCodePrefix] = self._onGCodePrefixMessage
|
||||
self._message_handlers[Cura_pb2.ObjectPrintTime] = self._onObjectPrintTimeMessage
|
||||
self._message_handlers["cura.proto.SlicedObjectList"] = self._onSlicedObjectListMessage
|
||||
self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
|
||||
self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
|
||||
self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
|
||||
self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage
|
||||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||
|
||||
self._slicing = False
|
||||
self._restart = False
|
||||
self._enabled = True
|
||||
self._always_restart = True
|
||||
self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers.
|
||||
|
||||
self._message = None
|
||||
|
||||
self.backendQuit.connect(self._onBackendQuit)
|
||||
|
||||
self.backendConnected.connect(self._onBackendConnected)
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
@ -107,15 +114,7 @@ class CuraEngineBackend(Backend):
|
||||
return
|
||||
|
||||
if self._slicing:
|
||||
self._slicing = False
|
||||
self._restart = True
|
||||
if self._process is not None:
|
||||
Logger.log("d", "Killing engine process")
|
||||
try:
|
||||
self._process.terminate()
|
||||
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
pass
|
||||
|
||||
self._terminate()
|
||||
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
@ -124,6 +123,10 @@ class CuraEngineBackend(Backend):
|
||||
self.slicingCancelled.emit()
|
||||
return
|
||||
|
||||
if self._process_layers_job:
|
||||
self._process_layers_job.abort()
|
||||
self._process_layers_job = None
|
||||
|
||||
if self._profile.hasErrorValue():
|
||||
Logger.log("w", "Profile has error values. Aborting slicing")
|
||||
if self._message:
|
||||
@ -134,6 +137,7 @@ class CuraEngineBackend(Backend):
|
||||
return #No slicing if we have error values since those are by definition illegal values.
|
||||
|
||||
self.processingProgress.emit(0.0)
|
||||
self.backendStateChange.emit(BackendState.NOT_STARTED)
|
||||
if self._message:
|
||||
self._message.setProgress(-1)
|
||||
#else:
|
||||
@ -142,11 +146,24 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
self._scene.gcode_list = []
|
||||
self._slicing = True
|
||||
self.slicingStarted.emit()
|
||||
|
||||
job = StartSliceJob.StartSliceJob(self._profile, self._socket)
|
||||
job.start()
|
||||
job.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
def _terminate(self):
|
||||
self._slicing = False
|
||||
self._restart = True
|
||||
self.slicingCancelled.emit()
|
||||
if self._process is not None:
|
||||
Logger.log("d", "Killing engine process")
|
||||
try:
|
||||
self._process.terminate()
|
||||
self._process = None
|
||||
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
pass
|
||||
|
||||
def _onStartSliceCompleted(self, job):
|
||||
if job.getError() or job.getResult() != True:
|
||||
if self._message:
|
||||
@ -169,11 +186,20 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
self._onChanged()
|
||||
|
||||
def _onSocketError(self, error):
|
||||
super()._onSocketError(error)
|
||||
|
||||
self._slicing = False
|
||||
self.processingProgress.emit(0)
|
||||
|
||||
if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]:
|
||||
Logger.log("e", "A socket error caused the connection to be reset")
|
||||
|
||||
def _onActiveProfileChanged(self):
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.disconnect(self._onSettingChanged)
|
||||
|
||||
self._profile = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
self._profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
if self._profile:
|
||||
self._profile.settingValueChanged.connect(self._onSettingChanged)
|
||||
self._onChanged()
|
||||
@ -183,8 +209,8 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
def _onSlicedObjectListMessage(self, message):
|
||||
if self._layer_view_active:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||
job.start()
|
||||
self._process_layers_job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||
self._process_layers_job.start()
|
||||
else :
|
||||
self._stored_layer_data = message
|
||||
|
||||
@ -193,15 +219,10 @@ class CuraEngineBackend(Backend):
|
||||
self._message.setProgress(round(message.amount * 100))
|
||||
|
||||
self.processingProgress.emit(message.amount)
|
||||
self.backendStateChange.emit(BackendState.PROCESSING)
|
||||
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onObjectPrintTimeMessage(self, message):
|
||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||
def _onSlicingFinishedMessage(self, message):
|
||||
self.backendStateChange.emit(BackendState.DONE)
|
||||
self.processingProgress.emit(1.0)
|
||||
|
||||
self._slicing = False
|
||||
@ -211,23 +232,17 @@ class CuraEngineBackend(Backend):
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
|
||||
if self._always_restart:
|
||||
try:
|
||||
self._process.terminate()
|
||||
self._createSocket()
|
||||
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
pass
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onObjectPrintTimeMessage(self, message):
|
||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||
|
||||
def _createSocket(self):
|
||||
super()._createSocket()
|
||||
|
||||
self._socket.registerMessageType(1, Cura_pb2.Slice)
|
||||
self._socket.registerMessageType(2, Cura_pb2.SlicedObjectList)
|
||||
self._socket.registerMessageType(3, Cura_pb2.Progress)
|
||||
self._socket.registerMessageType(4, Cura_pb2.GCodeLayer)
|
||||
self._socket.registerMessageType(5, Cura_pb2.ObjectPrintTime)
|
||||
self._socket.registerMessageType(6, Cura_pb2.SettingList)
|
||||
self._socket.registerMessageType(7, Cura_pb2.GCodePrefix)
|
||||
super()._createSocket(os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto")))
|
||||
|
||||
## Manually triggers a reslice
|
||||
def forceSlice(self):
|
||||
@ -245,32 +260,31 @@ class CuraEngineBackend(Backend):
|
||||
self._restart = False
|
||||
|
||||
def _onToolOperationStarted(self, tool):
|
||||
self._terminate() # Do not continue slicing once a tool has started
|
||||
self._enabled = False # Do not reslice when a tool is doing it's 'thing'
|
||||
|
||||
def _onToolOperationStopped(self, tool):
|
||||
self._enabled = True # Tool stop, start listening for changes again.
|
||||
self._onChanged()
|
||||
|
||||
def _onActiveViewChanged(self):
|
||||
if Application.getInstance().getController().getActiveView():
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView":
|
||||
self._layer_view_active = True
|
||||
if self._stored_layer_data:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
|
||||
job.start()
|
||||
# There is data and we're not slicing at the moment
|
||||
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
|
||||
if self._stored_layer_data and not self._slicing:
|
||||
self._process_layers_job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data)
|
||||
self._process_layers_job.start()
|
||||
self._stored_layer_data = None
|
||||
else:
|
||||
self._layer_view_active = False
|
||||
|
||||
|
||||
def _onInstanceChanged(self):
|
||||
self._slicing = False
|
||||
self._restart = True
|
||||
if self._process is not None:
|
||||
Logger.log("d", "Killing engine process")
|
||||
try:
|
||||
self._process.terminate()
|
||||
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
pass
|
||||
self._terminate()
|
||||
self.slicingCancelled.emit()
|
||||
|
||||
def _onBackendQuit(self):
|
||||
if not self._restart and self._process:
|
||||
self._process = None
|
||||
self._createSocket()
|
||||
|
@ -1,705 +0,0 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: Cura.proto
|
||||
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf import descriptor_pb2
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='Cura.proto',
|
||||
package='cura.proto',
|
||||
syntax='proto3',
|
||||
serialized_pb=b'\n\nCura.proto\x12\ncura.proto\"X\n\nObjectList\x12#\n\x07objects\x18\x01 \x03(\x0b\x32\x12.cura.proto.Object\x12%\n\x08settings\x18\x02 \x03(\x0b\x32\x13.cura.proto.Setting\"5\n\x05Slice\x12,\n\x0cobject_lists\x18\x01 \x03(\x0b\x32\x16.cura.proto.ObjectList\"o\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%\n\x08settings\x18\x05 \x03(\x0b\x32\x13.cura.proto.Setting\"\x1a\n\x08Progress\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x02\"=\n\x10SlicedObjectList\x12)\n\x07objects\x18\x01 \x03(\x0b\x32\x18.cura.proto.SlicedObject\"=\n\x0cSlicedObject\x12\n\n\x02id\x18\x01 \x01(\x03\x12!\n\x06layers\x18\x02 \x03(\x0b\x32\x11.cura.proto.Layer\"]\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%\n\x08polygons\x18\x04 \x03(\x0b\x32\x13.cura.proto.Polygon\"\x8e\x02\n\x07Polygon\x12&\n\x04type\x18\x01 \x01(\x0e\x32\x18.cura.proto.Polygon.Type\x12\x0e\n\x06points\x18\x02 \x01(\x0c\x12\x12\n\nline_width\x18\x03 \x01(\x02\"\xb6\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\x12\x13\n\x0fMoveCombingType\x10\x08\x12\x16\n\x12MoveRetractionType\x10\t\"&\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\"4\n\x0bSettingList\x12%\n\x08settings\x18\x01 \x03(\x0b\x32\x13.cura.proto.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)
|
||||
|
||||
|
||||
|
||||
_POLYGON_TYPE = _descriptor.EnumDescriptor(
|
||||
name='Type',
|
||||
full_name='cura.proto.Polygon.Type',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='NoneType', index=0, number=0,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='Inset0Type', index=1, number=1,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='InsetXType', index=2, number=2,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SkinType', index=3, number=3,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SupportType', index=4, number=4,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
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),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MoveCombingType', index=8, number=8,
|
||||
options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MoveRetractionType', index=9, number=9,
|
||||
options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=622,
|
||||
serialized_end=804,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_POLYGON_TYPE)
|
||||
|
||||
|
||||
_OBJECTLIST = _descriptor.Descriptor(
|
||||
name='ObjectList',
|
||||
full_name='cura.proto.ObjectList',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='objects', full_name='cura.proto.ObjectList.objects', index=0,
|
||||
number=1, 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,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='settings', full_name='cura.proto.ObjectList.settings', index=1,
|
||||
number=2, 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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=26,
|
||||
serialized_end=114,
|
||||
)
|
||||
|
||||
|
||||
_SLICE = _descriptor.Descriptor(
|
||||
name='Slice',
|
||||
full_name='cura.proto.Slice',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='object_lists', full_name='cura.proto.Slice.object_lists', index=0,
|
||||
number=1, 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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=116,
|
||||
serialized_end=169,
|
||||
)
|
||||
|
||||
|
||||
_OBJECT = _descriptor.Descriptor(
|
||||
name='Object',
|
||||
full_name='cura.proto.Object',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='id', full_name='cura.proto.Object.id', index=0,
|
||||
number=1, type=3, cpp_type=2, 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='vertices', full_name='cura.proto.Object.vertices', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='normals', full_name='cura.proto.Object.normals', index=2,
|
||||
number=3, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='indices', full_name='cura.proto.Object.indices', index=3,
|
||||
number=4, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='settings', full_name='cura.proto.Object.settings', index=4,
|
||||
number=5, 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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=171,
|
||||
serialized_end=282,
|
||||
)
|
||||
|
||||
|
||||
_PROGRESS = _descriptor.Descriptor(
|
||||
name='Progress',
|
||||
full_name='cura.proto.Progress',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='amount', full_name='cura.proto.Progress.amount', index=0,
|
||||
number=1, 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=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=284,
|
||||
serialized_end=310,
|
||||
)
|
||||
|
||||
|
||||
_SLICEDOBJECTLIST = _descriptor.Descriptor(
|
||||
name='SlicedObjectList',
|
||||
full_name='cura.proto.SlicedObjectList',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='objects', full_name='cura.proto.SlicedObjectList.objects', index=0,
|
||||
number=1, 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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=312,
|
||||
serialized_end=373,
|
||||
)
|
||||
|
||||
|
||||
_SLICEDOBJECT = _descriptor.Descriptor(
|
||||
name='SlicedObject',
|
||||
full_name='cura.proto.SlicedObject',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='id', full_name='cura.proto.SlicedObject.id', index=0,
|
||||
number=1, type=3, cpp_type=2, 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='layers', full_name='cura.proto.SlicedObject.layers', index=1,
|
||||
number=2, 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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=375,
|
||||
serialized_end=436,
|
||||
)
|
||||
|
||||
|
||||
_LAYER = _descriptor.Descriptor(
|
||||
name='Layer',
|
||||
full_name='cura.proto.Layer',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='id', full_name='cura.proto.Layer.id', index=0,
|
||||
number=1, type=5, cpp_type=1, 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='height', full_name='cura.proto.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.proto.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.proto.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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=438,
|
||||
serialized_end=531,
|
||||
)
|
||||
|
||||
|
||||
_POLYGON = _descriptor.Descriptor(
|
||||
name='Polygon',
|
||||
full_name='cura.proto.Polygon',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='type', full_name='cura.proto.Polygon.type', index=0,
|
||||
number=1, type=14, cpp_type=8, 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='points', full_name='cura.proto.Polygon.points', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
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.proto.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=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
_POLYGON_TYPE,
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=534,
|
||||
serialized_end=804,
|
||||
)
|
||||
|
||||
|
||||
_GCODELAYER = _descriptor.Descriptor(
|
||||
name='GCodeLayer',
|
||||
full_name='cura.proto.GCodeLayer',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='id', full_name='cura.proto.GCodeLayer.id', index=0,
|
||||
number=1, type=3, cpp_type=2, 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='data', full_name='cura.proto.GCodeLayer.data', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=806,
|
||||
serialized_end=844,
|
||||
)
|
||||
|
||||
|
||||
_OBJECTPRINTTIME = _descriptor.Descriptor(
|
||||
name='ObjectPrintTime',
|
||||
full_name='cura.proto.ObjectPrintTime',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='id', full_name='cura.proto.ObjectPrintTime.id', index=0,
|
||||
number=1, type=3, cpp_type=2, 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='time', full_name='cura.proto.ObjectPrintTime.time', 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='material_amount', full_name='cura.proto.ObjectPrintTime.material_amount', 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=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=846,
|
||||
serialized_end=914,
|
||||
)
|
||||
|
||||
|
||||
_SETTINGLIST = _descriptor.Descriptor(
|
||||
name='SettingList',
|
||||
full_name='cura.proto.SettingList',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='settings', full_name='cura.proto.SettingList.settings', index=0,
|
||||
number=1, 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,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=916,
|
||||
serialized_end=968,
|
||||
)
|
||||
|
||||
|
||||
_SETTING = _descriptor.Descriptor(
|
||||
name='Setting',
|
||||
full_name='cura.proto.Setting',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='name', full_name='cura.proto.Setting.name', index=0,
|
||||
number=1, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='value', full_name='cura.proto.Setting.value', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=970,
|
||||
serialized_end=1008,
|
||||
)
|
||||
|
||||
|
||||
_GCODEPREFIX = _descriptor.Descriptor(
|
||||
name='GCodePrefix',
|
||||
full_name='cura.proto.GCodePrefix',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='data', full_name='cura.proto.GCodePrefix.data', index=0,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
options=None),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1010,
|
||||
serialized_end=1037,
|
||||
)
|
||||
|
||||
_OBJECTLIST.fields_by_name['objects'].message_type = _OBJECT
|
||||
_OBJECTLIST.fields_by_name['settings'].message_type = _SETTING
|
||||
_SLICE.fields_by_name['object_lists'].message_type = _OBJECTLIST
|
||||
_OBJECT.fields_by_name['settings'].message_type = _SETTING
|
||||
_SLICEDOBJECTLIST.fields_by_name['objects'].message_type = _SLICEDOBJECT
|
||||
_SLICEDOBJECT.fields_by_name['layers'].message_type = _LAYER
|
||||
_LAYER.fields_by_name['polygons'].message_type = _POLYGON
|
||||
_POLYGON.fields_by_name['type'].enum_type = _POLYGON_TYPE
|
||||
_POLYGON_TYPE.containing_type = _POLYGON
|
||||
_SETTINGLIST.fields_by_name['settings'].message_type = _SETTING
|
||||
DESCRIPTOR.message_types_by_name['ObjectList'] = _OBJECTLIST
|
||||
DESCRIPTOR.message_types_by_name['Slice'] = _SLICE
|
||||
DESCRIPTOR.message_types_by_name['Object'] = _OBJECT
|
||||
DESCRIPTOR.message_types_by_name['Progress'] = _PROGRESS
|
||||
DESCRIPTOR.message_types_by_name['SlicedObjectList'] = _SLICEDOBJECTLIST
|
||||
DESCRIPTOR.message_types_by_name['SlicedObject'] = _SLICEDOBJECT
|
||||
DESCRIPTOR.message_types_by_name['Layer'] = _LAYER
|
||||
DESCRIPTOR.message_types_by_name['Polygon'] = _POLYGON
|
||||
DESCRIPTOR.message_types_by_name['GCodeLayer'] = _GCODELAYER
|
||||
DESCRIPTOR.message_types_by_name['ObjectPrintTime'] = _OBJECTPRINTTIME
|
||||
DESCRIPTOR.message_types_by_name['SettingList'] = _SETTINGLIST
|
||||
DESCRIPTOR.message_types_by_name['Setting'] = _SETTING
|
||||
DESCRIPTOR.message_types_by_name['GCodePrefix'] = _GCODEPREFIX
|
||||
|
||||
ObjectList = _reflection.GeneratedProtocolMessageType('ObjectList', (_message.Message,), dict(
|
||||
DESCRIPTOR = _OBJECTLIST,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.ObjectList)
|
||||
))
|
||||
_sym_db.RegisterMessage(ObjectList)
|
||||
|
||||
Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), dict(
|
||||
DESCRIPTOR = _SLICE,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.Slice)
|
||||
))
|
||||
_sym_db.RegisterMessage(Slice)
|
||||
|
||||
Object = _reflection.GeneratedProtocolMessageType('Object', (_message.Message,), dict(
|
||||
DESCRIPTOR = _OBJECT,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.Object)
|
||||
))
|
||||
_sym_db.RegisterMessage(Object)
|
||||
|
||||
Progress = _reflection.GeneratedProtocolMessageType('Progress', (_message.Message,), dict(
|
||||
DESCRIPTOR = _PROGRESS,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.Progress)
|
||||
))
|
||||
_sym_db.RegisterMessage(Progress)
|
||||
|
||||
SlicedObjectList = _reflection.GeneratedProtocolMessageType('SlicedObjectList', (_message.Message,), dict(
|
||||
DESCRIPTOR = _SLICEDOBJECTLIST,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.SlicedObjectList)
|
||||
))
|
||||
_sym_db.RegisterMessage(SlicedObjectList)
|
||||
|
||||
SlicedObject = _reflection.GeneratedProtocolMessageType('SlicedObject', (_message.Message,), dict(
|
||||
DESCRIPTOR = _SLICEDOBJECT,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.SlicedObject)
|
||||
))
|
||||
_sym_db.RegisterMessage(SlicedObject)
|
||||
|
||||
Layer = _reflection.GeneratedProtocolMessageType('Layer', (_message.Message,), dict(
|
||||
DESCRIPTOR = _LAYER,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.Layer)
|
||||
))
|
||||
_sym_db.RegisterMessage(Layer)
|
||||
|
||||
Polygon = _reflection.GeneratedProtocolMessageType('Polygon', (_message.Message,), dict(
|
||||
DESCRIPTOR = _POLYGON,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.Polygon)
|
||||
))
|
||||
_sym_db.RegisterMessage(Polygon)
|
||||
|
||||
GCodeLayer = _reflection.GeneratedProtocolMessageType('GCodeLayer', (_message.Message,), dict(
|
||||
DESCRIPTOR = _GCODELAYER,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.GCodeLayer)
|
||||
))
|
||||
_sym_db.RegisterMessage(GCodeLayer)
|
||||
|
||||
ObjectPrintTime = _reflection.GeneratedProtocolMessageType('ObjectPrintTime', (_message.Message,), dict(
|
||||
DESCRIPTOR = _OBJECTPRINTTIME,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.ObjectPrintTime)
|
||||
))
|
||||
_sym_db.RegisterMessage(ObjectPrintTime)
|
||||
|
||||
SettingList = _reflection.GeneratedProtocolMessageType('SettingList', (_message.Message,), dict(
|
||||
DESCRIPTOR = _SETTINGLIST,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.SettingList)
|
||||
))
|
||||
_sym_db.RegisterMessage(SettingList)
|
||||
|
||||
Setting = _reflection.GeneratedProtocolMessageType('Setting', (_message.Message,), dict(
|
||||
DESCRIPTOR = _SETTING,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.Setting)
|
||||
))
|
||||
_sym_db.RegisterMessage(Setting)
|
||||
|
||||
GCodePrefix = _reflection.GeneratedProtocolMessageType('GCodePrefix', (_message.Message,), dict(
|
||||
DESCRIPTOR = _GCODEPREFIX,
|
||||
__module__ = 'Cura_pb2'
|
||||
# @@protoc_insertion_point(class_scope:cura.proto.GCodePrefix)
|
||||
))
|
||||
_sym_db.RegisterMessage(GCodePrefix)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
@ -10,6 +10,8 @@ from UM.Mesh.MeshData import MeshData
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from UM.Math.Vector import Vector
|
||||
|
||||
from cura import LayerData
|
||||
from cura import LayerDataDecorator
|
||||
|
||||
@ -24,11 +26,26 @@ class ProcessSlicedObjectListJob(Job):
|
||||
self._message = message
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._progress = None
|
||||
self._abort_requested = False
|
||||
|
||||
## Aborts the processing of layers.
|
||||
#
|
||||
# This abort is made on a best-effort basis, meaning that the actual
|
||||
# job thread will check once in a while to see whether an abort is
|
||||
# requested and then stop processing by itself. There is no guarantee
|
||||
# that the abort will stop the job any time soon or even at all.
|
||||
def abort(self):
|
||||
self._abort_requested = True
|
||||
|
||||
def run(self):
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
||||
self._progress.show()
|
||||
Job.yieldThread()
|
||||
if self._abort_requested:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
return
|
||||
|
||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
@ -42,66 +59,84 @@ class ProcessSlicedObjectListJob(Job):
|
||||
else:
|
||||
object_id_map[id(node)] = node
|
||||
Job.yieldThread()
|
||||
if self._abort_requested:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
return
|
||||
|
||||
settings = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
|
||||
center = None
|
||||
if not settings.getSettingValue("machine_center_is_zero"):
|
||||
center = numpy.array([settings.getSettingValue("machine_width") / 2, 0.0, -settings.getSettingValue("machine_depth") / 2])
|
||||
else:
|
||||
center = numpy.array([0.0, 0.0, 0.0])
|
||||
settings = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
mesh = MeshData()
|
||||
layer_data = LayerData.LayerData()
|
||||
|
||||
layer_count = 0
|
||||
for object in self._message.objects:
|
||||
layer_count += len(object.layers)
|
||||
for i in range(self._message.repeatedMessageCount("objects")):
|
||||
layer_count += self._message.getRepeatedMessage("objects", i).repeatedMessageCount("layers")
|
||||
|
||||
current_layer = 0
|
||||
for object in self._message.objects:
|
||||
for i in range(self._message.repeatedMessageCount("objects")):
|
||||
object = self._message.getRepeatedMessage("objects", i)
|
||||
try:
|
||||
node = object_id_map[object.id]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for layer in object.layers:
|
||||
for l in range(object.repeatedMessageCount("layers")):
|
||||
layer = object.getRepeatedMessage("layers", l)
|
||||
|
||||
layer_data.addLayer(layer.id)
|
||||
layer_data.setLayerHeight(layer.id, layer.height)
|
||||
layer_data.setLayerThickness(layer.id, layer.thickness)
|
||||
for polygon in layer.polygons:
|
||||
|
||||
for p in range(layer.repeatedMessageCount("polygons")):
|
||||
polygon = layer.getRepeatedMessage("polygons", p)
|
||||
|
||||
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.height / 1000), axis = 1)
|
||||
|
||||
points[:,2] *= -1
|
||||
# Create a new 3D-array, copy the 2D points over and insert the right height.
|
||||
# This uses manual array creation + copy rather than numpy.insert since this is
|
||||
# faster.
|
||||
new_points = numpy.empty((len(points), 3), numpy.float32)
|
||||
new_points[:,0] = points[:,0]
|
||||
new_points[:,1] = layer.height
|
||||
new_points[:,2] = -points[:,1]
|
||||
|
||||
points -= center
|
||||
|
||||
layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)
|
||||
new_points /= 1000
|
||||
|
||||
layer_data.addPolygon(layer.id, polygon.type, new_points, polygon.line_width)
|
||||
Job.yieldThread()
|
||||
Job.yieldThread()
|
||||
|
||||
current_layer += 1
|
||||
progress = (current_layer / layer_count) * 100
|
||||
# TODO: Rebuild the layer data mesh once the layer has been processed.
|
||||
# This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.
|
||||
|
||||
if self._abort_requested:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
return
|
||||
if self._progress:
|
||||
self._progress.setProgress(progress)
|
||||
|
||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||
layer_data.build()
|
||||
|
||||
if self._abort_requested:
|
||||
if self._progress:
|
||||
self._progress.hide()
|
||||
return
|
||||
|
||||
#Add layerdata decorator to scene node to indicate that the node has layerdata
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_data)
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot())
|
||||
new_node.setParent(self._scene.getRoot()) #Note: After this we can no longer abort!
|
||||
|
||||
if not settings.getSettingValue("machine_center_is_zero"):
|
||||
new_node.setPosition(Vector(-settings.getSettingValue("machine_width") / 2, 0.0, settings.getSettingValue("machine_depth") / 2))
|
||||
|
||||
if self._progress:
|
||||
self._progress.setProgress(100)
|
||||
@ -118,6 +153,7 @@ class ProcessSlicedObjectListJob(Job):
|
||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||
if not self._progress:
|
||||
self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, 0)
|
||||
if self._progress.getProgress() != 100:
|
||||
self._progress.show()
|
||||
else:
|
||||
if self._progress:
|
||||
|
@ -14,8 +14,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
|
||||
from . import Cura_pb2
|
||||
|
||||
## Formatter class that handles token expansion in start/end gcod
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
@ -63,6 +61,8 @@ class StartSliceJob(Job):
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
Job.yieldThread()
|
||||
if len(object_groups) == 0:
|
||||
Logger.log("w", "No objects suitable for one at a time found, or no correct order found")
|
||||
else:
|
||||
temp_list = []
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
@ -81,20 +81,23 @@ class StartSliceJob(Job):
|
||||
|
||||
self._sendSettings(self._profile)
|
||||
|
||||
slice_message = Cura_pb2.Slice()
|
||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||
|
||||
for group in object_groups:
|
||||
group_message = slice_message.object_lists.add()
|
||||
group_message = slice_message.addRepeatedMessage("object_lists")
|
||||
if group[0].getParent().callDecoration("isGroup"):
|
||||
self._handlePerObjectSettings(group[0].getParent(), group_message)
|
||||
for object in group:
|
||||
mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())
|
||||
|
||||
obj = group_message.objects.add()
|
||||
obj = group_message.addRepeatedMessage("objects")
|
||||
obj.id = id(object)
|
||||
|
||||
verts = numpy.array(mesh_data.getVertices())
|
||||
verts[:,[1,2]] = verts[:,[2,1]]
|
||||
verts[:,1] *= -1
|
||||
obj.vertices = verts.tostring()
|
||||
|
||||
obj.vertices = verts
|
||||
|
||||
self._handlePerObjectSettings(object, obj)
|
||||
|
||||
@ -115,13 +118,13 @@ class StartSliceJob(Job):
|
||||
return str(value).encode("utf-8")
|
||||
|
||||
def _sendSettings(self, profile):
|
||||
msg = Cura_pb2.SettingList()
|
||||
msg = self._socket.createMessage("cura.proto.SettingList");
|
||||
settings = profile.getAllSettingValues(include_machine = True)
|
||||
start_gcode = settings["machine_start_gcode"]
|
||||
settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode
|
||||
settings["material_print_temp_prepend"] = "{material_print_temperature}" not in start_gcode
|
||||
for key, value in settings.items():
|
||||
s = msg.settings.add()
|
||||
s = msg.addRepeatedMessage("settings")
|
||||
s.name = key
|
||||
if key == "machine_start_gcode" or key == "machine_end_gcode":
|
||||
s.value = self._expandGcodeTokens(key, value, settings)
|
||||
@ -134,7 +137,7 @@ class StartSliceJob(Job):
|
||||
profile = node.callDecoration("getProfile")
|
||||
if profile:
|
||||
for key, value in profile.getAllSettingValues().items():
|
||||
setting = message.settings.add()
|
||||
setting = message.addRepeatedMessage("settings")
|
||||
setting.name = key
|
||||
setting.value = str(value).encode()
|
||||
|
||||
@ -143,9 +146,8 @@ class StartSliceJob(Job):
|
||||
object_settings = node.callDecoration("getAllSettingValues")
|
||||
if not object_settings:
|
||||
return
|
||||
|
||||
for key, value in object_settings.items():
|
||||
setting = message.settings.add()
|
||||
setting = message.addRepeatedMessage("settings")
|
||||
setting.name = key
|
||||
setting.value = str(value).encode()
|
||||
|
||||
|
39
plugins/CuraProfileReader/CuraProfileReader.py
Normal file
39
plugins/CuraProfileReader/CuraProfileReader.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
|
||||
## A plugin that reads profile data from Cura profile files.
|
||||
#
|
||||
# It reads a profile from a .curaprofile file, and returns it as a profile
|
||||
# instance.
|
||||
class CuraProfileReader(ProfileReader):
|
||||
## Initialises the cura profile reader.
|
||||
#
|
||||
# This does nothing since the only other function is basically stateless.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
## Reads a cura profile from a file and returns it.
|
||||
#
|
||||
# \param file_name The file to read the cura profile from.
|
||||
# \return The cura profile that was in the file, if any. If the file could
|
||||
# not be read or didn't contain a valid profile, \code None \endcode is
|
||||
# returned.
|
||||
def read(self, file_name):
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) #Create an empty profile.
|
||||
serialised = ""
|
||||
try:
|
||||
with open(file_name) as f: #Open file for reading.
|
||||
serialised = f.read()
|
||||
except IOError as e:
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
try:
|
||||
profile.unserialise(serialised)
|
||||
except Exception as e: #Parsing error. This is not a (valid) Cura profile then.
|
||||
return None
|
||||
return profile
|
27
plugins/CuraProfileReader/__init__.py
Normal file
27
plugins/CuraProfileReader/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import CuraProfileReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Cura Profile Reader"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for importing Cura profiles."),
|
||||
"api": 2
|
||||
},
|
||||
"profile_reader": [
|
||||
{
|
||||
"extension": "curaprofile",
|
||||
"description": catalog.i18nc("@item:inlistbox", "Cura Profile")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "profile_reader": CuraProfileReader.CuraProfileReader() }
|
26
plugins/CuraProfileWriter/CuraProfileWriter.py
Normal file
26
plugins/CuraProfileWriter/CuraProfileWriter.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Copyright (c) 2013 David Braam
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.SaveFile import SaveFile
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileWriter import ProfileWriter
|
||||
|
||||
## Writes profiles to Cura's own profile format with config files.
|
||||
class CuraProfileWriter(ProfileWriter):
|
||||
## Writes a profile to the specified file path.
|
||||
#
|
||||
# \param path \type{string} The file to output to.
|
||||
# \param profile \type{Profile} The profile to write to that file.
|
||||
# \return \code True \endcode if the writing was successful, or \code
|
||||
# False \endcode if it wasn't.
|
||||
def write(self, path, profile):
|
||||
serialised = profile.serialise()
|
||||
try:
|
||||
with SaveFile(path, "wt", -1, "utf-8") as f: #Open the specified file.
|
||||
f.write(serialised)
|
||||
except Exception as e:
|
||||
Logger.log("e", "Failed to write profile to %s: %s", path, str(e))
|
||||
return False
|
||||
return True
|
27
plugins/CuraProfileWriter/__init__.py
Normal file
27
plugins/CuraProfileWriter/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import CuraProfileWriter
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Cura Profile Writer"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for exporting Cura profiles."),
|
||||
"api": 2
|
||||
},
|
||||
"profile_writer": [
|
||||
{
|
||||
"extension": "curaprofile",
|
||||
"description": catalog.i18nc("@item:inlistbox", "Cura Profile")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "profile_writer": CuraProfileWriter.CuraProfileWriter() }
|
75
plugins/GCodeProfileReader/GCodeProfileReader.py
Normal file
75
plugins/GCodeProfileReader/GCodeProfileReader.py
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
from UM.Logger import Logger
|
||||
import re #Regular expressions for parsing escape characters in the settings.
|
||||
|
||||
## A class that reads profile data from g-code files.
|
||||
#
|
||||
# It reads the profile data from g-code files and stores it in a new profile.
|
||||
# This class currently does not process the rest of the g-code in any way.
|
||||
class GCodeProfileReader(ProfileReader):
|
||||
## The file format version of the serialised g-code.
|
||||
#
|
||||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 1
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
#
|
||||
# Note that the keys of this dictionary are regex strings. The values are
|
||||
# not.
|
||||
escape_characters = {
|
||||
re.escape("\\\\"): "\\", #The escape character.
|
||||
re.escape("\\n"): "\n", #Newlines. They break off the comment.
|
||||
re.escape("\\r"): "\r" #Carriage return. Windows users may need this for visualisation in their editors.
|
||||
}
|
||||
|
||||
## Initialises the g-code reader as a profile reader.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
## Reads a g-code file, loading the profile from it.
|
||||
#
|
||||
# \param file_name The name of the file to read the profile from.
|
||||
# \return The profile that was in the specified file, if any. If the
|
||||
# specified file was no g-code or contained no parsable profile, \code
|
||||
# None \endcode is returned.
|
||||
def read(self, file_name):
|
||||
if file_name.split(".")[-1] != "gcode":
|
||||
return None
|
||||
|
||||
prefix = ";SETTING_" + str(GCodeProfileReader.version) + " "
|
||||
prefix_length = len(prefix)
|
||||
|
||||
#Loading all settings from the file. They are all at the end, but Python has no reverse seek any more since Python3. TODO: Consider moving settings to the start?
|
||||
serialised = "" #Will be filled with the serialised profile.
|
||||
try:
|
||||
with open(file_name) as f:
|
||||
for line in f:
|
||||
if line.startswith(prefix):
|
||||
serialised += line[prefix_length : -1] #Remove the prefix and the newline from the line, and add it to the rest.
|
||||
except IOError as e:
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
#Unescape the serialised profile.
|
||||
pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
|
||||
serialised = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialised) #Perform the replacement with a regular expression.
|
||||
|
||||
#Apply the changes to the current profile.
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False)
|
||||
try:
|
||||
profile.unserialise(serialised)
|
||||
profile.setType(None) #Force type to none so it's correctly added.
|
||||
profile.setReadOnly(False)
|
||||
profile.setDirty(True)
|
||||
except Exception as e: #Not a valid g-code file.
|
||||
Logger.log("e", "Unable to serialise the profile: %s", str(e))
|
||||
return None
|
||||
return profile
|
27
plugins/GCodeProfileReader/__init__.py
Normal file
27
plugins/GCodeProfileReader/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import GCodeProfileReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "GCode Profile Reader"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for importing profiles from g-code files."),
|
||||
"api": 2
|
||||
},
|
||||
"profile_reader": [
|
||||
{
|
||||
"extension": "gcode",
|
||||
"description": catalog.i18nc("@item:inlistbox", "G-code File")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "profile_reader": GCodeProfileReader.GCodeProfileReader() }
|
@ -1,110 +0,0 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Application import Application
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
|
||||
from cura.LayerData import LayerData
|
||||
from cura.LayerDataDecorator import LayerDataDecorator
|
||||
|
||||
import os
|
||||
import numpy
|
||||
|
||||
class GCodeReader(MeshReader):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._supported_extension = ".gcode"
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
|
||||
def read(self, file_name):
|
||||
extension = os.path.splitext(file_name)[1]
|
||||
if extension.lower() == self._supported_extension:
|
||||
layer_data = LayerData()
|
||||
with open (file_name,"rt") as f:
|
||||
layer = ""
|
||||
current_path_type = ""
|
||||
|
||||
current_layer_nr = 0
|
||||
poly_list = []
|
||||
old_position = [0,0,0]
|
||||
current_z = 0
|
||||
for line in f:
|
||||
if line.startswith(';TYPE:'):
|
||||
current_path_type = line[6:].strip()
|
||||
#layer_data.addPolygon(current_layer_nr,3 ,None ,5 )
|
||||
elif line.startswith(';LAYER:'):
|
||||
current_layer_nr = int(line[7:].strip())
|
||||
layer_data.addLayer(int(line[7:].strip()))
|
||||
elif line.startswith(';'):
|
||||
pass # Ignore comments
|
||||
else:
|
||||
command_type = self.getCodeInt(line, 'G')
|
||||
if command_type == 0 or command_type == 1: #Move command
|
||||
x = self.getCodeFloat(line, 'X')
|
||||
y = self.getCodeFloat(line, 'Y')
|
||||
z = self.getCodeFloat(line, 'Z')
|
||||
if z:
|
||||
current_z = z
|
||||
if x and y:
|
||||
polygon_data = numpy.zeros((4,3)) #Square :)
|
||||
polygon_data[0,:] = old_position
|
||||
polygon_data[1,:] = old_position
|
||||
polygon_data[2,:] = [x,current_z,y]
|
||||
polygon_data[3,:] = [x,current_z,y]
|
||||
old_position = [x,current_z,y]
|
||||
if current_path_type == "SKIRT":
|
||||
layer_data.addPolygon(current_layer_nr,5 ,polygon_data ,5 )
|
||||
elif current_path_type == "WALL-INNER":
|
||||
layer_data.addPolygon(current_layer_nr,3 ,polygon_data ,5 )
|
||||
elif current_path_type == "WALL-OUTER":
|
||||
layer_data.addPolygon(current_layer_nr,1 ,polygon_data ,5 )
|
||||
else:
|
||||
layer_data.addPolygon(current_layer_nr,2 ,polygon_data ,5 )
|
||||
#e = self.getCodeFloat(line, 'E')
|
||||
#print(x , " ", y , " ", z, " " , e)
|
||||
pass
|
||||
layer_data.build()
|
||||
decorator = LayerDataDecorator()
|
||||
decorator.setLayerData(layer_data)
|
||||
new_node = SceneNode()
|
||||
new_node.setMeshData(MeshData())
|
||||
new_node.addDecorator(decorator)
|
||||
new_node.setParent(self._scene.getRoot())
|
||||
|
||||
|
||||
## Gets the value after the 'code' as int
|
||||
# example: line = "G50" and code is "G" this function will return 50
|
||||
# \param line string containing g-code.
|
||||
# \param code string Letter to look for.
|
||||
# \sa getCodeFloat
|
||||
# \returns int
|
||||
def getCodeInt(self, line, code):
|
||||
n = line.find(code) + 1
|
||||
if n < 1:
|
||||
return None
|
||||
m = line.find(' ', n)
|
||||
try:
|
||||
if m < 0:
|
||||
return int(line[n:])
|
||||
return int(line[n:m])
|
||||
except:
|
||||
return None
|
||||
## Gets the value after the 'code' as float
|
||||
# example: line = "G50" and code is "G" this function will return 50
|
||||
# \param line string containing g-code.
|
||||
# \param code string Letter to look for.
|
||||
# \sa getCodeInt
|
||||
# \returns float
|
||||
def getCodeFloat(self, line, code):
|
||||
n = line.find(code) + 1
|
||||
if n < 1:
|
||||
return None
|
||||
m = line.find(' ', n)
|
||||
try:
|
||||
if m < 0:
|
||||
return float(line[n:])
|
||||
return float(line[n:m])
|
||||
except:
|
||||
return None
|
@ -1,25 +0,0 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import GCodeReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "GCode Reader"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for reading GCode files."),
|
||||
"api": 2
|
||||
},
|
||||
"mesh_reader": {
|
||||
"extension": "gcode",
|
||||
"description": catalog.i18nc("@item:inlistbox", "Gcode File")
|
||||
}
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "mesh_reader": GCodeReader.GCodeReader() }
|
@ -5,9 +5,29 @@ from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Logger import Logger
|
||||
from UM.Application import Application
|
||||
import io
|
||||
import re #For escaping characters in the settings.
|
||||
import copy
|
||||
|
||||
|
||||
class GCodeWriter(MeshWriter):
|
||||
## The file format version of the serialised g-code.
|
||||
#
|
||||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 1
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
#
|
||||
# Note that the keys of this dictionary are regex strings. The values are
|
||||
# not.
|
||||
escape_characters = {
|
||||
re.escape("\\"): "\\\\", #The escape character.
|
||||
re.escape("\n"): "\\n", #Newlines. They break off the comment.
|
||||
re.escape("\r"): "\\r" #Carriage return. Windows users may need this for visualisation in their editors.
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@ -21,6 +41,34 @@ class GCodeWriter(MeshWriter):
|
||||
if gcode_list:
|
||||
for gcode in gcode_list:
|
||||
stream.write(gcode)
|
||||
profile = self._serialiseProfile(Application.getInstance().getMachineManager().getWorkingProfile()) #Serialise the profile and put them at the end of the file.
|
||||
stream.write(profile)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
## Serialises the profile to prepare it for saving in the g-code.
|
||||
#
|
||||
# The profile are serialised, and special characters (including newline)
|
||||
# are escaped.
|
||||
#
|
||||
# \param profile The profile to serialise.
|
||||
# \return A serialised string of the profile.
|
||||
def _serialiseProfile(self, profile):
|
||||
prefix = ";SETTING_" + str(GCodeWriter.version) + " " #The prefix to put before each line.
|
||||
prefix_length = len(prefix)
|
||||
|
||||
#Serialise a deepcopy to remove the defaults from the profile
|
||||
serialised = copy.deepcopy(profile).serialise()
|
||||
|
||||
#Escape characters that have a special meaning in g-code comments.
|
||||
pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
|
||||
serialised = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised) #Perform the replacement with a regular expression.
|
||||
|
||||
#Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
|
||||
result = ""
|
||||
for pos in range(0, len(serialised), 80 - prefix_length): #Lines have 80 characters, so the payload of each line is 80 - prefix.
|
||||
result += prefix + serialised[pos : pos + 80 - prefix_length] + "\n"
|
||||
serialised = result
|
||||
|
||||
return serialised
|
196
plugins/ImageReader/ConfigUI.qml
Normal file
196
plugins/ImageReader/ConfigUI.qml
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
width: 350 * Screen.devicePixelRatio;
|
||||
minimumWidth: 350 * Screen.devicePixelRatio;
|
||||
maximumWidth: 350 * Screen.devicePixelRatio;
|
||||
|
||||
height: 250 * Screen.devicePixelRatio;
|
||||
minimumHeight: 250 * Screen.devicePixelRatio;
|
||||
maximumHeight: 250 * Screen.devicePixelRatio;
|
||||
|
||||
title: catalog.i18nc("@title:window", "Convert Image...")
|
||||
|
||||
GridLayout
|
||||
{
|
||||
UM.I18nCatalog{id: catalog; name:"cura"}
|
||||
anchors.fill: parent;
|
||||
Layout.fillWidth: true
|
||||
columnSpacing: 16
|
||||
rowSpacing: 4
|
||||
columns: 1
|
||||
|
||||
UM.TooltipArea {
|
||||
Layout.fillWidth:true
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","The maximum distance of each pixel from \"Base.\"")
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
text: catalog.i18nc("@action:label","Height (mm)")
|
||||
width: 150
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: peak_height
|
||||
objectName: "Peak_Height"
|
||||
validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: -500; top: 500;}
|
||||
width: 180
|
||||
onTextChanged: { manager.onPeakHeightChanged(text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.TooltipArea {
|
||||
Layout.fillWidth:true
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","The base height from the build plate in millimeters.")
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
text: catalog.i18nc("@action:label","Base (mm)")
|
||||
width: 150
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: base_height
|
||||
objectName: "Base_Height"
|
||||
validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 0; top: 500;}
|
||||
width: 180
|
||||
onTextChanged: { manager.onBaseHeightChanged(text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.TooltipArea {
|
||||
Layout.fillWidth:true
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","The width in millimeters on the build plate.")
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
text: catalog.i18nc("@action:label","Width (mm)")
|
||||
width: 150
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: width
|
||||
objectName: "Width"
|
||||
focus: true
|
||||
validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 1; top: 500;}
|
||||
width: 180
|
||||
onTextChanged: { manager.onWidthChanged(text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.TooltipArea {
|
||||
Layout.fillWidth:true
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","The depth in millimeters on the build plate")
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
text: catalog.i18nc("@action:label","Depth (mm)")
|
||||
width: 150
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
TextField {
|
||||
id: depth
|
||||
objectName: "Depth"
|
||||
focus: true
|
||||
validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 1; top: 500;}
|
||||
width: 180
|
||||
onTextChanged: { manager.onDepthChanged(text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.TooltipArea {
|
||||
Layout.fillWidth:true
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","By default, white pixels represent high points on the mesh and black pixels represent low points on the mesh. Change this option to reverse the behavior such that black pixels represent high points on the mesh and white pixels represent low points on the mesh.")
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
//Empty label so 2 column layout works.
|
||||
Text {
|
||||
text: ""
|
||||
width: 150
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
ComboBox {
|
||||
id: image_color_invert
|
||||
objectName: "Image_Color_Invert"
|
||||
model: [ catalog.i18nc("@item:inlistbox","Lighter is higher"), catalog.i18nc("@item:inlistbox","Darker is higher") ]
|
||||
width: 180
|
||||
onCurrentIndexChanged: { manager.onImageColorInvertChanged(currentIndex) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.TooltipArea {
|
||||
Layout.fillWidth:true
|
||||
height: childrenRect.height
|
||||
text: catalog.i18nc("@info:tooltip","The amount of smoothing to apply to the image.")
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
text: catalog.i18nc("@action:label","Smoothing")
|
||||
width: 150
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 180
|
||||
height: 20
|
||||
Layout.fillWidth:true
|
||||
color: "transparent"
|
||||
|
||||
Slider {
|
||||
id: smoothing
|
||||
objectName: "Smoothing"
|
||||
maximumValue: 100.0
|
||||
stepSize: 1.0
|
||||
width: 180
|
||||
onValueChanged: { manager.onSmoothingChanged(value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
Button
|
||||
{
|
||||
id:ok_button
|
||||
text: catalog.i18nc("@action:button","OK");
|
||||
onClicked: { manager.onOkButtonClicked() }
|
||||
enabled: true
|
||||
},
|
||||
Button
|
||||
{
|
||||
id:cancel_button
|
||||
text: catalog.i18nc("@action:button","Cancel");
|
||||
onClicked: { manager.onCancelButtonClicked() }
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
}
|
216
plugins/ImageReader/ImageReader.py
Normal file
216
plugins/ImageReader/ImageReader.py
Normal file
@ -0,0 +1,216 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os
|
||||
import numpy
|
||||
|
||||
from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from .ImageReaderUI import ImageReaderUI
|
||||
|
||||
|
||||
class ImageReader(MeshReader):
|
||||
def __init__(self):
|
||||
super(ImageReader, self).__init__()
|
||||
self._supported_extensions = [".jpg", ".jpeg", ".bmp", ".gif", ".png"]
|
||||
self._ui = ImageReaderUI(self)
|
||||
|
||||
def preRead(self, file_name):
|
||||
img = QImage(file_name)
|
||||
|
||||
if img.isNull():
|
||||
Logger.log("e", "Image is corrupt.")
|
||||
return MeshReader.PreReadResult.failed
|
||||
|
||||
width = img.width()
|
||||
depth = img.height()
|
||||
|
||||
largest = max(width, depth)
|
||||
width = width / largest * self._ui.default_width
|
||||
depth = depth / largest * self._ui.default_depth
|
||||
|
||||
self._ui.setWidthAndDepth(width, depth)
|
||||
self._ui.showConfigUI()
|
||||
self._ui.waitForUIToClose()
|
||||
|
||||
if self._ui.getCancelled():
|
||||
return MeshReader.PreReadResult.cancelled
|
||||
return MeshReader.PreReadResult.accepted
|
||||
|
||||
def read(self, file_name):
|
||||
size = max(self._ui.getWidth(), self._ui.getDepth())
|
||||
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert)
|
||||
|
||||
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert):
|
||||
mesh = None
|
||||
scene_node = None
|
||||
|
||||
scene_node = SceneNode()
|
||||
|
||||
mesh = MeshData()
|
||||
scene_node.setMeshData(mesh)
|
||||
|
||||
img = QImage(file_name)
|
||||
|
||||
if img.isNull():
|
||||
Logger.log("e", "Image is corrupt.")
|
||||
return None
|
||||
|
||||
width = max(img.width(), 2)
|
||||
height = max(img.height(), 2)
|
||||
aspect = height / width
|
||||
|
||||
if img.width() < 2 or img.height() < 2:
|
||||
img = img.scaled(width, height, Qt.IgnoreAspectRatio)
|
||||
|
||||
base_height = max(base_height, 0)
|
||||
peak_height = max(peak_height, -base_height)
|
||||
|
||||
xz_size = max(xz_size, 1)
|
||||
scale_vector = Vector(xz_size, peak_height, xz_size)
|
||||
|
||||
if width > height:
|
||||
scale_vector.setZ(scale_vector.z * aspect)
|
||||
elif height > width:
|
||||
scale_vector.setX(scale_vector.x / aspect)
|
||||
|
||||
if width > max_size or height > max_size:
|
||||
scale_factor = max_size / width
|
||||
if height > width:
|
||||
scale_factor = max_size / height
|
||||
|
||||
width = int(max(round(width * scale_factor), 2))
|
||||
height = int(max(round(height * scale_factor), 2))
|
||||
img = img.scaled(width, height, Qt.IgnoreAspectRatio)
|
||||
|
||||
width_minus_one = width - 1
|
||||
height_minus_one = height - 1
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
texel_width = 1.0 / (width_minus_one) * scale_vector.x
|
||||
texel_height = 1.0 / (height_minus_one) * scale_vector.z
|
||||
|
||||
height_data = numpy.zeros((height, width), dtype=numpy.float32)
|
||||
|
||||
for x in range(0, width):
|
||||
for y in range(0, height):
|
||||
qrgb = img.pixel(x, y)
|
||||
avg = float(qRed(qrgb) + qGreen(qrgb) + qBlue(qrgb)) / (3 * 255)
|
||||
height_data[y, x] = avg
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
if image_color_invert:
|
||||
height_data = 1 - height_data
|
||||
|
||||
for i in range(0, blur_iterations):
|
||||
copy = numpy.pad(height_data, ((1, 1), (1, 1)), mode= "edge")
|
||||
|
||||
height_data += copy[1:-1, 2:]
|
||||
height_data += copy[1:-1, :-2]
|
||||
height_data += copy[2:, 1:-1]
|
||||
height_data += copy[:-2, 1:-1]
|
||||
|
||||
height_data += copy[2:, 2:]
|
||||
height_data += copy[:-2, 2:]
|
||||
height_data += copy[2:, :-2]
|
||||
height_data += copy[:-2, :-2]
|
||||
|
||||
height_data /= 9
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
height_data *= scale_vector.y
|
||||
height_data += base_height
|
||||
|
||||
heightmap_face_count = 2 * height_minus_one * width_minus_one
|
||||
total_face_count = heightmap_face_count + (width_minus_one * 2) * (height_minus_one * 2) + 2
|
||||
|
||||
mesh.reserveFaceCount(total_face_count)
|
||||
|
||||
# initialize to texel space vertex offsets.
|
||||
# 6 is for 6 vertices for each texel quad.
|
||||
heightmap_vertices = numpy.zeros((width_minus_one * height_minus_one, 6, 3), dtype = numpy.float32)
|
||||
heightmap_vertices = heightmap_vertices + numpy.array([[
|
||||
[0, base_height, 0],
|
||||
[0, base_height, texel_height],
|
||||
[texel_width, base_height, texel_height],
|
||||
[texel_width, base_height, texel_height],
|
||||
[texel_width, base_height, 0],
|
||||
[0, base_height, 0]
|
||||
]], dtype = numpy.float32)
|
||||
|
||||
offsetsz, offsetsx = numpy.mgrid[0: height_minus_one, 0: width - 1]
|
||||
offsetsx = numpy.array(offsetsx, numpy.float32).reshape(-1, 1) * texel_width
|
||||
offsetsz = numpy.array(offsetsz, numpy.float32).reshape(-1, 1) * texel_height
|
||||
|
||||
# offsets for each texel quad
|
||||
heightmap_vertex_offsets = numpy.concatenate([offsetsx, numpy.zeros((offsetsx.shape[0], offsetsx.shape[1]), dtype=numpy.float32), offsetsz], 1)
|
||||
heightmap_vertices += heightmap_vertex_offsets.repeat(6, 0).reshape(-1, 6, 3)
|
||||
|
||||
# apply height data to y values
|
||||
heightmap_vertices[:, 0, 1] = heightmap_vertices[:, 5, 1] = height_data[:-1, :-1].reshape(-1)
|
||||
heightmap_vertices[:, 1, 1] = height_data[1:, :-1].reshape(-1)
|
||||
heightmap_vertices[:, 2, 1] = heightmap_vertices[:, 3, 1] = height_data[1:, 1:].reshape(-1)
|
||||
heightmap_vertices[:, 4, 1] = height_data[:-1, 1:].reshape(-1)
|
||||
|
||||
heightmap_indices = numpy.array(numpy.mgrid[0:heightmap_face_count * 3], dtype=numpy.int32).reshape(-1, 3)
|
||||
|
||||
mesh._vertices[0:(heightmap_vertices.size // 3), :] = heightmap_vertices.reshape(-1, 3)
|
||||
mesh._indices[0:(heightmap_indices.size // 3), :] = heightmap_indices
|
||||
|
||||
mesh._vertex_count = heightmap_vertices.size // 3
|
||||
mesh._face_count = heightmap_indices.size // 3
|
||||
|
||||
geo_width = width_minus_one * texel_width
|
||||
geo_height = height_minus_one * texel_height
|
||||
|
||||
# bottom
|
||||
mesh.addFace(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height)
|
||||
mesh.addFace(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0)
|
||||
|
||||
# north and south walls
|
||||
for n in range(0, width_minus_one):
|
||||
x = n * texel_width
|
||||
nx = (n + 1) * texel_width
|
||||
|
||||
hn0 = height_data[0, n]
|
||||
hn1 = height_data[0, n + 1]
|
||||
|
||||
hs0 = height_data[height_minus_one, n]
|
||||
hs1 = height_data[height_minus_one, n + 1]
|
||||
|
||||
mesh.addFace(x, 0, 0, nx, 0, 0, nx, hn1, 0)
|
||||
mesh.addFace(nx, hn1, 0, x, hn0, 0, x, 0, 0)
|
||||
|
||||
mesh.addFace(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height)
|
||||
mesh.addFace(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height)
|
||||
|
||||
# west and east walls
|
||||
for n in range(0, height_minus_one):
|
||||
y = n * texel_height
|
||||
ny = (n + 1) * texel_height
|
||||
|
||||
hw0 = height_data[n, 0]
|
||||
hw1 = height_data[n + 1, 0]
|
||||
|
||||
he0 = height_data[n, width_minus_one]
|
||||
he1 = height_data[n + 1, width_minus_one]
|
||||
|
||||
mesh.addFace(0, 0, y, 0, 0, ny, 0, hw1, ny)
|
||||
mesh.addFace(0, hw1, ny, 0, hw0, y, 0, 0, y)
|
||||
|
||||
mesh.addFace(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny)
|
||||
mesh.addFace(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y)
|
||||
|
||||
mesh.calculateNormals(fast=True)
|
||||
|
||||
return scene_node
|
155
plugins/ImageReader/ImageReaderUI.py
Normal file
155
plugins/ImageReader/ImageReaderUI.py
Normal file
@ -0,0 +1,155 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import os
|
||||
import threading
|
||||
|
||||
from PyQt5.QtCore import Qt, QUrl, pyqtSignal, pyqtSlot, QObject
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class ImageReaderUI(QObject):
|
||||
show_config_ui_trigger = pyqtSignal()
|
||||
|
||||
def __init__(self, image_reader):
|
||||
super(ImageReaderUI, self).__init__()
|
||||
self.image_reader = image_reader
|
||||
self._ui_view = None
|
||||
self.show_config_ui_trigger.connect(self._actualShowConfigUI)
|
||||
|
||||
self.default_width = 120
|
||||
self.default_depth = 120
|
||||
|
||||
self._aspect = 1
|
||||
self._width = self.default_width
|
||||
self._depth = self.default_depth
|
||||
|
||||
self.base_height = 1
|
||||
self.peak_height = 10
|
||||
self.smoothing = 1
|
||||
self.image_color_invert = False;
|
||||
|
||||
self._ui_lock = threading.Lock()
|
||||
self._cancelled = False
|
||||
self._disable_size_callbacks = False
|
||||
|
||||
def setWidthAndDepth(self, width, depth):
|
||||
self._aspect = width / depth
|
||||
self._width = width
|
||||
self._depth = depth
|
||||
|
||||
def getWidth(self):
|
||||
return self._width
|
||||
|
||||
def getDepth(self):
|
||||
return self._depth
|
||||
|
||||
def getCancelled(self):
|
||||
return self._cancelled
|
||||
|
||||
def waitForUIToClose(self):
|
||||
self._ui_lock.acquire()
|
||||
self._ui_lock.release()
|
||||
|
||||
def showConfigUI(self):
|
||||
self._ui_lock.acquire()
|
||||
self._cancelled = False
|
||||
self.show_config_ui_trigger.emit()
|
||||
|
||||
def _actualShowConfigUI(self):
|
||||
self._disable_size_callbacks = True
|
||||
|
||||
if self._ui_view is None:
|
||||
self._createConfigUI()
|
||||
self._ui_view.show()
|
||||
|
||||
self._ui_view.findChild(QObject, "Width").setProperty("text", str(self._width))
|
||||
self._ui_view.findChild(QObject, "Depth").setProperty("text", str(self._depth))
|
||||
self._disable_size_callbacks = False
|
||||
|
||||
self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height))
|
||||
self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height))
|
||||
self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing)
|
||||
|
||||
def _createConfigUI(self):
|
||||
if self._ui_view is None:
|
||||
Logger.log("d", "Creating ImageReader config UI")
|
||||
path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("ImageReader"), "ConfigUI.qml"))
|
||||
component = QQmlComponent(Application.getInstance()._engine, path)
|
||||
self._ui_context = QQmlContext(Application.getInstance()._engine.rootContext())
|
||||
self._ui_context.setContextProperty("manager", self)
|
||||
self._ui_view = component.create(self._ui_context)
|
||||
|
||||
self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowCloseButtonHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowMaximizeButtonHint);
|
||||
|
||||
self._disable_size_callbacks = False
|
||||
|
||||
@pyqtSlot()
|
||||
def onOkButtonClicked(self):
|
||||
self._cancelled = False
|
||||
self._ui_view.close()
|
||||
self._ui_lock.release()
|
||||
|
||||
@pyqtSlot()
|
||||
def onCancelButtonClicked(self):
|
||||
self._cancelled = True
|
||||
self._ui_view.close()
|
||||
self._ui_lock.release()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def onWidthChanged(self, value):
|
||||
if self._ui_view and not self._disable_size_callbacks:
|
||||
if len(value) > 0:
|
||||
self._width = float(value)
|
||||
else:
|
||||
self._width = 0
|
||||
|
||||
self._depth = self._width / self._aspect
|
||||
self._disable_size_callbacks = True
|
||||
self._ui_view.findChild(QObject, "Depth").setProperty("text", str(self._depth))
|
||||
self._disable_size_callbacks = False
|
||||
|
||||
@pyqtSlot(str)
|
||||
def onDepthChanged(self, value):
|
||||
if self._ui_view and not self._disable_size_callbacks:
|
||||
if len(value) > 0:
|
||||
self._depth = float(value)
|
||||
else:
|
||||
self._depth = 0
|
||||
|
||||
self._width = self._depth * self._aspect
|
||||
self._disable_size_callbacks = True
|
||||
self._ui_view.findChild(QObject, "Width").setProperty("text", str(self._width))
|
||||
self._disable_size_callbacks = False
|
||||
|
||||
@pyqtSlot(str)
|
||||
def onBaseHeightChanged(self, value):
|
||||
if (len(value) > 0):
|
||||
self.base_height = float(value)
|
||||
else:
|
||||
self.base_height = 0
|
||||
|
||||
@pyqtSlot(str)
|
||||
def onPeakHeightChanged(self, value):
|
||||
if (len(value) > 0):
|
||||
self.peak_height = float(value)
|
||||
else:
|
||||
self.peak_height = 0
|
||||
|
||||
@pyqtSlot(float)
|
||||
def onSmoothingChanged(self, value):
|
||||
self.smoothing = int(value)
|
||||
|
||||
@pyqtSlot(int)
|
||||
def onImageColorInvertChanged(self, value):
|
||||
if (value == 1):
|
||||
self.image_color_invert = True
|
||||
else:
|
||||
self.image_color_invert = False
|
43
plugins/ImageReader/__init__.py
Normal file
43
plugins/ImageReader/__init__.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import ImageReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": i18n_catalog.i18nc("@label", "Image Reader"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": i18n_catalog.i18nc("@info:whatsthis", "Enables ability to generate printable geometry from 2D image files."),
|
||||
"api": 2
|
||||
},
|
||||
"mesh_reader": [
|
||||
{
|
||||
"extension": "jpg",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "JPG Image")
|
||||
},
|
||||
{
|
||||
"extension": "jpeg",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "JPEG Image")
|
||||
},
|
||||
{
|
||||
"extension": "png",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "PNG Image")
|
||||
},
|
||||
{
|
||||
"extension": "bmp",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "BMP Image")
|
||||
},
|
||||
{
|
||||
"extension": "gif",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "GIF Image")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "mesh_reader": ImageReader.ImageReader() }
|
@ -10,16 +10,23 @@ from UM.Signal import Signal
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Math.Color import Color
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Job import Job
|
||||
from UM.Message import Message
|
||||
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
from cura.ConvexHullNode import ConvexHullNode
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from . import LayerViewProxy
|
||||
|
||||
import time
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
## View used to display g-code paths.
|
||||
class LayerView(View):
|
||||
def __init__(self):
|
||||
@ -29,27 +36,45 @@ class LayerView(View):
|
||||
self._num_layers = 0
|
||||
self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100)
|
||||
self._proxy = LayerViewProxy.LayerViewProxy()
|
||||
self._controller.getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
|
||||
self._max_layers = 10
|
||||
self._current_layer_num = 10
|
||||
self._current_layer_mesh = None
|
||||
self._current_layer_jumps = None
|
||||
self._top_layers_job = None
|
||||
self._activity = False
|
||||
|
||||
self._solid_layers = 5
|
||||
|
||||
self._top_layer_timer = QTimer()
|
||||
self._top_layer_timer.setInterval(50)
|
||||
self._top_layer_timer.setSingleShot(True)
|
||||
self._top_layer_timer.timeout.connect(self._startUpdateTopLayers)
|
||||
|
||||
self._busy = False
|
||||
|
||||
def getActivity(self):
|
||||
return self._activity
|
||||
|
||||
def getCurrentLayer(self):
|
||||
return self._current_layer_num
|
||||
|
||||
|
||||
def _onSceneChanged(self, node):
|
||||
self.calculateMaxLayers()
|
||||
|
||||
|
||||
def getMaxLayers(self):
|
||||
return self._max_layers
|
||||
|
||||
busyChanged = Signal()
|
||||
|
||||
def isBusy(self):
|
||||
return self._busy
|
||||
|
||||
def setBusy(self, busy):
|
||||
if busy != self._busy:
|
||||
self._busy = busy
|
||||
self.busyChanged.emit()
|
||||
|
||||
def resetLayerData(self):
|
||||
self._current_layer_mesh = None
|
||||
self._current_layer_jumps = None
|
||||
@ -59,7 +84,7 @@ class LayerView(View):
|
||||
renderer = self.getRenderer()
|
||||
|
||||
if not self._selection_shader:
|
||||
self._selection_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))
|
||||
self._selection_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
|
||||
self._selection_shader.setUniformValue("u_color", Color(32, 32, 32, 128))
|
||||
|
||||
for node in DepthFirstIterator(scene.getRoot()):
|
||||
@ -89,51 +114,11 @@ class LayerView(View):
|
||||
# This uses glDrawRangeElements internally to only draw a certain range of lines.
|
||||
renderer.queueNode(node, mesh = layer_data, mode = RenderBatch.RenderMode.Lines, range = (start, end))
|
||||
|
||||
# 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
|
||||
try:
|
||||
layer_mesh = layer_data.getLayer(layer).createMesh()
|
||||
if not layer_mesh or layer_mesh.getVertices() is None:
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
if self._current_layer_mesh: #Threading thing; Switching between views can cause the current layer mesh to be deleted.
|
||||
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
|
||||
if self._current_layer_mesh:
|
||||
self._current_layer_mesh.addColors(layer_mesh.getColors() * brightness)
|
||||
if self._current_layer_mesh:
|
||||
renderer.queueNode(node, mesh = self._current_layer_mesh)
|
||||
|
||||
if not self._current_layer_jumps:
|
||||
self._current_layer_jumps = MeshData()
|
||||
for i in range(1):
|
||||
layer = self._current_layer_num - i
|
||||
if layer < 0:
|
||||
continue
|
||||
try:
|
||||
layer_mesh = layer_data.getLayer(layer).createJumps()
|
||||
if not layer_mesh or layer_mesh.getVertices() is None:
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
||||
self._current_layer_jumps.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_jumps.addColors(layer_mesh.getColors() * brightness)
|
||||
|
||||
renderer.queueNode(node, mesh = self._current_layer_jumps)
|
||||
if self._current_layer_jumps:
|
||||
renderer.queueNode(node, mesh = self._current_layer_jumps)
|
||||
|
||||
def setLayer(self, value):
|
||||
if self._current_layer_num != value:
|
||||
@ -145,6 +130,9 @@ class LayerView(View):
|
||||
|
||||
self._current_layer_mesh = None
|
||||
self._current_layer_jumps = None
|
||||
|
||||
self._top_layer_timer.start()
|
||||
|
||||
self.currentLayerNumChanged.emit()
|
||||
|
||||
currentLayerNumChanged = Signal()
|
||||
@ -167,27 +155,32 @@ class LayerView(View):
|
||||
|
||||
if new_max_layers > 0 and new_max_layers != self._old_max_layers:
|
||||
self._max_layers = new_max_layers
|
||||
self.maxLayersChanged.emit()
|
||||
self._current_layer_num = self._max_layers
|
||||
|
||||
# This makes sure we update the current layer
|
||||
self.setLayer(int(self._max_layers))
|
||||
self.currentLayerNumChanged.emit()
|
||||
# The qt slider has a bit of weird behavior that if the maxvalue needs to be changed first
|
||||
# if it's the largest value. If we don't do this, we can have a slider block outside of the
|
||||
# slider.
|
||||
if new_max_layers > self._current_layer_num:
|
||||
self.maxLayersChanged.emit()
|
||||
self.setLayer(int(self._max_layers))
|
||||
else:
|
||||
self.setLayer(int(self._max_layers))
|
||||
self.maxLayersChanged.emit()
|
||||
self._top_layer_timer.start()
|
||||
|
||||
maxLayersChanged = Signal()
|
||||
currentLayerNumChanged = Signal()
|
||||
|
||||
|
||||
## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created
|
||||
# as this caused some issues.
|
||||
def getProxy(self, engine, script_engine):
|
||||
return self._proxy
|
||||
|
||||
|
||||
def endRendering(self):
|
||||
pass
|
||||
|
||||
|
||||
def event(self, event):
|
||||
modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||
ctrl_is_active = modifiers == QtCore.Qt.ControlModifier
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
ctrl_is_active = modifiers == Qt.ControlModifier
|
||||
if event.type == Event.KeyPressEvent and ctrl_is_active:
|
||||
if event.key == KeyEvent.UpKey:
|
||||
self.setLayer(self._current_layer_num + 1)
|
||||
@ -195,3 +188,86 @@ class LayerView(View):
|
||||
if event.key == KeyEvent.DownKey:
|
||||
self.setLayer(self._current_layer_num - 1)
|
||||
return True
|
||||
|
||||
def _startUpdateTopLayers(self):
|
||||
if self._top_layers_job:
|
||||
self._top_layers_job.finished.disconnect(self._updateCurrentLayerMesh)
|
||||
self._top_layers_job.cancel()
|
||||
|
||||
self.setBusy(True)
|
||||
|
||||
self._top_layers_job = _CreateTopLayersJob(self._controller.getScene(), self._current_layer_num, self._solid_layers)
|
||||
self._top_layers_job.finished.connect(self._updateCurrentLayerMesh)
|
||||
self._top_layers_job.start()
|
||||
|
||||
def _updateCurrentLayerMesh(self, job):
|
||||
self.setBusy(False)
|
||||
|
||||
if not job.getResult():
|
||||
return
|
||||
|
||||
self._current_layer_mesh = job.getResult().get("layers")
|
||||
self._current_layer_jumps = job.getResult().get("jumps")
|
||||
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
|
||||
|
||||
self._top_layers_job = None
|
||||
|
||||
class _CreateTopLayersJob(Job):
|
||||
def __init__(self, scene, layer_number, solid_layers):
|
||||
super().__init__()
|
||||
|
||||
self._scene = scene
|
||||
self._layer_number = layer_number
|
||||
self._solid_layers = solid_layers
|
||||
self._cancel = False
|
||||
|
||||
def run(self):
|
||||
layer_data = None
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if layer_data:
|
||||
break
|
||||
|
||||
if self._cancel or not layer_data:
|
||||
return
|
||||
|
||||
layer_mesh = MeshData()
|
||||
for i in range(self._solid_layers):
|
||||
layer_number = self._layer_number - i
|
||||
if layer_number < 0:
|
||||
continue
|
||||
|
||||
try:
|
||||
layer = layer_data.getLayer(layer_number).createMesh()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
if not layer or layer.getVertices() is None:
|
||||
continue
|
||||
|
||||
layer_mesh.addVertices(layer.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
|
||||
layer_mesh.addColors(layer.getColors() * brightness)
|
||||
|
||||
if self._cancel:
|
||||
return
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
if self._cancel:
|
||||
return
|
||||
|
||||
Job.yieldThread()
|
||||
jump_mesh = layer_data.getLayer(self._layer_number).createJumps()
|
||||
if not jump_mesh or jump_mesh.getVertices() is None:
|
||||
jump_mesh = None
|
||||
|
||||
self.setResult({ "layers": layer_mesh, "jumps": jump_mesh })
|
||||
|
||||
def cancel(self):
|
||||
self._cancel = True
|
||||
super().cancel()
|
||||
|
@ -8,37 +8,98 @@ import QtQuick.Controls.Styles 1.1
|
||||
|
||||
import UM 1.0 as UM
|
||||
|
||||
Item
|
||||
Item
|
||||
{
|
||||
width: UM.Theme.sizes.button.width
|
||||
height: UM.Theme.sizes.slider_layerview_size.height
|
||||
width: UM.Theme.getSize("button").width
|
||||
height: UM.Theme.getSize("slider_layerview_size").height
|
||||
|
||||
Slider
|
||||
Slider
|
||||
{
|
||||
id: slider
|
||||
width: UM.Theme.sizes.slider_layerview_size.width
|
||||
height: UM.Theme.sizes.slider_layerview_size.height
|
||||
width: UM.Theme.getSize("slider_layerview_size").width
|
||||
height: UM.Theme.getSize("slider_layerview_size").height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.sizes.slider_layerview_margin.width/2
|
||||
anchors.leftMargin: UM.Theme.getSize("slider_layerview_margin").width/2
|
||||
orientation: Qt.Vertical
|
||||
minimumValue: 0;
|
||||
maximumValue: UM.LayerView.numLayers;
|
||||
stepSize: 1
|
||||
|
||||
property real pixelsPerStep: ((height - UM.Theme.getSize("slider_handle").height) / (maximumValue - minimumValue)) * stepSize;
|
||||
|
||||
value: UM.LayerView.currentLayer
|
||||
onValueChanged: UM.LayerView.setCurrentLayer(value)
|
||||
|
||||
style: UM.Theme.styles.layerViewSlider
|
||||
style: UM.Theme.styles.slider;
|
||||
|
||||
Rectangle
|
||||
{
|
||||
x: parent.width + UM.Theme.getSize("slider_layerview_background").width / 2;
|
||||
y: parent.height - (parent.value * parent.pixelsPerStep) - UM.Theme.getSize("slider_handle").height * 1.25;
|
||||
|
||||
height: UM.Theme.getSize("slider_handle").height + UM.Theme.getSize("default_margin").height
|
||||
width: valueLabel.width + UM.Theme.getSize("default_margin").width
|
||||
Behavior on height { NumberAnimation { duration: 50; } }
|
||||
|
||||
border.width: UM.Theme.getSize("default_lining").width;
|
||||
border.color: UM.Theme.getColor("slider_groove_border");
|
||||
|
||||
visible: UM.LayerView.getLayerActivity && Printer.getPlatformActivity ? true : false
|
||||
|
||||
TextField
|
||||
{
|
||||
id: valueLabel
|
||||
property string maxValue: slider.maximumValue + 1
|
||||
text: slider.value + 1
|
||||
horizontalAlignment: TextInput.AlignRight;
|
||||
onEditingFinished:
|
||||
{
|
||||
if(valueLabel.text != '')
|
||||
{
|
||||
slider.value = valueLabel.text - 1
|
||||
}
|
||||
}
|
||||
validator: IntValidator { bottom: 1; top: slider.maximumValue + 1; }
|
||||
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
||||
width: UM.Theme.getSize("line").width * maxValue.length;
|
||||
|
||||
style: TextFieldStyle
|
||||
{
|
||||
textColor: UM.Theme.getColor("setting_control_text");
|
||||
font: UM.Theme.getFont("default");
|
||||
background: Item { }
|
||||
}
|
||||
}
|
||||
|
||||
BusyIndicator
|
||||
{
|
||||
id: busyIndicator;
|
||||
anchors.left: parent.right;
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
|
||||
width: UM.Theme.getSize("slider_handle").height;
|
||||
height: width;
|
||||
|
||||
running: UM.LayerView.busy;
|
||||
visible: UM.LayerView.busy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
z: slider.z - 1
|
||||
width: UM.Theme.sizes.slider_layerview_background.width
|
||||
height: slider.height + UM.Theme.sizes.default_margin.height * 2
|
||||
color: UM.Theme.colors.tool_panel_background;
|
||||
border.width: UM.Theme.sizes.default_lining.width
|
||||
border.color: UM.Theme.colors.lining
|
||||
width: UM.Theme.getSize("slider_layerview_background").width
|
||||
height: slider.height + UM.Theme.getSize("default_margin").height * 2
|
||||
color: UM.Theme.getColor("tool_panel_background");
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
|
||||
MouseArea {
|
||||
id: sliderMouseArea
|
||||
|
@ -33,6 +33,15 @@ class LayerViewProxy(QObject):
|
||||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
return active_view.getCurrentLayer()
|
||||
|
||||
busyChanged = pyqtSignal()
|
||||
@pyqtProperty(bool, notify = busyChanged)
|
||||
def busy(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
return active_view.isBusy()
|
||||
|
||||
return False
|
||||
|
||||
@pyqtSlot(int)
|
||||
def setCurrentLayer(self, layer_num):
|
||||
@ -49,9 +58,13 @@ class LayerViewProxy(QObject):
|
||||
|
||||
def _onMaxLayersChanged(self):
|
||||
self.maxLayersChanged.emit()
|
||||
|
||||
def _onBusyChanged(self):
|
||||
self.busyChanged.emit()
|
||||
|
||||
def _onActiveViewChanged(self):
|
||||
active_view = self._controller.getActiveView()
|
||||
if type(active_view) == LayerView.LayerView.LayerView:
|
||||
active_view.currentLayerNumChanged.connect(self._onLayerChanged)
|
||||
active_view.maxLayersChanged.connect(self._onMaxLayersChanged)
|
||||
active_view.busyChanged.connect(self._onBusyChanged)
|
||||
|
@ -18,7 +18,8 @@ def getMetaData():
|
||||
},
|
||||
"view": {
|
||||
"name": catalog.i18nc("@item:inlistbox", "Layers"),
|
||||
"view_panel": "LayerView.qml"
|
||||
"view_panel": "LayerView.qml",
|
||||
"weight": 2
|
||||
}
|
||||
}
|
||||
|
||||
|
157
plugins/LegacyProfileReader/DictionaryOfDoom.json
Normal file
157
plugins/LegacyProfileReader/DictionaryOfDoom.json
Normal file
@ -0,0 +1,157 @@
|
||||
{
|
||||
"source_version": "15.04",
|
||||
"target_version": 1,
|
||||
|
||||
"translation": {
|
||||
"machine_nozzle_size": "nozzle_size",
|
||||
"line_width": "wall_thickness if (spiralize == \"True\" or simple_mode == \"True\") else (nozzle_size if (float(wall_thickness) < 0.01) else (wall_thickness if (float(wall_thickness) < float(nozzle_size)) else (nozzle_size if ((int(float(wall_thickness) / (float(nozzle_size) - 0.0001))) == 0) else ((float(wall_thickness) / ((int(float(wall_thickness) / (float(nozzle_size) - 0.0001))) + 1)) if ((float(wall_thickness) / (int(float(wall_thickness) / (float(nozzle_size) - 0.0001)))) > float(nozzle_size) * 1.5) else ((float(wall_thickness) / (int(float(wall_thickness) / (float(nozzle_size) - 0.0001)))))))))",
|
||||
"layer_height": "layer_height",
|
||||
"layer_height_0": "bottom_thickness",
|
||||
"wall_thickness": "wall_thickness",
|
||||
"top_bottom_thickness": "solid_layer_thickness",
|
||||
"top_thickness": "0 if (solid_top == \"False\") else solid_layer_thickness",
|
||||
"bottom_thickness": "0 if (solid_bottom == \"False\") else solid_layer_thickness",
|
||||
"infill_sparse_density": "fill_density",
|
||||
"infill_overlap": "fill_overlap",
|
||||
"infill_before_walls": "False if (perimeter_before_infill == \"True\") else True",
|
||||
"material_print_temperature": "print_temperature",
|
||||
"material_bed_temperature": "print_bed_temperature",
|
||||
"material_diameter": "filament_diameter",
|
||||
"material_flow": "filament_flow",
|
||||
"retraction_enable": "retraction_enable",
|
||||
"retraction_amount": "retraction_amount",
|
||||
"retraction_speed": "retraction_speed",
|
||||
"retraction_min_travel": "retraction_min_travel",
|
||||
"retraction_hop": "retraction_hop",
|
||||
"speed_print": "print_speed",
|
||||
"speed_infill": "infill_speed if (float(infill_speed) != 0) else print_speed",
|
||||
"speed_wall_0": "inset0_speed if (float(inset0_speed) != 0) else print_speed",
|
||||
"speed_wall_x": "insetx_speed if (float(insetx_speed) != 0) else print_speed",
|
||||
"speed_topbottom": "solidarea_speed if (float(solidarea_speed) != 0) else print_speed",
|
||||
"speed_travel": "travel_speed if (float(travel_speed) != 0) else travel_speed",
|
||||
"speed_layer_0": "bottom_layer_speed",
|
||||
"retraction_combing": "True if (retraction_combing == \"All\" or retraction_combing == \"No Skin\") else False",
|
||||
"cool_fan_enabled": "fan_enabled",
|
||||
"cool_fan_speed_min": "fan_speed",
|
||||
"cool_fan_speed_max": "fan_speed_max",
|
||||
"cool_fan_full_at_height": "fan_full_height",
|
||||
"cool_min_layer_time": "cool_min_layer_time",
|
||||
"cool_min_speed": "cool_min_feedrate",
|
||||
"cool_lift_head": "cool_head_lift",
|
||||
"support_enable": "False if (support == \"None\") else True",
|
||||
"support_type": "\"buildplate\" if (support == \"Touching buildplate\") else \"everywhere\"",
|
||||
"support_angle": "support_angle",
|
||||
"support_xy_distance": "support_xy_distance",
|
||||
"support_z_distance": "support_z_distance",
|
||||
"support_pattern": "support_type.lower()",
|
||||
"support_infill_rate": "support_fill_rate",
|
||||
"adhesion_type": "\"skirt\" if (platform_adhesion == \"None\") else platform_adhesion.lower()",
|
||||
"skirt_line_count": "skirt_line_count",
|
||||
"skirt_gap": "skirt_gap",
|
||||
"skirt_minimal_length": "skirt_minimal_length",
|
||||
"brim_line_count": "brim_line_count",
|
||||
"raft_margin": "raft_margin",
|
||||
"raft_airgap": "raft_airgap_all",
|
||||
"raft_surface_layers": "raft_surface_layers",
|
||||
"raft_surface_thickness": "raft_surface_thickness",
|
||||
"raft_surface_line_width": "raft_surface_linewidth",
|
||||
"raft_surface_line_spacing": "raft_line_spacing",
|
||||
"raft_interface_thickness": "raft_interface_thickness",
|
||||
"raft_interface_line_width": "raft_interface_linewidth",
|
||||
"raft_interface_line_spacing": "raft_line_spacing",
|
||||
"raft_base_thickness": "raft_base_thickness",
|
||||
"raft_base_line_width": "raft_base_linewidth",
|
||||
"raft_base_line_spacing": "raft_line_spacing",
|
||||
"meshfix_union_all": "fix_horrible_union_all_type_a",
|
||||
"meshfix_union_all_remove_holes": "fix_horrible_union_all_type_b",
|
||||
"meshfix_extensive_stitching": "fix_horrible_extensive_stitching",
|
||||
"meshfix_keep_open_polygons": "fix_horrible_use_open_bits",
|
||||
"magic_mesh_surface_mode": "simple_mode",
|
||||
"magic_spiralize": "spiralize",
|
||||
"prime_tower_enable": "wipe_tower",
|
||||
"prime_tower_size": "math.sqrt(float(wipe_tower_volume) / float(layer_height))",
|
||||
"ooze_shield_enabled": "ooze_shield"
|
||||
},
|
||||
|
||||
"defaults": {
|
||||
"bottom_layer_speed": "20",
|
||||
"bottom_thickness": "0.3",
|
||||
"brim_line_count": "20",
|
||||
"cool_head_lift": "False",
|
||||
"cool_min_feedrate": "10",
|
||||
"cool_min_layer_time": "5",
|
||||
"fan_enabled": "True",
|
||||
"fan_full_height": "0.5",
|
||||
"fan_speed": "100",
|
||||
"fan_speed_max": "100",
|
||||
"filament_diameter": "2.85",
|
||||
"filament_diameter2": "0",
|
||||
"filament_diameter3": "0",
|
||||
"filament_diameter4": "0",
|
||||
"filament_diameter5": "0",
|
||||
"filament_flow": "100.0",
|
||||
"fill_density": "20",
|
||||
"fill_overlap": "15",
|
||||
"fix_horrible_extensive_stitching": "False",
|
||||
"fix_horrible_union_all_type_a": "True",
|
||||
"fix_horrible_union_all_type_b": "False",
|
||||
"fix_horrible_use_open_bits": "False",
|
||||
"infill_speed": "0.0",
|
||||
"inset0_speed": "0.0",
|
||||
"insetx_speed": "0.0",
|
||||
"layer_height": "0.1",
|
||||
"layer0_width_factor": "100",
|
||||
"nozzle_size": "0.4",
|
||||
"object_sink": "0.0",
|
||||
"ooze_shield": "False",
|
||||
"overlap_dual": "0.15",
|
||||
"perimeter_before_infill": "False",
|
||||
"platform_adhesion": "None",
|
||||
"print_bed_temperature": "70",
|
||||
"print_speed": "50",
|
||||
"print_temperature": "210",
|
||||
"print_temperature2": "0",
|
||||
"print_temperature3": "0",
|
||||
"print_temperature4": "0",
|
||||
"print_temperature5": "0",
|
||||
"raft_airgap": "0.22",
|
||||
"raft_airgap_all": "0.0",
|
||||
"raft_base_linewidth": "1.0",
|
||||
"raft_base_thickness": "0.3",
|
||||
"raft_interface_linewidth": "0.4",
|
||||
"raft_interface_thickness": "0.27",
|
||||
"raft_line_spacing": "3.0",
|
||||
"raft_margin": "5.0",
|
||||
"raft_surface_layers": "2",
|
||||
"raft_surface_linewidth": "0.4",
|
||||
"raft_surface_thickness": "0.27",
|
||||
"retraction_amount": "4.5",
|
||||
"retraction_combing": "All",
|
||||
"retraction_dual_amount": "16.5",
|
||||
"retraction_enable": "True",
|
||||
"retraction_hop": "0.0",
|
||||
"retraction_min_travel": "1.5",
|
||||
"retraction_minimal_extrusion": "0.02",
|
||||
"retraction_speed": "40.0",
|
||||
"simple_mode": "False",
|
||||
"skirt_gap": "3.0",
|
||||
"skirt_line_count": "1",
|
||||
"skirt_minimal_length": "150.0",
|
||||
"solid_bottom": "True",
|
||||
"solid_layer_thickness": "0.6",
|
||||
"solid_top": "True",
|
||||
"solidarea_speed": "0.0",
|
||||
"spiralize": "False",
|
||||
"support": "None",
|
||||
"support_angle": "60",
|
||||
"support_dual_extrusion": "Both",
|
||||
"support_fill_rate": "15",
|
||||
"support_type": "Lines",
|
||||
"support_xy_distance": "0.7",
|
||||
"support_z_distance": "0.15",
|
||||
"travel_speed": "150.0",
|
||||
"wall_thickness": "0.8",
|
||||
"wipe_tower": "False",
|
||||
"wipe_tower_volume": "15"
|
||||
}
|
||||
}
|
128
plugins/LegacyProfileReader/LegacyProfileReader.py
Normal file
128
plugins/LegacyProfileReader/LegacyProfileReader.py
Normal file
@ -0,0 +1,128 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import configparser #For reading the legacy profile INI files.
|
||||
import json #For reading the Dictionary of Doom.
|
||||
import math #For mathematical operations included in the Dictionary of Doom.
|
||||
import os.path #For concatenating the path to the plugin and the relative path to the Dictionary of Doom.
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Logger import Logger #Logging errors.
|
||||
from UM.PluginRegistry import PluginRegistry #For getting the path to this plugin's directory.
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
|
||||
## A plugin that reads profile data from legacy Cura versions.
|
||||
#
|
||||
# It reads a profile from an .ini file, and performs some translations on it.
|
||||
# Not all translations are correct, mind you, but it is a best effort.
|
||||
class LegacyProfileReader(ProfileReader):
|
||||
## Initialises the legacy profile reader.
|
||||
#
|
||||
# This does nothing since the only other function is basically stateless.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
## Prepares the default values of all legacy settings.
|
||||
#
|
||||
# These are loaded from the Dictionary of Doom.
|
||||
#
|
||||
# \param json The JSON file to load the default setting values from. This
|
||||
# should not be a URL but a pre-loaded JSON handle.
|
||||
# \return A dictionary of the default values of the legacy Cura version.
|
||||
def prepareDefaults(self, json):
|
||||
defaults = {}
|
||||
for key in json["defaults"]: #We have to copy over all defaults from the JSON handle to a normal dict.
|
||||
defaults[key] = json["defaults"][key]
|
||||
return defaults
|
||||
|
||||
## Prepares the local variables that can be used in evaluation of computing
|
||||
# new setting values from the old ones.
|
||||
#
|
||||
# This fills a dictionary with all settings from the legacy Cura version
|
||||
# and their values, so that they can be used in evaluating the new setting
|
||||
# values as Python code.
|
||||
#
|
||||
# \param config_parser The ConfigParser that finds the settings in the
|
||||
# legacy profile.
|
||||
# \param config_section The section in the profile where the settings
|
||||
# should be found.
|
||||
# \param defaults The default values for all settings in the legacy Cura.
|
||||
# \return A set of local variables, one for each setting in the legacy
|
||||
# profile.
|
||||
def prepareLocals(self, config_parser, config_section, defaults):
|
||||
locals = defaults.copy() #Don't edit the original!
|
||||
for option in config_parser.options(config_section):
|
||||
locals[option] = config_parser.get(config_section, option)
|
||||
return locals
|
||||
|
||||
## Reads a legacy Cura profile from a file and returns it.
|
||||
#
|
||||
# \param file_name The file to read the legacy Cura profile from.
|
||||
# \return The legacy Cura profile that was in the file, if any. If the
|
||||
# file could not be read or didn't contain a valid profile, \code None
|
||||
# \endcode is returned.
|
||||
def read(self, file_name):
|
||||
if file_name.split(".")[-1] != "ini":
|
||||
return None
|
||||
Logger.log("i", "Importing legacy profile from file " + file_name + ".")
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) #Create an empty profile.
|
||||
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
try:
|
||||
with open(file_name) as f:
|
||||
parser.readfp(f) #Parse the INI file.
|
||||
except Exception as e:
|
||||
Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
#Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
|
||||
#Since importing multiple machine profiles is out of scope, just import the first section we find.
|
||||
section = ""
|
||||
for found_section in parser.sections():
|
||||
if found_section.startswith("profile"):
|
||||
section = found_section
|
||||
break
|
||||
if not section: #No section starting with "profile" was found. Probably not a proper INI file.
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f:
|
||||
dict_of_doom = json.load(f) #Parse the Dictionary of Doom.
|
||||
except IOError as e:
|
||||
Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))
|
||||
return None
|
||||
except Exception as e:
|
||||
Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e))
|
||||
return None
|
||||
|
||||
defaults = self.prepareDefaults(dict_of_doom)
|
||||
legacy_settings = self.prepareLocals(parser, section, defaults) #Gets the settings from the legacy profile.
|
||||
|
||||
#Check the target version in the Dictionary of Doom with this application version.
|
||||
if "target_version" not in dict_of_doom:
|
||||
Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?")
|
||||
return None
|
||||
if Profile.ProfileVersion != dict_of_doom["target_version"]:
|
||||
Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the profile version (version %s)!", dict_of_doom["target_version"], str(Profile.ProfileVersion))
|
||||
return None
|
||||
|
||||
if "translation" not in dict_of_doom:
|
||||
Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
|
||||
return None
|
||||
for new_setting in dict_of_doom["translation"]: #Evaluate all new settings that would get a value from the translations.
|
||||
old_setting_expression = dict_of_doom["translation"][new_setting]
|
||||
compiled = compile(old_setting_expression, new_setting, "eval")
|
||||
try:
|
||||
new_value = eval(compiled, {"math": math}, legacy_settings) #Pass the legacy settings as local variables to allow access to in the evaluation.
|
||||
value_using_defaults = eval(compiled, {"math": math}, defaults) #Evaluate again using only the default values to try to see if they are default.
|
||||
except Exception as e: #Probably some setting name that was missing or something else that went wrong in the ini file.
|
||||
Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
|
||||
continue
|
||||
if new_value != value_using_defaults and profile.getSettingValue(new_setting) != new_value: #Not equal to the default in the new Cura OR the default in the legacy Cura.
|
||||
profile.setSettingValue(new_setting, new_value) #Store the setting in the profile!
|
||||
|
||||
if len(profile.getChangedSettings()) == 0:
|
||||
Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")
|
||||
profile.setDirty(True)
|
||||
return profile
|
27
plugins/LegacyProfileReader/__init__.py
Normal file
27
plugins/LegacyProfileReader/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import LegacyProfileReader
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Legacy Cura Profile Reader"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for importing profiles from legacy Cura versions."),
|
||||
"api": 2
|
||||
},
|
||||
"profile_reader": [
|
||||
{
|
||||
"extension": "ini",
|
||||
"description": catalog.i18nc("@item:inlistbox", "Cura 15.04 profiles")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "profile_reader": LegacyProfileReader.LegacyProfileReader() }
|
@ -24,16 +24,11 @@ class PerObjectSettingsModel(ListModel):
|
||||
super().__init__(parent)
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._root = self._scene.getRoot()
|
||||
self._root.transformationChanged.connect(self._updatePositions)
|
||||
self._root.childrenChanged.connect(self._updateNodes)
|
||||
self._updateNodes(None)
|
||||
|
||||
self.addRoleName(self.IdRole,"id")
|
||||
self.addRoleName(self.XRole,"x")
|
||||
self.addRoleName(self.YRole,"y")
|
||||
self.addRoleName(self.MaterialRole, "material")
|
||||
self.addRoleName(self.ProfileRole, "profile")
|
||||
self.addRoleName(self.SettingsRole, "settings")
|
||||
self._updateModel()
|
||||
|
||||
@pyqtSlot("quint64", str)
|
||||
def setObjectProfile(self, object_id, profile_name):
|
||||
@ -72,27 +67,11 @@ class PerObjectSettingsModel(ListModel):
|
||||
if len(node.callDecoration("getAllSettings")) == 0:
|
||||
node.removeDecorator(SettingOverrideDecorator)
|
||||
|
||||
def _updatePositions(self, source):
|
||||
camera = Application.getInstance().getController().getScene().getActiveCamera()
|
||||
for node in BreadthFirstIterator(self._root):
|
||||
if type(node) is not SceneNode or not node.getMeshData():
|
||||
continue
|
||||
|
||||
projected_position = camera.project(node.getWorldPosition())
|
||||
|
||||
index = self.find("id", id(node))
|
||||
self.setProperty(index, "x", float(projected_position[0]))
|
||||
self.setProperty(index, "y", float(projected_position[1]))
|
||||
|
||||
def _updateNodes(self, source):
|
||||
def _updateModel(self):
|
||||
self.clear()
|
||||
camera = Application.getInstance().getController().getScene().getActiveCamera()
|
||||
for node in BreadthFirstIterator(self._root):
|
||||
if type(node) is not SceneNode or not node.getMeshData() or not node.isSelectable():
|
||||
if type(node) is not SceneNode or not node.isSelectable():
|
||||
continue
|
||||
|
||||
projected_position = camera.project(node.getWorldPosition())
|
||||
|
||||
node_profile = node.callDecoration("getProfile")
|
||||
if not node_profile:
|
||||
node_profile = "global"
|
||||
@ -101,8 +80,6 @@ class PerObjectSettingsModel(ListModel):
|
||||
|
||||
self.appendItem({
|
||||
"id": id(node),
|
||||
"x": float(projected_position[0]),
|
||||
"y": float(projected_position[1]),
|
||||
"material": "",
|
||||
"profile": node_profile,
|
||||
"settings": SettingOverrideModel.SettingOverrideModel(node)
|
||||
|
@ -10,8 +10,9 @@ import UM 1.1 as UM
|
||||
|
||||
Item {
|
||||
id: base;
|
||||
property int currentIndex: UM.ActiveTool.properties.SelectedIndex;
|
||||
property string printSequence: UM.ActiveTool.properties.PrintSequence;
|
||||
property int currentIndex: UM.ActiveTool.properties.getValue("SelectedIndex")
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
||||
width: childrenRect.width;
|
||||
height: childrenRect.height;
|
||||
@ -21,51 +22,21 @@ Item {
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
||||
spacing: UM.Theme.sizes.default_margin.height;
|
||||
|
||||
Label {
|
||||
width: UM.Theme.sizes.setting.width;
|
||||
wrapMode: Text.Wrap;
|
||||
text: catalog.i18nc("@label", "Per Object Settings behavior may be unexpected when 'Print sequence' is set to 'All at Once'.")
|
||||
color: UM.Theme.colors.text;
|
||||
visible: base.printSequence == "all_at_once"
|
||||
}
|
||||
|
||||
UM.SettingItem {
|
||||
id: profileSelection
|
||||
|
||||
width: UM.Theme.sizes.setting.width;
|
||||
height: UM.Theme.sizes.setting.height;
|
||||
|
||||
name: catalog.i18nc("@label", "Object profile")
|
||||
type: "enum"
|
||||
indent: false
|
||||
|
||||
style: UM.Theme.styles.setting_item;
|
||||
|
||||
options: UM.ProfilesModel { addUseGlobal: true }
|
||||
|
||||
value: UM.ActiveTool.properties.Model.getItem(base.currentIndex).profile
|
||||
|
||||
onItemValueChanged: {
|
||||
var item = UM.ActiveTool.properties.Model.getItem(base.currentIndex);
|
||||
UM.ActiveTool.properties.Model.setObjectProfile(item.id, value)
|
||||
}
|
||||
}
|
||||
spacing: UM.Theme.getSize("default_margin").height;
|
||||
|
||||
Column {
|
||||
id: customisedSettings
|
||||
spacing: UM.Theme.sizes.default_lining.height;
|
||||
width: UM.Theme.sizes.setting.width + UM.Theme.sizes.setting.height/2;
|
||||
spacing: UM.Theme.getSize("default_lining").height;
|
||||
width: UM.Theme.getSize("setting").width + UM.Theme.getSize("setting").height/2;
|
||||
|
||||
Repeater {
|
||||
id: settings;
|
||||
|
||||
model: UM.ActiveTool.properties.Model.getItem(base.currentIndex).settings
|
||||
model: UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).settings
|
||||
|
||||
UM.SettingItem {
|
||||
width: UM.Theme.sizes.setting.width;
|
||||
height: UM.Theme.sizes.setting.height;
|
||||
width: UM.Theme.getSize("setting").width;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
|
||||
name: model.label;
|
||||
type: model.type;
|
||||
@ -73,6 +44,7 @@ Item {
|
||||
description: model.description;
|
||||
unit: model.unit;
|
||||
valid: model.valid;
|
||||
visible: !model.global_only
|
||||
options: model.options
|
||||
indent: false
|
||||
|
||||
@ -86,10 +58,10 @@ Item {
|
||||
{
|
||||
anchors.left: parent.right;
|
||||
|
||||
width: UM.Theme.sizes.setting.height;
|
||||
height: UM.Theme.sizes.setting.height;
|
||||
width: UM.Theme.getSize("setting").height;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
|
||||
onClicked: UM.ActiveTool.properties.Model.removeSettingOverride(UM.ActiveTool.properties.Model.getItem(base.currentIndex).id, model.key)
|
||||
onClicked: UM.ActiveTool.properties.getValue("Model").removeSettingOverride(UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).id, model.key)
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
@ -104,8 +76,8 @@ Item {
|
||||
height: parent.height/2
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: UM.Theme.colors.setting_control_revert
|
||||
source: UM.Theme.icons.cross1
|
||||
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
source: UM.Theme.getIcon("cross1")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,8 +89,7 @@ Item {
|
||||
Button
|
||||
{
|
||||
id: customise_settings_button;
|
||||
anchors.right: profileSelection.right;
|
||||
height: UM.Theme.sizes.setting.height;
|
||||
height: UM.Theme.getSize("setting").height;
|
||||
visible: parseInt(UM.Preferences.getValue("cura/active_mode")) == 1
|
||||
|
||||
text: catalog.i18nc("@action:button", "Add Setting");
|
||||
@ -129,16 +100,16 @@ Item {
|
||||
{
|
||||
width: control.width;
|
||||
height: control.height;
|
||||
border.width: UM.Theme.sizes.default_lining.width;
|
||||
border.color: control.pressed ? UM.Theme.colors.action_button_active_border :
|
||||
control.hovered ? UM.Theme.colors.action_button_hovered_border : UM.Theme.colors.action_button_border
|
||||
color: control.pressed ? UM.Theme.colors.action_button_active :
|
||||
control.hovered ? UM.Theme.colors.action_button_hovered : UM.Theme.colors.action_button
|
||||
border.width: UM.Theme.getSize("default_lining").width;
|
||||
border.color: control.pressed ? UM.Theme.getColor("action_button_active_border") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border")
|
||||
color: control.pressed ? UM.Theme.getColor("action_button_active") :
|
||||
control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
|
||||
}
|
||||
label: Label
|
||||
{
|
||||
text: control.text;
|
||||
color: UM.Theme.colors.setting_control_text;
|
||||
color: UM.Theme.getColor("setting_control_text");
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
@ -157,7 +128,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "uranium"; }
|
||||
|
||||
UM.Dialog {
|
||||
id: settingPickDialog
|
||||
@ -188,7 +158,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
width: view.width - UM.Theme.sizes.default_margin.width * 2;
|
||||
width: view.width - UM.Theme.getSize("default_margin").width * 2;
|
||||
height: childrenRect.height;
|
||||
|
||||
Repeater {
|
||||
@ -201,6 +171,7 @@ Item {
|
||||
|
||||
width: parent.width;
|
||||
height: childrenRect.height;
|
||||
visible: model.visible && settingsColumn.childrenHeight != 0 //If all children are hidden, the height is 0, and then the category header must also be hidden.
|
||||
|
||||
ToolButton {
|
||||
id: categoryHeader;
|
||||
@ -218,11 +189,11 @@ Item {
|
||||
}
|
||||
label: Row
|
||||
{
|
||||
spacing: UM.Theme.sizes.default_margin.width;
|
||||
spacing: UM.Theme.getSize("default_margin").width;
|
||||
Image
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
source: control.checked ? UM.Theme.icons.arrow_right : UM.Theme.icons.arrow_bottom;
|
||||
source: control.checked ? UM.Theme.getIcon("arrow_right") : UM.Theme.getIcon("arrow_bottom");
|
||||
}
|
||||
Label
|
||||
{
|
||||
@ -236,8 +207,6 @@ Item {
|
||||
|
||||
property variant settingsModel: model.settings;
|
||||
|
||||
visible: model.visible;
|
||||
|
||||
Column {
|
||||
id: settingsColumn;
|
||||
|
||||
@ -268,13 +237,15 @@ Item {
|
||||
|
||||
delegate: ToolButton {
|
||||
id: button;
|
||||
x: model.depth * UM.Theme.sizes.default_margin.width;
|
||||
x: model.depth * UM.Theme.getSize("default_margin").width;
|
||||
text: model.name;
|
||||
tooltip: model.description;
|
||||
visible: !model.global_only
|
||||
height: model.global_only ? 0 : undefined
|
||||
|
||||
onClicked: {
|
||||
var object_id = UM.ActiveTool.properties.Model.getItem(base.currentIndex).id;
|
||||
UM.ActiveTool.properties.Model.addSettingOverride(object_id, model.key);
|
||||
var object_id = UM.ActiveTool.properties.getValue("Model").getItem(base.currentIndex).id;
|
||||
UM.ActiveTool.properties.getValue("Model").addSettingOverride(object_id, model.key);
|
||||
settingPickDialog.visible = false;
|
||||
}
|
||||
|
||||
|
@ -4,26 +4,44 @@
|
||||
from UM.Tool import Tool
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Application import Application
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
from . import PerObjectSettingsModel
|
||||
|
||||
class PerObjectSettingsTool(Tool):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._model = None
|
||||
|
||||
self.setExposedProperties("Model", "SelectedIndex", "PrintSequence")
|
||||
self.setExposedProperties("Model", "SelectedIndex")
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged)
|
||||
self._onPreferenceChanged("cura/active_mode")
|
||||
|
||||
def event(self, event):
|
||||
return False
|
||||
|
||||
def getModel(self):
|
||||
return PerObjectSettingsModel.PerObjectSettingsModel()
|
||||
if not self._model:
|
||||
self._model = PerObjectSettingsModel.PerObjectSettingsModel()
|
||||
|
||||
#For some reason, casting this model to itself causes the model to properly be cast to a QVariant, even though it ultimately inherits from QVariant.
|
||||
#Yeah, we have no idea either...
|
||||
return PerObjectSettingsModel.PerObjectSettingsModel(self._model)
|
||||
|
||||
def getSelectedIndex(self):
|
||||
selected_object_id = id(Selection.getSelectedObject(0))
|
||||
try:
|
||||
selected_object = Selection.getSelectedObject(0)
|
||||
if selected_object.getParent().callDecoration("isGroup"):
|
||||
selected_object = selected_object.getParent()
|
||||
except:
|
||||
selected_object = None
|
||||
selected_object_id = id(selected_object)
|
||||
index = self.getModel().find("id", selected_object_id)
|
||||
return index
|
||||
|
||||
def getPrintSequence(self):
|
||||
settings = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
return settings.getSettingValue("print_sequence")
|
||||
def _onPreferenceChanged(self, preference):
|
||||
if preference == "cura/active_mode":
|
||||
enabled = Preferences.getInstance().getValue(preference)==1
|
||||
Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, enabled)
|
@ -18,6 +18,7 @@ class SettingOverrideModel(ListModel):
|
||||
OptionsRole = Qt.UserRole + 8
|
||||
WarningDescriptionRole = Qt.UserRole + 9
|
||||
ErrorDescriptionRole = Qt.UserRole + 10
|
||||
GlobalOnlyRole = Qt.UserRole + 11
|
||||
|
||||
def __init__(self, node, parent = None):
|
||||
super().__init__(parent)
|
||||
@ -28,6 +29,10 @@ class SettingOverrideModel(ListModel):
|
||||
self._node.decoratorsChanged.connect(self._onDecoratorsChanged)
|
||||
self._onDecoratorsChanged(None)
|
||||
|
||||
self._activeProfile = Application.getInstance().getMachineManager().getWorkingProfile() #To be able to get notified when a setting changes.
|
||||
self._activeProfile.settingValueChanged.connect(self._onProfileSettingValueChanged)
|
||||
Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onProfileChanged)
|
||||
|
||||
self.addRoleName(self.KeyRole, "key")
|
||||
self.addRoleName(self.LabelRole, "label")
|
||||
self.addRoleName(self.DescriptionRole, "description")
|
||||
@ -38,6 +43,7 @@ class SettingOverrideModel(ListModel):
|
||||
self.addRoleName(self.OptionsRole, "options")
|
||||
self.addRoleName(self.WarningDescriptionRole, "warning_description")
|
||||
self.addRoleName(self.ErrorDescriptionRole, "error_description")
|
||||
self.addRoleName(self.GlobalOnlyRole, "global_only")
|
||||
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setSettingValue(self, key, value):
|
||||
@ -68,6 +74,35 @@ class SettingOverrideModel(ListModel):
|
||||
model.appendItem({"value": str(value), "name": str(name)})
|
||||
return model
|
||||
|
||||
## Updates the active profile in this model if the active profile is
|
||||
# changed.
|
||||
#
|
||||
# This links the settingValueChanged of the new profile to this model's
|
||||
# _onSettingValueChanged function, so that it properly listens to those
|
||||
# events again.
|
||||
def _onProfileChanged(self):
|
||||
if self._activeProfile: #Unlink from the old profile.
|
||||
self._activeProfile.settingValueChanged.disconnect(self._onProfileSettingValueChanged)
|
||||
old_profile = self._activeProfile
|
||||
self._activeProfile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
self._activeProfile.settingValueChanged.connect(self._onProfileSettingValueChanged) #Re-link to the new profile.
|
||||
for setting_name in old_profile.getChangedSettings().keys(): #Update all changed settings in the old and new profiles.
|
||||
self._onProfileSettingValueChanged(setting_name)
|
||||
for setting_name in self._activeProfile.getChangedSettings().keys():
|
||||
self._onProfileSettingValueChanged(setting_name)
|
||||
|
||||
## Updates the global_only property of a setting once a setting value
|
||||
# changes.
|
||||
#
|
||||
# This method should only get called on settings that are dependent on the
|
||||
# changed setting.
|
||||
#
|
||||
# \param setting_name The setting that needs to be updated.
|
||||
def _onProfileSettingValueChanged(self, setting_name):
|
||||
index = self.find("key", setting_name)
|
||||
if index != -1:
|
||||
self.setProperty(index, "global_only", Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition().getSetting(setting_name).getGlobalOnly())
|
||||
|
||||
def _onSettingsChanged(self):
|
||||
self.clear()
|
||||
|
||||
@ -84,7 +119,8 @@ class SettingOverrideModel(ListModel):
|
||||
"valid": setting.validate(value),
|
||||
"options": self._createOptionsModel(setting.getOptions()),
|
||||
"warning_description": setting.getWarningDescription(),
|
||||
"error_description": setting.getErrorDescription()
|
||||
"error_description": setting.getErrorDescription(),
|
||||
"global_only": setting.getGlobalOnly()
|
||||
})
|
||||
|
||||
items.sort(key = lambda i: i["key"])
|
||||
@ -98,3 +134,4 @@ class SettingOverrideModel(ListModel):
|
||||
if index != -1:
|
||||
self.setProperty(index, "value", str(value))
|
||||
self.setProperty(index, "valid", setting.validate(value))
|
||||
self.setProperty(index, "global_only", setting.getGlobalOnly())
|
@ -4,7 +4,7 @@
|
||||
from . import PerObjectSettingsTool
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
i18n_catalog = i18nCatalog("uranium")
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
|
@ -8,6 +8,7 @@ from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.OutputDevice.OutputDevice import OutputDevice
|
||||
from UM.OutputDevice import OutputDeviceError
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
@ -24,14 +25,19 @@ class RemovableDriveOutputDevice(OutputDevice):
|
||||
|
||||
self._writing = False
|
||||
|
||||
def requestWrite(self, node, file_name = None):
|
||||
def requestWrite(self, node, file_name = None, filter_by_machine = False):
|
||||
if self._writing:
|
||||
raise OutputDeviceError.DeviceBusyError()
|
||||
|
||||
gcode_writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType("text/x-gcode")
|
||||
if not gcode_writer:
|
||||
Logger.log("e", "Could not find GCode writer, not writing to removable drive %s", self.getName())
|
||||
file_formats = Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite() #Formats supported by this application.
|
||||
if filter_by_machine:
|
||||
machine_file_formats = Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition().getFileFormats()
|
||||
file_formats = list(filter(lambda file_format: file_format["mime_type"] in machine_file_formats, file_formats)) #Take the intersection between file_formats and machine_file_formats.
|
||||
if len(file_formats) == 0:
|
||||
Logger.log("e", "There are no file formats available to write with!")
|
||||
raise OutputDeviceError.WriteRequestFailedError()
|
||||
writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType(file_formats[0]["mime_type"]) #Just take the first file format available.
|
||||
extension = file_formats[0]["extension"]
|
||||
|
||||
if file_name == None:
|
||||
for n in BreadthFirstIterator(node):
|
||||
@ -44,12 +50,14 @@ class RemovableDriveOutputDevice(OutputDevice):
|
||||
Logger.log("e", "Could not determine a proper file name when trying to write to %s, aborting", self.getName())
|
||||
raise OutputDeviceError.WriteRequestFailedError()
|
||||
|
||||
file_name = os.path.join(self.getId(), os.path.splitext(file_name)[0] + ".gcode")
|
||||
if extension: #Not empty string.
|
||||
extension = "." + extension
|
||||
file_name = os.path.join(self.getId(), os.path.splitext(file_name)[0] + extension)
|
||||
|
||||
try:
|
||||
Logger.log("d", "Writing to %s", file_name)
|
||||
stream = open(file_name, "wt")
|
||||
job = WriteMeshJob(gcode_writer, stream, node, MeshWriter.OutputMode.TextMode)
|
||||
job = WriteMeshJob(writer, stream, node, MeshWriter.OutputMode.TextMode)
|
||||
job.setFileName(file_name)
|
||||
job.progress.connect(self._onProgress)
|
||||
job.finished.connect(self._onFinished)
|
||||
|
@ -44,7 +44,7 @@ class SliceInfo(Extension):
|
||||
def _onWriteStarted(self, output_device):
|
||||
if not Preferences.getInstance().getValue("info/send_slice_info"):
|
||||
return # Do nothing, user does not want to send data
|
||||
settings = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
settings = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
# Load all machine definitions and put them in machine_settings dict
|
||||
#setting_file_name = Application.getInstance().getActiveMachineInstance().getMachineSettings()._json_file
|
||||
|
@ -33,14 +33,17 @@ class SolidView(View):
|
||||
if not self._disabled_shader:
|
||||
self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
|
||||
self._disabled_shader.setUniformValue("u_diffuseColor", [0.68, 0.68, 0.68, 1.0])
|
||||
self._disabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
||||
|
||||
if Application.getInstance().getMachineManager().getActiveProfile():
|
||||
profile = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
if Application.getInstance().getMachineManager().getWorkingProfile():
|
||||
profile = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
if profile.getSettingValue("support_enable") or not Preferences.getInstance().getValue("view/show_overhang"):
|
||||
if Preferences.getInstance().getValue("view/show_overhang"):
|
||||
angle = profile.getSettingValue("support_angle")
|
||||
if angle != None:
|
||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - angle)))
|
||||
else:
|
||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
|
||||
else:
|
||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
||||
|
||||
|
@ -16,7 +16,8 @@ def getMetaData():
|
||||
"api": 2
|
||||
},
|
||||
"view": {
|
||||
"name": i18n_catalog.i18nc("@item:inmenu", "Solid")
|
||||
"name": i18n_catalog.i18nc("@item:inmenu", "Solid"),
|
||||
"weight": 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ UM.Dialog
|
||||
anchors.fill: parent;
|
||||
Row
|
||||
{
|
||||
spacing: UM.Theme.sizes.default_margin.width;
|
||||
spacing: UM.Theme.getSize("default_margin").width;
|
||||
Text
|
||||
{
|
||||
//: USB Printing dialog label, %1 is head temperature
|
||||
|
@ -52,12 +52,13 @@ UM.Dialog
|
||||
wrapMode: Text.Wrap;
|
||||
}
|
||||
|
||||
ProgressBar
|
||||
ProgressBar
|
||||
{
|
||||
id: prog;
|
||||
id: prog
|
||||
value: manager.progress
|
||||
minimumValue: 0;
|
||||
maximumValue: 100;
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
indeterminate: manager.progress < 100
|
||||
anchors
|
||||
{
|
||||
left: parent.left;
|
||||
@ -65,7 +66,7 @@ UM.Dialog
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
SystemPalette
|
||||
{
|
||||
id: palette;
|
||||
|
@ -98,7 +98,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
||||
@pyqtSlot()
|
||||
def updateAllFirmware(self):
|
||||
if not self._printer_connections:
|
||||
Message("Cannot update firmware, there were no connected printers found.").show()
|
||||
Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show()
|
||||
return
|
||||
|
||||
self.spawnFirmwareInterface("")
|
||||
@ -106,6 +106,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
||||
try:
|
||||
self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
|
||||
except FileNotFoundError:
|
||||
self._printer_connections[printer_connection].setProgress(100, 100)
|
||||
Logger.log("w", "No firmware found for printer %s", printer_connection)
|
||||
continue
|
||||
|
||||
@ -132,31 +133,38 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension):
|
||||
return USBPrinterManager._instance
|
||||
|
||||
def _getDefaultFirmwareName(self):
|
||||
machine_type = Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition().getId()
|
||||
firmware_name = ""
|
||||
machine_instance = Application.getInstance().getMachineManager().getActiveMachineInstance()
|
||||
machine_type = machine_instance.getMachineDefinition().getId()
|
||||
baudrate = 250000
|
||||
if sys.platform.startswith("linux"):
|
||||
baudrate = 115200
|
||||
if machine_type == "ultimaker_original":
|
||||
firmware_name = "MarlinUltimaker"
|
||||
if machine_instance.getMachineSettingValue("machine_heated_bed"): #Has heated bed upgrade kit?
|
||||
firmware_name += "-HBK"
|
||||
firmware_name += "-%d" % (baudrate)
|
||||
return firmware_name + ".hex"
|
||||
elif machine_type == "ultimaker_original_plus":
|
||||
firmware_name = "MarlinUltimaker-UMOP-%d" % (baudrate)
|
||||
elif machine_type == "Witbox":
|
||||
return firmware_name + ".hex"
|
||||
elif machine_type == "bq_witbox":
|
||||
return "MarlinWitbox.hex"
|
||||
elif machine_type == "ultimaker2go":
|
||||
elif machine_type == "ultimaker2_go":
|
||||
return "MarlinUltimaker2go.hex"
|
||||
elif machine_type == "ultimaker2extended":
|
||||
elif machine_type == "ultimaker2_extended":
|
||||
return "MarlinUltimaker2extended.hex"
|
||||
elif machine_type == "ultimaker2":
|
||||
return "MarlinUltimaker2.hex"
|
||||
elif machine_type == "ultimaker2plus":
|
||||
return "MarlinUltimaker2plus.hex"
|
||||
elif machine_type == "ultimaker2_extended_plus":
|
||||
return "MarlinUltimaker2extended-plus.hex"
|
||||
else:
|
||||
Logger.log("e", "I don't know of any firmware for machine %s.", machine_type)
|
||||
raise FileNotFoundError()
|
||||
|
||||
##TODO: Add check for multiple extruders
|
||||
|
||||
if firmware_name != "":
|
||||
firmware_name += ".hex"
|
||||
return firmware_name
|
||||
|
||||
def _addRemovePorts(self, serial_ports):
|
||||
# First, find and add all new or changed keys
|
||||
for serial_port in list(serial_ports):
|
||||
|
@ -16,7 +16,8 @@ def getMetaData():
|
||||
"api": 2
|
||||
},
|
||||
"view": {
|
||||
"name": catalog.i18nc("@item:inlistbox", "X-Ray")
|
||||
"name": catalog.i18nc("@item:inlistbox", "X-Ray"),
|
||||
"weight": 1
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1757
resources/i18n/de/cura.po
Executable file → Normal file
1757
resources/i18n/de/cura.po
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
924
resources/i18n/de/fdmprinter.json.po
Executable file → Normal file
924
resources/i18n/de/fdmprinter.json.po
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
1320
resources/i18n/en/cura.po
Normal file
1320
resources/i18n/en/cura.po
Normal file
File diff suppressed because it is too large
Load Diff
2485
resources/i18n/en/fdmprinter.json.po
Normal file
2485
resources/i18n/en/fdmprinter.json.po
Normal file
File diff suppressed because it is too large
Load Diff
1320
resources/i18n/es/cura.po
Normal file
1320
resources/i18n/es/cura.po
Normal file
File diff suppressed because it is too large
Load Diff
2485
resources/i18n/es/fdmprinter.json.po
Normal file
2485
resources/i18n/es/fdmprinter.json.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Uranium json setting files\n"
|
||||
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
|
||||
"POT-Creation-Date: 2015-09-12 20:10+0000\n"
|
||||
"POT-Creation-Date: 2016-01-18 11:54+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE\n"
|
||||
@ -11,6 +11,21 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "machine label"
|
||||
msgid "Machine"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "machine_nozzle_size label"
|
||||
msgid "Nozzle Diameter"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "machine_nozzle_size description"
|
||||
msgid "The inner diameter of the nozzle."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "resolution label"
|
||||
msgid "Quality"
|
||||
@ -184,8 +199,8 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "wall_line_count description"
|
||||
msgid ""
|
||||
"Number of shell lines. This these lines are called perimeter lines in other "
|
||||
"tools and impact the strength and structural integrity of your print."
|
||||
"Number of shell lines. These lines are called perimeter lines in other tools "
|
||||
"and impact the strength and structural integrity of your print."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -209,9 +224,9 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "top_bottom_thickness description"
|
||||
msgid ""
|
||||
"This controls the thickness of the bottom and top layers, the amount of "
|
||||
"solid layers put down is calculated by the layer thickness and this value. "
|
||||
"Having this value a multiple of the layer thickness makes sense. And keep it "
|
||||
"This controls the thickness of the bottom and top layers. The number of "
|
||||
"solid layers put down is calculated from the layer thickness and this value. "
|
||||
"Having this value a multiple of the layer thickness makes sense. Keep it "
|
||||
"near your wall thickness to make an evenly strong part."
|
||||
msgstr ""
|
||||
|
||||
@ -225,8 +240,8 @@ msgctxt "top_thickness description"
|
||||
msgid ""
|
||||
"This controls the thickness of the top layers. The number of solid layers "
|
||||
"printed is calculated from the layer thickness and this value. Having this "
|
||||
"value be a multiple of the layer thickness makes sense. And keep it nearto "
|
||||
"your wall thickness to make an evenly strong part."
|
||||
"value be a multiple of the layer thickness makes sense. Keep it near your "
|
||||
"wall thickness to make an evenly strong part."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -236,7 +251,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "top_layers description"
|
||||
msgid "This controls the amount of top layers."
|
||||
msgid "This controls the number of top layers."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -351,7 +366,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "top_bottom_pattern description"
|
||||
msgid ""
|
||||
"Pattern of the top/bottom solid fill. This normally is done with lines to "
|
||||
"Pattern of the top/bottom solid fill. This is normally done with lines to "
|
||||
"get the best possible finish, but in some cases a concentric fill gives a "
|
||||
"nicer end result."
|
||||
msgstr ""
|
||||
@ -373,13 +388,13 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "skin_no_small_gaps_heuristic label"
|
||||
msgid "Ingore small Z gaps"
|
||||
msgid "Ignore small Z gaps"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "skin_no_small_gaps_heuristic description"
|
||||
msgid ""
|
||||
"When the model has small vertical gaps about 5% extra computation time can "
|
||||
"When the model has small vertical gaps, about 5% extra computation time can "
|
||||
"be spent on generating top and bottom skin in these narrow spaces. In such a "
|
||||
"case set this setting to false."
|
||||
msgstr ""
|
||||
@ -394,19 +409,19 @@ msgctxt "skin_alternate_rotation description"
|
||||
msgid ""
|
||||
"Alternate between diagonal skin fill and horizontal + vertical skin fill. "
|
||||
"Although the diagonal directions can print quicker, this option can improve "
|
||||
"on the printing quality by reducing the pillowing effect."
|
||||
"the printing quality by reducing the pillowing effect."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "skin_outline_count label"
|
||||
msgid "Skin Perimeter Line Count"
|
||||
msgid "Extra Skin Wall Count"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "skin_outline_count description"
|
||||
msgid ""
|
||||
"Number of lines around skin regions. Using one or two skin perimeter lines "
|
||||
"can greatly improve on roofs which would start in the middle of infill cells."
|
||||
"can greatly improve roofs which would start in the middle of infill cells."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -417,7 +432,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "xy_offset description"
|
||||
msgid ""
|
||||
"Amount of offset applied all polygons in each layer. Positive values can "
|
||||
"Amount of offset applied to all polygons in each layer. Positive values can "
|
||||
"compensate for too big holes; negative values can compensate for too small "
|
||||
"holes."
|
||||
msgstr ""
|
||||
@ -430,11 +445,11 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "z_seam_type description"
|
||||
msgid ""
|
||||
"Starting point of each part in a layer. When parts in consecutive layers "
|
||||
"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 part start will be less noticable. When taking the "
|
||||
"shortest path the print will be more quick."
|
||||
"inaccuracies at the paths' start will be less noticeable. When taking the "
|
||||
"shortest path the print will be quicker."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -466,8 +481,8 @@ msgstr ""
|
||||
msgctxt "infill_sparse_density description"
|
||||
msgid ""
|
||||
"This controls how densely filled the insides of your print will be. For a "
|
||||
"solid part use 100%, for an hollow part use 0%. A value around 20% is "
|
||||
"usually enough. This won't affect the outside of the print and only adjusts "
|
||||
"solid part use 100%, for a hollow part use 0%. A value around 20% is usually "
|
||||
"enough. This setting won't affect the outside of the print and only adjusts "
|
||||
"how strong the part becomes."
|
||||
msgstr ""
|
||||
|
||||
@ -489,7 +504,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "infill_pattern description"
|
||||
msgid ""
|
||||
"Cura defaults to switching between grid and line infill. But with this "
|
||||
"Cura defaults to switching between grid and line infill, but with this "
|
||||
"setting visible you can control this yourself. The line infill swaps "
|
||||
"direction on alternate layers of infill, while the grid prints the full "
|
||||
"cross-hatching on each layer of infill."
|
||||
@ -505,6 +520,11 @@ msgctxt "infill_pattern option lines"
|
||||
msgid "Lines"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "infill_pattern option triangles"
|
||||
msgid "Triangles"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "infill_pattern option concentric"
|
||||
msgid "Concentric"
|
||||
@ -536,7 +556,7 @@ msgstr ""
|
||||
msgctxt "infill_wipe_dist description"
|
||||
msgid ""
|
||||
"Distance of a travel move inserted after every infill line, to make the "
|
||||
"infill stick to the walls better. This option is imilar to infill overlap, "
|
||||
"infill stick to the walls better. This option is similar to infill overlap, "
|
||||
"but without extrusion and only on one end of the infill line."
|
||||
msgstr ""
|
||||
|
||||
@ -553,16 +573,6 @@ msgid ""
|
||||
"save printing time."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "infill_sparse_combine label"
|
||||
msgid "Infill Layers"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "infill_sparse_combine description"
|
||||
msgid "Amount of layers that are combined together to form sparse infill."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "infill_before_walls label"
|
||||
msgid "Infill Before Walls"
|
||||
@ -582,6 +592,18 @@ msgctxt "material label"
|
||||
msgid "Material"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_flow_dependent_temperature label"
|
||||
msgid "Auto Temperature"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_flow_dependent_temperature description"
|
||||
msgid ""
|
||||
"Change the temperature for each layer automatically with the average flow "
|
||||
"speed of that layer."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_print_temperature label"
|
||||
msgid "Printing Temperature"
|
||||
@ -595,6 +617,42 @@ msgid ""
|
||||
"For ABS a value of 230C or higher is required."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_flow_temp_graph label"
|
||||
msgid "Flow Temperature Graph"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_flow_temp_graph description"
|
||||
msgid ""
|
||||
"Data linking material flow (in mm3 per second) to temperature (degrees "
|
||||
"Celsius)."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_standby_temperature label"
|
||||
msgid "Standby Temperature"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_standby_temperature description"
|
||||
msgid ""
|
||||
"The temperature of the nozzle when another nozzle is currently used for "
|
||||
"printing."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_extrusion_cool_down_speed label"
|
||||
msgid "Extrusion Cool Down Speed Modifier"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_extrusion_cool_down_speed description"
|
||||
msgid ""
|
||||
"The extra speed by which the nozzle cools while extruding. The same value is "
|
||||
"used to signify the heat up speed lost when heating up while extruding."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "material_bed_temperature label"
|
||||
msgid "Bed Temperature"
|
||||
@ -617,7 +675,7 @@ msgctxt "material_diameter description"
|
||||
msgid ""
|
||||
"The diameter of your filament needs to be measured as accurately as "
|
||||
"possible.\n"
|
||||
"If you cannot measure this value you will have to calibrate it, a higher "
|
||||
"If you cannot measure this value you will have to calibrate it; a higher "
|
||||
"number means less extrusion, a smaller number generates more extrusion."
|
||||
msgstr ""
|
||||
|
||||
@ -654,7 +712,7 @@ msgstr ""
|
||||
msgctxt "retraction_amount description"
|
||||
msgid ""
|
||||
"The amount of retraction: Set at 0 for no retraction at all. A value of "
|
||||
"4.5mm seems to generate good results for 3mm filament in Bowden-tube fed "
|
||||
"4.5mm seems to generate good results for 3mm filament in bowden tube fed "
|
||||
"printers."
|
||||
msgstr ""
|
||||
|
||||
@ -700,8 +758,8 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "retraction_extra_prime_amount description"
|
||||
msgid ""
|
||||
"The amount of material extruded after unretracting. During a retracted "
|
||||
"travel material might get lost and so we need to compensate for this."
|
||||
"The amount of material extruded after a retraction. During a travel move, "
|
||||
"some material might get lost and so we need to compensate for this."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -713,33 +771,33 @@ msgstr ""
|
||||
msgctxt "retraction_min_travel description"
|
||||
msgid ""
|
||||
"The minimum distance of travel needed for a retraction to happen at all. "
|
||||
"This helps ensure you do not get a lot of retractions in a small area."
|
||||
"This helps to get fewer retractions in a small area."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "retraction_count_max label"
|
||||
msgid "Maximal Retraction Count"
|
||||
msgid "Maximum Retraction Count"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "retraction_count_max description"
|
||||
msgid ""
|
||||
"This settings limits the number of retractions occuring within the Minimal "
|
||||
"This setting limits the number of retractions occurring within the Minimum "
|
||||
"Extrusion Distance Window. Further retractions within this window will be "
|
||||
"ignored. This avoids retracting repeatedly on the same piece of filament as "
|
||||
"ignored. This avoids retracting repeatedly on the same piece of filament, as "
|
||||
"that can flatten the filament and cause grinding issues."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "retraction_extrusion_window label"
|
||||
msgid "Minimal Extrusion Distance Window"
|
||||
msgid "Minimum Extrusion Distance Window"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "retraction_extrusion_window description"
|
||||
msgid ""
|
||||
"The window in which the Maximal Retraction Count is enforced. This window "
|
||||
"should be approximately the size of the Retraction distance, so that "
|
||||
"The window in which the Maximum Retraction Count is enforced. This value "
|
||||
"should be approximately the same as the Retraction distance, so that "
|
||||
"effectively the number of times a retraction passes the same patch of "
|
||||
"material is limited."
|
||||
msgstr ""
|
||||
@ -753,7 +811,7 @@ msgstr ""
|
||||
msgctxt "retraction_hop description"
|
||||
msgid ""
|
||||
"Whenever a retraction is done, the head is lifted by this amount to travel "
|
||||
"over the print. A value of 0.075 works well. This feature has a lot of "
|
||||
"over the print. A value of 0.075 works well. This feature has a large "
|
||||
"positive effect on delta towers."
|
||||
msgstr ""
|
||||
|
||||
@ -796,7 +854,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "speed_wall description"
|
||||
msgid ""
|
||||
"The speed at which shell is printed. Printing the outer shell at a lower "
|
||||
"The speed at which the shell is printed. Printing the outer shell at a lower "
|
||||
"speed improves the final skin quality."
|
||||
msgstr ""
|
||||
|
||||
@ -808,7 +866,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "speed_wall_0 description"
|
||||
msgid ""
|
||||
"The speed at which outer shell is printed. Printing the outer shell at a "
|
||||
"The speed at which the outer shell is printed. Printing the outer shell at a "
|
||||
"lower speed improves the final skin quality. However, having a large "
|
||||
"difference between the inner shell speed and the outer shell speed will "
|
||||
"effect quality in a negative way."
|
||||
@ -822,8 +880,8 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "speed_wall_x description"
|
||||
msgid ""
|
||||
"The speed at which all inner shells are printed. Printing the inner shell "
|
||||
"fasster than the outer shell will reduce printing time. It is good to set "
|
||||
"The speed at which all inner shells are printed. Printing the inner shell "
|
||||
"faster than the outer shell will reduce printing time. It works well to set "
|
||||
"this in between the outer shell speed and the infill speed."
|
||||
msgstr ""
|
||||
|
||||
@ -849,8 +907,9 @@ msgstr ""
|
||||
msgctxt "speed_support description"
|
||||
msgid ""
|
||||
"The speed at which exterior support is printed. Printing exterior supports "
|
||||
"at higher speeds can greatly improve printing time. And the surface quality "
|
||||
"of exterior support is usually not important, so higher speeds can be used."
|
||||
"at higher speeds can greatly improve printing time. The surface quality of "
|
||||
"exterior support is usually not important anyway, so higher speeds can be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -862,7 +921,7 @@ msgstr ""
|
||||
msgctxt "speed_support_lines description"
|
||||
msgid ""
|
||||
"The speed at which the walls of exterior support are printed. Printing the "
|
||||
"walls at higher speeds can improve on the overall duration. "
|
||||
"walls at higher speeds can improve the overall duration."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -874,7 +933,7 @@ msgstr ""
|
||||
msgctxt "speed_support_roof description"
|
||||
msgid ""
|
||||
"The speed at which the roofs of exterior support are printed. Printing the "
|
||||
"support roof at lower speeds can improve on overhang quality. "
|
||||
"support roof at lower speeds can improve overhang quality."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -886,7 +945,7 @@ msgstr ""
|
||||
msgctxt "speed_travel description"
|
||||
msgid ""
|
||||
"The speed at which travel moves are done. A well-built Ultimaker can reach "
|
||||
"speeds of 250mm/s. But some machines might have misaligned layers then."
|
||||
"speeds of 250mm/s, but some machines might have misaligned layers then."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -898,7 +957,7 @@ msgstr ""
|
||||
msgctxt "speed_layer_0 description"
|
||||
msgid ""
|
||||
"The print speed for the bottom layer: You want to print the first layer "
|
||||
"slower so it sticks to the printer bed better."
|
||||
"slower so it sticks better to the printer bed."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -910,19 +969,19 @@ msgstr ""
|
||||
msgctxt "skirt_speed description"
|
||||
msgid ""
|
||||
"The speed at which the skirt and brim are printed. Normally this is done at "
|
||||
"the initial layer speed. But sometimes you want to print the skirt at a "
|
||||
"different speed."
|
||||
"the initial layer speed, but sometimes you might want to print the skirt at "
|
||||
"a different speed."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "speed_slowdown_layers label"
|
||||
msgid "Amount of Slower Layers"
|
||||
msgid "Number of Slower Layers"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "speed_slowdown_layers description"
|
||||
msgid ""
|
||||
"The first few layers are printed slower then the rest of the object, this to "
|
||||
"The first few layers are printed slower than the rest of the object, this to "
|
||||
"get better adhesion to the printer bed and improve the overall success rate "
|
||||
"of prints. The speed is gradually increased over these layers. 4 layers of "
|
||||
"speed-up is generally right for most materials and printers."
|
||||
@ -942,8 +1001,8 @@ msgstr ""
|
||||
msgctxt "retraction_combing description"
|
||||
msgid ""
|
||||
"Combing keeps the head within the interior of the print whenever possible "
|
||||
"when traveling from one part of the print to another, and does not use "
|
||||
"retraction. If combing is disabled the printer head moves straight from the "
|
||||
"when traveling from one part of the print to another and does not use "
|
||||
"retraction. If combing is disabled, the print head moves straight from the "
|
||||
"start point to the end point and it will always retract."
|
||||
msgstr ""
|
||||
|
||||
@ -992,26 +1051,6 @@ msgid ""
|
||||
"nozzle diameter cubed."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_volume_retract label"
|
||||
msgid "Retract-Coasting Volume"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_volume_retract description"
|
||||
msgid "The volume otherwise oozed in a travel move with retraction."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_volume_move label"
|
||||
msgid "Move-Coasting Volume"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_volume_move description"
|
||||
msgid "The volume otherwise oozed in a travel move without retraction."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_min_volume label"
|
||||
msgid "Minimal Volume Before Coasting"
|
||||
@ -1022,31 +1061,8 @@ msgctxt "coasting_min_volume description"
|
||||
msgid ""
|
||||
"The least volume an extrusion path should have to coast the full amount. For "
|
||||
"smaller extrusion paths, less pressure has been built up in the bowden tube "
|
||||
"and so the coasted volume is scaled linearly."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_min_volume_retract label"
|
||||
msgid "Min Volume Retract-Coasting"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_min_volume_retract description"
|
||||
msgid ""
|
||||
"The minimal volume an extrusion path must have in order to coast the full "
|
||||
"amount before doing a retraction."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_min_volume_move label"
|
||||
msgid "Min Volume Move-Coasting"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_min_volume_move description"
|
||||
msgid ""
|
||||
"The minimal volume an extrusion path must have in order to coast the full "
|
||||
"amount before doing a travel move without retraction."
|
||||
"and so the coasted volume is scaled linearly. This value should always be "
|
||||
"larger than the Coasting Volume."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1059,31 +1075,7 @@ msgctxt "coasting_speed description"
|
||||
msgid ""
|
||||
"The speed by which to move during coasting, relative to the speed of the "
|
||||
"extrusion path. A value slightly under 100% is advised, since during the "
|
||||
"coasting move, the pressure in the bowden tube drops."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_speed_retract label"
|
||||
msgid "Retract-Coasting Speed"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_speed_retract description"
|
||||
msgid ""
|
||||
"The speed by which to move during coasting before a retraction, relative to "
|
||||
"the speed of the extrusion path."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_speed_move label"
|
||||
msgid "Move-Coasting Speed"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "coasting_speed_move description"
|
||||
msgid ""
|
||||
"The speed by which to move during coasting before a travel move without "
|
||||
"retraction, relative to the speed of the extrusion path."
|
||||
"coasting move the pressure in the bowden tube drops."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1166,7 +1158,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "cool_min_layer_time label"
|
||||
msgid "Minimal Layer Time"
|
||||
msgid "Minimum Layer Time"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1180,16 +1172,16 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "cool_min_layer_time_fan_speed_max label"
|
||||
msgid "Minimal Layer Time Full Fan Speed"
|
||||
msgid "Minimum Layer Time Full Fan Speed"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "cool_min_layer_time_fan_speed_max description"
|
||||
msgid ""
|
||||
"The minimum time spent in a layer which will cause the fan to be at maximum "
|
||||
"speed. The fan speed increases linearly from minimal fan speed for layers "
|
||||
"taking minimal layer time to maximum fan speed for layers taking the time "
|
||||
"specified here."
|
||||
"speed. The fan speed increases linearly from minimum fan speed for layers "
|
||||
"taking the minimum layer time to maximum fan speed for layers taking the "
|
||||
"time specified here."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1243,7 +1235,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_type description"
|
||||
msgid ""
|
||||
"Where to place support structures. The placement can be restricted such that "
|
||||
"Where to place support structures. The placement can be restricted so that "
|
||||
"the support structures won't rest on the model, which could otherwise cause "
|
||||
"scarring."
|
||||
msgstr ""
|
||||
@ -1279,7 +1271,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_xy_distance description"
|
||||
msgid ""
|
||||
"Distance of the support structure from the print, in the X/Y directions. "
|
||||
"Distance of the support structure from the print in the X/Y directions. "
|
||||
"0.7mm typically gives a nice distance from the print so the support does not "
|
||||
"stick to the surface."
|
||||
msgstr ""
|
||||
@ -1352,7 +1344,7 @@ msgstr ""
|
||||
msgctxt "support_conical_min_width description"
|
||||
msgid ""
|
||||
"Minimal width to which conical support reduces the support areas. Small "
|
||||
"widths can cause the base of the support to not act well as fundament for "
|
||||
"widths can cause the base of the support to not act well as foundation for "
|
||||
"support above."
|
||||
msgstr ""
|
||||
|
||||
@ -1377,8 +1369,8 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_join_distance description"
|
||||
msgid ""
|
||||
"The maximum distance between support blocks, in the X/Y directions, such "
|
||||
"that the blocks will merge into a single block."
|
||||
"The maximum distance between support blocks in the X/Y directions, so that "
|
||||
"the blocks will merge into a single block."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1401,7 +1393,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_area_smoothing description"
|
||||
msgid ""
|
||||
"Maximal distance in the X/Y directions of a line segment which is to be "
|
||||
"Maximum distance in the X/Y directions of a line segment which is to be "
|
||||
"smoothed out. Ragged lines are introduced by the join distance and support "
|
||||
"bridge, which cause the machine to resonate. Smoothing the support areas "
|
||||
"won't cause them to break with the constraints, except it might change the "
|
||||
@ -1426,7 +1418,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_roof_height description"
|
||||
msgid "The height of the support roofs. "
|
||||
msgid "The height of the support roofs."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1438,7 +1430,8 @@ msgstr ""
|
||||
msgctxt "support_roof_density description"
|
||||
msgid ""
|
||||
"This controls how densely filled the roofs of the support will be. A higher "
|
||||
"percentage results in better overhangs, which are more difficult to remove."
|
||||
"percentage results in better overhangs, but makes the support more difficult "
|
||||
"to remove."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1488,7 +1481,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_use_towers label"
|
||||
msgid "Use towers."
|
||||
msgid "Use towers"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1501,14 +1494,14 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_minimal_diameter label"
|
||||
msgid "Minimal Diameter"
|
||||
msgid "Minimum Diameter"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_minimal_diameter description"
|
||||
msgid ""
|
||||
"Maximal diameter in the X/Y directions of a small area which is to be "
|
||||
"supported by a specialized support tower. "
|
||||
"Minimum diameter in the X/Y directions of a small area which is to be "
|
||||
"supported by a specialized support tower."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1518,7 +1511,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_tower_diameter description"
|
||||
msgid "The diameter of a special tower. "
|
||||
msgid "The diameter of a special tower."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1529,7 +1522,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_tower_roof_angle description"
|
||||
msgid ""
|
||||
"The angle of the rooftop of a tower. Larger angles mean more pointy towers. "
|
||||
"The angle of the rooftop of a tower. Larger angles mean more pointy towers."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1540,11 +1533,11 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_pattern description"
|
||||
msgid ""
|
||||
"Cura supports 3 distinct types of support structure. First is a grid based "
|
||||
"support structure which is quite solid and can be removed as 1 piece. The "
|
||||
"second is a line based support structure which has to be peeled off line by "
|
||||
"line. The third is a structure in between the other two; it consists of "
|
||||
"lines which are connected in an accordeon fashion."
|
||||
"Cura can generate 3 distinct types of support structure. First is a grid "
|
||||
"based support structure which is quite solid and can be removed in one "
|
||||
"piece. The second is a line based support structure which has to be peeled "
|
||||
"off line by line. The third is a structure in between the other two; it "
|
||||
"consists of lines which are connected in an accordion fashion."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1592,7 +1585,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "support_infill_rate description"
|
||||
msgid ""
|
||||
"The amount of infill structure in the support, less infill gives weaker "
|
||||
"The amount of infill structure in the support; less infill gives weaker "
|
||||
"support which is easier to remove."
|
||||
msgstr ""
|
||||
|
||||
@ -1619,11 +1612,14 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "adhesion_type description"
|
||||
msgid ""
|
||||
"Different options that help in preventing corners from lifting due to "
|
||||
"warping. Brim adds a single-layer-thick flat area around your object which "
|
||||
"is easy to cut off afterwards, and it is the recommended option. Raft adds a "
|
||||
"thick grid below the object and a thin interface between this and your "
|
||||
"object. (Note that enabling the brim or raft disables the skirt.)"
|
||||
"Different options that help to improve priming your extrusion.\n"
|
||||
"Brim and Raft help in preventing corners from lifting due to warping. Brim "
|
||||
"adds a single-layer-thick flat area around your object which is easy to cut "
|
||||
"off afterwards, and it is the recommended option.\n"
|
||||
"Raft adds a thick grid below the object and a thin interface between this "
|
||||
"and your object.\n"
|
||||
"The skirt is a line drawn around the first layer of the print, this helps to "
|
||||
"prime your extrusion and to see if the object fits on your platform."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1649,10 +1645,8 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "skirt_line_count description"
|
||||
msgid ""
|
||||
"The skirt is a line drawn around the first layer of the. This helps to prime "
|
||||
"your extruder, and to see if the object fits on your platform. Setting this "
|
||||
"to 0 will disable the skirt. Multiple skirt lines can help to prime your "
|
||||
"extruder better for small objects."
|
||||
"Multiple skirt lines help to prime your extrusion better for small objects. "
|
||||
"Setting this to 0 will disable the skirt."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1681,6 +1675,19 @@ msgid ""
|
||||
"count is set to 0 this is ignored."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "brim_width label"
|
||||
msgid "Brim Width"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "brim_width description"
|
||||
msgid ""
|
||||
"The distance from the model to the end of the brim. A larger brim sticks "
|
||||
"better to the build platform, but also makes your effective print area "
|
||||
"smaller."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "brim_line_count label"
|
||||
msgid "Brim Line Count"
|
||||
@ -1689,8 +1696,9 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "brim_line_count description"
|
||||
msgid ""
|
||||
"The amount of lines used for a brim: More lines means a larger brim which "
|
||||
"sticks better, but this also makes your effective print area smaller."
|
||||
"The number of lines used for a brim. More lines means a larger brim which "
|
||||
"sticks better to the build plate, but this also makes your effective print "
|
||||
"area smaller."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1721,84 +1729,84 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_layers label"
|
||||
msgid "Raft Surface Layers"
|
||||
msgid "Raft Top Layers"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_layers description"
|
||||
msgid ""
|
||||
"The number of surface layers on top of the 2nd raft layer. These are fully "
|
||||
"filled layers that the object sits on. 2 layers usually works fine."
|
||||
"The number of top layers on top of the 2nd raft layer. These are fully "
|
||||
"filled layers that the object sits on. 2 layers result in a smoother top "
|
||||
"surface than 1."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_thickness label"
|
||||
msgid "Raft Surface Thickness"
|
||||
msgid "Raft Top Layer Thickness"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_thickness description"
|
||||
msgid "Layer thickness of the surface raft layers."
|
||||
msgid "Layer thickness of the top raft layers."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_line_width label"
|
||||
msgid "Raft Surface Line Width"
|
||||
msgid "Raft Top Line Width"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_line_width description"
|
||||
msgid ""
|
||||
"Width of the lines in the surface raft layers. These can be thin lines so "
|
||||
"that the top of the raft becomes smooth."
|
||||
"Width of the lines in the top surface of the raft. These can be thin lines "
|
||||
"so that the top of the raft becomes smooth."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_line_spacing label"
|
||||
msgid "Raft Surface Spacing"
|
||||
msgid "Raft Top Spacing"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_line_spacing description"
|
||||
msgid ""
|
||||
"The distance between the raft lines for the surface raft layers. The spacing "
|
||||
"of the interface should be equal to the line width, so that the surface is "
|
||||
"solid."
|
||||
"The distance between the raft lines for the top raft layers. The spacing "
|
||||
"should be equal to the line width, so that the surface is solid."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_interface_thickness label"
|
||||
msgid "Raft Interface Thickness"
|
||||
msgid "Raft Middle Thickness"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_interface_thickness description"
|
||||
msgid "Layer thickness of the interface raft layer."
|
||||
msgid "Layer thickness of the middle raft layer."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_interface_line_width label"
|
||||
msgid "Raft Interface Line Width"
|
||||
msgid "Raft Middle Line Width"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_interface_line_width description"
|
||||
msgid ""
|
||||
"Width of the lines in the interface raft layer. Making the second layer "
|
||||
"extrude more causes the lines to stick to the bed."
|
||||
"Width of the lines in the middle raft layer. Making the second layer extrude "
|
||||
"more causes the lines to stick to the bed."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_interface_line_spacing label"
|
||||
msgid "Raft Interface Spacing"
|
||||
msgid "Raft Middle Spacing"
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_interface_line_spacing description"
|
||||
msgid ""
|
||||
"The distance between the raft lines for the interface raft layer. The "
|
||||
"spacing of the interface should be quite wide, while being dense enough to "
|
||||
"support the surface raft layers."
|
||||
"The distance between the raft lines for the middle raft layer. The spacing "
|
||||
"of the middle should be quite wide, while being dense enough to support the "
|
||||
"top raft layers."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1855,7 +1863,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "raft_surface_speed description"
|
||||
msgid ""
|
||||
"The speed at which the surface raft layers are printed. This should be "
|
||||
"The speed at which the surface raft layers are printed. These should be "
|
||||
"printed a bit slower, so that the nozzle can slowly smooth out adjacent "
|
||||
"surface lines."
|
||||
msgstr ""
|
||||
@ -1869,7 +1877,7 @@ msgstr ""
|
||||
msgctxt "raft_interface_speed description"
|
||||
msgid ""
|
||||
"The speed at which the interface raft layer is printed. This should be "
|
||||
"printed quite slowly, as the amount of material coming out of the nozzle is "
|
||||
"printed quite slowly, as the volume of material coming out of the nozzle is "
|
||||
"quite high."
|
||||
msgstr ""
|
||||
|
||||
@ -1882,7 +1890,7 @@ msgstr ""
|
||||
msgctxt "raft_base_speed description"
|
||||
msgid ""
|
||||
"The speed at which the base raft layer is printed. This should be printed "
|
||||
"quite slowly, as the amount of material coming out of the nozzle is quite "
|
||||
"quite slowly, as the volume of material coming out of the nozzle is quite "
|
||||
"high."
|
||||
msgstr ""
|
||||
|
||||
@ -1956,7 +1964,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "draft_shield_height_limitation description"
|
||||
msgid "Whether to limit the height of the draft shield"
|
||||
msgid "Whether or not to limit the height of the draft shield."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -1995,7 +2003,7 @@ msgstr ""
|
||||
msgctxt "meshfix_union_all description"
|
||||
msgid ""
|
||||
"Ignore the internal geometry arising from overlapping volumes and print the "
|
||||
"volumes as one. This may cause internal cavaties to disappear."
|
||||
"volumes as one. This may cause internal cavities to disappear."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2034,8 +2042,8 @@ msgctxt "meshfix_keep_open_polygons description"
|
||||
msgid ""
|
||||
"Normally Cura tries to stitch up small holes in the mesh and remove parts of "
|
||||
"a layer with big holes. Enabling this option keeps those parts which cannot "
|
||||
"be stitched. This option should be used as a last resort option when all "
|
||||
"else doesn produce proper GCode."
|
||||
"be stitched. This option should be used as a last resort option when "
|
||||
"everything else fails to produce proper GCode."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2053,9 +2061,9 @@ msgctxt "print_sequence description"
|
||||
msgid ""
|
||||
"Whether to print all objects one layer at a time or to wait for one object "
|
||||
"to finish, before moving on to the next. One at a time mode is only possible "
|
||||
"if all models are separated such that the whole print head can move between "
|
||||
"and all models are lower than the distance between the nozzle and the X/Y "
|
||||
"axles."
|
||||
"if all models are separated in such a way that the whole print head can move "
|
||||
"in between and all models are lower than the distance between the nozzle and "
|
||||
"the X/Y axes."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2108,7 +2116,7 @@ msgid ""
|
||||
"Spiralize smooths out the Z move of the outer edge. This will create a "
|
||||
"steady Z increase over the whole print. This feature turns a solid object "
|
||||
"into a single walled print with a solid bottom. This feature used to be "
|
||||
"called ‘Joris’ in older versions."
|
||||
"called Joris in older versions."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2311,9 +2319,7 @@ msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
msgctxt "wireframe_bottom_delay description"
|
||||
msgid ""
|
||||
"Delay time after a downward move. Only applies to Wire Printing. Only "
|
||||
"applies to Wire Printing."
|
||||
msgid "Delay time after a downward move. Only applies to Wire Printing."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2326,7 +2332,7 @@ msgctxt "wireframe_flat_delay description"
|
||||
msgid ""
|
||||
"Delay time between two horizontal segments. Introducing such a delay can "
|
||||
"cause better adhesion to previous layers at the connection points, while too "
|
||||
"large delay times cause sagging. Only applies to Wire Printing."
|
||||
"long delays cause sagging. Only applies to Wire Printing."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2391,10 +2397,10 @@ msgid ""
|
||||
"Strategy for making sure two consecutive layers connect at each connection "
|
||||
"point. Retraction lets the upward lines harden in the right position, but "
|
||||
"may cause filament grinding. A knot can be made at the end of an upward line "
|
||||
"to heighten the chance of connecting to it and to let the line cool; however "
|
||||
"it may require slow printing speeds. Another strategy is to compensate for "
|
||||
"the sagging of the top of an upward line; however, the lines won't always "
|
||||
"fall down as predicted."
|
||||
"to heighten the chance of connecting to it and to let the line cool; "
|
||||
"however, it may require slow printing speeds. Another strategy is to "
|
||||
"compensate for the sagging of the top of an upward line; however, the lines "
|
||||
"won't always fall down as predicted."
|
||||
msgstr ""
|
||||
|
||||
#: fdmprinter.json
|
||||
@ -2459,7 +2465,7 @@ msgstr ""
|
||||
#: fdmprinter.json
|
||||
msgctxt "wireframe_roof_outer_delay description"
|
||||
msgid ""
|
||||
"Time spent at the outer perimeters of hole which is to become a roof. Larger "
|
||||
"Time spent at the outer perimeters of hole which is to become a roof. Longer "
|
||||
"times can ensure a better connection. Only applies to Wire Printing."
|
||||
msgstr ""
|
||||
|
||||
|
2548
resources/i18n/fi/cura.po
Executable file → Normal file
2548
resources/i18n/fi/cura.po
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
5370
resources/i18n/fi/fdmprinter.json.po
Executable file → Normal file
5370
resources/i18n/fi/fdmprinter.json.po
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1320
resources/i18n/it/cura.po
Normal file
1320
resources/i18n/it/cura.po
Normal file
File diff suppressed because it is too large
Load Diff
2485
resources/i18n/it/fdmprinter.json.po
Normal file
2485
resources/i18n/it/fdmprinter.json.po
Normal file
File diff suppressed because it is too large
Load Diff
1320
resources/i18n/nl/cura.po
Normal file
1320
resources/i18n/nl/cura.po
Normal file
File diff suppressed because it is too large
Load Diff
2485
resources/i18n/nl/fdmprinter.json.po
Normal file
2485
resources/i18n/nl/fdmprinter.json.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
resources/images/Ultimaker2ExtendedPlusbackplate.png
Normal file
BIN
resources/images/Ultimaker2ExtendedPlusbackplate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
BIN
resources/images/Ultimaker2Plusbackplate.png
Normal file
BIN
resources/images/Ultimaker2Plusbackplate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -1,14 +1,14 @@
|
||||
{
|
||||
"id": "rigidbotbig",
|
||||
"id": "rigidbot",
|
||||
"version": 1,
|
||||
"name": "RigidBot",
|
||||
"manufacturer": "Other",
|
||||
"author": "RBC",
|
||||
"platform": "rigidbot_platform.stl",
|
||||
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
"machine_settings": {
|
||||
|
||||
"machine_width": { "default": 254 },
|
||||
"machine_depth": { "default": 254 },
|
||||
@ -32,8 +32,10 @@
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"layer_height": { "default": 0.2 },
|
||||
"shell_thickness": { "default": 0.8 },
|
||||
"wall_thickness": { "default": 0.8 },
|
||||
@ -49,8 +51,8 @@
|
||||
"speed_infill": { "default": 100.0, "visible": true },
|
||||
"speed_topbottom": { "default": 15.0, "visible": true },
|
||||
"speed_travel": { "default": 150.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": 0.1, "default": 15.0, "visible": true },
|
||||
"infill_overlap": { "default": 10.0 },
|
||||
"speed_layer_0": { "min_value": "0.1", "default": 15.0, "visible": true },
|
||||
"infill_overlap": { "default": 0.04, "inherit_function": "0.1 * line_width if infill_sparse_density < 95 else 0" },
|
||||
"cool_fan_enabled": { "default": false, "visible": true },
|
||||
"cool_fan_speed": { "default": 0.0, "visible": true },
|
||||
"skirt_line_count": { "default": 3, "active_if": { "setting": "adhesion_type", "value": "None" } },
|
||||
|
@ -5,10 +5,10 @@
|
||||
"manufacturer": "Other",
|
||||
"author": "RBC",
|
||||
"platform": "rigidbotbig_platform.stl",
|
||||
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
"machine_settings": {
|
||||
|
||||
"machine_width": { "default": 400 },
|
||||
"machine_depth": { "default": 300 },
|
||||
@ -30,8 +30,10 @@
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": ";End GCode\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+10 E-1 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y230 F3000 ;move Y so the head is out of the way and Plate is moved forward\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"layer_height": { "default": 0.2 },
|
||||
"shell_thickness": { "default": 0.8},
|
||||
"wall_thickness": { "default": 0.8 },
|
||||
@ -47,7 +49,7 @@
|
||||
"speed_infill": { "default": 100.0, "visible": true },
|
||||
"speed_topbottom": { "default": 15.0, "visible": true },
|
||||
"speed_travel": { "default": 150.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": 0.1, "default": 15.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": "0.1", "default": 15.0, "visible": true },
|
||||
"infill_overlap": { "default": 10.0 },
|
||||
"cool_fan_enabled": { "default": false, "visible": true},
|
||||
"cool_fan_speed": { "default": 0.0, "visible": true },
|
||||
|
@ -5,6 +5,7 @@
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_hephestos_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
|
@ -5,6 +5,7 @@
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_hephestos_2_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
@ -33,8 +34,8 @@
|
||||
"default": "RepRap"
|
||||
},
|
||||
"machine_platform_offset": {
|
||||
"default": [-6, 1320, 0]
|
||||
}
|
||||
"default": [6, 1320, 0]
|
||||
},
|
||||
"material_print_temperature": { "default": 210.0, "visible": true },
|
||||
"material_bed_temperature": { "default": 0 },
|
||||
"material_diameter": { "default": 1.75 },
|
||||
@ -45,7 +46,7 @@
|
||||
"wall_thickness": { "default": 1.2, "visible": false },
|
||||
"top_bottom_thickness": { "default": 1.2, "visible": false },
|
||||
"infill_sparse_density": { "default": 20.0 },
|
||||
"infill_overlap": { "default": 15.0, "visible": false },
|
||||
"infill_overlap": { "default": 0.06, "inherit_function": "0.15 * line_width if infill_sparse_density < 95 else 0", "visible": false },
|
||||
"speed_print": { "default": 60.0 },
|
||||
"speed_travel": { "default": 160.0 },
|
||||
"speed_layer_0": { "default": 30.0, "visible": true },
|
||||
|
@ -5,6 +5,7 @@
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_hephestos_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
|
@ -5,6 +5,7 @@
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_witbox_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
|
@ -5,6 +5,7 @@
|
||||
"manufacturer": "Other",
|
||||
"author": "BQ",
|
||||
"platform": "bq_witbox_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
@ -45,7 +46,7 @@
|
||||
"wall_thickness": { "default": 1.2, "visible": false },
|
||||
"top_bottom_thickness": { "default": 1.2, "visible": false },
|
||||
"infill_sparse_density": { "default": 20.0 },
|
||||
"infill_overlap": { "default": 15.0, "visible": false },
|
||||
"infill_overlap": { "default": 0.06, "inherit_function": "0.15 * line_width if infill_sparse_density < 95 else 0", "visible": false },
|
||||
"speed_print": { "default": 60.0 },
|
||||
"speed_travel": { "default": 160.0 },
|
||||
"speed_layer_0": { "default": 30.0, "visible": true },
|
||||
|
@ -2,12 +2,11 @@
|
||||
"version": 1,
|
||||
"id": "dual_extrusion",
|
||||
"name": "Dual Extrusion Base File",
|
||||
|
||||
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"visible": false,
|
||||
|
||||
|
||||
"machine_extruder_trains": {
|
||||
"0": {
|
||||
"extruder_nr": { "default": 0 },
|
||||
@ -18,7 +17,7 @@
|
||||
"extruder_nr": { "default": 1 }
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
"machine_settings": {
|
||||
"machine_use_extruder_offset_to_offset_coords": { "default": false },
|
||||
|
||||
@ -45,7 +44,8 @@
|
||||
"max_value_warning": "150",
|
||||
"default": 60,
|
||||
"visible": false,
|
||||
"enabled": "prime_tower_enable"
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -61,7 +61,8 @@
|
||||
"default": 0.4,
|
||||
"type": "float",
|
||||
"visible": false,
|
||||
"enabled": "prime_tower_enable"
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,7 +89,8 @@
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16"
|
||||
"max_value": "16",
|
||||
"global_only": true
|
||||
},
|
||||
"support_extruder_nr": {
|
||||
"label": "Support Extruder",
|
||||
@ -97,6 +99,7 @@
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"global_only": true,
|
||||
"children": {
|
||||
"support_infill_extruder_nr": {
|
||||
"label": "Support Infill Extruder",
|
||||
@ -104,7 +107,8 @@
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16"
|
||||
"max_value": "16",
|
||||
"global_only": true
|
||||
},
|
||||
"support_extruder_nr_layer_0": {
|
||||
"label": "First Layer Support Extruder",
|
||||
@ -112,7 +116,8 @@
|
||||
"type": "int",
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16"
|
||||
"max_value": "16",
|
||||
"global_only": true
|
||||
},
|
||||
"support_roof_extruder_nr": {
|
||||
"label": "Support Roof Extruder",
|
||||
@ -121,7 +126,8 @@
|
||||
"default": 0,
|
||||
"min_value": "0",
|
||||
"max_value": "16",
|
||||
"enabled": "support_roof_enable"
|
||||
"enabled": "support_roof_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,7 +138,8 @@
|
||||
"description": "Print a tower next to the print which serves to prime the material after each nozzle switch.",
|
||||
"type": "boolean",
|
||||
"visible": true,
|
||||
"default": false
|
||||
"default": false,
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_size": {
|
||||
"label": "Prime Tower Size",
|
||||
@ -144,7 +151,8 @@
|
||||
"min_value": "0",
|
||||
"max_value_warning": "20",
|
||||
"inherit_function": "15 if prime_tower_enable else 0",
|
||||
"enabled": "prime_tower_enable"
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_position_x": {
|
||||
"label": "Prime Tower X Position",
|
||||
@ -153,7 +161,10 @@
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default": 200,
|
||||
"enabled": "prime_tower_enable"
|
||||
"min_value_warning": "-1000",
|
||||
"max_value_warning": "1000",
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_position_y": {
|
||||
"label": "Prime Tower Y Position",
|
||||
@ -162,7 +173,10 @@
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"default": 200,
|
||||
"enabled": "prime_tower_enable"
|
||||
"min_value_warning": "-1000",
|
||||
"max_value_warning": "1000",
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_flow": {
|
||||
"label": "Prime Tower Flow",
|
||||
@ -174,14 +188,16 @@
|
||||
"min_value": "5",
|
||||
"min_value_warning": "50",
|
||||
"max_value_warning": "150",
|
||||
"enabled": "prime_tower_enable"
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"prime_tower_wipe_enabled": {
|
||||
"label": "Wipe Nozzle on Prime tower",
|
||||
"description": "After printing the prime tower with the one nozzle, wipe the oozed material from the other nozzle off on the prime tower.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"enabled": "prime_tower_enable"
|
||||
"enabled": "prime_tower_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"multiple_mesh_overlap": {
|
||||
"label": "Dual Extrusion Overlap",
|
||||
@ -191,13 +207,15 @@
|
||||
"unit": "mm",
|
||||
"default": 0.15,
|
||||
"min_value": "0",
|
||||
"max_value_warning": "1.0"
|
||||
"max_value_warning": "1.0",
|
||||
"global_only": true
|
||||
},
|
||||
"ooze_shield_enabled": {
|
||||
"label": "Enable Ooze Shield",
|
||||
"description": "Enable exterior ooze shield. This will create a shell around the object which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
"default": false,
|
||||
"global_only": true
|
||||
},
|
||||
"ooze_shield_angle": {
|
||||
"label": "Ooze Shield Angle",
|
||||
@ -208,7 +226,8 @@
|
||||
"max_value": "90",
|
||||
"default": 60,
|
||||
"visible": false,
|
||||
"enabled": "ooze_shield_enabled"
|
||||
"enabled": "ooze_shield_enabled",
|
||||
"global_only": true
|
||||
},
|
||||
"ooze_shield_dist": {
|
||||
"label": "Ooze Shields Distance",
|
||||
@ -219,21 +238,36 @@
|
||||
"max_value_warning": "30",
|
||||
"default": 2,
|
||||
"visible": false,
|
||||
"enabled": "ooze_shield_enabled"
|
||||
"enabled": "ooze_shield_enabled",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"material": {
|
||||
"settings": {
|
||||
"material_standby_temperature": {
|
||||
"label": "Standby Temperature",
|
||||
"description": "The temperature of the nozzle when another nozzle is currently used for printing.",
|
||||
"unit": "°C",
|
||||
"type": "float",
|
||||
"default": 150,
|
||||
"min_value": "0",
|
||||
"max_value_warning": "260",
|
||||
"global_only": "True",
|
||||
"visible": false
|
||||
},
|
||||
"switch_extruder_retraction_amount": {
|
||||
"label": "Nozzle Switch Retraction Distance",
|
||||
"description": "The amount of retraction: Set at 0 for no retraction at all. This should generally be the same as the length of the heat zone.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default": 16,
|
||||
"min_value_warning": "0",
|
||||
"max_value_warning": "100",
|
||||
"visible": false,
|
||||
"inherit_function": "machine_heat_zone_length",
|
||||
"enabled": "retraction_enable"
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"switch_extruder_retraction_speeds": {
|
||||
"label": "Nozzle Switch Retraction Speed",
|
||||
@ -241,9 +275,12 @@
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default": 20,
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "300",
|
||||
"visible": false,
|
||||
"inherit": false,
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true,
|
||||
"children": {
|
||||
"switch_extruder_retraction_speed": {
|
||||
"label": "Nozzle Switch Retract Speed",
|
||||
@ -251,8 +288,11 @@
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default": 20,
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "300",
|
||||
"visible": false,
|
||||
"enabled": "retraction_enable"
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true
|
||||
},
|
||||
"switch_extruder_prime_speed": {
|
||||
"label": "Nozzle Switch Prime Speed",
|
||||
@ -260,8 +300,11 @@
|
||||
"unit": "mm/s",
|
||||
"type": "float",
|
||||
"default": 20,
|
||||
"min_value": "0.1",
|
||||
"max_value_warning": "300",
|
||||
"visible": false,
|
||||
"enabled": "retraction_enable"
|
||||
"enabled": "retraction_enable",
|
||||
"global_only": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
"author": "Other",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "grr_neo_platform.stl",
|
||||
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
"visible": "true",
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
{
|
||||
"id": "innovo-inventor",
|
||||
"name": "Innovo INVENTOR",
|
||||
"manufacturer": "INNOVO",
|
||||
"author": "AR",
|
||||
"version": 1,
|
||||
"icon": "",
|
||||
"name": "Innovo INVENTOR",
|
||||
"manufacturer": "Other",
|
||||
"author": "AR",
|
||||
"platform": "inventor_platform.stl",
|
||||
"platform_texture": "",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_settings": {
|
||||
"machine_width": {"default": 340},
|
||||
"machine_height": {"default": 290},
|
||||
"machine_depth": {"default": 300},
|
||||
"machine_heated_bed": { "default": true }
|
||||
"machine_heated_bed": { "default": true},
|
||||
"machine_center_is_zero": {"default": false},
|
||||
"machine_nozzle_size": {"default": 0.4},
|
||||
"machine_head_shape_min_x": {"default": 43.7},
|
||||
@ -24,7 +24,8 @@
|
||||
"machine_nozzle_offset_y_1": {"default": 15},
|
||||
"machine_gcode_flavor": {"default": "RepRap (Marlin/Sprinter)"},
|
||||
"machine_start_gcode": {"default": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\n{IF_BED}M190 S{BED}\n{IF_EXT0}M104 T0 S{TEMP0}\n{IF_EXT0}M109 T0 S{TEMP0}\n{IF_EXT1}M104 T1 S{TEMP1}\n{IF_EXT1}M109 T1 S{TEMP1}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position"},
|
||||
"machine_end_gcode": {"default": "M104 S0\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"}
|
||||
"machine_end_gcode": {"default": "M104 S0\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"},
|
||||
"machine_platform_offset": {"default": [-180, -0.25, 160]}
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
@ -44,6 +45,6 @@
|
||||
"speed_topbottom": { "default": 30.0, "visible": true },
|
||||
"speed_travel": { "default": 150.0, "visible": true },
|
||||
"speed_layer_0": { "min_value": 0.1, "default": 30.0, "visible": true },
|
||||
"infill_overlap": { "default": 10.0 }
|
||||
"infill_overlap": { "default": 0.04, "inherit_function": "0.1 * line_width if infill_sparse_density < 95 else 0" }
|
||||
}
|
||||
}
|
||||
}
|
39
resources/machines/m180.json
Normal file
39
resources/machines/m180.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"id": "m180",
|
||||
"version": 1,
|
||||
"name": "Malyan M180",
|
||||
"manufacturer": "Other",
|
||||
"icon": "icon_ultimaker.png",
|
||||
"platform": "",
|
||||
"file_formats": "application/x3g",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_settings": {
|
||||
"machine_width": { "default": 230 },
|
||||
"machine_height": { "default": 165 },
|
||||
"machine_depth": { "default": 145 },
|
||||
"machine_center_is_zero": { "default": true },
|
||||
"machine_nozzle_size": { "default": 0.4, "min_value": "0.001" },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default": [
|
||||
[ -75, 35 ],
|
||||
[ -75, -18 ],
|
||||
[ 18, -18 ],
|
||||
[ 18, 35 ]
|
||||
]
|
||||
},
|
||||
"gantry_height": { "default": 55 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": { "default": "M136\nM73 P0\nM103\nG21\nG90\nM320\n;(**** begin homing ****)\nG162 X Y F4000\nG161 Z F3500\nG92 Z-5\nG1 Z0.0\nG161 Z F100\nM132 X Y Z A B\n;(**** end homing ****)\nG92 X147 Y66 Z5\nG1 X105 Y-60 Z10 F4000.0\nG130 X127 Y127 A127 B127\nG0 X105 Y-60\nG1 Z0.3 F300\nG92 E0\nG1 X100 E10 F300\nG92 E0\nG1 Z0.0 F300\nM320" },
|
||||
"machine_end_gcode": { "default": "G92 Z0\nG1 Z10 F400\nM18\nM109 S0 T0\nM104 S0 T0\nM73 P100 (end build progress)\nG162 X Y F3000\nM18" }
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"material_bed_temperature": { "visible": "True" },
|
||||
"material_diameter": {
|
||||
"default": 1.75,
|
||||
"min_value_warning": "1.5",
|
||||
"max_value_warning": "2.0"
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"author": "Other",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "makerstarter_platform.stl",
|
||||
|
||||
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
|
@ -6,7 +6,7 @@
|
||||
"author": "Other",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "prusai3_platform.stl",
|
||||
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
@ -16,6 +16,7 @@
|
||||
"machine_depth": { "default": 200 },
|
||||
"machine_center_is_zero": { "default": false },
|
||||
"machine_nozzle_size": { "default": 0.4 },
|
||||
"material_diameter": { "default": 1.75 },
|
||||
"machine_nozzle_heat_up_speed": { "default": 2.0 },
|
||||
"machine_nozzle_cool_down_speed": { "default": 2.0 },
|
||||
"machine_head_shape_min_x": { "default": 75 },
|
||||
|
36
resources/machines/prusa_i3_xl.json
Normal file
36
resources/machines/prusa_i3_xl.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"id": "prusa_i3_xl",
|
||||
"version": 1,
|
||||
"name": "Prusa i3 xl",
|
||||
"manufacturer": "Other",
|
||||
"author": "Other",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "prusai3_xl_platform.stl",
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"overrides": {
|
||||
"machine_heated_bed": { "default": true },
|
||||
"machine_width": { "default": 200 },
|
||||
"machine_height": { "default": 200 },
|
||||
"machine_depth": { "default": 270 },
|
||||
"machine_center_is_zero": { "default": false },
|
||||
"machine_nozzle_size": { "default": 0.4 },
|
||||
"material_diameter": { "default": 1.75 },
|
||||
"machine_nozzle_heat_up_speed": { "default": 2.0 },
|
||||
"machine_nozzle_cool_down_speed": { "default": 2.0 },
|
||||
"machine_head_shape_min_x": { "default": 75 },
|
||||
"machine_head_shape_min_y": { "default": 18 },
|
||||
"machine_head_shape_max_x": { "default": 18 },
|
||||
"machine_head_shape_max_y": { "default": 35 },
|
||||
"machine_nozzle_gantry_distance": { "default": 55 },
|
||||
"machine_gcode_flavor": { "default": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"machine_start_gcode": {
|
||||
"default": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
|
||||
}
|
||||
}
|
||||
}
|
15
resources/machines/ultimaker.json
Normal file
15
resources/machines/ultimaker.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"id": "ultimaker_base",
|
||||
"version": 1,
|
||||
"visible": false,
|
||||
"name": "Ultimaker",
|
||||
"manufacturer": "Ultimaker",
|
||||
"author": "Ultimaker",
|
||||
"inherits": "fdmprinter.json",
|
||||
|
||||
"machine_preferences": {
|
||||
"prefered_profile": "Normal Quality",
|
||||
"prefered_variant": "0.4 mm",
|
||||
"prefered_material": "PLA"
|
||||
}
|
||||
}
|
@ -1,23 +1,27 @@
|
||||
{
|
||||
"id": "ultimaker2",
|
||||
"version": 1,
|
||||
"version": 1,
|
||||
"name": "Ultimaker 2",
|
||||
"manufacturer": "Ultimaker",
|
||||
"author": "Ultimaker",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2backplate.png",
|
||||
"file_formats": "text/x-gcode",
|
||||
|
||||
"inherits": "fdmprinter.json",
|
||||
"inherits": "ultimaker.json",
|
||||
|
||||
|
||||
"machine_extruder_trains": {
|
||||
"0": {
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default": 2.0
|
||||
"pages": [
|
||||
"SelectUpgradedPartsUM2"
|
||||
],
|
||||
|
||||
"machine_extruder_trains": [
|
||||
{
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default": 2.0
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default": 2.0
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default": 2.0
|
||||
},
|
||||
"machine_nozzle_tip_outer_diameter": {
|
||||
"default": 1
|
||||
@ -28,12 +32,12 @@
|
||||
"machine_nozzle_expansion_angle": {
|
||||
"default": 45
|
||||
},
|
||||
"machine_heat_zone_length": {
|
||||
"machine_heat_zone_length": {
|
||||
"default": 16
|
||||
}
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
],
|
||||
"machine_settings": {
|
||||
"machine_start_gcode" : { "default": "" },
|
||||
"machine_end_gcode" : { "default": "" },
|
||||
"machine_width": { "default": 230 },
|
||||
@ -76,14 +80,20 @@
|
||||
[[ 115.0, -112.5], [ 108.0, -112.5], [ 110.0, -104.5], [ 115.0, -104.5]]
|
||||
]},
|
||||
"machine_platform_offset": { "default": [9.0, 0.0, 0.0] },
|
||||
|
||||
|
||||
"machine_nozzle_tip_outer_diameter": { "default": 1.0 },
|
||||
"machine_nozzle_head_distance": { "default": 3.0 },
|
||||
"machine_nozzle_expansion_angle": { "default": 45 },
|
||||
|
||||
"machine_nozzle_expansion_angle": { "default": 45 }
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"material_print_temperature": { "enabled": "False" },
|
||||
"material_bed_temperature": { "enabled": "False" },
|
||||
"material_diameter": { "enabled": "False" },
|
||||
"material_flow": { "enabled": "False" }
|
||||
"material_flow": { "enabled": "False" },
|
||||
"retraction_amount": { "enabled": "False" },
|
||||
"retraction_speed": { "enabled": "False" },
|
||||
"retraction_retract_speed": { "enabled": "False" },
|
||||
"retraction_prime_speed": { "enabled": "False" }
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,14 @@
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2Extendedbackplate.png",
|
||||
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "ultimaker2.json",
|
||||
|
||||
"overrides": {
|
||||
"pages": [
|
||||
"SelectUpgradedPartsUM2"
|
||||
],
|
||||
|
||||
"machine_settings": {
|
||||
"machine_width": { "default": 230 },
|
||||
"machine_depth": { "default": 225 },
|
||||
"machine_height": { "default": 315 }
|
||||
|
20
resources/machines/ultimaker2_extended_olsson.json
Normal file
20
resources/machines/ultimaker2_extended_olsson.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"id": "ultimaker2_extended_olsson_base",
|
||||
"version": 1,
|
||||
"name": "Ultimaker 2 Extended with Olsson Block",
|
||||
"manufacturer": "Ultimaker",
|
||||
"author": "Ultimaker",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2backplate.png",
|
||||
"visible": false,
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "ultimaker2.json",
|
||||
|
||||
"machine_settings": {
|
||||
"machine_width": { "default": 230 },
|
||||
"machine_depth": { "default": 225 },
|
||||
"machine_height": { "default": 310 },
|
||||
"machine_show_variants": { "default": true },
|
||||
"gantry_height": { "default": 50 }
|
||||
}
|
||||
}
|
17
resources/machines/ultimaker2_extended_olsson_025.json
Normal file
17
resources/machines/ultimaker2_extended_olsson_025.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "ultimaker2_extended_olsson",
|
||||
"version": 1,
|
||||
"name": "Ultimaker 2 Extended with Olsson Block",
|
||||
"manufacturer": "Ultimaker",
|
||||
"author": "Ultimaker",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2backplate.png",
|
||||
"visible": false,
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "ultimaker2_extended_olsson.json",
|
||||
"variant": "0.25 mm",
|
||||
"profiles_machine": "ultimaker2_olsson",
|
||||
"machine_settings": {
|
||||
"machine_nozzle_size": { "default": 0.25 }
|
||||
}
|
||||
}
|
18
resources/machines/ultimaker2_extended_olsson_040.json
Normal file
18
resources/machines/ultimaker2_extended_olsson_040.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"id": "ultimaker2_extended_olsson",
|
||||
"version": 1,
|
||||
"name": "Ultimaker 2 Extended with Olsson Block",
|
||||
"manufacturer": "Ultimaker",
|
||||
"author": "Ultimaker",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2backplate.png",
|
||||
"visible": false,
|
||||
"file_formats": "text/x-gcode",
|
||||
"inherits": "ultimaker2_extended_olsson.json",
|
||||
|
||||
"variant": "0.4 mm",
|
||||
"profiles_machine": "ultimaker2_olsson",
|
||||
"machine_settings": {
|
||||
"machine_nozzle_size": { "default": 0.40 }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user