mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-06-26 01:15:28 -06:00
Converted comments in dir Cura/cura to rst style
Converted doxygen style comments to reStructuredText style in the files found in Cura/cura directory using the script dox_2_rst.py (provided in the Uranium repo). Comments were manually checked and changed if needed.
This commit is contained in:
parent
fb4aec96a8
commit
6aedab78dc
15 changed files with 371 additions and 208 deletions
|
@ -44,8 +44,9 @@ catalog = i18nCatalog("cura")
|
|||
PRIME_CLEARANCE = 6.5
|
||||
|
||||
|
||||
## Build volume is a special kind of node that is responsible for rendering the printable area & disallowed areas.
|
||||
class BuildVolume(SceneNode):
|
||||
"""Build volume is a special kind of node that is responsible for rendering the printable area & disallowed areas."""
|
||||
|
||||
raftThicknessChanged = Signal()
|
||||
|
||||
def __init__(self, application: "CuraApplication", parent: Optional[SceneNode] = None) -> None:
|
||||
|
@ -113,7 +114,7 @@ class BuildVolume(SceneNode):
|
|||
self._has_errors = False
|
||||
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
#Objects loaded at the moment. We are connected to the property changed events of these objects.
|
||||
# Objects loaded at the moment. We are connected to the property changed events of these objects.
|
||||
self._scene_objects = set() # type: Set[SceneNode]
|
||||
|
||||
self._scene_change_timer = QTimer()
|
||||
|
@ -163,10 +164,12 @@ class BuildVolume(SceneNode):
|
|||
self._scene_objects = new_scene_objects
|
||||
self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered.
|
||||
|
||||
## Updates the listeners that listen for changes in per-mesh stacks.
|
||||
#
|
||||
# \param node The node for which the decorators changed.
|
||||
def _updateNodeListeners(self, node: SceneNode):
|
||||
"""Updates the listeners that listen for changes in per-mesh stacks.
|
||||
|
||||
:param node: The node for which the decorators changed.
|
||||
"""
|
||||
|
||||
per_mesh_stack = node.callDecoration("getStack")
|
||||
if per_mesh_stack:
|
||||
per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged)
|
||||
|
@ -187,10 +190,14 @@ class BuildVolume(SceneNode):
|
|||
if shape:
|
||||
self._shape = shape
|
||||
|
||||
## Get the length of the 3D diagonal through the build volume.
|
||||
#
|
||||
# This gives a sense of the scale of the build volume in general.
|
||||
def getDiagonalSize(self) -> float:
|
||||
"""Get the length of the 3D diagonal through the build volume.
|
||||
|
||||
This gives a sense of the scale of the build volume in general.
|
||||
|
||||
:return: length of the 3D diagonal through the build volume
|
||||
"""
|
||||
|
||||
return math.sqrt(self._width * self._width + self._height * self._height + self._depth * self._depth)
|
||||
|
||||
def getDisallowedAreas(self) -> List[Polygon]:
|
||||
|
@ -226,9 +233,9 @@ class BuildVolume(SceneNode):
|
|||
|
||||
return True
|
||||
|
||||
## For every sliceable node, update node._outside_buildarea
|
||||
#
|
||||
def updateNodeBoundaryCheck(self):
|
||||
"""For every sliceable node, update node._outside_buildarea"""
|
||||
|
||||
if not self._global_container_stack:
|
||||
return
|
||||
|
||||
|
@ -295,8 +302,13 @@ class BuildVolume(SceneNode):
|
|||
for child_node in children:
|
||||
child_node.setOutsideBuildArea(group_node.isOutsideBuildArea())
|
||||
|
||||
## Update the outsideBuildArea of a single node, given bounds or current build volume
|
||||
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None) -> None:
|
||||
"""Update the outsideBuildArea of a single node, given bounds or current build volume
|
||||
|
||||
:param node: single node
|
||||
:param bounds: bounds or current build volume
|
||||
"""
|
||||
|
||||
if not isinstance(node, CuraSceneNode) or self._global_container_stack is None:
|
||||
return
|
||||
|
||||
|
@ -484,8 +496,9 @@ class BuildVolume(SceneNode):
|
|||
self._disallowed_area_size = max(size, self._disallowed_area_size)
|
||||
return mb.build()
|
||||
|
||||
## Recalculates the build volume & disallowed areas.
|
||||
def rebuild(self) -> None:
|
||||
"""Recalculates the build volume & disallowed areas."""
|
||||
|
||||
if not self._width or not self._height or not self._depth:
|
||||
return
|
||||
|
||||
|
@ -586,8 +599,9 @@ class BuildVolume(SceneNode):
|
|||
def _onStackChanged(self):
|
||||
self._stack_change_timer.start()
|
||||
|
||||
## Update the build volume visualization
|
||||
def _onStackChangeTimerFinished(self) -> None:
|
||||
"""Update the build volume visualization"""
|
||||
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||
extruders = ExtruderManager.getInstance().getActiveExtruderStacks()
|
||||
|
@ -712,15 +726,15 @@ class BuildVolume(SceneNode):
|
|||
self._depth = self._global_container_stack.getProperty("machine_depth", "value")
|
||||
self._shape = self._global_container_stack.getProperty("machine_shape", "value")
|
||||
|
||||
## Calls _updateDisallowedAreas and makes sure the changes appear in the
|
||||
# scene.
|
||||
#
|
||||
# This is required for a signal to trigger the update in one go. The
|
||||
# ``_updateDisallowedAreas`` method itself shouldn't call ``rebuild``,
|
||||
# since there may be other changes before it needs to be rebuilt, which
|
||||
# would hit performance.
|
||||
|
||||
def _updateDisallowedAreasAndRebuild(self):
|
||||
"""Calls :py:meth:`cura.BuildVolume._updateDisallowedAreas` and makes sure the changes appear in the scene.
|
||||
|
||||
This is required for a signal to trigger the update in one go. The
|
||||
:py:meth:`cura.BuildVolume._updateDisallowedAreas` method itself shouldn't call
|
||||
:py:meth:`cura.BuildVolume.rebuild`, since there may be other changes before it needs to be rebuilt,
|
||||
which would hit performance.
|
||||
"""
|
||||
|
||||
self._updateDisallowedAreas()
|
||||
self._updateRaftThickness()
|
||||
self._extra_z_clearance = self._calculateExtraZClearance(ExtruderManager.getInstance().getUsedExtruderStacks())
|
||||
|
@ -782,15 +796,14 @@ class BuildVolume(SceneNode):
|
|||
for extruder_id in result_areas_no_brim:
|
||||
self._disallowed_areas_no_brim.extend(result_areas_no_brim[extruder_id])
|
||||
|
||||
## Computes the disallowed areas for objects that are printed with print
|
||||
# features.
|
||||
#
|
||||
# This means that the brim, travel avoidance and such will be applied to
|
||||
# these features.
|
||||
#
|
||||
# \return A dictionary with for each used extruder ID the disallowed areas
|
||||
# where that extruder may not print.
|
||||
def _computeDisallowedAreasPrinted(self, used_extruders):
|
||||
"""Computes the disallowed areas for objects that are printed with print features.
|
||||
|
||||
This means that the brim, travel avoidance and such will be applied to these features.
|
||||
|
||||
:return: A dictionary with for each used extruder ID the disallowed areas where that extruder may not print.
|
||||
"""
|
||||
|
||||
result = {}
|
||||
adhesion_extruder = None #type: ExtruderStack
|
||||
for extruder in used_extruders:
|
||||
|
@ -828,18 +841,18 @@ class BuildVolume(SceneNode):
|
|||
|
||||
return result
|
||||
|
||||
## Computes the disallowed areas for the prime blobs.
|
||||
#
|
||||
# These are special because they are not subject to things like brim or
|
||||
# travel avoidance. They do get a dilute with the border size though
|
||||
# because they may not intersect with brims and such of other objects.
|
||||
#
|
||||
# \param border_size The size with which to offset the disallowed areas
|
||||
# due to skirt, brim, travel avoid distance, etc.
|
||||
# \param used_extruders The extruder stacks to generate disallowed areas
|
||||
# for.
|
||||
# \return A dictionary with for each used extruder ID the prime areas.
|
||||
def _computeDisallowedAreasPrimeBlob(self, border_size: float, used_extruders: List["ExtruderStack"]) -> Dict[str, List[Polygon]]:
|
||||
"""Computes the disallowed areas for the prime blobs.
|
||||
|
||||
These are special because they are not subject to things like brim or travel avoidance. They do get a dilute
|
||||
with the border size though because they may not intersect with brims and such of other objects.
|
||||
|
||||
:param border_size: The size with which to offset the disallowed areas due to skirt, brim, travel avoid distance
|
||||
, etc.
|
||||
:param used_extruders: The extruder stacks to generate disallowed areas for.
|
||||
:return: A dictionary with for each used extruder ID the prime areas.
|
||||
"""
|
||||
|
||||
result = {} # type: Dict[str, List[Polygon]]
|
||||
if not self._global_container_stack:
|
||||
return result
|
||||
|
@ -867,19 +880,18 @@ class BuildVolume(SceneNode):
|
|||
|
||||
return result
|
||||
|
||||
## Computes the disallowed areas that are statically placed in the machine.
|
||||
#
|
||||
# It computes different disallowed areas depending on the offset of the
|
||||
# extruder. The resulting dictionary will therefore have an entry for each
|
||||
# extruder that is used.
|
||||
#
|
||||
# \param border_size The size with which to offset the disallowed areas
|
||||
# due to skirt, brim, travel avoid distance, etc.
|
||||
# \param used_extruders The extruder stacks to generate disallowed areas
|
||||
# for.
|
||||
# \return A dictionary with for each used extruder ID the disallowed areas
|
||||
# where that extruder may not print.
|
||||
def _computeDisallowedAreasStatic(self, border_size:float, used_extruders: List["ExtruderStack"]) -> Dict[str, List[Polygon]]:
|
||||
"""Computes the disallowed areas that are statically placed in the machine.
|
||||
|
||||
It computes different disallowed areas depending on the offset of the extruder. The resulting dictionary will
|
||||
therefore have an entry for each extruder that is used.
|
||||
|
||||
:param border_size: The size with which to offset the disallowed areas due to skirt, brim, travel avoid distance
|
||||
, etc.
|
||||
:param used_extruders: The extruder stacks to generate disallowed areas for.
|
||||
:return: A dictionary with for each used extruder ID the disallowed areas where that extruder may not print.
|
||||
"""
|
||||
|
||||
# Convert disallowed areas to polygons and dilate them.
|
||||
machine_disallowed_polygons = []
|
||||
if self._global_container_stack is None:
|
||||
|
@ -1010,13 +1022,14 @@ class BuildVolume(SceneNode):
|
|||
|
||||
return result
|
||||
|
||||
## Private convenience function to get a setting from every extruder.
|
||||
#
|
||||
# For single extrusion machines, this gets the setting from the global
|
||||
# stack.
|
||||
#
|
||||
# \return A sequence of setting values, one for each extruder.
|
||||
def _getSettingFromAllExtruders(self, setting_key: str) -> List[Any]:
|
||||
"""Private convenience function to get a setting from every extruder.
|
||||
|
||||
For single extrusion machines, this gets the setting from the global stack.
|
||||
|
||||
:return: A sequence of setting values, one for each extruder.
|
||||
"""
|
||||
|
||||
all_values = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "value")
|
||||
all_types = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "type")
|
||||
for i, (setting_value, setting_type) in enumerate(zip(all_values, all_types)):
|
||||
|
@ -1101,12 +1114,13 @@ class BuildVolume(SceneNode):
|
|||
|
||||
return move_from_wall_radius
|
||||
|
||||
## Calculate the disallowed radius around the edge.
|
||||
#
|
||||
# This disallowed radius is to allow for space around the models that is
|
||||
# not part of the collision radius, such as bed adhesion (skirt/brim/raft)
|
||||
# and travel avoid distance.
|
||||
def getEdgeDisallowedSize(self):
|
||||
"""Calculate the disallowed radius around the edge.
|
||||
|
||||
This disallowed radius is to allow for space around the models that is not part of the collision radius, such as
|
||||
bed adhesion (skirt/brim/raft) and travel avoid distance.
|
||||
"""
|
||||
|
||||
if not self._global_container_stack or not self._global_container_stack.extruderList:
|
||||
return 0
|
||||
|
||||
|
|
|
@ -150,8 +150,9 @@ class CrashHandler:
|
|||
self._sendCrashReport()
|
||||
os._exit(1)
|
||||
|
||||
## Backup the current resource directories and create clean ones.
|
||||
def _backupAndStartClean(self):
|
||||
"""Backup the current resource directories and create clean ones."""
|
||||
|
||||
Resources.factoryReset()
|
||||
self.early_crash_dialog.close()
|
||||
|
||||
|
@ -162,8 +163,9 @@ class CrashHandler:
|
|||
def _showDetailedReport(self):
|
||||
self.dialog.exec_()
|
||||
|
||||
## Creates a modal dialog.
|
||||
def _createDialog(self):
|
||||
"""Creates a modal dialog."""
|
||||
|
||||
self.dialog.setMinimumWidth(640)
|
||||
self.dialog.setMinimumHeight(640)
|
||||
self.dialog.setWindowTitle(catalog.i18nc("@title:window", "Crash Report"))
|
||||
|
|
|
@ -43,9 +43,10 @@ class CuraActions(QObject):
|
|||
event = CallFunctionEvent(self._openUrl, [QUrl("https://github.com/Ultimaker/Cura/issues")], {})
|
||||
cura.CuraApplication.CuraApplication.getInstance().functionEvent(event)
|
||||
|
||||
## Reset camera position and direction to default
|
||||
@pyqtSlot()
|
||||
def homeCamera(self) -> None:
|
||||
"""Reset camera position and direction to default"""
|
||||
|
||||
scene = cura.CuraApplication.CuraApplication.getInstance().getController().getScene()
|
||||
camera = scene.getActiveCamera()
|
||||
if camera:
|
||||
|
@ -54,9 +55,10 @@ class CuraActions(QObject):
|
|||
camera.setPerspective(True)
|
||||
camera.lookAt(Vector(0, 0, 0))
|
||||
|
||||
## Center all objects in the selection
|
||||
@pyqtSlot()
|
||||
def centerSelection(self) -> None:
|
||||
"""Center all objects in the selection"""
|
||||
|
||||
operation = GroupedOperation()
|
||||
for node in Selection.getAllSelectedObjects():
|
||||
current_node = node
|
||||
|
@ -73,18 +75,21 @@ class CuraActions(QObject):
|
|||
operation.addOperation(center_operation)
|
||||
operation.push()
|
||||
|
||||
## Multiply all objects in the selection
|
||||
#
|
||||
# \param count The number of times to multiply the selection.
|
||||
@pyqtSlot(int)
|
||||
def multiplySelection(self, count: int) -> None:
|
||||
"""Multiply all objects in the selection
|
||||
|
||||
:param count: The number of times to multiply the selection.
|
||||
"""
|
||||
|
||||
min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors
|
||||
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = max(min_offset, 8))
|
||||
job.start()
|
||||
|
||||
## Delete all selected objects.
|
||||
@pyqtSlot()
|
||||
def deleteSelection(self) -> None:
|
||||
"""Delete all selected objects."""
|
||||
|
||||
if not cura.CuraApplication.CuraApplication.getInstance().getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
|
@ -106,11 +111,13 @@ class CuraActions(QObject):
|
|||
|
||||
op.push()
|
||||
|
||||
## Set the extruder that should be used to print the selection.
|
||||
#
|
||||
# \param extruder_id The ID of the extruder stack to use for the selected objects.
|
||||
@pyqtSlot(str)
|
||||
def setExtruderForSelection(self, extruder_id: str) -> None:
|
||||
"""Set the extruder that should be used to print the selection.
|
||||
|
||||
:param extruder_id: The ID of the extruder stack to use for the selected objects.
|
||||
"""
|
||||
|
||||
operation = GroupedOperation()
|
||||
|
||||
nodes_to_change = []
|
||||
|
|
|
@ -258,9 +258,12 @@ class CuraApplication(QtApplication):
|
|||
def ultimakerCloudAccountRootUrl(self) -> str:
|
||||
return UltimakerCloudAuthentication.CuraCloudAccountAPIRoot
|
||||
|
||||
# Adds command line options to the command line parser. This should be called after the application is created and
|
||||
# before the pre-start.
|
||||
def addCommandLineOptions(self):
|
||||
"""Adds command line options to the command line parser.
|
||||
|
||||
This should be called after the application is created and before the pre-start.
|
||||
"""
|
||||
|
||||
super().addCommandLineOptions()
|
||||
self._cli_parser.add_argument("--help", "-h",
|
||||
action = "store_true",
|
||||
|
@ -322,8 +325,9 @@ class CuraApplication(QtApplication):
|
|||
Logger.log("i", "Single instance commands were sent, exiting")
|
||||
sys.exit(0)
|
||||
|
||||
# Adds expected directory names and search paths for Resources.
|
||||
def __addExpectedResourceDirsAndSearchPaths(self):
|
||||
"""Adds expected directory names and search paths for Resources."""
|
||||
|
||||
# this list of dir names will be used by UM to detect an old cura directory
|
||||
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "quality_changes", "user", "variants", "intent"]:
|
||||
Resources.addExpectedDirNameInData(dir_name)
|
||||
|
@ -365,9 +369,12 @@ class CuraApplication(QtApplication):
|
|||
SettingDefinition.addSettingType("[int]", None, str, None)
|
||||
|
||||
|
||||
# Adds custom property types, settings types, and extra operators (functions) that need to be registered in
|
||||
# SettingDefinition and SettingFunction.
|
||||
def _initializeSettingFunctions(self):
|
||||
"""Adds custom property types, settings types, and extra operators (functions).
|
||||
|
||||
Whom need to be registered in SettingDefinition and SettingFunction.
|
||||
"""
|
||||
|
||||
self._cura_formula_functions = CuraFormulaFunctions(self)
|
||||
|
||||
SettingFunction.registerOperator("extruderValue", self._cura_formula_functions.getValueInExtruder)
|
||||
|
@ -377,8 +384,9 @@ class CuraApplication(QtApplication):
|
|||
SettingFunction.registerOperator("valueFromContainer", self._cura_formula_functions.getValueFromContainerAtIndex)
|
||||
SettingFunction.registerOperator("extruderValueFromContainer", self._cura_formula_functions.getValueFromContainerAtIndexInExtruder)
|
||||
|
||||
# Adds all resources and container related resources.
|
||||
def __addAllResourcesAndContainerResources(self) -> None:
|
||||
"""Adds all resources and container related resources."""
|
||||
|
||||
Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality")
|
||||
Resources.addStorageType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
|
||||
Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants")
|
||||
|
@ -403,8 +411,9 @@ class CuraApplication(QtApplication):
|
|||
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
|
||||
Resources.addType(self.ResourceTypes.Firmware, "firmware")
|
||||
|
||||
# Adds all empty containers.
|
||||
def __addAllEmptyContainers(self) -> None:
|
||||
"""Adds all empty containers."""
|
||||
|
||||
# Add empty variant, material and quality containers.
|
||||
# Since they are empty, they should never be serialized and instead just programmatically created.
|
||||
# We need them to simplify the switching between materials.
|
||||
|
@ -429,9 +438,11 @@ class CuraApplication(QtApplication):
|
|||
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_changes_container)
|
||||
self.empty_quality_changes_container = cura.Settings.cura_empty_instance_containers.empty_quality_changes_container
|
||||
|
||||
# Initializes the version upgrade manager with by providing the paths for each resource type and the latest
|
||||
# versions.
|
||||
def __setLatestResouceVersionsForVersionUpgrade(self):
|
||||
"""Initializes the version upgrade manager with by providing the paths for each resource type and the latest
|
||||
versions.
|
||||
"""
|
||||
|
||||
self._version_upgrade_manager.setCurrentVersions(
|
||||
{
|
||||
("quality", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
|
||||
|
@ -446,8 +457,9 @@ class CuraApplication(QtApplication):
|
|||
}
|
||||
)
|
||||
|
||||
# Runs preparations that needs to be done before the starting process.
|
||||
def startSplashWindowPhase(self) -> None:
|
||||
"""Runs preparations that needs to be done before the starting process."""
|
||||
|
||||
super().startSplashWindowPhase()
|
||||
|
||||
if not self.getIsHeadLess():
|
||||
|
@ -503,7 +515,7 @@ class CuraApplication(QtApplication):
|
|||
# Set the setting version for Preferences
|
||||
preferences = self.getPreferences()
|
||||
preferences.addPreference("metadata/setting_version", 0)
|
||||
preferences.setValue("metadata/setting_version", self.SettingVersion) #Don't make it equal to the default so that the setting version always gets written to the file.
|
||||
preferences.setValue("metadata/setting_version", self.SettingVersion) # Don't make it equal to the default so that the setting version always gets written to the file.
|
||||
|
||||
preferences.addPreference("cura/active_mode", "simple")
|
||||
|
||||
|
@ -607,12 +619,13 @@ class CuraApplication(QtApplication):
|
|||
def callConfirmExitDialogCallback(self, yes_or_no: bool) -> None:
|
||||
self._confirm_exit_dialog_callback(yes_or_no)
|
||||
|
||||
## Signal to connect preferences action in QML
|
||||
showPreferencesWindow = pyqtSignal()
|
||||
"""Signal to connect preferences action in QML"""
|
||||
|
||||
## Show the preferences window
|
||||
@pyqtSlot()
|
||||
def showPreferences(self) -> None:
|
||||
"""Show the preferences window"""
|
||||
|
||||
self.showPreferencesWindow.emit()
|
||||
|
||||
# This is called by drag-and-dropping curapackage files.
|
||||
|
@ -630,10 +643,9 @@ class CuraApplication(QtApplication):
|
|||
self._setLoadingHint(self._i18n_catalog.i18nc("@info:progress", "Initializing Active Machine..."))
|
||||
super().setGlobalContainerStack(stack)
|
||||
|
||||
## A reusable dialogbox
|
||||
#
|
||||
showMessageBox = pyqtSignal(str,str, str, str, int, int,
|
||||
arguments = ["title", "text", "informativeText", "detailedText","buttons", "icon"])
|
||||
""" A reusable dialogbox"""
|
||||
|
||||
def messageBox(self, title, text,
|
||||
informativeText = "",
|
||||
|
@ -711,9 +723,12 @@ class CuraApplication(QtApplication):
|
|||
def setDefaultPath(self, key, default_path):
|
||||
self.getPreferences().setValue("local_file/%s" % key, QUrl(default_path).toLocalFile())
|
||||
|
||||
## Handle loading of all plugin types (and the backend explicitly)
|
||||
# \sa PluginRegistry
|
||||
def _loadPlugins(self) -> None:
|
||||
"""Handle loading of all plugin types (and the backend explicitly)
|
||||
|
||||
:py:class:`Uranium.UM.PluginRegistry`
|
||||
"""
|
||||
|
||||
self._plugin_registry.setCheckIfTrusted(ApplicationMetadata.IsEnterpriseVersion)
|
||||
|
||||
self._plugin_registry.addType("profile_reader", self._addProfileReader)
|
||||
|
@ -737,9 +752,12 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._plugins_loaded = True
|
||||
|
||||
## Set a short, user-friendly hint about current loading status.
|
||||
# The way this message is displayed depends on application state
|
||||
def _setLoadingHint(self, hint: str):
|
||||
"""Set a short, user-friendly hint about current loading status.
|
||||
|
||||
The way this message is displayed depends on application state
|
||||
"""
|
||||
|
||||
if self.started:
|
||||
Logger.info(hint)
|
||||
else:
|
||||
|
@ -824,12 +842,14 @@ class CuraApplication(QtApplication):
|
|||
|
||||
initializationFinished = pyqtSignal()
|
||||
|
||||
## Run Cura without GUI elements and interaction (server mode).
|
||||
def runWithoutGUI(self):
|
||||
"""Run Cura without GUI elements and interaction (server mode)."""
|
||||
|
||||
self.closeSplash()
|
||||
|
||||
## Run Cura with GUI (desktop mode).
|
||||
def runWithGUI(self):
|
||||
"""Run Cura with GUI (desktop mode)."""
|
||||
|
||||
self._setLoadingHint(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
|
||||
|
||||
controller = self.getController()
|
||||
|
@ -979,10 +999,13 @@ class CuraApplication(QtApplication):
|
|||
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
||||
return self._setting_inheritance_manager
|
||||
|
||||
## Get the machine action manager
|
||||
# We ignore any *args given to this, as we also register the machine manager as qml singleton.
|
||||
# It wants to give this function an engine and script engine, but we don't care about that.
|
||||
def getMachineActionManager(self, *args: Any) -> MachineActionManager.MachineActionManager:
|
||||
"""Get the machine action manager
|
||||
|
||||
We ignore any *args given to this, as we also register the machine manager as qml singleton.
|
||||
It wants to give this function an engine and script engine, but we don't care about that.
|
||||
"""
|
||||
|
||||
return cast(MachineActionManager.MachineActionManager, self._machine_action_manager)
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
|
@ -1002,8 +1025,9 @@ class CuraApplication(QtApplication):
|
|||
self._simple_mode_settings_manager = SimpleModeSettingsManager()
|
||||
return self._simple_mode_settings_manager
|
||||
|
||||
## Handle Qt events
|
||||
def event(self, event):
|
||||
"""Handle Qt events"""
|
||||
|
||||
if event.type() == QEvent.FileOpen:
|
||||
if self._plugins_loaded:
|
||||
self._openFile(event.file())
|
||||
|
@ -1015,8 +1039,9 @@ class CuraApplication(QtApplication):
|
|||
def getAutoSave(self) -> Optional[AutoSave]:
|
||||
return self._auto_save
|
||||
|
||||
## Get print information (duration / material used)
|
||||
def getPrintInformation(self):
|
||||
"""Get print information (duration / material used)"""
|
||||
|
||||
return self._print_information
|
||||
|
||||
def getQualityProfilesDropDownMenuModel(self, *args, **kwargs):
|
||||
|
@ -1032,10 +1057,12 @@ class CuraApplication(QtApplication):
|
|||
def getCuraAPI(self, *args, **kwargs) -> "CuraAPI":
|
||||
return self._cura_API
|
||||
|
||||
## Registers objects for the QML engine to use.
|
||||
#
|
||||
# \param engine The QML engine.
|
||||
def registerObjects(self, engine):
|
||||
"""Registers objects for the QML engine to use.
|
||||
|
||||
:param engine: The QML engine.
|
||||
"""
|
||||
|
||||
super().registerObjects(engine)
|
||||
|
||||
# global contexts
|
||||
|
@ -1169,8 +1196,9 @@ class CuraApplication(QtApplication):
|
|||
if node is not None and (node.getMeshData() is not None or node.callDecoration("getLayerData")):
|
||||
self._update_platform_activity_timer.start()
|
||||
|
||||
## Update scene bounding box for current build plate
|
||||
def updatePlatformActivity(self, node = None):
|
||||
"""Update scene bounding box for current build plate"""
|
||||
|
||||
count = 0
|
||||
scene_bounding_box = None
|
||||
is_block_slicing_node = False
|
||||
|
@ -1214,9 +1242,10 @@ class CuraApplication(QtApplication):
|
|||
self._platform_activity = True if count > 0 else False
|
||||
self.activityChanged.emit()
|
||||
|
||||
## Select all nodes containing mesh data in the scene.
|
||||
@pyqtSlot()
|
||||
def selectAll(self):
|
||||
"""Select all nodes containing mesh data in the scene."""
|
||||
|
||||
if not self.getController().getToolsEnabled():
|
||||
return
|
||||
|
||||
|
@ -1235,9 +1264,10 @@ class CuraApplication(QtApplication):
|
|||
|
||||
Selection.add(node)
|
||||
|
||||
## Reset all translation on nodes with mesh data.
|
||||
@pyqtSlot()
|
||||
def resetAllTranslation(self):
|
||||
"""Reset all translation on nodes with mesh data."""
|
||||
|
||||
Logger.log("i", "Resetting all scene translations")
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
|
@ -1263,9 +1293,10 @@ class CuraApplication(QtApplication):
|
|||
op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0)))
|
||||
op.push()
|
||||
|
||||
## Reset all transformations on nodes with mesh data.
|
||||
@pyqtSlot()
|
||||
def resetAll(self):
|
||||
"""Reset all transformations on nodes with mesh data."""
|
||||
|
||||
Logger.log("i", "Resetting all scene transformations")
|
||||
nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
|
@ -1291,9 +1322,10 @@ class CuraApplication(QtApplication):
|
|||
op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1)))
|
||||
op.push()
|
||||
|
||||
## Arrange all objects.
|
||||
@pyqtSlot()
|
||||
def arrangeObjectsToAllBuildPlates(self) -> None:
|
||||
"""Arrange all objects."""
|
||||
|
||||
nodes_to_arrange = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if not isinstance(node, SceneNode):
|
||||
|
@ -1346,17 +1378,21 @@ class CuraApplication(QtApplication):
|
|||
nodes_to_arrange.append(node)
|
||||
self.arrange(nodes_to_arrange, fixed_nodes = [])
|
||||
|
||||
## Arrange a set of nodes given a set of fixed nodes
|
||||
# \param nodes nodes that we have to place
|
||||
# \param fixed_nodes nodes that are placed in the arranger before finding spots for nodes
|
||||
def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode]) -> None:
|
||||
"""Arrange a set of nodes given a set of fixed nodes
|
||||
|
||||
:param nodes: nodes that we have to place
|
||||
:param fixed_nodes: nodes that are placed in the arranger before finding spots for nodes
|
||||
"""
|
||||
|
||||
min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors
|
||||
job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset = max(min_offset, 8))
|
||||
job.start()
|
||||
|
||||
## Reload all mesh data on the screen from file.
|
||||
@pyqtSlot()
|
||||
def reloadAll(self) -> None:
|
||||
"""Reload all mesh data on the screen from file."""
|
||||
|
||||
Logger.log("i", "Reloading all loaded mesh data.")
|
||||
nodes = []
|
||||
has_merged_nodes = False
|
||||
|
@ -1466,8 +1502,9 @@ class CuraApplication(QtApplication):
|
|||
group_node.setName("MergedMesh") # add a specific name to distinguish this node
|
||||
|
||||
|
||||
## Updates origin position of all merged meshes
|
||||
def updateOriginOfMergedMeshes(self, _):
|
||||
"""Updates origin position of all merged meshes"""
|
||||
|
||||
group_nodes = []
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if isinstance(node, CuraSceneNode) and node.getName() == "MergedMesh":
|
||||
|
@ -1585,9 +1622,10 @@ class CuraApplication(QtApplication):
|
|||
scene from its source file. The function gets all the nodes that exist in the file through the job result, and
|
||||
then finds the scene node that it wants to refresh by its object id. Each job refreshes only one node.
|
||||
|
||||
:param job: The ReadMeshJob running in the background that reads all the meshes in a file
|
||||
:return: None
|
||||
:param job: The :py:class:`Uranium.UM.ReadMeshJob.ReadMeshJob` running in the background that reads all the
|
||||
meshes in a file
|
||||
"""
|
||||
|
||||
job_result = job.getResult() # nodes that exist inside the file read by this job
|
||||
if len(job_result) == 0:
|
||||
Logger.log("e", "Reloading the mesh failed.")
|
||||
|
@ -1633,12 +1671,15 @@ class CuraApplication(QtApplication):
|
|||
def additionalComponents(self):
|
||||
return self._additional_components
|
||||
|
||||
## Add a component to a list of components to be reparented to another area in the GUI.
|
||||
# The actual reparenting is done by the area itself.
|
||||
# \param area_id \type{str} Identifying name of the area to which the component should be reparented
|
||||
# \param component \type{QQuickComponent} The component that should be reparented
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def addAdditionalComponent(self, area_id, component):
|
||||
def addAdditionalComponent(self, area_id: str, component):
|
||||
"""Add a component to a list of components to be reparented to another area in the GUI.
|
||||
|
||||
The actual reparenting is done by the area itself.
|
||||
:param area_id: dentifying name of the area to which the component should be reparented
|
||||
:param (QQuickComponent) component: The component that should be reparented
|
||||
"""
|
||||
|
||||
if area_id not in self._additional_components:
|
||||
self._additional_components[area_id] = []
|
||||
self._additional_components[area_id].append(component)
|
||||
|
@ -1653,10 +1694,13 @@ class CuraApplication(QtApplication):
|
|||
|
||||
@pyqtSlot(QUrl, str)
|
||||
@pyqtSlot(QUrl)
|
||||
## Open a local file
|
||||
# \param project_mode How to handle project files. Either None(default): Follow user preference, "open_as_model" or
|
||||
# "open_as_project". This parameter is only considered if the file is a project file.
|
||||
def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None):
|
||||
"""Open a local file
|
||||
|
||||
:param project_mode: How to handle project files. Either None(default): Follow user preference, "open_as_model"
|
||||
or "open_as_project". This parameter is only considered if the file is a project file.
|
||||
"""
|
||||
|
||||
if not file.isValid():
|
||||
return
|
||||
|
||||
|
@ -1829,9 +1873,8 @@ class CuraApplication(QtApplication):
|
|||
|
||||
@pyqtSlot(str, result=bool)
|
||||
def checkIsValidProjectFile(self, file_url):
|
||||
"""
|
||||
Checks if the given file URL is a valid project file.
|
||||
"""
|
||||
""" Checks if the given file URL is a valid project file. """
|
||||
|
||||
file_path = QUrl(file_url).toLocalFile()
|
||||
workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
|
||||
if workspace_reader is None:
|
||||
|
|
|
@ -24,11 +24,15 @@ class CuraPackageManager(PackageManager):
|
|||
|
||||
super().initialize()
|
||||
|
||||
## Returns a list of where the package is used
|
||||
# empty if it is never used.
|
||||
# It loops through all the package contents and see if some of the ids are used.
|
||||
# The list consists of 3-tuples: (global_stack, extruder_nr, container_id)
|
||||
def getMachinesUsingPackage(self, package_id: str) -> Tuple[List[Tuple[GlobalStack, str, str]], List[Tuple[GlobalStack, str, str]]]:
|
||||
"""Returns a list of where the package is used
|
||||
|
||||
It loops through all the package contents and see if some of the ids are used.
|
||||
|
||||
:param package_id: package id to search for
|
||||
:return: empty if it is never used, otherwise a list consisting of 3-tuples
|
||||
"""
|
||||
|
||||
ids = self.getPackageContainerIds(package_id)
|
||||
container_stacks = self._application.getContainerRegistry().findContainerStacks()
|
||||
global_stacks = [container_stack for container_stack in container_stacks if isinstance(container_stack, GlobalStack)]
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
from UM.Mesh.MeshData import MeshData
|
||||
|
||||
|
||||
## Class to holds the layer mesh and information about the layers.
|
||||
# Immutable, use LayerDataBuilder to create one of these.
|
||||
class LayerData(MeshData):
|
||||
"""Class to holds the layer mesh and information about the layers.
|
||||
|
||||
Immutable, use :py:class:`cura.LayerDataBuilder.LayerDataBuilder` to create one of these.
|
||||
"""
|
||||
|
||||
def __init__(self, vertices = None, normals = None, indices = None, colors = None, uvs = None, file_name = None,
|
||||
center_position = None, layers=None, element_counts=None, attributes=None):
|
||||
super().__init__(vertices=vertices, normals=normals, indices=indices, colors=colors, uvs=uvs,
|
||||
|
|
|
@ -10,8 +10,9 @@ import numpy
|
|||
from typing import Dict, Optional
|
||||
|
||||
|
||||
## Builder class for constructing a LayerData object
|
||||
class LayerDataBuilder(MeshBuilder):
|
||||
"""Builder class for constructing a :py:class:`cura.LayerData.LayerData` object"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._layers = {} # type: Dict[int, Layer]
|
||||
|
@ -42,11 +43,13 @@ class LayerDataBuilder(MeshBuilder):
|
|||
|
||||
self._layers[layer].setThickness(thickness)
|
||||
|
||||
## Return the layer data as LayerData.
|
||||
#
|
||||
# \param material_color_map: [r, g, b, a] for each extruder row.
|
||||
# \param line_type_brightness: compatibility layer view uses line type brightness of 0.5
|
||||
def build(self, material_color_map, line_type_brightness = 1.0):
|
||||
"""Return the layer data as :py:class:`cura.LayerData.LayerData`.
|
||||
|
||||
:param material_color_map:: [r, g, b, a] for each extruder row.
|
||||
:param line_type_brightness:: compatibility layer view uses line type brightness of 0.5
|
||||
"""
|
||||
|
||||
vertex_count = 0
|
||||
index_count = 0
|
||||
for layer, data in self._layers.items():
|
||||
|
|
|
@ -7,8 +7,9 @@ from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
|||
from cura.LayerData import LayerData
|
||||
|
||||
|
||||
## Simple decorator to indicate a scene node holds layer data.
|
||||
class LayerDataDecorator(SceneNodeDecorator):
|
||||
"""Simple decorator to indicate a scene node holds layer data."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._layer_data = None # type: Optional[LayerData]
|
||||
|
|
|
@ -26,14 +26,17 @@ class LayerPolygon:
|
|||
|
||||
__jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(__number_of_types) == NoneType, numpy.arange(__number_of_types) == MoveCombingType), numpy.arange(__number_of_types) == MoveRetractionType)
|
||||
|
||||
## LayerPolygon, used in ProcessSlicedLayersJob
|
||||
# \param extruder The position of the extruder
|
||||
# \param line_types array with line_types
|
||||
# \param data new_points
|
||||
# \param line_widths array with line widths
|
||||
# \param line_thicknesses: array with type as index and thickness as value
|
||||
# \param line_feedrates array with line feedrates
|
||||
def __init__(self, extruder: int, line_types: numpy.ndarray, data: numpy.ndarray, line_widths: numpy.ndarray, line_thicknesses: numpy.ndarray, line_feedrates: numpy.ndarray) -> None:
|
||||
"""LayerPolygon, used in ProcessSlicedLayersJob
|
||||
|
||||
:param extruder: The position of the extruder
|
||||
:param line_types: array with line_types
|
||||
:param data: new_points
|
||||
:param line_widths: array with line widths
|
||||
:param line_thicknesses: array with type as index and thickness as value
|
||||
:param line_feedrates: array with line feedrates
|
||||
"""
|
||||
|
||||
self._extruder = extruder
|
||||
self._types = line_types
|
||||
for i in range(len(self._types)):
|
||||
|
@ -83,19 +86,22 @@ class LayerPolygon:
|
|||
self._vertex_begin = 0
|
||||
self._vertex_end = numpy.sum( self._build_cache_needed_points )
|
||||
|
||||
## Set all the arrays provided by the function caller, representing the LayerPolygon
|
||||
# The arrays are either by vertex or by indices.
|
||||
#
|
||||
# \param vertex_offset : determines where to start and end filling the arrays
|
||||
# \param index_offset : determines where to start and end filling the arrays
|
||||
# \param vertices : vertex numpy array to be filled
|
||||
# \param colors : vertex numpy array to be filled
|
||||
# \param line_dimensions : vertex numpy array to be filled
|
||||
# \param feedrates : vertex numpy array to be filled
|
||||
# \param extruders : vertex numpy array to be filled
|
||||
# \param line_types : vertex numpy array to be filled
|
||||
# \param indices : index numpy array to be filled
|
||||
def build(self, vertex_offset: int, index_offset: int, vertices: numpy.ndarray, colors: numpy.ndarray, line_dimensions: numpy.ndarray, feedrates: numpy.ndarray, extruders: numpy.ndarray, line_types: numpy.ndarray, indices: numpy.ndarray) -> None:
|
||||
"""Set all the arrays provided by the function caller, representing the LayerPolygon
|
||||
|
||||
The arrays are either by vertex or by indices.
|
||||
|
||||
:param vertex_offset: : determines where to start and end filling the arrays
|
||||
:param index_offset: : determines where to start and end filling the arrays
|
||||
:param vertices: : vertex numpy array to be filled
|
||||
:param colors: : vertex numpy array to be filled
|
||||
:param line_dimensions: : vertex numpy array to be filled
|
||||
:param feedrates: : vertex numpy array to be filled
|
||||
:param extruders: : vertex numpy array to be filled
|
||||
:param line_types: : vertex numpy array to be filled
|
||||
:param indices: : index numpy array to be filled
|
||||
"""
|
||||
|
||||
if self._build_cache_line_mesh_mask is None or self._build_cache_needed_points is None:
|
||||
self.buildCache()
|
||||
|
||||
|
@ -202,8 +208,12 @@ class LayerPolygon:
|
|||
def jumpCount(self):
|
||||
return self._jump_count
|
||||
|
||||
# Calculate normals for the entire polygon using numpy.
|
||||
def getNormals(self) -> numpy.ndarray:
|
||||
"""Calculate normals for the entire polygon using numpy.
|
||||
|
||||
:return: normals for the entire polygon
|
||||
"""
|
||||
|
||||
normals = numpy.copy(self._data)
|
||||
normals[:, 1] = 0.0 # We are only interested in 2D normals
|
||||
|
||||
|
@ -229,9 +239,10 @@ class LayerPolygon:
|
|||
|
||||
__color_map = None # type: numpy.ndarray
|
||||
|
||||
## Gets the instance of the VersionUpgradeManager, or creates one.
|
||||
@classmethod
|
||||
def getColorMap(cls) -> numpy.ndarray:
|
||||
"""Gets the instance of the VersionUpgradeManager, or creates one."""
|
||||
|
||||
if cls.__color_map is None:
|
||||
theme = cast(Theme, QtApplication.getInstance().getTheme())
|
||||
cls.__color_map = numpy.array([
|
||||
|
|
|
@ -11,16 +11,22 @@ from UM.PluginObject import PluginObject
|
|||
from UM.PluginRegistry import PluginRegistry
|
||||
|
||||
|
||||
## Machine actions are actions that are added to a specific machine type. Examples of such actions are
|
||||
# updating the firmware, connecting with remote devices or doing bed leveling. A machine action can also have a
|
||||
# qml, which should contain a "Cura.MachineAction" item. When activated, the item will be displayed in a dialog
|
||||
# and this object will be added as "manager" (so all pyqtSlot() functions can be called by calling manager.func())
|
||||
class MachineAction(QObject, PluginObject):
|
||||
"""Machine actions are actions that are added to a specific machine type.
|
||||
|
||||
Examples of such actions are updating the firmware, connecting with remote devices or doing bed leveling. A
|
||||
machine action can also have a qml, which should contain a :py:class:`cura.MachineAction.MachineAction` item.
|
||||
When activated, the item will be displayed in a dialog and this object will be added as "manager" (so all
|
||||
pyqtSlot() functions can be called by calling manager.func())
|
||||
"""
|
||||
|
||||
## Create a new Machine action.
|
||||
# \param key unique key of the machine action
|
||||
# \param label Human readable label used to identify the machine action.
|
||||
def __init__(self, key: str, label: str = "") -> None:
|
||||
"""Create a new Machine action.
|
||||
|
||||
:param key: unique key of the machine action
|
||||
:param label: Human readable label used to identify the machine action.
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
self._key = key
|
||||
self._label = label
|
||||
|
@ -34,10 +40,14 @@ class MachineAction(QObject, PluginObject):
|
|||
def getKey(self) -> str:
|
||||
return self._key
|
||||
|
||||
## Whether this action needs to ask the user anything.
|
||||
# If not, we shouldn't present the user with certain screens which otherwise show up.
|
||||
# Defaults to true to be in line with the old behaviour.
|
||||
def needsUserInteraction(self) -> bool:
|
||||
"""Whether this action needs to ask the user anything.
|
||||
|
||||
If not, we shouldn't present the user with certain screens which otherwise show up.
|
||||
|
||||
:return: Defaults to true to be in line with the old behaviour.
|
||||
"""
|
||||
|
||||
return True
|
||||
|
||||
@pyqtProperty(str, notify = labelChanged)
|
||||
|
@ -49,17 +59,24 @@ class MachineAction(QObject, PluginObject):
|
|||
self._label = label
|
||||
self.labelChanged.emit()
|
||||
|
||||
## Reset the action to it's default state.
|
||||
# This should not be re-implemented by child classes, instead re-implement _reset.
|
||||
# /sa _reset
|
||||
@pyqtSlot()
|
||||
def reset(self) -> None:
|
||||
"""Reset the action to it's default state.
|
||||
|
||||
This should not be re-implemented by child classes, instead re-implement _reset.
|
||||
|
||||
:py:meth:`cura.MachineAction.MachineAction._reset`
|
||||
"""
|
||||
|
||||
self._finished = False
|
||||
self._reset()
|
||||
|
||||
## Protected implementation of reset.
|
||||
# /sa reset()
|
||||
def _reset(self) -> None:
|
||||
"""Protected implementation of reset.
|
||||
|
||||
:py:meth:`cura.MachineAction.MachineAction.reset`
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
|
@ -72,8 +89,9 @@ class MachineAction(QObject, PluginObject):
|
|||
def finished(self) -> bool:
|
||||
return self._finished
|
||||
|
||||
## Protected helper to create a view object based on provided QML.
|
||||
def _createViewFromQML(self) -> Optional["QObject"]:
|
||||
"""Protected helper to create a view object based on provided QML."""
|
||||
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if plugin_path is None:
|
||||
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
||||
|
|
|
@ -7,18 +7,21 @@ from UM.Scene.Iterator import Iterator
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from functools import cmp_to_key
|
||||
|
||||
## Iterator that returns a list of nodes in the order that they need to be printed
|
||||
# If there is no solution an empty list is returned.
|
||||
# Take note that the list of nodes can have children (that may or may not contain mesh data)
|
||||
class OneAtATimeIterator(Iterator.Iterator):
|
||||
"""Iterator that returns a list of nodes in the order that they need to be printed
|
||||
|
||||
If there is no solution an empty list is returned.
|
||||
Take note that the list of nodes can have children (that may or may not contain mesh data)
|
||||
"""
|
||||
|
||||
def __init__(self, scene_node) -> None:
|
||||
super().__init__(scene_node) # Call super to make multiple inheritance work.
|
||||
self._hit_map = [[]] # type: List[List[bool]] # For each node, which other nodes this hits. A grid of booleans on which nodes hit which.
|
||||
self._original_node_list = [] # type: List[SceneNode] # The nodes that need to be checked for collisions.
|
||||
|
||||
## Fills the ``_node_stack`` with a list of scene nodes that need to be
|
||||
# printed in order.
|
||||
def _fillStack(self) -> None:
|
||||
"""Fills the ``_node_stack`` with a list of scene nodes that need to be printed in order. """
|
||||
|
||||
node_list = []
|
||||
for node in self._scene_node.getChildren():
|
||||
if not issubclass(type(node), SceneNode):
|
||||
|
@ -75,10 +78,14 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
return True
|
||||
return False
|
||||
|
||||
## Check for a node whether it hits any of the other nodes.
|
||||
# \param node The node to check whether it collides with the other nodes.
|
||||
# \param other_nodes The nodes to check for collisions.
|
||||
def _checkBlockMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
|
||||
"""Check for a node whether it hits any of the other nodes.
|
||||
|
||||
:param node: The node to check whether it collides with the other nodes.
|
||||
:param other_nodes: The nodes to check for collisions.
|
||||
:return: returns collision between nodes
|
||||
"""
|
||||
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
|
@ -86,14 +93,26 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
return True
|
||||
return False
|
||||
|
||||
## Calculate score simply sums the number of other objects it 'blocks'
|
||||
def _calculateScore(self, a: SceneNode, b: SceneNode) -> int:
|
||||
"""Calculate score simply sums the number of other objects it 'blocks'
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: sum of the number of other objects
|
||||
"""
|
||||
|
||||
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: SceneNode, b: SceneNode) -> bool:
|
||||
"""Checks if a can be printed before b
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: true if a can be printed before b
|
||||
"""
|
||||
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
|
@ -116,12 +135,14 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
return False
|
||||
|
||||
|
||||
## Internal object used to keep track of a possible order in which to print objects.
|
||||
class _ObjectOrder:
|
||||
## Creates the _ObjectOrder instance.
|
||||
# \param order List of indices in which to print objects, ordered by printing
|
||||
# order.
|
||||
# \param todo: List of indices which are not yet inserted into the order list.
|
||||
"""Internal object used to keep track of a possible order in which to print objects."""
|
||||
|
||||
def __init__(self, order: List[SceneNode], todo: List[SceneNode]) -> None:
|
||||
"""Creates the _ObjectOrder instance.
|
||||
|
||||
:param order: List of indices in which to print objects, ordered by printing order.
|
||||
:param todo: List of indices which are not yet inserted into the order list.
|
||||
"""
|
||||
self.order = order
|
||||
self.todo = todo
|
||||
|
|
|
@ -16,11 +16,15 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
if TYPE_CHECKING:
|
||||
from UM.View.GL.ShaderProgram import ShaderProgram
|
||||
|
||||
## A RenderPass subclass that renders a the distance of selectable objects from the active camera to a texture.
|
||||
# The texture is used to map a 2d location (eg the mouse location) to a world space position
|
||||
#
|
||||
# Note that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels
|
||||
class PickingPass(RenderPass):
|
||||
"""A :py:class:`Uranium.UM.View.RenderPass` subclass that renders a the distance of selectable objects from the
|
||||
active camera to a texture.
|
||||
|
||||
The texture is used to map a 2d location (eg the mouse location) to a world space position
|
||||
|
||||
.. note:: that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels
|
||||
"""
|
||||
|
||||
def __init__(self, width: int, height: int) -> None:
|
||||
super().__init__("picking", width, height)
|
||||
|
||||
|
@ -50,8 +54,14 @@ class PickingPass(RenderPass):
|
|||
batch.render(self._scene.getActiveCamera())
|
||||
self.release()
|
||||
|
||||
## Get the distance in mm from the camera to at a certain pixel coordinate.
|
||||
def getPickedDepth(self, x: int, y: int) -> float:
|
||||
"""Get the distance in mm from the camera to at a certain pixel coordinate.
|
||||
|
||||
:param x: x component of coordinate vector in pixels
|
||||
:param y: y component of coordinate vector in pixels
|
||||
:return: distance in mm from the camera to pixel coordinate
|
||||
"""
|
||||
|
||||
output = self.getOutput()
|
||||
|
||||
window_size = self._renderer.getWindowSize()
|
||||
|
@ -66,8 +76,14 @@ class PickingPass(RenderPass):
|
|||
distance = (distance & 0x00ffffff) / 1000. # drop the alpha channel and covert to mm
|
||||
return distance
|
||||
|
||||
## Get the world coordinates of a picked point
|
||||
def getPickedPosition(self, x: int, y: int) -> Vector:
|
||||
"""Get the world coordinates of a picked point
|
||||
|
||||
:param x: x component of coordinate vector in pixels
|
||||
:param y: y component of coordinate vector in pixels
|
||||
:return: vector of the world coordinate
|
||||
"""
|
||||
|
||||
distance = self.getPickedDepth(x, y)
|
||||
camera = self._scene.getActiveCamera()
|
||||
if camera:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional, TYPE_CHECKING, cast
|
||||
from typing import Optional, TYPE_CHECKING, cast, List
|
||||
|
||||
|
||||
from UM.Application import Application
|
||||
|
@ -20,9 +20,14 @@ if TYPE_CHECKING:
|
|||
from UM.Scene.Camera import Camera
|
||||
|
||||
|
||||
# Make color brighter by normalizing it (maximum factor 2.5 brighter)
|
||||
# color_list is a list of 4 elements: [r, g, b, a], each element is a float 0..1
|
||||
def prettier_color(color_list):
|
||||
def prettier_color(color_list: List[float]) -> List[float]:
|
||||
"""Make color brighter by normalizing
|
||||
|
||||
maximum factor 2.5 brighter
|
||||
|
||||
:param color_list: a list of 4 elements: [r, g, b, a], each element is a float 0..1
|
||||
:return: a normalized list of 4 elements: [r, g, b, a], each element is a float 0..1
|
||||
"""
|
||||
maximum = max(color_list[:3])
|
||||
if maximum > 0:
|
||||
factor = min(1 / maximum, 2.5)
|
||||
|
@ -31,11 +36,14 @@ def prettier_color(color_list):
|
|||
return [min(i * factor, 1.0) for i in color_list]
|
||||
|
||||
|
||||
## A render pass subclass that renders slicable objects with default parameters.
|
||||
# It uses the active camera by default, but it can be overridden to use a different camera.
|
||||
#
|
||||
# This is useful to get a preview image of a scene taken from a different location as the active camera.
|
||||
class PreviewPass(RenderPass):
|
||||
"""A :py:class:`Uranium.UM.View.RenderPass` subclass that renders slicable objects with default parameters.
|
||||
|
||||
It uses the active camera by default, but it can be overridden to use a different camera.
|
||||
|
||||
This is useful to get a preview image of a scene taken from a different location as the active camera.
|
||||
"""
|
||||
|
||||
def __init__(self, width: int, height: int) -> None:
|
||||
super().__init__("preview", width, height, 0)
|
||||
|
||||
|
|
|
@ -10,8 +10,14 @@ class PrintJobPreviewImageProvider(QQuickImageProvider):
|
|||
def __init__(self):
|
||||
super().__init__(QQuickImageProvider.Image)
|
||||
|
||||
## Request a new image.
|
||||
def requestImage(self, id: str, size: QSize) -> Tuple[QImage, QSize]:
|
||||
"""Request a new image.
|
||||
|
||||
:param id: id of the requested image
|
||||
:param size: is not used defaults to QSize(15, 15)
|
||||
:return: an tuple containing the image and size
|
||||
"""
|
||||
|
||||
# The id will have an uuid and an increment separated by a slash. As we don't care about the value of the
|
||||
# increment, we need to strip that first.
|
||||
uuid = id[id.find("/") + 1:]
|
||||
|
|
|
@ -30,11 +30,17 @@ class Snapshot:
|
|||
|
||||
return min_x, max_x, min_y, max_y
|
||||
|
||||
## Return a QImage of the scene
|
||||
# Uses PreviewPass that leaves out some elements
|
||||
# Aspect ratio assumes a square
|
||||
@staticmethod
|
||||
def snapshot(width = 300, height = 300):
|
||||
"""Return a QImage of the scene
|
||||
|
||||
Uses PreviewPass that leaves out some elements Aspect ratio assumes a square
|
||||
|
||||
:param width: width of the aspect ratio default 300
|
||||
:param height: height of the aspect ratio default 300
|
||||
:return: None when there is no model on the build plate otherwise it will return an image
|
||||
"""
|
||||
|
||||
scene = Application.getInstance().getController().getScene()
|
||||
active_camera = scene.getActiveCamera()
|
||||
render_width, render_height = active_camera.getWindowSize()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue