Browse Source

Merge branch 'master' into Setting-Optimization

Diego Prado Gesto 7 years ago
parent
commit
d2ba63147d

+ 4 - 0
README.md

@@ -27,6 +27,10 @@ Build scripts
 -------------
 Please checkout [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions.
 
+Running from Source
+-------------
+Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Running-Cura-from-Source) for details about running Cura from source.
+
 Plugins
 -------------
 Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Plugin-Directory) for details about creating and using plugins.

+ 3 - 3
cura.appdata.xml

@@ -3,7 +3,7 @@
 <component type="desktop">
   <id>cura.desktop</id>
   <metadata_license>CC0-1.0</metadata_license>
-  <project_license>AGPL-3.0 and CC-BY-SA-4.0</project_license>
+  <project_license>LGPL-3.0 and CC-BY-SA-4.0</project_license>
   <name>Cura</name>
   <summary>The world's most advanced 3d printer software</summary>
   <description>
@@ -15,7 +15,7 @@
     </p>
     <ul>
       <li>Novices can start printing right away</li>
-      <li>Experts are able to customize 200 settings to achieve the best results</li>
+      <li>Experts are able to customize 300 settings to achieve the best results</li>
       <li>Optimized profiles for Ultimaker materials</li>
       <li>Supported by a global network of Ultimaker certified service partners</li>
       <li>Print multiple objects at once with different settings for each object</li>
@@ -26,6 +26,6 @@
   <screenshots>
     <screenshot type="default" width="1280" height="720">http://software.ultimaker.com/Cura.png</screenshot>
   </screenshots>
-  <url type="homepage">https://ultimaker.com/en/products/cura-software</url>
+  <url type="homepage">https://ultimaker.com/en/products/cura-software?utm_source=cura&utm_medium=software&utm_campaign=resources</url>
   <translation type="gettext">Cura</translation>
 </component>

+ 12 - 7
cura/BuildVolume.py

@@ -737,12 +737,17 @@ class BuildVolume(SceneNode):
                 prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
                 prime_tower_y = prime_tower_y + machine_depth / 2
 
-            prime_tower_area = Polygon([
-                [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
-                [prime_tower_x, prime_tower_y - prime_tower_size],
-                [prime_tower_x, prime_tower_y],
-                [prime_tower_x - prime_tower_size, prime_tower_y],
-            ])
+            if self._global_container_stack.getProperty("prime_tower_circular", "value"):
+                radius = prime_tower_size / 2
+                prime_tower_area = Polygon.approximatedCircle(radius)
+                prime_tower_area = prime_tower_area.translate(prime_tower_x - radius, prime_tower_y - radius)
+            else:
+                prime_tower_area = Polygon([
+                    [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
+                    [prime_tower_x, prime_tower_y - prime_tower_size],
+                    [prime_tower_x, prime_tower_y],
+                    [prime_tower_x - prime_tower_size, prime_tower_y],
+                ])
             prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0))
             for extruder in used_extruders:
                 result[extruder.getId()].append(prime_tower_area) #The prime tower location is the same for each extruder, regardless of offset.
@@ -1023,7 +1028,7 @@ class BuildVolume(SceneNode):
     _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap", "layer_0_z_overlap"]
     _extra_z_settings = ["retraction_hop_enabled", "retraction_hop"]
     _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z", "prime_blob_enable"]
-    _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
+    _tower_settings = ["prime_tower_enable", "prime_tower_circular", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
     _ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"]
     _distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts"]
     _extruder_settings = ["support_enable", "support_bottom_enable", "support_roof_enable", "support_infill_extruder_nr", "support_extruder_nr_layer_0", "support_bottom_extruder_nr", "support_roof_extruder_nr", "brim_line_count", "adhesion_extruder_nr", "adhesion_type"] #Settings that can affect which extruders are used.

+ 11 - 7
cura/CrashHandler.py

@@ -13,9 +13,8 @@ import ssl
 import urllib.request
 import urllib.error
 import shutil
-import sys
 
-from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR
+from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QFile
 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton
 
 from UM.Application import Application
@@ -23,6 +22,7 @@ from UM.Logger import Logger
 from UM.View.GL.OpenGL import OpenGL
 from UM.i18n import i18nCatalog
 from UM.Platform import Platform
+from UM.Resources import Resources
 
 catalog = i18nCatalog("cura")
 
@@ -91,6 +91,7 @@ class CrashHandler:
         label = QLabel()
         label.setText(catalog.i18nc("@label crash message", """<p><b>A fatal error has occurred.</p></b>
                     <p>Unfortunately, Cura encountered an unrecoverable error during start up. It was possibly caused by some incorrect configuration files. We suggest to backup and reset your configuration.</p>
+                    <p>Your backup can be found in your Configuration folder.</p>
                     <p>Please send us this Crash Report to fix the problem.</p>
                 """))
         label.setWordWrap(True)
@@ -162,12 +163,15 @@ class CrashHandler:
                 file_name = base_name + "_" + date_now + "_" + idx
                 zip_file_path = os.path.join(root_dir, file_name + ".zip")
             try:
-                # remove the .zip extension because make_archive() adds it
-                zip_file_path = zip_file_path[:-4]
-                shutil.make_archive(zip_file_path, "zip", root_dir = root_dir, base_dir = base_name)
+                # only create the zip backup when the folder exists
+                if os.path.exists(folder):
+                    # remove the .zip extension because make_archive() adds it
+                    zip_file_path = zip_file_path[:-4]
+                    shutil.make_archive(zip_file_path, "zip", root_dir = root_dir, base_dir = base_name)
+
+                    # remove the folder only when the backup is successful
+                    shutil.rmtree(folder, ignore_errors = True)
 
-                # remove the folder only when the backup is successful
-                shutil.rmtree(folder, ignore_errors = True)
                 # create an empty folder so Resources will not try to copy the old ones
                 os.makedirs(folder, 0o0755, exist_ok=True)
 

+ 9 - 2
cura/CuraApplication.py

@@ -12,6 +12,7 @@ from UM.Math.Vector import Vector
 from UM.Math.Quaternion import Quaternion
 from UM.Math.AxisAlignedBox import AxisAlignedBox
 from UM.Math.Matrix import Matrix
+from UM.Platform import Platform
 from UM.Resources import Resources
 from UM.Scene.ToolHandle import ToolHandle
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@@ -588,7 +589,13 @@ class CuraApplication(QtApplication):
     def _loadPlugins(self):
         self._plugin_registry.addType("profile_reader", self._addProfileReader)
         self._plugin_registry.addType("profile_writer", self._addProfileWriter)
-        self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib", "cura"))
+
+        if Platform.isLinux():
+            lib_suffixes = {"", "64", "32", "x32"} #A few common ones on different distributions.
+        else:
+            lib_suffixes = {""}
+        for suffix in lib_suffixes:
+            self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib" + suffix, "cura"))
         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")
@@ -1287,7 +1294,7 @@ class CuraApplication(QtApplication):
         Logger.log("i", "Reloading all loaded mesh data.")
         nodes = []
         for node in DepthFirstIterator(self.getController().getScene().getRoot()):
-            if not isinstance(node, SceneNode) or not node.getMeshData():
+            if not isinstance(node, CuraSceneNode) or not node.getMeshData():
                 continue
 
             nodes.append(node)

+ 16 - 1
cura/PreviewPass.py

@@ -17,6 +17,18 @@ MYPY = False
 if MYPY:
     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):
+    maximum = max(color_list[:3])
+    if maximum > 0:
+        factor = min(1 / maximum, 2.5)
+    else:
+        factor = 1.0
+    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.
 #
@@ -41,6 +53,9 @@ class PreviewPass(RenderPass):
         if not self._shader:
             self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
             self._shader.setUniformValue("u_overhangAngle", 1.0)
+            self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0])
+            self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0])
+            self._shader.setUniformValue("u_shininess", 20.0)
 
         self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
         self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
@@ -52,7 +67,7 @@ class PreviewPass(RenderPass):
         for node in DepthFirstIterator(self._scene.getRoot()):
             if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
                 uniforms = {}
-                uniforms["diffuse_color"] = node.getDiffuseColor()
+                uniforms["diffuse_color"] = prettier_color(node.getDiffuseColor())
                 batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms)
 
         self.bind()

+ 2 - 2
cura/PrintInformation.py

@@ -361,10 +361,10 @@ class PrintInformation(QObject):
         if not global_container_stack:
             self._abbr_machine = ""
             return
-        active_machine_type_id = global_container_stack.definition.getId()
+        active_machine_type_name = global_container_stack.definition.getName()
 
         abbr_machine = ""
-        for word in re.findall(r"[\w']+", active_machine_type_id):
+        for word in re.findall(r"[\w']+", active_machine_type_name):
             if word.lower() == "ultimaker":
                 abbr_machine += "UM"
             elif word.isdigit():

+ 1 - 1
cura/Settings/CuraContainerRegistry.py

@@ -235,7 +235,7 @@ class CuraContainerRegistry(ContainerRegistry):
                     if not expected_machine_definition:
                         expected_machine_definition = global_container_stack.definition.getId()
                 if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition:
-                    Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name)
+                    Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name, profile_definition, expected_machine_definition)
                     return { "status": "error",
                              "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "The machine defined in profile <filename>{0}</filename> doesn't match with your current machine, could not import it.", file_name)}
 

+ 24 - 25
cura/Settings/MachineManager.py

@@ -4,12 +4,13 @@
 import collections
 import time
 #Type hinting.
-from typing import Union, List, Dict
+from typing import Union, List, Dict, TYPE_CHECKING, Optional
 
 from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
 from UM.Signal import Signal
 
 from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
+import UM.FlameProfiler
 from UM.FlameProfiler import pyqtSlot
 from PyQt5.QtWidgets import QMessageBox
 from UM import Util
@@ -25,7 +26,7 @@ from UM.Settings.ContainerStack import ContainerStack
 from UM.Settings.InstanceContainer import InstanceContainer
 from UM.Settings.SettingFunction import SettingFunction
 from UM.Signal import postponeSignals, CompressTechnique
-import UM.FlameProfiler
+
 
 from cura.QualityManager import QualityManager
 from cura.PrinterOutputDevice import PrinterOutputDevice
@@ -37,7 +38,6 @@ from UM.i18n import i18nCatalog
 catalog = i18nCatalog("cura")
 
 from cura.Settings.ProfilesModel import ProfilesModel
-from typing import TYPE_CHECKING, Optional
 
 if TYPE_CHECKING:
     from UM.Settings.DefinitionContainer import DefinitionContainer
@@ -55,10 +55,10 @@ class MachineManager(QObject):
         self.machine_extruder_material_update_dict = collections.defaultdict(list)
 
         # Used to store the new containers until after confirming the dialog
-        self._new_variant_container = None
-        self._new_buildplate_container = None
-        self._new_material_container = None
-        self._new_quality_containers = []
+        self._new_variant_container = None  # type: Optional[InstanceContainer]
+        self._new_buildplate_container = None  # type: Optional[InstanceContainer]
+        self._new_material_container = None  # type: Optional[InstanceContainer]
+        self._new_quality_containers = [] # type: List[Dict]
 
         self._error_check_timer = QTimer()
         self._error_check_timer.setInterval(250)
@@ -79,7 +79,7 @@ class MachineManager(QObject):
         self.globalContainerChanged.connect(self.activeVariantChanged)
         self.globalContainerChanged.connect(self.activeQualityChanged)
 
-        self._stacks_have_errors = None
+        self._stacks_have_errors = None  # type:Optional[bool]
 
         self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0]
         self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0]
@@ -181,7 +181,7 @@ class MachineManager(QObject):
     def totalNumberOfSettings(self) -> int:
         return len(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0].getAllKeys())
 
-    def _onHotendIdChanged(self):
+    def _onHotendIdChanged(self) -> None:
         if not self._global_container_stack or not self._printer_output_devices:
             return
         
@@ -209,7 +209,7 @@ class MachineManager(QObject):
             # A change was found, let the output device handle this.
             self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
 
-    def _onMaterialIdChanged(self):
+    def _onMaterialIdChanged(self) -> None:
         if not self._global_container_stack or not self._printer_output_devices:
             return
 
@@ -246,7 +246,7 @@ class MachineManager(QObject):
             # A change was found, let the output device handle this.
             self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
 
-    def _materialHotendChangedCallback(self, button):
+    def _materialHotendChangedCallback(self, button) -> None:
         if button == QMessageBox.No:
             self._auto_materials_changed = {}
             self._auto_hotends_changed = {}
@@ -254,7 +254,7 @@ class MachineManager(QObject):
         self._autoUpdateMaterials()
         self._autoUpdateHotends()
 
-    def _autoUpdateMaterials(self):
+    def _autoUpdateMaterials(self) -> None:
         extruder_manager = ExtruderManager.getInstance()
         for position in self._auto_materials_changed:
             material_id = self._auto_materials_changed[position]
@@ -270,9 +270,9 @@ class MachineManager(QObject):
 
             if old_index is not None:
                 extruder_manager.setActiveExtruderIndex(old_index)
-        self._auto_materials_changed = {} #Processed all of them now.
+        self._auto_materials_changed = {}  # Processed all of them now.
 
-    def _autoUpdateHotends(self):
+    def _autoUpdateHotends(self) -> None:
         extruder_manager = ExtruderManager.getInstance()
         for position in self._auto_hotends_changed:
             hotend_id = self._auto_hotends_changed[position]
@@ -289,7 +289,7 @@ class MachineManager(QObject):
                 extruder_manager.setActiveExtruderIndex(old_index)
         self._auto_hotends_changed = {}  # Processed all of them now.
 
-    def _onGlobalContainerChanged(self):
+    def _onGlobalContainerChanged(self) -> None:
         if self._global_container_stack:
             try:
                 self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
@@ -308,7 +308,7 @@ class MachineManager(QObject):
                 extruder_stack.propertyChanged.disconnect(self._onPropertyChanged)
                 extruder_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
 
-        # update the local global container stack reference
+        # Update the local global container stack reference
         self._global_container_stack = Application.getInstance().getGlobalContainerStack()
 
         self.globalContainerChanged.emit()
@@ -345,14 +345,14 @@ class MachineManager(QObject):
         self._error_check_timer.start()
 
     ##  Update self._stacks_valid according to _checkStacksForErrors and emit if change.
-    def _updateStacksHaveErrors(self):
+    def _updateStacksHaveErrors(self) -> None:
         old_stacks_have_errors = self._stacks_have_errors
         self._stacks_have_errors = self._checkStacksHaveErrors()
         if old_stacks_have_errors != self._stacks_have_errors:
             self.stacksValidationChanged.emit()
         Application.getInstance().stacksValidationFinished.emit()
 
-    def _onActiveExtruderStackChanged(self):
+    def _onActiveExtruderStackChanged(self) -> None:
         self.blurSettings.emit()  # Ensure no-one has focus.
         old_active_container_stack = self._active_container_stack
 
@@ -365,17 +365,16 @@ class MachineManager(QObject):
             # on _active_container_stack. If it changes, then the properties change.
             self.activeQualityChanged.emit()
 
-    def __emitChangedSignals(self):
+    def __emitChangedSignals(self) -> None:
         self.activeQualityChanged.emit()
         self.activeVariantChanged.emit()
         self.activeMaterialChanged.emit()
-        self._updateStacksHaveErrors()  # Prevents unwanted re-slices after changing machine
         self._error_check_timer.start()
 
-    def _onProfilesModelChanged(self, *args):
+    def _onProfilesModelChanged(self, *args) -> None:
         self.__emitChangedSignals()
 
-    def _onInstanceContainersChanged(self, container):
+    def _onInstanceContainersChanged(self, container) -> None:
         # This should not trigger the ProfilesModel to be created, or there will be an infinite recursion
         if not self._connected_to_profiles_model and ProfilesModel.hasInstance():
             # This triggers updating the qualityModel in SidebarSimple whenever ProfilesModel is updated
@@ -385,7 +384,7 @@ class MachineManager(QObject):
 
         self._instance_container_timer.start()
 
-    def _onPropertyChanged(self, key: str, property_name: str):
+    def _onPropertyChanged(self, key: str, property_name: str) -> None:
         if property_name == "value":
             # Notify UI items, such as the "changed" star in profile pull down menu.
             self.activeStackValueChanged.emit()
@@ -442,7 +441,7 @@ class MachineManager(QObject):
 
     ##  Remove all instances from the top instanceContainer (effectively removing all user-changed settings)
     @pyqtSlot()
-    def clearUserSettings(self):
+    def clearUserSettings(self) -> None:
         if not self._active_container_stack:
             return
 
@@ -480,7 +479,7 @@ class MachineManager(QObject):
     ##  Delete a user setting from the global stack and all extruder stacks.
     #   \param key \type{str} the name of the key to delete
     @pyqtSlot(str)
-    def clearUserSettingAllCurrentStacks(self, key: str):
+    def clearUserSettingAllCurrentStacks(self, key: str) -> None:
         if not self._global_container_stack:
             return
 

+ 2 - 1
cura/Snapshot.py

@@ -57,8 +57,9 @@ class Snapshot:
                 else:
                     bbox = bbox + node.getBoundingBox()
 
+        # If there is no bounding box, it means that there is no model in the buildplate
         if bbox is None:
-            bbox = AxisAlignedBox()
+            return None
 
         look_at = bbox.center
         # guessed size so the objects are hopefully big

Some files were not shown because too many files changed in this diff