Merge pull request #2758 from fieldOfView/feature_mesh_types

Improved mesh type UX (Per Model Settings)
This commit is contained in:
ChrisTerBeke 2017-11-29 13:24:38 +01:00 committed by GitHub
commit 0668f80792
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 168 additions and 42 deletions

View File

@ -52,6 +52,8 @@ class Arrange:
# Place all objects fixed nodes # Place all objects fixed nodes
for fixed_node in fixed_nodes: for fixed_node in fixed_nodes:
vertices = fixed_node.callDecoration("getConvexHull") vertices = fixed_node.callDecoration("getConvexHull")
if not vertices:
continue
points = copy.deepcopy(vertices._points) points = copy.deepcopy(vertices._points)
shape_arr = ShapeArray.fromPolygon(points, scale = scale) shape_arr = ShapeArray.fromPolygon(points, scale = scale)
arranger.place(0, 0, shape_arr) arranger.place(0, 0, shape_arr)

View File

@ -56,6 +56,11 @@ class ConvexHullDecorator(SceneNodeDecorator):
if self._node is None: if self._node is None:
return None return None
if getattr(self._node, "_non_printing_mesh", False):
# infill_mesh, cutting_mesh and anti_overhang_mesh do not need a convex hull
# node._non_printing_mesh is set in SettingOverrideDecorator
return None
hull = self._compute2DConvexHull() hull = self._compute2DConvexHull()
if self._global_stack and self._node: if self._global_stack and self._node:

View File

@ -22,6 +22,14 @@ class SettingOverrideDecorator(SceneNodeDecorator):
## Event indicating that the user selected a different extruder. ## Event indicating that the user selected a different extruder.
activeExtruderChanged = Signal() activeExtruderChanged = Signal()
## Non-printing meshes
#
# If these settings are True for any mesh, the mesh does not need a convex hull,
# and is sent to the slicer regardless of whether it fits inside the build volume.
# Note that Support Mesh is not in here because it actually generates
# g-code in the volume of the mesh.
_non_printing_mesh_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh"}
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._stack = PerObjectContainerStack(stack_id = id(self)) self._stack = PerObjectContainerStack(stack_id = id(self))
@ -78,6 +86,8 @@ class SettingOverrideDecorator(SceneNodeDecorator):
Application.getInstance().getBackend().needsSlicing() Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle() Application.getInstance().getBackend().tickle()
self._node._non_printing_mesh = any(self._stack.getProperty(setting, "value") for setting in self._non_printing_mesh_settings)
## Makes sure that the stack upon which the container stack is placed is ## Makes sure that the stack upon which the container stack is placed is
# kept up to date. # kept up to date.
def _updateNextStack(self): def _updateNextStack(self):

View File

@ -45,14 +45,6 @@ class GcodeStartEndFormatter(Formatter):
## Job class that builds up the message of scene data to send to CuraEngine. ## Job class that builds up the message of scene data to send to CuraEngine.
class StartSliceJob(Job): class StartSliceJob(Job):
## Meshes that are sent to the engine regardless of being outside of the
# build volume.
#
# If these settings are True for any mesh, the build volume is ignored.
# Note that Support Mesh is not in here because it actually generates
# g-code in the volume of the mesh.
_not_printed_mesh_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh"}
def __init__(self, slice_message): def __init__(self, slice_message):
super().__init__() super().__init__()
@ -141,8 +133,7 @@ class StartSliceJob(Job):
temp_list = [] temp_list = []
for node in DepthFirstIterator(self._scene.getRoot()): for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
if not getattr(node, "_outside_buildarea", False)\ if not getattr(node, "_outside_buildarea", False) or getattr(node, "_non_printing_mesh", False):
or (node.callDecoration("getStack") and any(node.callDecoration("getStack").getProperty(setting, "value") for setting in self._not_printed_mesh_settings)):
temp_list.append(node) temp_list.append(node)
Job.yieldThread() Job.yieldThread()

View File

@ -26,17 +26,92 @@ Item {
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
Row
{
spacing: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label","Mesh Type")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
height: UM.Theme.getSize("setting").height
verticalAlignment: Text.AlignVCenter
}
ComboBox
{
id: meshTypeSelection
style: UM.Theme.styles.combobox
onActivated: {
UM.ActiveTool.setProperty("MeshType", model.get(index).type)
}
model: ListModel
{
id: meshTypeModel
Component.onCompleted:
{
meshTypeModel.append({
type: "",
text: catalog.i18nc("@label", "Normal model")
});
meshTypeModel.append({
type: "support_mesh",
text: catalog.i18nc("@label", "Print as support")
});
meshTypeModel.append({
type: "anti_overhang_mesh",
text: catalog.i18nc("@label", "Don't support overlap with other models")
});
meshTypeModel.append({
type: "cutting_mesh",
text: catalog.i18nc("@label", "Modify settings for overlap with other models")
});
meshTypeModel.append({
type: "infill_mesh",
text: catalog.i18nc("@label", "Modify settings for infill of other models")
});
meshTypeSelection.updateCurrentIndex();
}
}
function updateCurrentIndex()
{
var mesh_type = UM.ActiveTool.properties.getValue("MeshType");
for(var index=0; index < meshTypeSelection.model.count; index++)
{
if(meshTypeSelection.model.get(index).type == mesh_type)
{
meshTypeSelection.currentIndex = index;
return;
}
}
meshTypeSelection.currentIndex = 0;
}
}
Connections
{
target: UM.Selection
onSelectionChanged: meshTypeSelection.updateCurrentIndex()
}
}
Column Column
{ {
// This is to ensure that the panel is first increasing in size up to 200 and then shows a scrollbar. // This is to ensure that the panel is first increasing in size up to 200 and then shows a scrollbar.
// It kinda looks ugly otherwise (big panel, no content on it) // It kinda looks ugly otherwise (big panel, no content on it)
id: currentSettings
property int maximumHeight: 200 * screenScaleFactor property int maximumHeight: 200 * screenScaleFactor
height: Math.min(contents.count * (UM.Theme.getSize("section").height + UM.Theme.getSize("default_lining").height), maximumHeight) height: Math.min(contents.count * (UM.Theme.getSize("section").height + UM.Theme.getSize("default_lining").height), maximumHeight)
visible: ["support_mesh", "anti_overhang_mesh"].indexOf(meshTypeSelection.model.get(meshTypeSelection.currentIndex).type) == -1
ScrollView ScrollView
{ {
height: parent.height height: parent.height
width: UM.Theme.getSize("setting").width + UM.Theme.getSize("setting").height width: UM.Theme.getSize("setting").width
style: UM.Theme.styles.scrollview style: UM.Theme.styles.scrollview
ListView ListView
@ -49,6 +124,7 @@ Item {
id: addedSettingsModel; id: addedSettingsModel;
containerId: Cura.MachineManager.activeDefinitionId containerId: Cura.MachineManager.activeDefinitionId
expanded: [ "*" ] expanded: [ "*" ]
exclude: [ "support_mesh", "anti_overhang_mesh", "cutting_mesh", "infill_mesh" ]
visibilityHandler: Cura.PerObjectSettingVisibilityHandler visibilityHandler: Cura.PerObjectSettingVisibilityHandler
{ {
@ -58,6 +134,7 @@ Item {
delegate: Row delegate: Row
{ {
spacing: - UM.Theme.getSize("default_margin").width
Loader Loader
{ {
id: settingLoader id: settingLoader
@ -112,7 +189,7 @@ Item {
Button Button
{ {
width: (UM.Theme.getSize("setting").height / 2) | 0 width: Math.floor(UM.Theme.getSize("setting").height / 2)
height: UM.Theme.getSize("setting").height height: UM.Theme.getSize("setting").height
onClicked: addedSettingsModel.setVisible(model.key, false) onClicked: addedSettingsModel.setVisible(model.key, false)
@ -125,7 +202,7 @@ Item {
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: parent.width width: parent.width
height: parent.height / 2 height: width
sourceSize.width: width sourceSize.width: width
sourceSize.height: width sourceSize.height: width
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
@ -201,9 +278,9 @@ Item {
Button Button
{ {
id: customise_settings_button; id: customiseSettingsButton;
height: UM.Theme.getSize("setting").height; height: UM.Theme.getSize("setting_control").height;
visible: parseInt(UM.Preferences.getValue("cura/active_mode")) == 1 visible: currentSettings.visible
text: catalog.i18nc("@action:button", "Select settings"); text: catalog.i18nc("@action:button", "Select settings");
@ -223,21 +300,12 @@ Item {
{ {
text: control.text; text: control.text;
color: UM.Theme.getColor("setting_control_text"); color: UM.Theme.getColor("setting_control_text");
font: UM.Theme.getFont("default")
anchors.centerIn: parent anchors.centerIn: parent
} }
} }
onClicked: settingPickDialog.visible = true; onClicked: settingPickDialog.visible = true;
Connections
{
target: UM.Preferences;
onPreferenceChanged:
{
customise_settings_button.visible = parseInt(UM.Preferences.getValue("cura/active_mode"))
}
}
} }
} }
@ -325,7 +393,7 @@ Item {
} }
visibilityHandler: UM.SettingPreferenceVisibilityHandler {} visibilityHandler: UM.SettingPreferenceVisibilityHandler {}
expanded: [ "*" ] expanded: [ "*" ]
exclude: [ "machine_settings", "command_line_settings" ] exclude: [ "machine_settings", "command_line_settings", "support_mesh", "anti_overhang_mesh", "cutting_mesh", "infill_mesh" ]
} }
delegate:Loader delegate:Loader
{ {

View File

@ -8,6 +8,7 @@ from UM.Application import Application
from UM.Preferences import Preferences from UM.Preferences import Preferences
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.SettingInstance import SettingInstance
from UM.Event import Event from UM.Event import Event
@ -18,7 +19,7 @@ class PerObjectSettingsTool(Tool):
super().__init__() super().__init__()
self._model = None self._model = None
self.setExposedProperties("SelectedObjectId", "ContainerID", "SelectedActiveExtruder") self.setExposedProperties("SelectedObjectId", "ContainerID", "SelectedActiveExtruder", "MeshType")
self._advanced_mode = False self._advanced_mode = False
self._multi_extrusion = False self._multi_extrusion = False
@ -70,6 +71,39 @@ class PerObjectSettingsTool(Tool):
selected_object.addDecorator(SettingOverrideDecorator()) selected_object.addDecorator(SettingOverrideDecorator())
selected_object.callDecoration("setActiveExtruder", extruder_stack_id) selected_object.callDecoration("setActiveExtruder", extruder_stack_id)
def setMeshType(self, mesh_type):
selected_object = Selection.getSelectedObject(0)
stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
if not stack:
selected_object.addDecorator(SettingOverrideDecorator())
stack = selected_object.callDecoration("getStack")
settings = stack.getTop()
for property_key in ["infill_mesh", "cutting_mesh", "support_mesh", "anti_overhang_mesh"]:
if property_key != mesh_type:
if settings.getInstance(property_key):
settings.removeInstance(property_key)
else:
if not (settings.getInstance(property_key) and settings.getProperty(property_key, "value")):
definition = stack.getSettingDefinition(property_key)
new_instance = SettingInstance(definition, settings)
new_instance.setProperty("value", True)
new_instance.resetState() # Ensure that the state is not seen as a user state.
settings.addInstance(new_instance)
def getMeshType(self):
selected_object = Selection.getSelectedObject(0)
stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway.
if not stack:
return ""
settings = stack.getTop()
for property_key in ["infill_mesh", "cutting_mesh", "support_mesh", "anti_overhang_mesh"]:
if settings.getInstance(property_key) and settings.getProperty(property_key, "value"):
return property_key
return ""
def _onPreferenceChanged(self, preference): def _onPreferenceChanged(self, preference):
if preference == "cura/active_mode": if preference == "cura/active_mode":
self._advanced_mode = Preferences.getInstance().getValue(preference) == 1 self._advanced_mode = Preferences.getInstance().getValue(preference) == 1

View File

@ -27,25 +27,33 @@ class SolidView(View):
self._enabled_shader = None self._enabled_shader = None
self._disabled_shader = None self._disabled_shader = None
self._non_printing_shader = None
self._extruders_model = ExtrudersModel() self._extruders_model = ExtrudersModel()
self._theme = None
def beginRendering(self): def beginRendering(self):
scene = self.getController().getScene() scene = self.getController().getScene()
renderer = self.getRenderer() renderer = self.getRenderer()
if not self._theme:
self._theme = Application.getInstance().getTheme()
if not self._enabled_shader: if not self._enabled_shader:
self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
theme = Application.getInstance().getTheme() self._enabled_shader.setUniformValue("u_overhangColor", Color(*self._theme.getColor("model_overhang").getRgb()))
self._enabled_shader.setUniformValue("u_overhangColor", Color(*theme.getColor("model_overhang").getRgb()))
if not self._disabled_shader: if not self._disabled_shader:
self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader"))
theme = Application.getInstance().getTheme() self._disabled_shader.setUniformValue("u_diffuseColor1", Color(*self._theme.getColor("model_unslicable").getRgb()))
self._disabled_shader.setUniformValue("u_diffuseColor1", Color(*theme.getColor("model_unslicable").getRgb())) self._disabled_shader.setUniformValue("u_diffuseColor2", Color(*self._theme.getColor("model_unslicable_alt").getRgb()))
self._disabled_shader.setUniformValue("u_diffuseColor2", Color(*theme.getColor("model_unslicable_alt").getRgb()))
self._disabled_shader.setUniformValue("u_width", 50.0) self._disabled_shader.setUniformValue("u_width", 50.0)
if not self._non_printing_shader:
self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
self._non_printing_shader.setUniformValue("u_diffuseColor", Color(*self._theme.getColor("model_non_printing").getRgb()))
self._non_printing_shader.setUniformValue("u_opacity", 0.6)
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack: if global_container_stack:
support_extruder_nr = global_container_stack.getProperty("support_extruder_nr", "value") support_extruder_nr = global_container_stack.getProperty("support_extruder_nr", "value")
@ -68,11 +76,19 @@ class SolidView(View):
uniforms = {} uniforms = {}
shade_factor = 1.0 shade_factor = 1.0
per_mesh_stack = node.callDecoration("getStack")
# Get color to render this mesh in from ExtrudersModel # Get color to render this mesh in from ExtrudersModel
extruder_index = 0 extruder_index = 0
extruder_id = node.callDecoration("getActiveExtruder") extruder_id = node.callDecoration("getActiveExtruder")
if extruder_id: if extruder_id:
extruder_index = max(0, self._extruders_model.find("id", extruder_id)) extruder_index = max(0, self._extruders_model.find("id", extruder_id))
# Use the support extruder instead of the active extruder if this is a support_mesh
if per_mesh_stack:
if per_mesh_stack.getProperty("support_mesh", "value"):
extruder_index = int(global_container_stack.getProperty("support_extruder_nr", "value"))
try: try:
material_color = self._extruders_model.getItem(extruder_index)["color"] material_color = self._extruders_model.getItem(extruder_index)["color"]
except KeyError: except KeyError:
@ -94,19 +110,17 @@ class SolidView(View):
except ValueError: except ValueError:
pass pass
if hasattr(node, "_outside_buildarea"): if getattr(node, "_non_printing_mesh", False):
if node._outside_buildarea: if per_mesh_stack and (per_mesh_stack.getProperty("infill_mesh", "value") or per_mesh_stack.getProperty("cutting_mesh", "value")):
renderer.queueNode(node, shader = self._non_printing_shader, uniforms = uniforms, transparent = True)
else:
renderer.queueNode(node, shader = self._non_printing_shader, transparent = True)
elif getattr(node, "_outside_buildarea", False):
renderer.queueNode(node, shader = self._disabled_shader) renderer.queueNode(node, shader = self._disabled_shader)
else: else:
renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms) renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms)
else:
renderer.queueNode(node, material = self._enabled_shader, uniforms = uniforms)
if node.callDecoration("isGroup") and Selection.isSelected(node): if node.callDecoration("isGroup") and Selection.isSelected(node):
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = RenderBatch.RenderMode.LineLoop) renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = RenderBatch.RenderMode.LineLoop)
def endRendering(self): def endRendering(self):
pass pass
#def _onPreferenceChanged(self, preference):
#if preference == "view/show_overhang": ## Todo: This a printer only setting. Should be removed from Uranium.
#self._enabled_material = None

View File

@ -111,6 +111,7 @@ u_modelMatrix = model_matrix
u_viewProjectionMatrix = view_projection_matrix u_viewProjectionMatrix = view_projection_matrix
u_normalMatrix = normal_matrix u_normalMatrix = normal_matrix
u_lightPosition = light_0_position u_lightPosition = light_0_position
u_diffuseColor = diffuse_color
[attributes] [attributes]
a_vertex = vertex a_vertex = vertex

View File

@ -266,6 +266,7 @@
"model_unslicable": [122, 122, 122, 255], "model_unslicable": [122, 122, 122, 255],
"model_unslicable_alt": [172, 172, 127, 255], "model_unslicable_alt": [172, 172, 127, 255],
"model_selection_outline": [12, 169, 227, 255], "model_selection_outline": [12, 169, 227, 255],
"model_non_printing": [122, 122, 122, 255],
"xray": [26, 26, 62, 255], "xray": [26, 26, 62, 255],
"xray_error": [255, 0, 0, 255], "xray_error": [255, 0, 0, 255],