diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index 41358b83f5..bfd2b0c50a 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -217,7 +217,7 @@ class CuraApplication(QtApplication):
"CuraEngineBackend",
"UserAgreement",
"SolidView",
- "LayerView",
+ "SimulationView",
"STLReader",
"SelectionTool",
"CameraTool",
@@ -1383,7 +1383,7 @@ class CuraApplication(QtApplication):
extension = os.path.splitext(filename)[1]
if extension.lower() in self._non_sliceable_extensions:
- self.getController().setActiveView("LayerView")
+ self.getController().setActiveView("SimulationView")
view = self.getController().getActiveView()
view.resetLayerData()
view.setLayer(9999999)
diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py
index 44e028093b..a5a1a5b584 100755
--- a/plugins/CuraEngineBackend/CuraEngineBackend.py
+++ b/plugins/CuraEngineBackend/CuraEngineBackend.py
@@ -588,7 +588,7 @@ class CuraEngineBackend(QObject, Backend):
def _onActiveViewChanged(self):
if Application.getInstance().getController().getActiveView():
view = Application.getInstance().getController().getActiveView()
- if view.getPluginId() in ("LayerView", "SimulationView"): # If switching to layer view, we should process the layers if that hasn't been done yet.
+ if view.getPluginId() == "SimulationView": # If switching to layer view, we should process the layers if that hasn't been done yet.
self._layer_view_active = True
# There is data and we're not slicing at the moment
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
index 30fcf6cced..14646cbac1 100644
--- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
+++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
@@ -61,7 +61,7 @@ class ProcessSlicedLayersJob(Job):
def run(self):
start_time = time()
- if Application.getInstance().getController().getActiveView().getPluginId() in ("LayerView", "SimulationView"):
+ if Application.getInstance().getController().getActiveView().getPluginId() == "SimulationView":
self._progress_message.show()
Job.yieldThread()
if self._abort_requested:
@@ -221,7 +221,7 @@ class ProcessSlicedLayersJob(Job):
self._progress_message.setProgress(100)
view = Application.getInstance().getController().getActiveView()
- if view.getPluginId() in ("LayerView", "SimulationView"):
+ if view.getPluginId() == "SimulationView":
view.resetLayerData()
if self._progress_message:
@@ -234,7 +234,7 @@ class ProcessSlicedLayersJob(Job):
def _onActiveViewChanged(self):
if self.isRunning():
- if Application.getInstance().getController().getActiveView().getPluginId() in ("LayerView", "SimulationView"):
+ if Application.getInstance().getController().getActiveView().getPluginId() == "SimulationView":
if not self._progress_message:
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, 0, catalog.i18nc("@info:title", "Information"))
if self._progress_message.getProgress() != 100:
diff --git a/plugins/LayerView/LayerPass.py b/plugins/LayerView/LayerPass.py
deleted file mode 100755
index 963c8c75c8..0000000000
--- a/plugins/LayerView/LayerPass.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright (c) 2016 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
-from UM.Resources import Resources
-from UM.Scene.SceneNode import SceneNode
-from UM.Scene.ToolHandle import ToolHandle
-from UM.Application import Application
-from UM.PluginRegistry import PluginRegistry
-
-from UM.View.RenderPass import RenderPass
-from UM.View.RenderBatch import RenderBatch
-from UM.View.GL.OpenGL import OpenGL
-
-from cura.Settings.ExtruderManager import ExtruderManager
-
-
-import os.path
-
-## RenderPass used to display g-code paths.
-class LayerPass(RenderPass):
- def __init__(self, width, height):
- super().__init__("layerview", width, height)
-
- self._layer_shader = None
- self._tool_handle_shader = None
- self._gl = OpenGL.getInstance().getBindingsObject()
- self._scene = Application.getInstance().getController().getScene()
- self._extruder_manager = ExtruderManager.getInstance()
-
- self._layer_view = None
- self._compatibility_mode = None
-
- def setLayerView(self, layerview):
- self._layer_view = layerview
- self._compatibility_mode = layerview.getCompatibilityMode()
-
- def render(self):
- if not self._layer_shader:
- if self._compatibility_mode:
- shader_filename = "layers.shader"
- else:
- shader_filename = "layers3d.shader"
- self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), shader_filename))
- # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
- self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
- if self._layer_view:
- self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getLayerViewType())
- self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
- self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
- self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
- self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
- self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
- else:
- #defaults
- self._layer_shader.setUniformValue("u_layer_view_type", 1)
- self._layer_shader.setUniformValue("u_extruder_opacity", [1, 1, 1, 1])
- self._layer_shader.setUniformValue("u_show_travel_moves", 0)
- self._layer_shader.setUniformValue("u_show_helpers", 1)
- self._layer_shader.setUniformValue("u_show_skin", 1)
- self._layer_shader.setUniformValue("u_show_infill", 1)
-
- if not self._tool_handle_shader:
- self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
-
- self.bind()
-
- tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
-
- for node in DepthFirstIterator(self._scene.getRoot()):
-
- if isinstance(node, ToolHandle):
- tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
-
- elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
- layer_data = node.callDecoration("getLayerData")
- if not layer_data:
- continue
-
- # Render all layers below a certain number as line mesh instead of vertices.
- if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
- start = 0
- end = 0
- element_counts = layer_data.getElementCounts()
- for layer in sorted(element_counts.keys()):
- if layer > self._layer_view._current_layer_num:
- break
- if self._layer_view._minimum_layer_num > layer:
- start += element_counts[layer]
- end += element_counts[layer]
-
- # This uses glDrawRangeElements internally to only draw a certain range of lines.
- batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end))
- batch.addItem(node.getWorldTransformation(), layer_data)
- batch.render(self._scene.getActiveCamera())
-
- # Create a new batch that is not range-limited
- batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
-
- if self._layer_view.getCurrentLayerMesh():
- batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh())
-
- if self._layer_view.getCurrentLayerJumps():
- batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps())
-
- if len(batch.items) > 0:
- batch.render(self._scene.getActiveCamera())
-
- # Render toolhandles on top of the layerview
- if len(tool_handle_batch.items) > 0:
- tool_handle_batch.render(self._scene.getActiveCamera())
-
- self.release()
diff --git a/plugins/LayerView/LayerViewProxy.py b/plugins/LayerView/LayerViewProxy.py
deleted file mode 100644
index 4cbff65040..0000000000
--- a/plugins/LayerView/LayerViewProxy.py
+++ /dev/null
@@ -1,154 +0,0 @@
-from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
-from UM.FlameProfiler import pyqtSlot
-from UM.Application import Application
-
-import LayerView
-
-
-class LayerViewProxy(QObject):
- def __init__(self, parent=None):
- super().__init__(parent)
- self._current_layer = 0
- self._controller = Application.getInstance().getController()
- self._controller.activeViewChanged.connect(self._onActiveViewChanged)
- self._onActiveViewChanged()
-
- currentLayerChanged = pyqtSignal()
- maxLayersChanged = pyqtSignal()
- activityChanged = pyqtSignal()
- globalStackChanged = pyqtSignal()
- preferencesChanged = pyqtSignal()
- busyChanged = pyqtSignal()
-
- @pyqtProperty(bool, notify=activityChanged)
- def layerActivity(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getActivity()
- return False
-
- @pyqtProperty(int, notify=maxLayersChanged)
- def numLayers(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getMaxLayers()
- return 0
-
- @pyqtProperty(int, notify=currentLayerChanged)
- def currentLayer(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getCurrentLayer()
- return 0
-
- @pyqtProperty(int, notify=currentLayerChanged)
- def minimumLayer(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getMinimumLayer()
- return 0
-
- @pyqtProperty(bool, notify=busyChanged)
- def busy(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.isBusy()
- return False
-
- @pyqtProperty(bool, notify=preferencesChanged)
- def compatibilityMode(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getCompatibilityMode()
- return False
-
- @pyqtSlot(int)
- def setCurrentLayer(self, layer_num):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setLayer(layer_num)
-
- @pyqtSlot(int)
- def setMinimumLayer(self, layer_num):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setMinimumLayer(layer_num)
-
- @pyqtSlot(int)
- def setLayerViewType(self, layer_view_type):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setLayerViewType(layer_view_type)
-
- @pyqtSlot(result=int)
- def getLayerViewType(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getLayerViewType()
- return 0
-
- # Opacity 0..1
- @pyqtSlot(int, float)
- def setExtruderOpacity(self, extruder_nr, opacity):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setExtruderOpacity(extruder_nr, opacity)
-
- @pyqtSlot(int)
- def setShowTravelMoves(self, show):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setShowTravelMoves(show)
-
- @pyqtSlot(int)
- def setShowHelpers(self, show):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setShowHelpers(show)
-
- @pyqtSlot(int)
- def setShowSkin(self, show):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setShowSkin(show)
-
- @pyqtSlot(int)
- def setShowInfill(self, show):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.setShowInfill(show)
-
- @pyqtProperty(int, notify=globalStackChanged)
- def extruderCount(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- return active_view.getExtruderCount()
- return 0
-
- def _layerActivityChanged(self):
- self.activityChanged.emit()
-
- def _onLayerChanged(self):
- self.currentLayerChanged.emit()
- self._layerActivityChanged()
-
- def _onMaxLayersChanged(self):
- self.maxLayersChanged.emit()
-
- def _onBusyChanged(self):
- self.busyChanged.emit()
-
- def _onGlobalStackChanged(self):
- self.globalStackChanged.emit()
-
- def _onPreferencesChanged(self):
- self.preferencesChanged.emit()
-
- def _onActiveViewChanged(self):
- active_view = self._controller.getActiveView()
- if isinstance(active_view, LayerView.LayerView.LayerView):
- active_view.currentLayerNumChanged.connect(self._onLayerChanged)
- active_view.maxLayersChanged.connect(self._onMaxLayersChanged)
- active_view.busyChanged.connect(self._onBusyChanged)
- active_view.globalStackChanged.connect(self._onGlobalStackChanged)
- active_view.preferencesChanged.connect(self._onPreferencesChanged)
diff --git a/plugins/LayerView/__init__.py b/plugins/LayerView/__init__.py
deleted file mode 100644
index da1a5aed19..0000000000
--- a/plugins/LayerView/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2015 Ultimaker B.V.
-# Cura is released under the terms of the LGPLv3 or higher.
-
-from . import LayerView, LayerViewProxy
-from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType
-
-from UM.i18n import i18nCatalog
-catalog = i18nCatalog("cura")
-
-def getMetaData():
- return {
- "view": {
- "name": catalog.i18nc("@item:inlistbox", "Layer view"),
- "view_panel": "LayerView.qml",
- "weight": 2
- }
- }
-
-def createLayerViewProxy(engine, script_engine):
- return LayerViewProxy.LayerViewProxy()
-
-def register(app):
- layer_view = LayerView.LayerView()
- qmlRegisterSingletonType(LayerViewProxy.LayerViewProxy, "UM", 1, 0, "LayerView", layer_view.getProxy)
- return { "view": LayerView.LayerView() }
diff --git a/plugins/LayerView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml
similarity index 85%
rename from plugins/LayerView/LayerSlider.qml
rename to plugins/SimulationView/LayerSlider.qml
index 9abeb01148..22f9d91340 100644
--- a/plugins/LayerView/LayerSlider.qml
+++ b/plugins/SimulationView/LayerSlider.qml
@@ -1,312 +1,325 @@
-// Copyright (c) 2017 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.2
-import QtQuick.Controls 1.2
-import QtQuick.Layouts 1.1
-import QtQuick.Controls.Styles 1.1
-
-import UM 1.0 as UM
-import Cura 1.0 as Cura
-
-Item {
- id: sliderRoot
-
- // handle properties
- property real handleSize: 10
- property real handleRadius: handleSize / 2
- property real minimumRangeHandleSize: handleSize / 2
- property color upperHandleColor: "black"
- property color lowerHandleColor: "black"
- property color rangeHandleColor: "black"
- property real handleLabelWidth: width
- property var activeHandle: upperHandle
-
- // track properties
- property real trackThickness: 4 // width of the slider track
- property real trackRadius: trackThickness / 2
- property color trackColor: "white"
- property real trackBorderWidth: 1 // width of the slider track border
- property color trackBorderColor: "black"
-
- // value properties
- property real maximumValue: 100
- property real minimumValue: 0
- property real minimumRange: 0 // minimum range allowed between min and max values
- property bool roundValues: true
- property real upperValue: maximumValue
- property real lowerValue: minimumValue
-
- property bool layersVisible: true
-
- function getUpperValueFromSliderHandle () {
- return upperHandle.getValue()
- }
-
- function setUpperValue (value) {
- upperHandle.setValue(value)
- updateRangeHandle()
- }
-
- function getLowerValueFromSliderHandle () {
- return lowerHandle.getValue()
- }
-
- function setLowerValue (value) {
- lowerHandle.setValue(value)
- updateRangeHandle()
- }
-
- function updateRangeHandle () {
- rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
- }
-
- // set the active handle to show only one label at a time
- function setActiveHandle (handle) {
- activeHandle = handle
- }
-
- // slider track
- Rectangle {
- id: track
-
- width: sliderRoot.trackThickness
- height: sliderRoot.height - sliderRoot.handleSize
- radius: sliderRoot.trackRadius
- anchors.centerIn: sliderRoot
- color: sliderRoot.trackColor
- border.width: sliderRoot.trackBorderWidth
- border.color: sliderRoot.trackBorderColor
- visible: sliderRoot.layersVisible
- }
-
- // Range handle
- Item {
- id: rangeHandle
-
- y: upperHandle.y + upperHandle.height
- width: sliderRoot.handleSize
- height: sliderRoot.minimumRangeHandleSize
- anchors.horizontalCenter: sliderRoot.horizontalCenter
- visible: sliderRoot.layersVisible
-
- // set the new value when dragging
- function onHandleDragged () {
-
- upperHandle.y = y - upperHandle.height
- lowerHandle.y = y + height
-
- var upperValue = sliderRoot.getUpperValueFromSliderHandle()
- var lowerValue = sliderRoot.getLowerValueFromSliderHandle()
-
- // set both values after moving the handle position
- UM.LayerView.setCurrentLayer(upperValue)
- UM.LayerView.setMinimumLayer(lowerValue)
- }
-
- function setValue (value) {
- var range = sliderRoot.upperValue - sliderRoot.lowerValue
- value = Math.min(value, sliderRoot.maximumValue)
- value = Math.max(value, sliderRoot.minimumValue + range)
-
- UM.LayerView.setCurrentLayer(value)
- UM.LayerView.setMinimumLayer(value - range)
- }
-
- Rectangle {
- width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
- height: parent.height + sliderRoot.handleSize
- anchors.centerIn: parent
- color: sliderRoot.rangeHandleColor
- }
-
- MouseArea {
- anchors.fill: parent
-
- drag {
- target: parent
- axis: Drag.YAxis
- minimumY: upperHandle.height
- maximumY: sliderRoot.height - (rangeHandle.height + lowerHandle.height)
- }
-
- onPositionChanged: parent.onHandleDragged()
- onPressed: sliderRoot.setActiveHandle(rangeHandle)
- }
-
- LayerSliderLabel {
- id: rangleHandleLabel
-
- height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
- x: parent.x - width - UM.Theme.getSize("default_margin").width
- anchors.verticalCenter: parent.verticalCenter
- target: Qt.point(sliderRoot.width, y + height / 2)
- visible: sliderRoot.activeHandle == parent
-
- // custom properties
- maximumValue: sliderRoot.maximumValue
- value: sliderRoot.upperValue
- busy: UM.LayerView.busy
- setValue: rangeHandle.setValue // connect callback functions
- }
- }
-
- // Upper handle
- Rectangle {
- id: upperHandle
-
- y: sliderRoot.height - (sliderRoot.minimumRangeHandleSize + 2 * sliderRoot.handleSize)
- width: sliderRoot.handleSize
- height: sliderRoot.handleSize
- anchors.horizontalCenter: sliderRoot.horizontalCenter
- radius: sliderRoot.handleRadius
- color: sliderRoot.upperHandleColor
- visible: sliderRoot.layersVisible
-
- function onHandleDragged () {
-
- // don't allow the lower handle to be heigher than the upper handle
- if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize) {
- lowerHandle.y = y + height + sliderRoot.minimumRangeHandleSize
- }
-
- // update the range handle
- sliderRoot.updateRangeHandle()
-
- // set the new value after moving the handle position
- UM.LayerView.setCurrentLayer(getValue())
- }
-
- // get the upper value based on the slider position
- function getValue () {
- var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize))
- result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue))
- result = sliderRoot.roundValues ? Math.round(result) : result
- return result
- }
-
- // set the slider position based on the upper value
- function setValue (value) {
-
- UM.LayerView.setCurrentLayer(value)
-
- var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
- var newUpperYPosition = Math.round(diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
- y = newUpperYPosition
-
- // update the range handle
- sliderRoot.updateRangeHandle()
- }
-
- // dragging
- MouseArea {
- anchors.fill: parent
-
- drag {
- target: parent
- axis: Drag.YAxis
- minimumY: 0
- maximumY: sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)
- }
-
- onPositionChanged: parent.onHandleDragged()
- onPressed: sliderRoot.setActiveHandle(upperHandle)
- }
-
- LayerSliderLabel {
- id: upperHandleLabel
-
- height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
- x: parent.x - width - UM.Theme.getSize("default_margin").width
- anchors.verticalCenter: parent.verticalCenter
- target: Qt.point(sliderRoot.width, y + height / 2)
- visible: sliderRoot.activeHandle == parent
-
- // custom properties
- maximumValue: sliderRoot.maximumValue
- value: sliderRoot.upperValue
- busy: UM.LayerView.busy
- setValue: upperHandle.setValue // connect callback functions
- }
- }
-
- // Lower handle
- Rectangle {
- id: lowerHandle
-
- y: sliderRoot.height - sliderRoot.handleSize
- width: parent.handleSize
- height: parent.handleSize
- anchors.horizontalCenter: parent.horizontalCenter
- radius: sliderRoot.handleRadius
- color: sliderRoot.lowerHandleColor
-
- visible: slider.layersVisible
-
- function onHandleDragged () {
-
- // don't allow the upper handle to be lower than the lower handle
- if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize) {
- upperHandle.y = y - (upperHandle.heigth + sliderRoot.minimumRangeHandleSize)
- }
-
- // update the range handle
- sliderRoot.updateRangeHandle()
-
- // set the new value after moving the handle position
- UM.LayerView.setMinimumLayer(getValue())
- }
-
- // get the lower value from the current slider position
- function getValue () {
- var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize));
- result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange))
- result = sliderRoot.roundValues ? Math.round(result) : result
- return result
- }
-
- // set the slider position based on the lower value
- function setValue (value) {
-
- UM.LayerView.setMinimumLayer(value)
-
- var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
- var newLowerYPosition = Math.round((sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize) + diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
- y = newLowerYPosition
-
- // update the range handle
- sliderRoot.updateRangeHandle()
- }
-
- // dragging
- MouseArea {
- anchors.fill: parent
-
- drag {
- target: parent
- axis: Drag.YAxis
- minimumY: upperHandle.height + sliderRoot.minimumRangeHandleSize
- maximumY: sliderRoot.height - parent.height
- }
-
- onPositionChanged: parent.onHandleDragged()
- onPressed: sliderRoot.setActiveHandle(lowerHandle)
- }
-
- LayerSliderLabel {
- id: lowerHandleLabel
-
- height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
- x: parent.x - width - UM.Theme.getSize("default_margin").width
- anchors.verticalCenter: parent.verticalCenter
- target: Qt.point(sliderRoot.width, y + height / 2)
- visible: sliderRoot.activeHandle == parent
-
- // custom properties
- maximumValue: sliderRoot.maximumValue
- value: sliderRoot.lowerValue
- busy: UM.LayerView.busy
- setValue: lowerHandle.setValue // connect callback functions
- }
- }
-}
+// Copyright (c) 2017 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.1
+import QtQuick.Controls.Styles 1.1
+
+import UM 1.0 as UM
+import Cura 1.0 as Cura
+
+Item {
+ id: sliderRoot
+
+ // handle properties
+ property real handleSize: 10
+ property real handleRadius: handleSize / 2
+ property real minimumRangeHandleSize: handleSize / 2
+ property color upperHandleColor: "black"
+ property color lowerHandleColor: "black"
+ property color rangeHandleColor: "black"
+ property color handleActiveColor: "white"
+ property real handleLabelWidth: width
+ property var activeHandle: upperHandle
+
+ // track properties
+ property real trackThickness: 4 // width of the slider track
+ property real trackRadius: trackThickness / 2
+ property color trackColor: "white"
+ property real trackBorderWidth: 1 // width of the slider track border
+ property color trackBorderColor: "black"
+
+ // value properties
+ property real maximumValue: 100
+ property real minimumValue: 0
+ property real minimumRange: 0 // minimum range allowed between min and max values
+ property bool roundValues: true
+ property real upperValue: maximumValue
+ property real lowerValue: minimumValue
+
+ property bool layersVisible: true
+
+ function getUpperValueFromSliderHandle () {
+ return upperHandle.getValue()
+ }
+
+ function setUpperValue (value) {
+ upperHandle.setValue(value)
+ updateRangeHandle()
+ }
+
+ function getLowerValueFromSliderHandle () {
+ return lowerHandle.getValue()
+ }
+
+ function setLowerValue (value) {
+ lowerHandle.setValue(value)
+ updateRangeHandle()
+ }
+
+ function updateRangeHandle () {
+ rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
+ }
+
+ // set the active handle to show only one label at a time
+ function setActiveHandle (handle) {
+ activeHandle = handle
+ }
+
+ // slider track
+ Rectangle {
+ id: track
+
+ width: sliderRoot.trackThickness
+ height: sliderRoot.height - sliderRoot.handleSize
+ radius: sliderRoot.trackRadius
+ anchors.centerIn: sliderRoot
+ color: sliderRoot.trackColor
+ border.width: sliderRoot.trackBorderWidth
+ border.color: sliderRoot.trackBorderColor
+ visible: sliderRoot.layersVisible
+ }
+
+ // Range handle
+ Item {
+ id: rangeHandle
+
+ y: upperHandle.y + upperHandle.height
+ width: sliderRoot.handleSize
+ height: sliderRoot.minimumRangeHandleSize
+ anchors.horizontalCenter: sliderRoot.horizontalCenter
+ visible: sliderRoot.layersVisible
+
+ // set the new value when dragging
+ function onHandleDragged () {
+
+ upperHandle.y = y - upperHandle.height
+ lowerHandle.y = y + height
+
+ var upperValue = sliderRoot.getUpperValueFromSliderHandle()
+ var lowerValue = sliderRoot.getLowerValueFromSliderHandle()
+
+ // set both values after moving the handle position
+ UM.SimulationView.setCurrentLayer(upperValue)
+ UM.SimulationView.setMinimumLayer(lowerValue)
+ }
+
+ function setValue (value) {
+ var range = sliderRoot.upperValue - sliderRoot.lowerValue
+ value = Math.min(value, sliderRoot.maximumValue)
+ value = Math.max(value, sliderRoot.minimumValue + range)
+
+ UM.SimulationView.setCurrentLayer(value)
+ UM.SimulationView.setMinimumLayer(value - range)
+ }
+
+ Rectangle {
+ width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
+ height: parent.height + sliderRoot.handleSize
+ anchors.centerIn: parent
+ color: sliderRoot.rangeHandleColor
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ drag {
+ target: parent
+ axis: Drag.YAxis
+ minimumY: upperHandle.height
+ maximumY: sliderRoot.height - (rangeHandle.height + lowerHandle.height)
+ }
+
+ onPositionChanged: parent.onHandleDragged()
+ onPressed: sliderRoot.setActiveHandle(rangeHandle)
+ }
+
+ SimulationSliderLabel {
+ id: rangleHandleLabel
+
+ height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
+ x: parent.x - width - UM.Theme.getSize("default_margin").width
+ anchors.verticalCenter: parent.verticalCenter
+ target: Qt.point(sliderRoot.width, y + height / 2)
+ visible: sliderRoot.activeHandle == parent
+
+ // custom properties
+ maximumValue: sliderRoot.maximumValue
+ value: sliderRoot.upperValue
+ busy: UM.SimulationView.busy
+ setValue: rangeHandle.setValue // connect callback functions
+ }
+ }
+
+ // Upper handle
+ Rectangle {
+ id: upperHandle
+
+ y: sliderRoot.height - (sliderRoot.minimumRangeHandleSize + 2 * sliderRoot.handleSize)
+ width: sliderRoot.handleSize
+ height: sliderRoot.handleSize
+ anchors.horizontalCenter: sliderRoot.horizontalCenter
+ radius: sliderRoot.handleRadius
+ color: upperHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.upperHandleColor
+ visible: sliderRoot.layersVisible
+
+ function onHandleDragged () {
+
+ // don't allow the lower handle to be heigher than the upper handle
+ if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize) {
+ lowerHandle.y = y + height + sliderRoot.minimumRangeHandleSize
+ }
+
+ // update the range handle
+ sliderRoot.updateRangeHandle()
+
+ // set the new value after moving the handle position
+ UM.SimulationView.setCurrentLayer(getValue())
+ }
+
+ // get the upper value based on the slider position
+ function getValue () {
+ var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize))
+ result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue))
+ result = sliderRoot.roundValues ? Math.round(result) : result
+ return result
+ }
+
+ // set the slider position based on the upper value
+ function setValue (value) {
+
+ UM.SimulationView.setCurrentLayer(value)
+
+ var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
+ var newUpperYPosition = Math.round(diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
+ y = newUpperYPosition
+
+ // update the range handle
+ sliderRoot.updateRangeHandle()
+ }
+
+ Keys.onUpPressed: upperHandleLabel.setValue(upperHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+ Keys.onDownPressed: upperHandleLabel.setValue(upperHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+
+ // dragging
+ MouseArea {
+ anchors.fill: parent
+
+ drag {
+ target: parent
+ axis: Drag.YAxis
+ minimumY: 0
+ maximumY: sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)
+ }
+
+ onPositionChanged: parent.onHandleDragged()
+ onPressed: {
+ sliderRoot.setActiveHandle(upperHandle)
+ upperHandleLabel.forceActiveFocus()
+ }
+ }
+
+ SimulationSliderLabel {
+ id: upperHandleLabel
+
+ height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
+ x: parent.x - width - UM.Theme.getSize("default_margin").width
+ anchors.verticalCenter: parent.verticalCenter
+ target: Qt.point(sliderRoot.width, y + height / 2)
+ visible: sliderRoot.activeHandle == parent
+
+ // custom properties
+ maximumValue: sliderRoot.maximumValue
+ value: sliderRoot.upperValue
+ busy: UM.SimulationView.busy
+ setValue: upperHandle.setValue // connect callback functions
+ }
+ }
+
+ // Lower handle
+ Rectangle {
+ id: lowerHandle
+
+ y: sliderRoot.height - sliderRoot.handleSize
+ width: parent.handleSize
+ height: parent.handleSize
+ anchors.horizontalCenter: parent.horizontalCenter
+ radius: sliderRoot.handleRadius
+ color: lowerHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.lowerHandleColor
+
+ visible: sliderRoot.layersVisible
+
+ function onHandleDragged () {
+
+ // don't allow the upper handle to be lower than the lower handle
+ if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize) {
+ upperHandle.y = y - (upperHandle.heigth + sliderRoot.minimumRangeHandleSize)
+ }
+
+ // update the range handle
+ sliderRoot.updateRangeHandle()
+
+ // set the new value after moving the handle position
+ UM.SimulationView.setMinimumLayer(getValue())
+ }
+
+ // get the lower value from the current slider position
+ function getValue () {
+ var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize));
+ result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange))
+ result = sliderRoot.roundValues ? Math.round(result) : result
+ return result
+ }
+
+ // set the slider position based on the lower value
+ function setValue (value) {
+
+ UM.SimulationView.setMinimumLayer(value)
+
+ var diff = (value - sliderRoot.maximumValue) / (sliderRoot.minimumValue - sliderRoot.maximumValue)
+ var newLowerYPosition = Math.round((sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize) + diff * (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)))
+ y = newLowerYPosition
+
+ // update the range handle
+ sliderRoot.updateRangeHandle()
+ }
+
+ Keys.onUpPressed: lowerHandleLabel.setValue(lowerHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+ Keys.onDownPressed: lowerHandleLabel.setValue(lowerHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+
+ // dragging
+ MouseArea {
+ anchors.fill: parent
+
+ drag {
+ target: parent
+ axis: Drag.YAxis
+ minimumY: upperHandle.height + sliderRoot.minimumRangeHandleSize
+ maximumY: sliderRoot.height - parent.height
+ }
+
+ onPositionChanged: parent.onHandleDragged()
+ onPressed: {
+ sliderRoot.setActiveHandle(lowerHandle)
+ lowerHandleLabel.forceActiveFocus()
+ }
+ }
+
+ SimulationSliderLabel {
+ id: lowerHandleLabel
+
+ height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
+ x: parent.x - width - UM.Theme.getSize("default_margin").width
+ anchors.verticalCenter: parent.verticalCenter
+ target: Qt.point(sliderRoot.width, y + height / 2)
+ visible: sliderRoot.activeHandle == parent
+
+ // custom properties
+ maximumValue: sliderRoot.maximumValue
+ value: sliderRoot.lowerValue
+ busy: UM.SimulationView.busy
+ setValue: lowerHandle.setValue // connect callback functions
+ }
+ }
+}
diff --git a/plugins/SimulationView/NozzleNode.py b/plugins/SimulationView/NozzleNode.py
new file mode 100644
index 0000000000..8a29871775
--- /dev/null
+++ b/plugins/SimulationView/NozzleNode.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2017 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from UM.Application import Application
+from UM.Math.Color import Color
+from UM.Math.Vector import Vector
+from UM.PluginRegistry import PluginRegistry
+from UM.Scene.SceneNode import SceneNode
+from UM.View.GL.OpenGL import OpenGL
+from UM.Resources import Resources
+
+import os
+
+class NozzleNode(SceneNode):
+ def __init__(self, parent = None):
+ super().__init__(parent)
+
+ self._shader = None
+ self.setCalculateBoundingBox(False)
+ self._createNozzleMesh()
+
+ def _createNozzleMesh(self):
+ mesh_file = "resources/nozzle.stl"
+ try:
+ path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), mesh_file)
+ except FileNotFoundError:
+ path = ""
+
+ reader = Application.getInstance().getMeshFileHandler().getReaderForFile(path)
+ node = reader.read(path)
+
+ if node.getMeshData():
+ self.setMeshData(node.getMeshData())
+
+ def render(self, renderer):
+ # Avoid to render if it is not visible
+ if not self.isVisible():
+ return False
+
+ if not self._shader:
+ # We now misuse the platform shader, as it actually supports textures
+ self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
+ self._shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_nozzle").getRgb()))
+ # Set the opacity to 0, so that the template is in full control.
+ self._shader.setUniformValue("u_opacity", 0)
+
+ if self.getMeshData():
+ renderer.queueNode(self, shader = self._shader, transparent = True)
+ return True
diff --git a/plugins/SimulationView/PathSlider.qml b/plugins/SimulationView/PathSlider.qml
new file mode 100644
index 0000000000..0a4af904aa
--- /dev/null
+++ b/plugins/SimulationView/PathSlider.qml
@@ -0,0 +1,161 @@
+// Copyright (c) 2017 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.1
+import QtQuick.Controls.Styles 1.1
+
+import UM 1.0 as UM
+import Cura 1.0 as Cura
+
+Item {
+ id: sliderRoot
+
+ // handle properties
+ property real handleSize: 10
+ property real handleRadius: handleSize / 2
+ property color handleColor: "black"
+ property color handleActiveColor: "white"
+ property color rangeColor: "black"
+ property real handleLabelWidth: width
+
+ // track properties
+ property real trackThickness: 4 // width of the slider track
+ property real trackRadius: trackThickness / 2
+ property color trackColor: "white"
+ property real trackBorderWidth: 1 // width of the slider track border
+ property color trackBorderColor: "black"
+
+ // value properties
+ property real maximumValue: 100
+ property bool roundValues: true
+ property real handleValue: maximumValue
+
+ property bool pathsVisible: true
+
+ function getHandleValueFromSliderHandle () {
+ return handle.getValue()
+ }
+
+ function setHandleValue (value) {
+ handle.setValue(value)
+ updateRangeHandle()
+ }
+
+ function updateRangeHandle () {
+ rangeHandle.width = handle.x - sliderRoot.handleSize
+ }
+
+ // slider track
+ Rectangle {
+ id: track
+
+ width: sliderRoot.width - sliderRoot.handleSize
+ height: sliderRoot.trackThickness
+ radius: sliderRoot.trackRadius
+ anchors.centerIn: sliderRoot
+ color: sliderRoot.trackColor
+ border.width: sliderRoot.trackBorderWidth
+ border.color: sliderRoot.trackBorderColor
+ visible: sliderRoot.pathsVisible
+ }
+
+ // Progress indicator
+ Item {
+ id: rangeHandle
+
+ x: handle.width
+ height: sliderRoot.handleSize
+ width: handle.x - sliderRoot.handleSize
+ anchors.verticalCenter: sliderRoot.verticalCenter
+ visible: sliderRoot.pathsVisible
+
+ Rectangle {
+ height: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth
+ width: parent.width + sliderRoot.handleSize
+ anchors.centerIn: parent
+ color: sliderRoot.rangeColor
+ }
+ }
+
+ // Handle
+ Rectangle {
+ id: handle
+
+ x: sliderRoot.handleSize
+ width: sliderRoot.handleSize
+ height: sliderRoot.handleSize
+ anchors.verticalCenter: sliderRoot.verticalCenter
+ radius: sliderRoot.handleRadius
+ color: handleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.handleColor
+ visible: sliderRoot.pathsVisible
+
+ function onHandleDragged () {
+
+ // update the range handle
+ sliderRoot.updateRangeHandle()
+
+ // set the new value after moving the handle position
+ UM.SimulationView.setCurrentPath(getValue())
+ }
+
+ // get the value based on the slider position
+ function getValue () {
+ var result = x / (sliderRoot.width - sliderRoot.handleSize)
+ result = result * sliderRoot.maximumValue
+ result = sliderRoot.roundValues ? Math.round(result) : result
+ return result
+ }
+
+ // set the slider position based on the value
+ function setValue (value) {
+
+ UM.SimulationView.setCurrentPath(value)
+
+ var diff = value / sliderRoot.maximumValue
+ var newXPosition = Math.round(diff * (sliderRoot.width - sliderRoot.handleSize))
+ x = newXPosition
+
+ // update the range handle
+ sliderRoot.updateRangeHandle()
+ }
+
+ Keys.onRightPressed: handleLabel.setValue(handleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+ Keys.onLeftPressed: handleLabel.setValue(handleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+
+ // dragging
+ MouseArea {
+ anchors.fill: parent
+
+ drag {
+ target: parent
+ axis: Drag.XAxis
+ minimumX: 0
+ maximumX: sliderRoot.width - sliderRoot.handleSize
+ }
+ onPressed: {
+ handleLabel.forceActiveFocus()
+ }
+
+ onPositionChanged: parent.onHandleDragged()
+ }
+
+ SimulationSliderLabel {
+ id: handleLabel
+
+ height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
+ y: parent.y + sliderRoot.handleSize + UM.Theme.getSize("default_margin").height
+ anchors.horizontalCenter: parent.horizontalCenter
+ target: Qt.point(x + width / 2, sliderRoot.height)
+ visible: false
+ startFrom: 0
+
+ // custom properties
+ maximumValue: sliderRoot.maximumValue
+ value: sliderRoot.handleValue
+ busy: UM.SimulationView.busy
+ setValue: handle.setValue // connect callback functions
+ }
+ }
+}
diff --git a/plugins/SimulationView/SimulationPass.py b/plugins/SimulationView/SimulationPass.py
new file mode 100644
index 0000000000..4963568935
--- /dev/null
+++ b/plugins/SimulationView/SimulationPass.py
@@ -0,0 +1,187 @@
+# Copyright (c) 2017 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from UM.Math.Color import Color
+from UM.Math.Vector import Vector
+from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+from UM.Resources import Resources
+from UM.Scene.SceneNode import SceneNode
+from UM.Scene.ToolHandle import ToolHandle
+from UM.Application import Application
+from UM.PluginRegistry import PluginRegistry
+
+from UM.View.RenderPass import RenderPass
+from UM.View.RenderBatch import RenderBatch
+from UM.View.GL.OpenGL import OpenGL
+
+from cura.Settings.ExtruderManager import ExtruderManager
+
+
+import os.path
+
+## RenderPass used to display g-code paths.
+from plugins.SimulationView.NozzleNode import NozzleNode
+
+
+class SimulationPass(RenderPass):
+ def __init__(self, width, height):
+ super().__init__("simulationview", width, height)
+
+ self._layer_shader = None
+ self._layer_shadow_shader = None
+ self._current_shader = None # This shader will be the shadow or the normal depending if the user wants to see the paths or the layers
+ self._tool_handle_shader = None
+ self._nozzle_shader = None
+ self._old_current_layer = 0
+ self._old_current_path = 0
+ self._gl = OpenGL.getInstance().getBindingsObject()
+ self._scene = Application.getInstance().getController().getScene()
+ self._extruder_manager = ExtruderManager.getInstance()
+
+ self._layer_view = None
+ self._compatibility_mode = None
+
+ def setSimulationView(self, layerview):
+ self._layer_view = layerview
+ self._compatibility_mode = layerview.getCompatibilityMode()
+
+ def render(self):
+ if not self._layer_shader:
+ if self._compatibility_mode:
+ shader_filename = "layers.shader"
+ shadow_shader_filename = "layers_shadow.shader"
+ else:
+ shader_filename = "layers3d.shader"
+ shadow_shader_filename = "layers3d_shadow.shader"
+ self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shader_filename))
+ self._layer_shadow_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shadow_shader_filename))
+ self._current_shader = self._layer_shader
+ # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
+ self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
+ if self._layer_view:
+ self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
+ self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
+ self._layer_shader.setUniformValue("u_max_thickness", self._layer_view.getMaxThickness())
+ self._layer_shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness())
+ self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType())
+ self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
+ self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
+ self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
+ self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
+ self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
+ else:
+ #defaults
+ self._layer_shader.setUniformValue("u_max_feedrate", 1)
+ self._layer_shader.setUniformValue("u_min_feedrate", 0)
+ self._layer_shader.setUniformValue("u_max_thickness", 1)
+ self._layer_shader.setUniformValue("u_min_thickness", 0)
+ self._layer_shader.setUniformValue("u_layer_view_type", 1)
+ self._layer_shader.setUniformValue("u_extruder_opacity", [1, 1, 1, 1])
+ self._layer_shader.setUniformValue("u_show_travel_moves", 0)
+ self._layer_shader.setUniformValue("u_show_helpers", 1)
+ self._layer_shader.setUniformValue("u_show_skin", 1)
+ self._layer_shader.setUniformValue("u_show_infill", 1)
+
+ if not self._tool_handle_shader:
+ self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
+
+ if not self._nozzle_shader:
+ self._nozzle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
+ self._nozzle_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_nozzle").getRgb()))
+
+ self.bind()
+
+ tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Solid)
+ head_position = None # Indicates the current position of the print head
+ nozzle_node = None
+
+ for node in DepthFirstIterator(self._scene.getRoot()):
+
+ if isinstance(node, ToolHandle):
+ tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
+
+
+ elif isinstance(node, NozzleNode):
+ nozzle_node = node
+ nozzle_node.setVisible(False)
+
+ elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
+ layer_data = node.callDecoration("getLayerData")
+ if not layer_data:
+ continue
+
+ # Render all layers below a certain number as line mesh instead of vertices.
+ if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
+ start = 0
+ end = 0
+ element_counts = layer_data.getElementCounts()
+ for layer in sorted(element_counts.keys()):
+ # In the current layer, we show just the indicated paths
+ if layer == self._layer_view._current_layer_num:
+ # We look for the position of the head, searching the point of the current path
+ index = self._layer_view._current_path_num
+ offset = 0
+ for polygon in layer_data.getLayer(layer).polygons:
+ # The size indicates all values in the two-dimension array, and the second dimension is
+ # always size 3 because we have 3D points.
+ if index >= polygon.data.size // 3 - offset:
+ index -= polygon.data.size // 3 - offset
+ offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
+ continue
+ # The head position is calculated and translated
+ head_position = Vector(polygon.data[index+offset][0], polygon.data[index+offset][1], polygon.data[index+offset][2]) + node.getWorldPosition()
+ break
+ break
+ if self._layer_view._minimum_layer_num > layer:
+ start += element_counts[layer]
+ end += element_counts[layer]
+
+ # Calculate the range of paths in the last layer
+ current_layer_start = end
+ current_layer_end = end + self._layer_view._current_path_num * 2 # Because each point is used twice
+
+ # This uses glDrawRangeElements internally to only draw a certain range of lines.
+ # All the layers but the current selected layer are rendered first
+ if self._old_current_path != self._layer_view._current_path_num:
+ self._current_shader = self._layer_shadow_shader
+ if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view._current_layer_num:
+ self._current_shader = self._layer_shader
+
+ layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end))
+ layers_batch.addItem(node.getWorldTransformation(), layer_data)
+ layers_batch.render(self._scene.getActiveCamera())
+
+ # Current selected layer is rendered
+ current_layer_batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (current_layer_start, current_layer_end))
+ current_layer_batch.addItem(node.getWorldTransformation(), layer_data)
+ current_layer_batch.render(self._scene.getActiveCamera())
+
+ self._old_current_layer = self._layer_view._current_layer_num
+ self._old_current_path = self._layer_view._current_path_num
+
+ # Create a new batch that is not range-limited
+ batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
+
+ if self._layer_view.getCurrentLayerMesh():
+ batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh())
+
+ if self._layer_view.getCurrentLayerJumps():
+ batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps())
+
+ if len(batch.items) > 0:
+ batch.render(self._scene.getActiveCamera())
+
+ # The nozzle is drawn once we know the correct position
+ if self._layer_view.getActivity() and nozzle_node is not None:
+ if head_position is not None:
+ nozzle_node.setVisible(True)
+ nozzle_node.setPosition(head_position)
+ nozzle_batch = RenderBatch(self._nozzle_shader, type = RenderBatch.RenderType.Solid)
+ nozzle_batch.addItem(nozzle_node.getWorldTransformation(), mesh = nozzle_node.getMeshData())
+ nozzle_batch.render(self._scene.getActiveCamera())
+
+ # Render toolhandles on top of the layerview
+ if len(tool_handle_batch.items) > 0:
+ tool_handle_batch.render(self._scene.getActiveCamera())
+
+ self.release()
diff --git a/plugins/LayerView/LayerSliderLabel.qml b/plugins/SimulationView/SimulationSliderLabel.qml
similarity index 88%
rename from plugins/LayerView/LayerSliderLabel.qml
rename to plugins/SimulationView/SimulationSliderLabel.qml
index c989679285..1c8daf867f 100644
--- a/plugins/LayerView/LayerSliderLabel.qml
+++ b/plugins/SimulationView/SimulationSliderLabel.qml
@@ -1,103 +1,104 @@
-// Copyright (c) 2017 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.2
-import QtQuick.Controls 1.2
-import QtQuick.Layouts 1.1
-import QtQuick.Controls.Styles 1.1
-
-import UM 1.0 as UM
-import Cura 1.0 as Cura
-
-UM.PointingRectangle {
- id: sliderLabelRoot
-
- // custom properties
- property real maximumValue: 100
- property real value: 0
- property var setValue // Function
- property bool busy: false
-
- target: Qt.point(parent.width, y + height / 2)
- arrowSize: UM.Theme.getSize("default_arrow").width
- height: parent.height
- width: valueLabel.width + UM.Theme.getSize("default_margin").width
- visible: false
-
- // make sure the text field is focussed when pressing the parent handle
- // needed to connect the key bindings when switching active handle
- onVisibleChanged: if (visible) valueLabel.forceActiveFocus()
-
- color: UM.Theme.getColor("tool_panel_background")
- borderColor: UM.Theme.getColor("lining")
- borderWidth: UM.Theme.getSize("default_lining").width
-
- Behavior on height {
- NumberAnimation {
- duration: 50
- }
- }
-
- // catch all mouse events so they're not handled by underlying 3D scene
- MouseArea {
- anchors.fill: parent
- }
-
- TextField {
- id: valueLabel
-
- anchors {
- left: parent.left
- leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
- verticalCenter: parent.verticalCenter
- }
-
- width: 40 * screenScaleFactor
- text: sliderLabelRoot.value + 1 // the current handle value, add 1 because layers is an array
- horizontalAlignment: TextInput.AlignRight
-
- // key bindings, work when label is currenctly focused (active handle in LayerSlider)
- Keys.onUpPressed: sliderLabelRoot.setValue(sliderLabelRoot.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
- Keys.onDownPressed: sliderLabelRoot.setValue(sliderLabelRoot.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
-
- style: TextFieldStyle {
- textColor: UM.Theme.getColor("setting_control_text")
- font: UM.Theme.getFont("default")
- background: Item { }
- }
-
- onEditingFinished: {
-
- // Ensure that the cursor is at the first position. On some systems the text isn't fully visible
- // Seems to have to do something with different dpi densities that QML doesn't quite handle.
- // Another option would be to increase the size even further, but that gives pretty ugly results.
- cursorPosition = 0
-
- if (valueLabel.text != "") {
- // -1 because we need to convert back to an array structure
- sliderLabelRoot.setValue(parseInt(valueLabel.text) - 1)
- }
- }
-
- validator: IntValidator {
- bottom: 1
- top: sliderLabelRoot.maximumValue + 1 // +1 because actual layers is an array
- }
- }
-
- BusyIndicator {
- id: busyIndicator
-
- anchors {
- left: parent.right
- leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
- verticalCenter: parent.verticalCenter
- }
-
- width: sliderLabelRoot.height
- height: width
-
- visible: sliderLabelRoot.busy
- running: sliderLabelRoot.busy
- }
-}
+// Copyright (c) 2017 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.1
+import QtQuick.Controls.Styles 1.1
+
+import UM 1.0 as UM
+import Cura 1.0 as Cura
+
+UM.PointingRectangle {
+ id: sliderLabelRoot
+
+ // custom properties
+ property real maximumValue: 100
+ property real value: 0
+ property var setValue // Function
+ property bool busy: false
+ property int startFrom: 1
+
+ target: Qt.point(parent.width, y + height / 2)
+ arrowSize: UM.Theme.getSize("default_arrow").width
+ height: parent.height
+ width: valueLabel.width + UM.Theme.getSize("default_margin").width
+ visible: false
+
+ // make sure the text field is focussed when pressing the parent handle
+ // needed to connect the key bindings when switching active handle
+ onVisibleChanged: if (visible) valueLabel.forceActiveFocus()
+
+ color: UM.Theme.getColor("tool_panel_background")
+ borderColor: UM.Theme.getColor("lining")
+ borderWidth: UM.Theme.getSize("default_lining").width
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 50
+ }
+ }
+
+ // catch all mouse events so they're not handled by underlying 3D scene
+ MouseArea {
+ anchors.fill: parent
+ }
+
+ TextField {
+ id: valueLabel
+
+ anchors {
+ left: parent.left
+ leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
+ verticalCenter: parent.verticalCenter
+ }
+
+ width: 40 * screenScaleFactor
+ text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
+ horizontalAlignment: TextInput.AlignRight
+
+ // key bindings, work when label is currenctly focused (active handle in LayerSlider)
+ Keys.onUpPressed: sliderLabelRoot.setValue(sliderLabelRoot.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+ Keys.onDownPressed: sliderLabelRoot.setValue(sliderLabelRoot.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1))
+
+ style: TextFieldStyle {
+ textColor: UM.Theme.getColor("setting_control_text")
+ font: UM.Theme.getFont("default")
+ background: Item { }
+ }
+
+ onEditingFinished: {
+
+ // Ensure that the cursor is at the first position. On some systems the text isn't fully visible
+ // Seems to have to do something with different dpi densities that QML doesn't quite handle.
+ // Another option would be to increase the size even further, but that gives pretty ugly results.
+ cursorPosition = 0
+
+ if (valueLabel.text != "") {
+ // -startFrom because we need to convert back to an array structure
+ sliderLabelRoot.setValue(parseInt(valueLabel.text) - startFrom)
+ }
+ }
+
+ validator: IntValidator {
+ bottom:startFrom
+ top: sliderLabelRoot.maximumValue + startFrom // +startFrom because maybe we want to start in a different value rather than 0
+ }
+ }
+
+ BusyIndicator {
+ id: busyIndicator
+
+ anchors {
+ left: parent.right
+ leftMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
+ verticalCenter: parent.verticalCenter
+ }
+
+ width: sliderLabelRoot.height
+ height: width
+
+ visible: sliderLabelRoot.busy
+ running: sliderLabelRoot.busy
+ }
+}
diff --git a/plugins/LayerView/LayerView.py b/plugins/SimulationView/SimulationView.py
old mode 100755
new mode 100644
similarity index 74%
rename from plugins/LayerView/LayerView.py
rename to plugins/SimulationView/SimulationView.py
index 04be97b747..90f64a8224
--- a/plugins/LayerView/LayerView.py
+++ b/plugins/SimulationView/SimulationView.py
@@ -1,46 +1,46 @@
-# Copyright (c) 2015 Ultimaker B.V.
+# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import sys
-from UM.PluginRegistry import PluginRegistry
-from UM.View.View import View
-from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
-from UM.Resources import Resources
-from UM.Event import Event, KeyEvent
-from UM.Signal import Signal
-from UM.Scene.Selection import Selection
-from UM.Math.Color import Color
-from UM.Mesh.MeshBuilder import MeshBuilder
-from UM.Job import Job
-from UM.Preferences import Preferences
-from UM.Logger import Logger
-from UM.View.GL.OpenGL import OpenGL
-from UM.Message import Message
-from UM.Application import Application
-from UM.View.GL.OpenGLContext import OpenGLContext
-
-from cura.ConvexHullNode import ConvexHullNode
-from cura.Settings.ExtruderManager import ExtruderManager
-
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
-from . import LayerViewProxy
-
+from UM.Application import Application
+from UM.Event import Event, KeyEvent
+from UM.Job import Job
+from UM.Logger import Logger
+from UM.Math.Color import Color
+from UM.Math.Vector import Vector
+from UM.Mesh.MeshBuilder import MeshBuilder
+from UM.Message import Message
+from UM.PluginRegistry import PluginRegistry
+from UM.Preferences import Preferences
+from UM.Resources import Resources
+from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+from UM.Scene.SceneNode import SceneNode
+from UM.Scene.Selection import Selection
+from UM.Signal import Signal
+from UM.View.GL.OpenGL import OpenGL
+from UM.View.GL.OpenGLContext import OpenGLContext
+from UM.View.View import View
from UM.i18n import i18nCatalog
-catalog = i18nCatalog("cura")
+from cura.ConvexHullNode import ConvexHullNode
+from plugins.SimulationView.NozzleNode import NozzleNode
+from . import SimulationPass, SimulationViewProxy
-from . import LayerPass
+catalog = i18nCatalog("cura")
import numpy
import os.path
## View used to display g-code paths.
-class LayerView(View):
- # Must match LayerView.qml
+class SimulationView(View):
+ # Must match SimulationView.qml
LAYER_VIEW_TYPE_MATERIAL_TYPE = 0
LAYER_VIEW_TYPE_LINE_TYPE = 1
+ LAYER_VIEW_TYPE_FEEDRATE = 2
+ LAYER_VIEW_TYPE_THICKNESS = 3
def __init__(self):
super().__init__()
@@ -54,22 +54,29 @@ class LayerView(View):
self._activity = False
self._old_max_layers = 0
+ self._max_paths = 0
+ self._current_path_num = 0
+ self._minimum_path_num = 0
+ self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)
+
self._busy = False
+ self._simulation_running = False
self._ghost_shader = None
self._layer_pass = None
self._composite_pass = None
self._old_layer_bindings = None
- self._layerview_composite_shader = None
+ self._simulationview_composite_shader = None
self._old_composite_shader = None
self._global_container_stack = None
- self._proxy = LayerViewProxy.LayerViewProxy()
+ self._proxy = SimulationViewProxy.SimulationViewProxy()
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
self._resetSettings()
self._legend_items = None
self._show_travel_moves = False
+ self._nozzle_node = None
Preferences.getInstance().addPreference("view/top_layer_count", 5)
Preferences.getInstance().addPreference("view/only_show_top_layers", False)
@@ -91,7 +98,7 @@ class LayerView(View):
self._compatibility_mode = True # for safety
self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"),
- title = catalog.i18nc("@info:title", "Layer View"))
+ title = catalog.i18nc("@info:title", "Simulation View"))
def _resetSettings(self):
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed
@@ -101,17 +108,24 @@ class LayerView(View):
self._show_helpers = 1
self._show_skin = 1
self._show_infill = 1
+ self.resetLayerData()
def getActivity(self):
return self._activity
- def getLayerPass(self):
+ def setActivity(self, activity):
+ if self._activity == activity:
+ return
+ self._activity = activity
+ self.activityChanged.emit()
+
+ def getSimulationPass(self):
if not self._layer_pass:
# Currently the RenderPass constructor requires a size > 0
# This should be fixed in RenderPass's constructor.
- self._layer_pass = LayerPass.LayerPass(1, 1)
+ self._layer_pass = SimulationPass.SimulationPass(1, 1)
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
- self._layer_pass.setLayerView(self)
+ self._layer_pass.setSimulationView(self)
return self._layer_pass
def getCurrentLayer(self):
@@ -120,13 +134,26 @@ class LayerView(View):
def getMinimumLayer(self):
return self._minimum_layer_num
- def _onSceneChanged(self, node):
- self.calculateMaxLayers()
-
def getMaxLayers(self):
return self._max_layers
- busyChanged = Signal()
+ def getCurrentPath(self):
+ return self._current_path_num
+
+ def getMinimumPath(self):
+ return self._minimum_path_num
+
+ def getMaxPaths(self):
+ return self._max_paths
+
+ def getNozzleNode(self):
+ if not self._nozzle_node:
+ self._nozzle_node = NozzleNode()
+ return self._nozzle_node
+
+ def _onSceneChanged(self, node):
+ self.setActivity(False)
+ self.calculateMaxLayers()
def isBusy(self):
return self._busy
@@ -136,9 +163,19 @@ class LayerView(View):
self._busy = busy
self.busyChanged.emit()
+ def isSimulationRunning(self):
+ return self._simulation_running
+
+ def setSimulationRunning(self, running):
+ self._simulation_running = running
+
def resetLayerData(self):
self._current_layer_mesh = None
self._current_layer_jumps = None
+ self._max_feedrate = sys.float_info.min
+ self._min_feedrate = sys.float_info.max
+ self._max_thickness = sys.float_info.min
+ self._min_thickness = sys.float_info.max
def beginRendering(self):
scene = self.getController().getScene()
@@ -186,15 +223,43 @@ class LayerView(View):
self.currentLayerNumChanged.emit()
+ def setPath(self, value):
+ if self._current_path_num != value:
+ self._current_path_num = value
+ if self._current_path_num < 0:
+ self._current_path_num = 0
+ if self._current_path_num > self._max_paths:
+ self._current_path_num = self._max_paths
+ if self._current_path_num < self._minimum_path_num:
+ self._minimum_path_num = self._current_path_num
+
+ self._startUpdateTopLayers()
+
+ self.currentPathNumChanged.emit()
+
+ def setMinimumPath(self, value):
+ if self._minimum_path_num != value:
+ self._minimum_path_num = value
+ if self._minimum_path_num < 0:
+ self._minimum_path_num = 0
+ if self._minimum_path_num > self._max_layers:
+ self._minimum_path_num = self._max_layers
+ if self._minimum_path_num > self._current_path_num:
+ self._current_path_num = self._minimum_path_num
+
+ self._startUpdateTopLayers()
+
+ self.currentPathNumChanged.emit()
+
## Set the layer view type
#
- # \param layer_view_type integer as in LayerView.qml and this class
- def setLayerViewType(self, layer_view_type):
+ # \param layer_view_type integer as in SimulationView.qml and this class
+ def setSimulationViewType(self, layer_view_type):
self._layer_view_type = layer_view_type
self.currentLayerNumChanged.emit()
- ## Return the layer view type, integer as in LayerView.qml and this class
- def getLayerViewType(self):
+ ## Return the layer view type, integer as in SimulationView.qml and this class
+ def getSimulationViewType(self):
return self._layer_view_type
## Set the extruder opacity
@@ -243,9 +308,20 @@ class LayerView(View):
def getExtruderCount(self):
return self._extruder_count
+ def getMinFeedrate(self):
+ return self._min_feedrate
+
+ def getMaxFeedrate(self):
+ return self._max_feedrate
+
+ def getMinThickness(self):
+ return self._min_thickness
+
+ def getMaxThickness(self):
+ return self._max_thickness
+
def calculateMaxLayers(self):
scene = self.getController().getScene()
- self._activity = True
self._old_max_layers = self._max_layers
## Recalculate num max layers
@@ -255,9 +331,16 @@ class LayerView(View):
if not layer_data:
continue
+ self.setActivity(True)
min_layer_number = sys.maxsize
max_layer_number = -sys.maxsize
for layer_id in layer_data.getLayers():
+ # Store the max and min feedrates and thicknesses for display purposes
+ for p in layer_data.getLayer(layer_id).polygons:
+ self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
+ self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate)
+ self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness)
+ self._min_thickness = min(float(p.lineThicknesses.min()), self._min_thickness)
if max_layer_number < layer_id:
max_layer_number = layer_id
if min_layer_number > layer_id:
@@ -281,10 +364,32 @@ class LayerView(View):
self.maxLayersChanged.emit()
self._startUpdateTopLayers()
+ def calculateMaxPathsOnLayer(self, layer_num):
+ # Update the currentPath
+ scene = self.getController().getScene()
+ for node in DepthFirstIterator(scene.getRoot()):
+ layer_data = node.callDecoration("getLayerData")
+ if not layer_data:
+ continue
+
+ layer = layer_data.getLayer(layer_num)
+ if layer is None:
+ return
+ new_max_paths = layer.lineMeshElementCount()
+ if new_max_paths > 0 and new_max_paths != self._max_paths:
+ self._max_paths = new_max_paths
+ self.maxPathsChanged.emit()
+
+ self.setPath(int(new_max_paths))
+
maxLayersChanged = Signal()
+ maxPathsChanged = Signal()
currentLayerNumChanged = Signal()
+ currentPathNumChanged = Signal()
globalStackChanged = Signal()
preferencesChanged = Signal()
+ busyChanged = Signal()
+ activityChanged = Signal()
## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created
# as this caused some issues.
@@ -308,26 +413,31 @@ class LayerView(View):
return True
if event.type == Event.ViewActivateEvent:
- # Make sure the LayerPass is created
- layer_pass = self.getLayerPass()
+ # Make sure the SimulationPass is created
+ layer_pass = self.getSimulationPass()
self.getRenderer().addRenderPass(layer_pass)
+ # Make sure the NozzleNode is add to the root
+ nozzle = self.getNozzleNode()
+ nozzle.setParent(self.getController().getScene().getRoot())
+ nozzle.setVisible(False)
+
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
- if not self._layerview_composite_shader:
- self._layerview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), "layerview_composite.shader"))
+ if not self._simulationview_composite_shader:
+ self._simulationview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), "simulationview_composite.shader"))
theme = Application.getInstance().getTheme()
- self._layerview_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb()))
- self._layerview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb()))
+ self._simulationview_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb()))
+ self._simulationview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb()))
if not self._composite_pass:
self._composite_pass = self.getRenderer().getRenderPass("composite")
self._old_layer_bindings = self._composite_pass.getLayerBindings()[:] # make a copy so we can restore to it later
- self._composite_pass.getLayerBindings().append("layerview")
+ self._composite_pass.getLayerBindings().append("simulationview")
self._old_composite_shader = self._composite_pass.getCompositeShader()
- self._composite_pass.setCompositeShader(self._layerview_composite_shader)
+ self._composite_pass.setCompositeShader(self._simulationview_composite_shader)
elif event.type == Event.ViewDeactivateEvent:
self._wireprint_warning_message.hide()
@@ -335,6 +445,7 @@ class LayerView(View):
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
+ self._nozzle_node.setParent(None)
self.getRenderer().removeRenderPass(self._layer_pass)
self._composite_pass.setLayerBindings(self._old_layer_bindings)
self._composite_pass.setCompositeShader(self._old_composite_shader)
@@ -364,6 +475,9 @@ class LayerView(View):
else:
self._wireprint_warning_message.hide()
+ def _onCurrentLayerNumChanged(self):
+ self.calculateMaxPathsOnLayer(self._current_layer_num)
+
def _startUpdateTopLayers(self):
if not self._compatibility_mode:
return
@@ -397,7 +511,7 @@ class LayerView(View):
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(
Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
- self.setLayerViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));
+ self.setSimulationViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type"))));
for extruder_nr, extruder_opacity in enumerate(Preferences.getInstance().getValue("layerview/extruder_opacities").split("|")):
try:
diff --git a/plugins/LayerView/LayerView.qml b/plugins/SimulationView/SimulationView.qml
old mode 100755
new mode 100644
similarity index 51%
rename from plugins/LayerView/LayerView.qml
rename to plugins/SimulationView/SimulationView.qml
index 7261926bc5..e2e0dc3aed
--- a/plugins/LayerView/LayerView.qml
+++ b/plugins/SimulationView/SimulationView.qml
@@ -13,19 +13,19 @@ Item
{
id: base
width: {
- if (UM.LayerView.compatibilityMode) {
+ if (UM.SimulationView.compatibilityMode) {
return UM.Theme.getSize("layerview_menu_size_compatibility").width;
} else {
return UM.Theme.getSize("layerview_menu_size").width;
}
}
height: {
- if (UM.LayerView.compatibilityMode) {
+ if (UM.SimulationView.compatibilityMode) {
return UM.Theme.getSize("layerview_menu_size_compatibility").height;
} else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) {
- return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.LayerView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
+ return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
} else {
- return UM.Theme.getSize("layerview_menu_size").height + UM.LayerView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
+ return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
}
}
@@ -46,7 +46,7 @@ Item
anchors.top: parent.top
width: parent.width
height: parent.height
- z: slider.z - 1
+ z: layerSlider.z - 1
color: UM.Theme.getColor("tool_panel_background")
borderWidth: UM.Theme.getSize("default_lining").width
borderColor: UM.Theme.getColor("lining")
@@ -61,7 +61,8 @@ Item
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
// if we are in compatibility mode, we only show the "line type"
- property bool show_legend: UM.LayerView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type") == 1
+ property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1
+ property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers")
property int top_layer_count: UM.Preferences.getValue("view/top_layer_count")
@@ -79,12 +80,12 @@ Item
anchors.left: parent.left
text: catalog.i18nc("@label","Color scheme")
font: UM.Theme.getFont("default");
- visible: !UM.LayerView.compatibilityMode
+ visible: !UM.SimulationView.compatibilityMode
Layout.fillWidth: true
color: UM.Theme.getColor("setting_control_text")
}
- ListModel // matches LayerView.py
+ ListModel // matches SimulationView.py
{
id: layerViewTypes
}
@@ -97,7 +98,15 @@ Item
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Line Type"),
- type_id: 1 // these ids match the switching in the shader
+ type_id: 1
+ })
+ layerViewTypes.append({
+ text: catalog.i18nc("@label:listbox", "Feedrate"),
+ type_id: 2
+ })
+ layerViewTypes.append({
+ text: catalog.i18nc("@label:listbox", "Layer thickness"),
+ type_id: 3 // these ids match the switching in the shader
})
}
@@ -108,7 +117,7 @@ Item
Layout.fillWidth: true
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
model: layerViewTypes
- visible: !UM.LayerView.compatibilityMode
+ visible: !UM.SimulationView.compatibilityMode
style: UM.Theme.styles.combobox
anchors.right: parent.right
anchors.rightMargin: 10 * screenScaleFactor
@@ -120,14 +129,14 @@ Item
Component.onCompleted:
{
- currentIndex = UM.LayerView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
+ currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
updateLegends(currentIndex);
}
function updateLegends(type_id)
{
// update visibility of legends
- view_settings.show_legend = UM.LayerView.compatibilityMode || (type_id == 1);
+ view_settings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1);
}
}
@@ -139,7 +148,7 @@ Item
text: catalog.i18nc("@label","Compatibility Mode")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
- visible: UM.LayerView.compatibilityMode
+ visible: UM.SimulationView.compatibilityMode
Layout.fillWidth: true
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
@@ -157,7 +166,7 @@ Item
target: UM.Preferences
onPreferenceChanged:
{
- layerTypeCombobox.currentIndex = UM.LayerView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
+ layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type");
layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex);
view_settings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|");
view_settings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves");
@@ -178,7 +187,7 @@ Item
view_settings.extruder_opacities[index] = checked ? 1.0 : 0.0
UM.Preferences.setValue("layerview/extruder_opacities", view_settings.extruder_opacities.join("|"));
}
- visible: !UM.LayerView.compatibilityMode
+ visible: !UM.SimulationView.compatibilityMode
enabled: index + 1 <= 4
Rectangle {
anchors.verticalCenter: parent.verticalCenter
@@ -190,7 +199,7 @@ Item
radius: width / 2
border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining")
- visible: !view_settings.show_legend
+ visible: !view_settings.show_legend & !view_settings.show_gradient
}
Layout.fillWidth: true
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
@@ -213,28 +222,28 @@ Item
Repeater {
model: ListModel {
- id: typesLegenModel
+ id: typesLegendModel
Component.onCompleted:
{
- typesLegenModel.append({
+ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Travels"),
initialValue: view_settings.show_travel_moves,
preference: "layerview/show_travel_moves",
colorId: "layerview_move_combing"
});
- typesLegenModel.append({
+ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Helpers"),
initialValue: view_settings.show_helpers,
preference: "layerview/show_helpers",
colorId: "layerview_support"
});
- typesLegenModel.append({
+ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Shell"),
initialValue: view_settings.show_skin,
preference: "layerview/show_skin",
colorId: "layerview_inset_0"
});
- typesLegenModel.append({
+ typesLegendModel.append({
label: catalog.i18nc("@label", "Show Infill"),
initialValue: view_settings.show_infill,
preference: "layerview/show_infill",
@@ -285,7 +294,7 @@ Item
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
}
text: catalog.i18nc("@label", "Only Show Top Layers")
- visible: UM.LayerView.compatibilityMode
+ visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
}
CheckBox {
@@ -294,20 +303,20 @@ Item
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
}
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
- visible: UM.LayerView.compatibilityMode
+ visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox
}
Repeater {
model: ListModel {
- id: typesLegenModelNoCheck
+ id: typesLegendModelNoCheck
Component.onCompleted:
{
- typesLegenModelNoCheck.append({
+ typesLegendModelNoCheck.append({
label: catalog.i18nc("@label", "Top / Bottom"),
colorId: "layerview_skin",
});
- typesLegenModelNoCheck.append({
+ typesLegendModelNoCheck.append({
label: catalog.i18nc("@label", "Inner Wall"),
colorId: "layerview_inset_x",
});
@@ -336,47 +345,272 @@ Item
font: UM.Theme.getFont("default")
}
}
+
+ // Text for the minimum, maximum and units for the feedrates and layer thickness
+ Rectangle {
+ id: gradientLegend
+ visible: view_settings.show_gradient
+ width: parent.width
+ height: UM.Theme.getSize("layerview_row").height
+ anchors {
+ topMargin: UM.Theme.getSize("slider_layerview_margin").height
+ horizontalCenter: parent.horizontalCenter
+ }
+
+ Label {
+ text: minText()
+ anchors.left: parent.left
+ color: UM.Theme.getColor("setting_control_text")
+ font: UM.Theme.getFont("default")
+
+ function minText() {
+ if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
+ // Feedrate selected
+ if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
+ return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
+ }
+ // Layer thickness selected
+ if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
+ return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
+ }
+ }
+ return catalog.i18nc("@label","min")
+ }
+ }
+
+ Label {
+ text: unitsText()
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: UM.Theme.getColor("setting_control_text")
+ font: UM.Theme.getFont("default")
+
+ function unitsText() {
+ if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
+ // Feedrate selected
+ if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
+ return "mm/s"
+ }
+ // Layer thickness selected
+ if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
+ return "mm"
+ }
+ }
+ return ""
+ }
+ }
+
+ Label {
+ text: maxText()
+ anchors.right: parent.right
+ color: UM.Theme.getColor("setting_control_text")
+ font: UM.Theme.getFont("default")
+
+ function maxText() {
+ if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
+ // Feedrate selected
+ if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
+ return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
+ }
+ // Layer thickness selected
+ if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
+ return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
+ }
+ }
+ return catalog.i18nc("@label","max")
+ }
+ }
+ }
+
+ // Gradient colors for feedrate and thickness
+ Rectangle { // In QML 5.9 can be changed by LinearGradient
+ // Invert values because then the bar is rotated 90 degrees
+ id: gradient
+ visible: view_settings.show_gradient
+ anchors.left: parent.right
+ height: parent.width
+ width: UM.Theme.getSize("layerview_row").height * 1.5
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: UM.Theme.getColor("lining")
+ transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
+ gradient: Gradient {
+ GradientStop {
+ position: 0.000
+ color: Qt.rgba(1, 0, 0, 1)
+ }
+ GradientStop {
+ position: 0.25
+ color: Qt.rgba(0.75, 0.5, 0.25, 1)
+ }
+ GradientStop {
+ position: 0.5
+ color: Qt.rgba(0.5, 1, 0.5, 1)
+ }
+ GradientStop {
+ position: 0.75
+ color: Qt.rgba(0.25, 0.5, 0.75, 1)
+ }
+ GradientStop {
+ position: 1.0
+ color: Qt.rgba(0, 0, 1, 1)
+ }
+ }
+ }
}
- LayerSlider {
- id: slider
+ Item {
+ id: slidersBox
- width: UM.Theme.getSize("slider_handle").width
- height: UM.Theme.getSize("layerview_menu_size").height
+ width: parent.width
+ visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
anchors {
top: parent.bottom
topMargin: UM.Theme.getSize("slider_layerview_margin").height
- right: layerViewMenu.right
- rightMargin: UM.Theme.getSize("slider_layerview_margin").width
+ left: parent.left
}
- // custom properties
- upperValue: UM.LayerView.currentLayer
- lowerValue: UM.LayerView.minimumLayer
- maximumValue: UM.LayerView.numLayers
- handleSize: UM.Theme.getSize("slider_handle").width
- trackThickness: UM.Theme.getSize("slider_groove").width
- trackColor: UM.Theme.getColor("slider_groove")
- trackBorderColor: UM.Theme.getColor("slider_groove_border")
- upperHandleColor: UM.Theme.getColor("slider_handle")
- lowerHandleColor: UM.Theme.getColor("slider_handle")
- rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
- handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
- layersVisible: UM.LayerView.layerActivity && CuraApplication.platformActivity ? true : false
+ PathSlider {
+ id: pathSlider
- // update values when layer data changes
- Connections {
- target: UM.LayerView
- onMaxLayersChanged: slider.setUpperValue(UM.LayerView.currentLayer)
- onMinimumLayerChanged: slider.setLowerValue(UM.LayerView.minimumLayer)
- onCurrentLayerChanged: slider.setUpperValue(UM.LayerView.currentLayer)
+ width: parent.width
+ height: UM.Theme.getSize("slider_handle").width
+ anchors.left: parent.left
+ visible: !UM.SimulationView.compatibilityMode
+
+ // custom properties
+ handleValue: UM.SimulationView.currentPath
+ maximumValue: UM.SimulationView.numPaths
+ handleSize: UM.Theme.getSize("slider_handle").width
+ trackThickness: UM.Theme.getSize("slider_groove").width
+ trackColor: UM.Theme.getColor("slider_groove")
+ trackBorderColor: UM.Theme.getColor("slider_groove_border")
+ handleColor: UM.Theme.getColor("slider_handle")
+ handleActiveColor: UM.Theme.getColor("slider_handle_active")
+ rangeColor: UM.Theme.getColor("slider_groove_fill")
+
+ // update values when layer data changes
+ Connections {
+ target: UM.SimulationView
+ onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
+ onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
+ }
+
+ // make sure the slider handlers show the correct value after switching views
+ Component.onCompleted: {
+ pathSlider.setHandleValue(UM.SimulationView.currentPath)
+ }
}
- // make sure the slider handlers show the correct value after switching views
- Component.onCompleted: {
- slider.setLowerValue(UM.LayerView.minimumLayer)
- slider.setUpperValue(UM.LayerView.currentLayer)
+ LayerSlider {
+ id: layerSlider
+
+ width: UM.Theme.getSize("slider_handle").width
+ height: UM.Theme.getSize("layerview_menu_size").height
+
+ anchors {
+ top: pathSlider.bottom
+ topMargin: UM.Theme.getSize("slider_layerview_margin").height
+ right: parent.right
+ rightMargin: UM.Theme.getSize("slider_layerview_margin").width
+ }
+
+ // custom properties
+ upperValue: UM.SimulationView.currentLayer
+ lowerValue: UM.SimulationView.minimumLayer
+ maximumValue: UM.SimulationView.numLayers
+ handleSize: UM.Theme.getSize("slider_handle").width
+ trackThickness: UM.Theme.getSize("slider_groove").width
+ trackColor: UM.Theme.getColor("slider_groove")
+ trackBorderColor: UM.Theme.getColor("slider_groove_border")
+ upperHandleColor: UM.Theme.getColor("slider_handle")
+ lowerHandleColor: UM.Theme.getColor("slider_handle")
+ rangeHandleColor: UM.Theme.getColor("slider_groove_fill")
+ handleActiveColor: UM.Theme.getColor("slider_handle_active")
+ handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
+
+ // update values when layer data changes
+ Connections {
+ target: UM.SimulationView
+ onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
+ onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
+ onCurrentLayerChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
+ }
+
+ // make sure the slider handlers show the correct value after switching views
+ Component.onCompleted: {
+ layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
+ layerSlider.setUpperValue(UM.SimulationView.currentLayer)
+ }
+ }
+
+ // Play simulation button
+ Button {
+ id: playButton
+ implicitWidth: UM.Theme.getSize("button").width * 0.75;
+ implicitHeight: UM.Theme.getSize("button").height * 0.75;
+ iconSource: "./resources/simulation_resume.svg"
+ style: UM.Theme.styles.tool_button
+ visible: !UM.SimulationView.compatibilityMode
+ anchors {
+ horizontalCenter: layerSlider.horizontalCenter
+ top: layerSlider.bottom
+ topMargin: UM.Theme.getSize("slider_layerview_margin").width
+ }
+
+ property var status: 0 // indicates if it's stopped (0) or playing (1)
+
+ onClicked: {
+ switch(status) {
+ case 0: {
+ resumeSimulation()
+ break
+ }
+ case 1: {
+ pauseSimulation()
+ break
+ }
+ }
+ }
+
+ function pauseSimulation() {
+ UM.SimulationView.setSimulationRunning(false)
+ iconSource = "./resources/simulation_resume.svg"
+ simulationTimer.stop()
+ status = 0
+ }
+
+ function resumeSimulation() {
+ UM.SimulationView.setSimulationRunning(true)
+ iconSource = "./resources/simulation_pause.svg"
+ simulationTimer.start()
+ status = 1
+ }
+ }
+ }
+
+ Timer
+ {
+ id: simulationTimer
+ interval: 250
+ running: false
+ repeat: true
+ onTriggered: {
+ var currentPath = UM.SimulationView.currentPath
+ var numPaths = UM.SimulationView.numPaths
+ var currentLayer = UM.SimulationView.currentLayer
+ var numLayers = UM.SimulationView.numLayers
+ if (currentPath >= numPaths) {
+ if (currentLayer >= numLayers) {
+ playButton.pauseSimulation()
+ }
+ else {
+ UM.SimulationView.setCurrentLayer(currentLayer+1)
+ UM.SimulationView.setCurrentPath(0)
+ }
+ }
+ else {
+ UM.SimulationView.setCurrentPath(currentPath+1)
+ }
}
}
}
diff --git a/plugins/SimulationView/SimulationViewProxy.py b/plugins/SimulationView/SimulationViewProxy.py
new file mode 100644
index 0000000000..21a962104d
--- /dev/null
+++ b/plugins/SimulationView/SimulationViewProxy.py
@@ -0,0 +1,246 @@
+# Copyright (c) 2017 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
+from UM.FlameProfiler import pyqtSlot
+from UM.Application import Application
+
+import SimulationView
+
+
+class SimulationViewProxy(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._current_layer = 0
+ self._controller = Application.getInstance().getController()
+ self._controller.activeViewChanged.connect(self._onActiveViewChanged)
+ self._onActiveViewChanged()
+
+ currentLayerChanged = pyqtSignal()
+ currentPathChanged = pyqtSignal()
+ maxLayersChanged = pyqtSignal()
+ maxPathsChanged = pyqtSignal()
+ activityChanged = pyqtSignal()
+ globalStackChanged = pyqtSignal()
+ preferencesChanged = pyqtSignal()
+ busyChanged = pyqtSignal()
+
+ @pyqtProperty(bool, notify=activityChanged)
+ def layerActivity(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getActivity()
+ return False
+
+ @pyqtProperty(int, notify=maxLayersChanged)
+ def numLayers(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMaxLayers()
+ return 0
+
+ @pyqtProperty(int, notify=currentLayerChanged)
+ def currentLayer(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getCurrentLayer()
+ return 0
+
+ @pyqtProperty(int, notify=currentLayerChanged)
+ def minimumLayer(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMinimumLayer()
+ return 0
+
+ @pyqtProperty(int, notify=maxPathsChanged)
+ def numPaths(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMaxPaths()
+ return 0
+
+ @pyqtProperty(int, notify=currentPathChanged)
+ def currentPath(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getCurrentPath()
+ return 0
+
+ @pyqtProperty(int, notify=currentPathChanged)
+ def minimumPath(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMinimumPath()
+ return 0
+
+ @pyqtProperty(bool, notify=busyChanged)
+ def busy(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.isBusy()
+ return False
+
+ @pyqtProperty(bool, notify=preferencesChanged)
+ def compatibilityMode(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getCompatibilityMode()
+ return False
+
+ @pyqtSlot(int)
+ def setCurrentLayer(self, layer_num):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setLayer(layer_num)
+
+ @pyqtSlot(int)
+ def setMinimumLayer(self, layer_num):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setMinimumLayer(layer_num)
+
+ @pyqtSlot(int)
+ def setCurrentPath(self, path_num):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setPath(path_num)
+
+ @pyqtSlot(int)
+ def setMinimumPath(self, path_num):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setMinimumPath(path_num)
+
+ @pyqtSlot(int)
+ def setSimulationViewType(self, layer_view_type):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setSimulationViewisinstance(layer_view_type)
+
+ @pyqtSlot(result=int)
+ def getSimulationViewType(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getSimulationViewType()
+ return 0
+
+ @pyqtSlot(bool)
+ def setSimulationRunning(self, running):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setSimulationRunning(running)
+
+ @pyqtSlot(result=bool)
+ def getSimulationRunning(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.isSimulationRunning()
+ return False
+
+ @pyqtSlot(result=float)
+ def getMinFeedrate(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMinFeedrate()
+ return 0
+
+ @pyqtSlot(result=float)
+ def getMaxFeedrate(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMaxFeedrate()
+ return 0
+
+ @pyqtSlot(result=float)
+ def getMinThickness(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMinThickness()
+ return 0
+
+ @pyqtSlot(result=float)
+ def getMaxThickness(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getMaxThickness()
+ return 0
+
+ # Opacity 0..1
+ @pyqtSlot(int, float)
+ def setExtruderOpacity(self, extruder_nr, opacity):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setExtruderOpacity(extruder_nr, opacity)
+
+ @pyqtSlot(int)
+ def setShowTravelMoves(self, show):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setShowTravelMoves(show)
+
+ @pyqtSlot(int)
+ def setShowHelpers(self, show):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setShowHelpers(show)
+
+ @pyqtSlot(int)
+ def setShowSkin(self, show):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setShowSkin(show)
+
+ @pyqtSlot(int)
+ def setShowInfill(self, show):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.setShowInfill(show)
+
+ @pyqtProperty(int, notify=globalStackChanged)
+ def extruderCount(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ return active_view.getExtruderCount()
+ return 0
+
+ def _layerActivityChanged(self):
+ self.activityChanged.emit()
+
+ def _onLayerChanged(self):
+ self.currentLayerChanged.emit()
+ self._layerActivityChanged()
+
+ def _onPathChanged(self):
+ self.currentPathChanged.emit()
+ self._layerActivityChanged()
+
+ def _onMaxLayersChanged(self):
+ self.maxLayersChanged.emit()
+
+ def _onMaxPathsChanged(self):
+ self.maxPathsChanged.emit()
+
+ def _onBusyChanged(self):
+ self.busyChanged.emit()
+
+ def _onActivityChanged(self):
+ self.activityChanged.emit()
+
+ def _onGlobalStackChanged(self):
+ self.globalStackChanged.emit()
+
+ def _onPreferencesChanged(self):
+ self.preferencesChanged.emit()
+
+ def _onActiveViewChanged(self):
+ active_view = self._controller.getActiveView()
+ if isinstance(active_view, SimulationView.SimulationView.SimulationView):
+ active_view.currentLayerNumChanged.connect(self._onLayerChanged)
+ active_view.currentPathNumChanged.connect(self._onPathChanged)
+ active_view.maxLayersChanged.connect(self._onMaxLayersChanged)
+ active_view.maxPathsChanged.connect(self._onMaxPathsChanged)
+ active_view.busyChanged.connect(self._onBusyChanged)
+ active_view.activityChanged.connect(self._onActivityChanged)
+ active_view.globalStackChanged.connect(self._onGlobalStackChanged)
+ active_view.preferencesChanged.connect(self._onPreferencesChanged)
diff --git a/plugins/SimulationView/__init__.py b/plugins/SimulationView/__init__.py
new file mode 100644
index 0000000000..f7ccf41acc
--- /dev/null
+++ b/plugins/SimulationView/__init__.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2017 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+from PyQt5.QtQml import qmlRegisterSingletonType
+
+from UM.i18n import i18nCatalog
+from . import SimulationViewProxy, SimulationView
+
+catalog = i18nCatalog("cura")
+
+def getMetaData():
+ return {
+ "view": {
+ "name": catalog.i18nc("@item:inlistbox", "Simulation view"),
+ "view_panel": "SimulationView.qml",
+ "weight": 2
+ }
+ }
+
+def createSimulationViewProxy(engine, script_engine):
+ return SimulationViewProxy.SimulatorViewProxy()
+
+def register(app):
+ simulation_view = SimulationView.SimulationView()
+ qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy)
+ return { "view": SimulationView.SimulationView()}
diff --git a/plugins/LayerView/layers.shader b/plugins/SimulationView/layers.shader
old mode 100755
new mode 100644
similarity index 100%
rename from plugins/LayerView/layers.shader
rename to plugins/SimulationView/layers.shader
diff --git a/plugins/LayerView/layers3d.shader b/plugins/SimulationView/layers3d.shader
old mode 100755
new mode 100644
similarity index 92%
rename from plugins/LayerView/layers3d.shader
rename to plugins/SimulationView/layers3d.shader
index e8fe425c70..f377fca055
--- a/plugins/LayerView/layers3d.shader
+++ b/plugins/SimulationView/layers3d.shader
@@ -6,6 +6,10 @@ vertex41core =
uniform highp mat4 u_modelMatrix;
uniform highp mat4 u_viewProjectionMatrix;
uniform lowp float u_active_extruder;
+ uniform lowp float u_max_feedrate;
+ uniform lowp float u_min_feedrate;
+ uniform lowp float u_max_thickness;
+ uniform lowp float u_min_thickness;
uniform lowp int u_layer_view_type;
uniform lowp vec4 u_extruder_opacity; // currently only for max 4 extruders, others always visible
@@ -18,6 +22,8 @@ vertex41core =
in highp vec2 a_line_dim; // line width and thickness
in highp float a_extruder;
in highp float a_line_type;
+ in highp float a_feedrate;
+ in highp float a_thickness;
out lowp vec4 v_color;
@@ -32,6 +38,15 @@ vertex41core =
out highp vec3 f_vertex;
out highp vec3 f_normal;
+ vec4 gradientColor(float abs_value, float min_value, float max_value)
+ {
+ float value = (abs_value - min_value)/(max_value - min_value);
+ float red = value;
+ float green = 1-abs(1-2*value);
+ float blue = 1-value;
+ return vec4(red, green, blue, 1.0);
+ }
+
void main()
{
vec4 v1_vertex = a_vertex;
@@ -48,6 +63,12 @@ vertex41core =
case 1: // "Line type"
v_color = a_color;
break;
+ case 2: // "Feedrate"
+ v_color = gradientColor(a_feedrate, u_min_feedrate, u_max_feedrate);
+ break;
+ case 3: // "Layer thickness"
+ v_color = gradientColor(a_line_dim.y, u_min_thickness, u_max_thickness);
+ break;
}
v_vertex = world_space_vert.xyz;
@@ -247,6 +268,12 @@ u_show_helpers = 1
u_show_skin = 1
u_show_infill = 1
+u_min_feedrate = 0
+u_max_feedrate = 1
+
+u_min_thickness = 0
+u_max_thickness = 1
+
[bindings]
u_modelViewProjectionMatrix = model_view_projection_matrix
u_modelMatrix = model_matrix
@@ -262,3 +289,5 @@ a_line_dim = line_dim
a_extruder = extruder
a_material_color = material_color
a_line_type = line_type
+a_feedrate = feedrate
+a_thickness = thickness
diff --git a/plugins/SimulationView/layers3d_shadow.shader b/plugins/SimulationView/layers3d_shadow.shader
new file mode 100644
index 0000000000..ad75fcf9d0
--- /dev/null
+++ b/plugins/SimulationView/layers3d_shadow.shader
@@ -0,0 +1,256 @@
+[shaders]
+vertex41core =
+ #version 410
+ uniform highp mat4 u_modelViewProjectionMatrix;
+
+ uniform highp mat4 u_modelMatrix;
+ uniform highp mat4 u_viewProjectionMatrix;
+ uniform lowp float u_active_extruder;
+ uniform lowp vec4 u_extruder_opacity; // currently only for max 4 extruders, others always visible
+
+ uniform highp mat4 u_normalMatrix;
+
+ in highp vec4 a_vertex;
+ in lowp vec4 a_color;
+ in lowp vec4 a_grayColor;
+ in lowp vec4 a_material_color;
+ in highp vec4 a_normal;
+ in highp vec2 a_line_dim; // line width and thickness
+ in highp float a_extruder;
+ in highp float a_line_type;
+
+ out lowp vec4 v_color;
+
+ out highp vec3 v_vertex;
+ out highp vec3 v_normal;
+ out lowp vec2 v_line_dim;
+ out highp int v_extruder;
+ out highp vec4 v_extruder_opacity;
+ out float v_line_type;
+
+ out lowp vec4 f_color;
+ out highp vec3 f_vertex;
+ out highp vec3 f_normal;
+
+ void main()
+ {
+ vec4 v1_vertex = a_vertex;
+ v1_vertex.y -= a_line_dim.y / 2; // half layer down
+
+ vec4 world_space_vert = u_modelMatrix * v1_vertex;
+ gl_Position = world_space_vert;
+ // shade the color depending on the extruder index stored in the alpha component of the color
+
+ v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer
+ v_vertex = world_space_vert.xyz;
+ v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
+ v_line_dim = a_line_dim;
+ v_extruder = int(a_extruder);
+ v_line_type = a_line_type;
+ v_extruder_opacity = u_extruder_opacity;
+
+ // for testing without geometry shader
+ f_color = v_color;
+ f_vertex = v_vertex;
+ f_normal = v_normal;
+ }
+
+geometry41core =
+ #version 410
+
+ uniform highp mat4 u_viewProjectionMatrix;
+ uniform int u_show_travel_moves;
+ uniform int u_show_helpers;
+ uniform int u_show_skin;
+ uniform int u_show_infill;
+
+ layout(lines) in;
+ layout(triangle_strip, max_vertices = 26) out;
+
+ in vec4 v_color[];
+ in vec3 v_vertex[];
+ in vec3 v_normal[];
+ in vec2 v_line_dim[];
+ in int v_extruder[];
+ in vec4 v_extruder_opacity[];
+ in float v_line_type[];
+
+ out vec4 f_color;
+ out vec3 f_normal;
+ out vec3 f_vertex;
+
+ // Set the set of variables and EmitVertex
+ void myEmitVertex(vec3 vertex, vec4 color, vec3 normal, vec4 pos) {
+ f_vertex = vertex;
+ f_color = color;
+ f_normal = normal;
+ gl_Position = pos;
+ EmitVertex();
+ }
+
+ void main()
+ {
+ vec4 g_vertex_delta;
+ vec3 g_vertex_normal_horz; // horizontal and vertical in respect to layers
+ vec4 g_vertex_offset_horz; // vec4 to match gl_in[x].gl_Position
+ vec3 g_vertex_normal_vert;
+ vec4 g_vertex_offset_vert;
+ vec3 g_vertex_normal_horz_head;
+ vec4 g_vertex_offset_horz_head;
+
+ float size_x;
+ float size_y;
+
+ if ((v_extruder_opacity[0][v_extruder[0]] == 0.0) && (v_line_type[0] != 8) && (v_line_type[0] != 9)) {
+ return;
+ }
+ // See LayerPolygon; 8 is MoveCombingType, 9 is RetractionType
+ if ((u_show_travel_moves == 0) && ((v_line_type[0] == 8) || (v_line_type[0] == 9))) {
+ return;
+ }
+ if ((u_show_helpers == 0) && ((v_line_type[0] == 4) || (v_line_type[0] == 5) || (v_line_type[0] == 7) || (v_line_type[0] == 10))) {
+ return;
+ }
+ if ((u_show_skin == 0) && ((v_line_type[0] == 1) || (v_line_type[0] == 2) || (v_line_type[0] == 3))) {
+ return;
+ }
+ if ((u_show_infill == 0) && (v_line_type[0] == 6)) {
+ return;
+ }
+
+ if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
+ // fixed size for movements
+ size_x = 0.05;
+ } else {
+ size_x = v_line_dim[1].x / 2 + 0.01; // radius, and make it nicely overlapping
+ }
+ size_y = v_line_dim[1].y / 2 + 0.01;
+
+ g_vertex_delta = gl_in[1].gl_Position - gl_in[0].gl_Position;
+ g_vertex_normal_horz_head = normalize(vec3(-g_vertex_delta.x, -g_vertex_delta.y, -g_vertex_delta.z));
+ g_vertex_offset_horz_head = vec4(g_vertex_normal_horz_head * size_x, 0.0);
+
+ g_vertex_normal_horz = normalize(vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x));
+
+ g_vertex_offset_horz = vec4(g_vertex_normal_horz * size_x, 0.0); //size * g_vertex_normal_horz;
+ g_vertex_normal_vert = vec3(0.0, 1.0, 0.0);
+ g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0);
+
+ if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
+ // Travels: flat plane with pointy ends
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert));
+
+ EndPrimitive();
+ } else {
+ // All normal lines are rendered as 3d tubes.
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
+ myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
+
+ EndPrimitive();
+
+ // left side
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
+ myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
+
+ EndPrimitive();
+
+ myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
+ myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head));
+ myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
+
+ EndPrimitive();
+
+ // right side
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
+
+ EndPrimitive();
+
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
+ myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz_head, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head));
+ myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
+
+ EndPrimitive();
+ }
+ }
+
+fragment41core =
+ #version 410
+ in lowp vec4 f_color;
+ in lowp vec3 f_normal;
+ in lowp vec3 f_vertex;
+
+ out vec4 frag_color;
+
+ uniform mediump vec4 u_ambientColor;
+ uniform highp vec3 u_lightPosition;
+
+ void main()
+ {
+ mediump vec4 finalColor = vec4(0.0);
+ float alpha = f_color.a;
+
+ finalColor.rgb += f_color.rgb * 0.3;
+
+ highp vec3 normal = normalize(f_normal);
+ highp vec3 light_dir = normalize(u_lightPosition - f_vertex);
+
+ // Diffuse Component
+ highp float NdotL = clamp(dot(normal, light_dir), 0.0, 1.0);
+ finalColor += (NdotL * f_color);
+ finalColor.a = alpha; // Do not change alpha in any way
+
+ frag_color = finalColor;
+ }
+
+
+[defaults]
+u_active_extruder = 0.0
+u_extruder_opacity = [1.0, 1.0, 1.0, 1.0]
+
+u_specularColor = [0.4, 0.4, 0.4, 1.0]
+u_ambientColor = [0.3, 0.3, 0.3, 0.0]
+u_diffuseColor = [1.0, 0.79, 0.14, 1.0]
+u_shininess = 20.0
+
+u_show_travel_moves = 0
+u_show_helpers = 1
+u_show_skin = 1
+u_show_infill = 1
+
+[bindings]
+u_modelViewProjectionMatrix = model_view_projection_matrix
+u_modelMatrix = model_matrix
+u_viewProjectionMatrix = view_projection_matrix
+u_normalMatrix = normal_matrix
+u_lightPosition = light_0_position
+
+[attributes]
+a_vertex = vertex
+a_color = color
+a_grayColor = vec4(0.87, 0.12, 0.45, 1.0)
+a_normal = normal
+a_line_dim = line_dim
+a_extruder = extruder
+a_material_color = material_color
+a_line_type = line_type
diff --git a/plugins/SimulationView/layers_shadow.shader b/plugins/SimulationView/layers_shadow.shader
new file mode 100644
index 0000000000..972f18c921
--- /dev/null
+++ b/plugins/SimulationView/layers_shadow.shader
@@ -0,0 +1,156 @@
+[shaders]
+vertex =
+ uniform highp mat4 u_modelViewProjectionMatrix;
+ uniform lowp float u_active_extruder;
+ uniform lowp float u_shade_factor;
+ uniform highp int u_layer_view_type;
+
+ attribute highp float a_extruder;
+ attribute highp float a_line_type;
+ attribute highp vec4 a_vertex;
+ attribute lowp vec4 a_color;
+ attribute lowp vec4 a_material_color;
+
+ varying lowp vec4 v_color;
+ varying float v_line_type;
+
+ void main()
+ {
+ gl_Position = u_modelViewProjectionMatrix * a_vertex;
+ // shade the color depending on the extruder index
+ v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer;
+ // 8 and 9 are travel moves
+ // if ((a_line_type != 8.0) && (a_line_type != 9.0)) {
+ // v_color = (a_extruder == u_active_extruder) ? v_color : vec4(u_shade_factor * v_color.rgb, v_color.a);
+ // }
+
+ v_line_type = a_line_type;
+ }
+
+fragment =
+ varying lowp vec4 v_color;
+ varying float v_line_type;
+
+ uniform int u_show_travel_moves;
+ uniform int u_show_helpers;
+ uniform int u_show_skin;
+ uniform int u_show_infill;
+
+ void main()
+ {
+ if ((u_show_travel_moves == 0) && (v_line_type >= 7.5) && (v_line_type <= 9.5)) { // actually, 8 and 9
+ // discard movements
+ discard;
+ }
+ // support: 4, 5, 7, 10
+ if ((u_show_helpers == 0) && (
+ ((v_line_type >= 3.5) && (v_line_type <= 4.5)) ||
+ ((v_line_type >= 6.5) && (v_line_type <= 7.5)) ||
+ ((v_line_type >= 9.5) && (v_line_type <= 10.5)) ||
+ ((v_line_type >= 4.5) && (v_line_type <= 5.5))
+ )) {
+ discard;
+ }
+ // skin: 1, 2, 3
+ if ((u_show_skin == 0) && (
+ (v_line_type >= 0.5) && (v_line_type <= 3.5)
+ )) {
+ discard;
+ }
+ // infill:
+ if ((u_show_infill == 0) && (v_line_type >= 5.5) && (v_line_type <= 6.5)) {
+ // discard movements
+ discard;
+ }
+
+ gl_FragColor = v_color;
+ }
+
+vertex41core =
+ #version 410
+ uniform highp mat4 u_modelViewProjectionMatrix;
+ uniform lowp float u_active_extruder;
+ uniform lowp float u_shade_factor;
+ uniform highp int u_layer_view_type;
+
+ in highp float a_extruder;
+ in highp float a_line_type;
+ in highp vec4 a_vertex;
+ in lowp vec4 a_color;
+ in lowp vec4 a_material_color;
+
+ out lowp vec4 v_color;
+ out float v_line_type;
+
+ void main()
+ {
+ gl_Position = u_modelViewProjectionMatrix * a_vertex;
+ v_color = vec4(0.4, 0.4, 0.4, 0.9); // default color for not current layer
+ // if ((a_line_type != 8) && (a_line_type != 9)) {
+ // v_color = (a_extruder == u_active_extruder) ? v_color : vec4(u_shade_factor * v_color.rgb, v_color.a);
+ // }
+
+ v_line_type = a_line_type;
+ }
+
+fragment41core =
+ #version 410
+ in lowp vec4 v_color;
+ in float v_line_type;
+ out vec4 frag_color;
+
+ uniform int u_show_travel_moves;
+ uniform int u_show_helpers;
+ uniform int u_show_skin;
+ uniform int u_show_infill;
+
+ void main()
+ {
+ if ((u_show_travel_moves == 0) && (v_line_type >= 7.5) && (v_line_type <= 9.5)) { // actually, 8 and 9
+ // discard movements
+ discard;
+ }
+ // helpers: 4, 5, 7, 10
+ if ((u_show_helpers == 0) && (
+ ((v_line_type >= 3.5) && (v_line_type <= 4.5)) ||
+ ((v_line_type >= 6.5) && (v_line_type <= 7.5)) ||
+ ((v_line_type >= 9.5) && (v_line_type <= 10.5)) ||
+ ((v_line_type >= 4.5) && (v_line_type <= 5.5))
+ )) {
+ discard;
+ }
+ // skin: 1, 2, 3
+ if ((u_show_skin == 0) && (
+ (v_line_type >= 0.5) && (v_line_type <= 3.5)
+ )) {
+ discard;
+ }
+ // infill:
+ if ((u_show_infill == 0) && (v_line_type >= 5.5) && (v_line_type <= 6.5)) {
+ // discard movements
+ discard;
+ }
+
+ frag_color = v_color;
+ }
+
+[defaults]
+u_active_extruder = 0.0
+u_shade_factor = 0.60
+u_layer_view_type = 0
+u_extruder_opacity = [1.0, 1.0, 1.0, 1.0]
+
+u_show_travel_moves = 0
+u_show_helpers = 1
+u_show_skin = 1
+u_show_infill = 1
+
+[bindings]
+u_modelViewProjectionMatrix = model_view_projection_matrix
+
+[attributes]
+a_vertex = vertex
+a_color = color
+a_extruder = extruder
+a_line_type = line_type
+a_material_color = material_color
diff --git a/plugins/LayerView/plugin.json b/plugins/SimulationView/plugin.json
similarity index 54%
rename from plugins/LayerView/plugin.json
rename to plugins/SimulationView/plugin.json
index 232fe9c361..0e7bec0626 100644
--- a/plugins/LayerView/plugin.json
+++ b/plugins/SimulationView/plugin.json
@@ -1,8 +1,8 @@
{
- "name": "Layer View",
+ "name": "Simulation View",
"author": "Ultimaker B.V.",
"version": "1.0.0",
- "description": "Provides the Layer view.",
+ "description": "Provides the Simulation view.",
"api": 4,
"i18n-catalog": "cura"
}
diff --git a/plugins/SimulationView/resources/nozzle.stl b/plugins/SimulationView/resources/nozzle.stl
new file mode 100644
index 0000000000..7f4b22804a
Binary files /dev/null and b/plugins/SimulationView/resources/nozzle.stl differ
diff --git a/plugins/SimulationView/resources/simulation_pause.svg b/plugins/SimulationView/resources/simulation_pause.svg
new file mode 100644
index 0000000000..67f7deea5d
--- /dev/null
+++ b/plugins/SimulationView/resources/simulation_pause.svg
@@ -0,0 +1,79 @@
+
+
diff --git a/plugins/SimulationView/resources/simulation_resume.svg b/plugins/SimulationView/resources/simulation_resume.svg
new file mode 100644
index 0000000000..a8ed8e79a3
--- /dev/null
+++ b/plugins/SimulationView/resources/simulation_resume.svg
@@ -0,0 +1,82 @@
+
+
diff --git a/plugins/LayerView/layerview_composite.shader b/plugins/SimulationView/simulationview_composite.shader
similarity index 100%
rename from plugins/LayerView/layerview_composite.shader
rename to plugins/SimulationView/simulationview_composite.shader