mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-20 12:49:38 +08:00
CURA-4526 Add Simulation View plugin
This commit is contained in:
parent
b6e997c88d
commit
2df06bbbb9
325
plugins/SimulationView/LayerSlider.qml
Normal file
325
plugins/SimulationView/LayerSlider.qml
Normal file
@ -0,0 +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 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
plugins/SimulationView/NozzleNode.py
Normal file
49
plugins/SimulationView/NozzleNode.py
Normal file
@ -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
|
161
plugins/SimulationView/PathSlider.qml
Normal file
161
plugins/SimulationView/PathSlider.qml
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
186
plugins/SimulationView/SimulationPass.py
Normal file
186
plugins/SimulationView/SimulationPass.py
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
# 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 .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()
|
104
plugins/SimulationView/SimulationSliderLabel.qml
Normal file
104
plugins/SimulationView/SimulationSliderLabel.qml
Normal file
@ -0,0 +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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
609
plugins/SimulationView/SimulationView.py
Normal file
609
plugins/SimulationView/SimulationView.py
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
|
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.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.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
|
||||||
|
from cura.ConvexHullNode import ConvexHullNode
|
||||||
|
|
||||||
|
from .NozzleNode import NozzleNode
|
||||||
|
from .SimulationPass import SimulationPass
|
||||||
|
from .SimulationViewProxy import SimulationViewProxy
|
||||||
|
|
||||||
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
## View used to display g-code paths.
|
||||||
|
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__()
|
||||||
|
|
||||||
|
self._max_layers = 0
|
||||||
|
self._current_layer_num = 0
|
||||||
|
self._minimum_layer_num = 0
|
||||||
|
self._current_layer_mesh = None
|
||||||
|
self._current_layer_jumps = None
|
||||||
|
self._top_layers_job = None
|
||||||
|
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._simulationview_composite_shader = None
|
||||||
|
self._old_composite_shader = None
|
||||||
|
|
||||||
|
self._global_container_stack = None
|
||||||
|
self._proxy = 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)
|
||||||
|
Preferences.getInstance().addPreference("view/force_layer_view_compatibility_mode", False)
|
||||||
|
|
||||||
|
Preferences.getInstance().addPreference("layerview/layer_view_type", 0)
|
||||||
|
Preferences.getInstance().addPreference("layerview/extruder_opacities", "")
|
||||||
|
|
||||||
|
Preferences.getInstance().addPreference("layerview/show_travel_moves", False)
|
||||||
|
Preferences.getInstance().addPreference("layerview/show_helpers", True)
|
||||||
|
Preferences.getInstance().addPreference("layerview/show_skin", True)
|
||||||
|
Preferences.getInstance().addPreference("layerview/show_infill", True)
|
||||||
|
|
||||||
|
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
|
||||||
|
self._updateWithPreferences()
|
||||||
|
|
||||||
|
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
|
||||||
|
self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
|
||||||
|
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", "Simulation View"))
|
||||||
|
|
||||||
|
def _resetSettings(self):
|
||||||
|
self._layer_view_type = 0 # 0 is material color, 1 is color by linetype, 2 is speed
|
||||||
|
self._extruder_count = 0
|
||||||
|
self._extruder_opacity = [1.0, 1.0, 1.0, 1.0]
|
||||||
|
self._show_travel_moves = 0
|
||||||
|
self._show_helpers = 1
|
||||||
|
self._show_skin = 1
|
||||||
|
self._show_infill = 1
|
||||||
|
self.resetLayerData()
|
||||||
|
|
||||||
|
def getActivity(self):
|
||||||
|
return self._activity
|
||||||
|
|
||||||
|
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 = SimulationPass(1, 1)
|
||||||
|
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
||||||
|
self._layer_pass.setSimulationView(self)
|
||||||
|
return self._layer_pass
|
||||||
|
|
||||||
|
def getCurrentLayer(self):
|
||||||
|
return self._current_layer_num
|
||||||
|
|
||||||
|
def getMinimumLayer(self):
|
||||||
|
return self._minimum_layer_num
|
||||||
|
|
||||||
|
def getMaxLayers(self):
|
||||||
|
return self._max_layers
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def setBusy(self, busy):
|
||||||
|
if busy != self._busy:
|
||||||
|
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()
|
||||||
|
renderer = self.getRenderer()
|
||||||
|
|
||||||
|
if not self._ghost_shader:
|
||||||
|
self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
|
||||||
|
self._ghost_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_ghost").getRgb()))
|
||||||
|
|
||||||
|
for node in DepthFirstIterator(scene.getRoot()):
|
||||||
|
# We do not want to render ConvexHullNode as it conflicts with the bottom layers.
|
||||||
|
# However, it is somewhat relevant when the node is selected, so do render it then.
|
||||||
|
if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not node.render(renderer):
|
||||||
|
if (node.getMeshData()) and node.isVisible():
|
||||||
|
renderer.queueNode(node, transparent = True, shader = self._ghost_shader)
|
||||||
|
|
||||||
|
def setLayer(self, value):
|
||||||
|
if self._current_layer_num != value:
|
||||||
|
self._current_layer_num = value
|
||||||
|
if self._current_layer_num < 0:
|
||||||
|
self._current_layer_num = 0
|
||||||
|
if self._current_layer_num > self._max_layers:
|
||||||
|
self._current_layer_num = self._max_layers
|
||||||
|
if self._current_layer_num < self._minimum_layer_num:
|
||||||
|
self._minimum_layer_num = self._current_layer_num
|
||||||
|
|
||||||
|
self._startUpdateTopLayers()
|
||||||
|
|
||||||
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def setMinimumLayer(self, value):
|
||||||
|
if self._minimum_layer_num != value:
|
||||||
|
self._minimum_layer_num = value
|
||||||
|
if self._minimum_layer_num < 0:
|
||||||
|
self._minimum_layer_num = 0
|
||||||
|
if self._minimum_layer_num > self._max_layers:
|
||||||
|
self._minimum_layer_num = self._max_layers
|
||||||
|
if self._minimum_layer_num > self._current_layer_num:
|
||||||
|
self._current_layer_num = self._minimum_layer_num
|
||||||
|
|
||||||
|
self._startUpdateTopLayers()
|
||||||
|
|
||||||
|
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 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 SimulationView.qml and this class
|
||||||
|
def getSimulationViewType(self):
|
||||||
|
return self._layer_view_type
|
||||||
|
|
||||||
|
## Set the extruder opacity
|
||||||
|
#
|
||||||
|
# \param extruder_nr 0..3
|
||||||
|
# \param opacity 0.0 .. 1.0
|
||||||
|
def setExtruderOpacity(self, extruder_nr, opacity):
|
||||||
|
if 0 <= extruder_nr <= 3:
|
||||||
|
self._extruder_opacity[extruder_nr] = opacity
|
||||||
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def getExtruderOpacities(self):
|
||||||
|
return self._extruder_opacity
|
||||||
|
|
||||||
|
def setShowTravelMoves(self, show):
|
||||||
|
self._show_travel_moves = show
|
||||||
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def getShowTravelMoves(self):
|
||||||
|
return self._show_travel_moves
|
||||||
|
|
||||||
|
def setShowHelpers(self, show):
|
||||||
|
self._show_helpers = show
|
||||||
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def getShowHelpers(self):
|
||||||
|
return self._show_helpers
|
||||||
|
|
||||||
|
def setShowSkin(self, show):
|
||||||
|
self._show_skin = show
|
||||||
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def getShowSkin(self):
|
||||||
|
return self._show_skin
|
||||||
|
|
||||||
|
def setShowInfill(self, show):
|
||||||
|
self._show_infill = show
|
||||||
|
self.currentLayerNumChanged.emit()
|
||||||
|
|
||||||
|
def getShowInfill(self):
|
||||||
|
return self._show_infill
|
||||||
|
|
||||||
|
def getCompatibilityMode(self):
|
||||||
|
return self._compatibility_mode
|
||||||
|
|
||||||
|
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._old_max_layers = self._max_layers
|
||||||
|
## Recalculate num max layers
|
||||||
|
new_max_layers = 0
|
||||||
|
for node in DepthFirstIterator(scene.getRoot()):
|
||||||
|
layer_data = node.callDecoration("getLayerData")
|
||||||
|
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:
|
||||||
|
min_layer_number = layer_id
|
||||||
|
layer_count = max_layer_number - min_layer_number
|
||||||
|
|
||||||
|
if new_max_layers < layer_count:
|
||||||
|
new_max_layers = layer_count
|
||||||
|
|
||||||
|
if new_max_layers > 0 and new_max_layers != self._old_max_layers:
|
||||||
|
self._max_layers = new_max_layers
|
||||||
|
|
||||||
|
# The qt slider has a bit of weird behavior that if the maxvalue needs to be changed first
|
||||||
|
# if it's the largest value. If we don't do this, we can have a slider block outside of the
|
||||||
|
# slider.
|
||||||
|
if new_max_layers > self._current_layer_num:
|
||||||
|
self.maxLayersChanged.emit()
|
||||||
|
self.setLayer(int(self._max_layers))
|
||||||
|
else:
|
||||||
|
self.setLayer(int(self._max_layers))
|
||||||
|
self.maxLayersChanged.emit()
|
||||||
|
self._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.
|
||||||
|
def getProxy(self, engine, script_engine):
|
||||||
|
return self._proxy
|
||||||
|
|
||||||
|
def endRendering(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def event(self, event):
|
||||||
|
modifiers = QApplication.keyboardModifiers()
|
||||||
|
ctrl_is_active = modifiers & Qt.ControlModifier
|
||||||
|
shift_is_active = modifiers & Qt.ShiftModifier
|
||||||
|
if event.type == Event.KeyPressEvent and ctrl_is_active:
|
||||||
|
amount = 10 if shift_is_active else 1
|
||||||
|
if event.key == KeyEvent.UpKey:
|
||||||
|
self.setLayer(self._current_layer_num + amount)
|
||||||
|
return True
|
||||||
|
if event.key == KeyEvent.DownKey:
|
||||||
|
self.setLayer(self._current_layer_num - amount)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if event.type == Event.ViewActivateEvent:
|
||||||
|
# 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._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._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("simulationview")
|
||||||
|
self._old_composite_shader = self._composite_pass.getCompositeShader()
|
||||||
|
self._composite_pass.setCompositeShader(self._simulationview_composite_shader)
|
||||||
|
|
||||||
|
elif event.type == Event.ViewDeactivateEvent:
|
||||||
|
self._wireprint_warning_message.hide()
|
||||||
|
Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged)
|
||||||
|
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)
|
||||||
|
|
||||||
|
def getCurrentLayerMesh(self):
|
||||||
|
return self._current_layer_mesh
|
||||||
|
|
||||||
|
def getCurrentLayerJumps(self):
|
||||||
|
return self._current_layer_jumps
|
||||||
|
|
||||||
|
def _onGlobalStackChanged(self):
|
||||||
|
if self._global_container_stack:
|
||||||
|
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
|
||||||
|
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
if self._global_container_stack:
|
||||||
|
self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
|
||||||
|
self._extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
|
||||||
|
self._onPropertyChanged("wireframe_enabled", "value")
|
||||||
|
self.globalStackChanged.emit()
|
||||||
|
else:
|
||||||
|
self._wireprint_warning_message.hide()
|
||||||
|
|
||||||
|
def _onPropertyChanged(self, key, property_name):
|
||||||
|
if key == "wireframe_enabled" and property_name == "value":
|
||||||
|
if self._global_container_stack.getProperty("wireframe_enabled", "value"):
|
||||||
|
self._wireprint_warning_message.show()
|
||||||
|
else:
|
||||||
|
self._wireprint_warning_message.hide()
|
||||||
|
|
||||||
|
def _onCurrentLayerNumChanged(self):
|
||||||
|
self.calculateMaxPathsOnLayer(self._current_layer_num)
|
||||||
|
|
||||||
|
def _startUpdateTopLayers(self):
|
||||||
|
if not self._compatibility_mode:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._top_layers_job:
|
||||||
|
self._top_layers_job.finished.disconnect(self._updateCurrentLayerMesh)
|
||||||
|
self._top_layers_job.cancel()
|
||||||
|
|
||||||
|
self.setBusy(True)
|
||||||
|
|
||||||
|
self._top_layers_job = _CreateTopLayersJob(self._controller.getScene(), self._current_layer_num, self._solid_layers)
|
||||||
|
self._top_layers_job.finished.connect(self._updateCurrentLayerMesh)
|
||||||
|
self._top_layers_job.start()
|
||||||
|
|
||||||
|
def _updateCurrentLayerMesh(self, job):
|
||||||
|
self.setBusy(False)
|
||||||
|
|
||||||
|
if not job.getResult():
|
||||||
|
return
|
||||||
|
self.resetLayerData() # Reset the layer data only when job is done. Doing it now prevents "blinking" data.
|
||||||
|
self._current_layer_mesh = job.getResult().get("layers")
|
||||||
|
if self._show_travel_moves:
|
||||||
|
self._current_layer_jumps = job.getResult().get("jumps")
|
||||||
|
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
|
||||||
|
|
||||||
|
self._top_layers_job = None
|
||||||
|
|
||||||
|
def _updateWithPreferences(self):
|
||||||
|
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
|
||||||
|
self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
|
||||||
|
self._compatibility_mode = OpenGLContext.isLegacyOpenGL() or bool(
|
||||||
|
Preferences.getInstance().getValue("view/force_layer_view_compatibility_mode"))
|
||||||
|
|
||||||
|
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:
|
||||||
|
opacity = float(extruder_opacity)
|
||||||
|
except ValueError:
|
||||||
|
opacity = 1.0
|
||||||
|
self.setExtruderOpacity(extruder_nr, opacity)
|
||||||
|
|
||||||
|
self.setShowTravelMoves(bool(Preferences.getInstance().getValue("layerview/show_travel_moves")))
|
||||||
|
self.setShowHelpers(bool(Preferences.getInstance().getValue("layerview/show_helpers")))
|
||||||
|
self.setShowSkin(bool(Preferences.getInstance().getValue("layerview/show_skin")))
|
||||||
|
self.setShowInfill(bool(Preferences.getInstance().getValue("layerview/show_infill")))
|
||||||
|
|
||||||
|
self._startUpdateTopLayers()
|
||||||
|
self.preferencesChanged.emit()
|
||||||
|
|
||||||
|
def _onPreferencesChanged(self, preference):
|
||||||
|
if preference not in {
|
||||||
|
"view/top_layer_count",
|
||||||
|
"view/only_show_top_layers",
|
||||||
|
"view/force_layer_view_compatibility_mode",
|
||||||
|
"layerview/layer_view_type",
|
||||||
|
"layerview/extruder_opacities",
|
||||||
|
"layerview/show_travel_moves",
|
||||||
|
"layerview/show_helpers",
|
||||||
|
"layerview/show_skin",
|
||||||
|
"layerview/show_infill",
|
||||||
|
}:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._updateWithPreferences()
|
||||||
|
|
||||||
|
|
||||||
|
class _CreateTopLayersJob(Job):
|
||||||
|
def __init__(self, scene, layer_number, solid_layers):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._scene = scene
|
||||||
|
self._layer_number = layer_number
|
||||||
|
self._solid_layers = solid_layers
|
||||||
|
self._cancel = False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
layer_data = None
|
||||||
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
|
layer_data = node.callDecoration("getLayerData")
|
||||||
|
if layer_data:
|
||||||
|
break
|
||||||
|
|
||||||
|
if self._cancel or not layer_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
layer_mesh = MeshBuilder()
|
||||||
|
for i in range(self._solid_layers):
|
||||||
|
layer_number = self._layer_number - i
|
||||||
|
if layer_number < 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
layer = layer_data.getLayer(layer_number).createMesh()
|
||||||
|
except Exception:
|
||||||
|
Logger.logException("w", "An exception occurred while creating layer mesh.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not layer or layer.getVertices() is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices())
|
||||||
|
layer_mesh.addVertices(layer.getVertices())
|
||||||
|
|
||||||
|
# Scale layer color by a brightness factor based on the current layer number
|
||||||
|
# This will result in a range of 0.5 - 1.0 to multiply colors by.
|
||||||
|
brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0
|
||||||
|
brightness[0, 3] = 1.0
|
||||||
|
layer_mesh.addColors(layer.getColors() * brightness)
|
||||||
|
|
||||||
|
if self._cancel:
|
||||||
|
return
|
||||||
|
|
||||||
|
Job.yieldThread()
|
||||||
|
|
||||||
|
if self._cancel:
|
||||||
|
return
|
||||||
|
|
||||||
|
Job.yieldThread()
|
||||||
|
jump_mesh = layer_data.getLayer(self._layer_number).createJumps()
|
||||||
|
if not jump_mesh or jump_mesh.getVertices() is None:
|
||||||
|
jump_mesh = None
|
||||||
|
|
||||||
|
self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh})
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
self._cancel = True
|
||||||
|
super().cancel()
|
||||||
|
|
645
plugins/SimulationView/SimulationView.qml
Normal file
645
plugins/SimulationView/SimulationView.qml
Normal file
@ -0,0 +1,645 @@
|
|||||||
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.4
|
||||||
|
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: base
|
||||||
|
width: {
|
||||||
|
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.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.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.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var buttonTarget: {
|
||||||
|
if(parent != null)
|
||||||
|
{
|
||||||
|
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
|
||||||
|
return base.mapFromItem(parent.parent, parent.buttonTarget.x, parent.buttonTarget.y)
|
||||||
|
}
|
||||||
|
return Qt.point(0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: parent != null ? !parent.parent.monitoringPrint: true
|
||||||
|
|
||||||
|
UM.PointingRectangle {
|
||||||
|
id: layerViewMenu
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
z: layerSlider.z - 1
|
||||||
|
color: UM.Theme.getColor("tool_panel_background")
|
||||||
|
borderWidth: UM.Theme.getSize("default_lining").width
|
||||||
|
borderColor: UM.Theme.getColor("lining")
|
||||||
|
arrowSize: 0 // hide arrow until weird issue with first time rendering is fixed
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: view_settings
|
||||||
|
|
||||||
|
property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|")
|
||||||
|
property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves")
|
||||||
|
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
|
||||||
|
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.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")
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
spacing: UM.Theme.getSize("layerview_row_spacing").height
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: layerViewTypesLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: catalog.i18nc("@label","Color scheme")
|
||||||
|
font: UM.Theme.getFont("default");
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel // matches SimulationView.py
|
||||||
|
{
|
||||||
|
id: layerViewTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
layerViewTypes.append({
|
||||||
|
text: catalog.i18nc("@label:listbox", "Material Color"),
|
||||||
|
type_id: 0
|
||||||
|
})
|
||||||
|
layerViewTypes.append({
|
||||||
|
text: catalog.i18nc("@label:listbox", "Line Type"),
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox
|
||||||
|
{
|
||||||
|
id: layerTypeCombobox
|
||||||
|
anchors.left: parent.left
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
model: layerViewTypes
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
style: UM.Theme.styles.combobox
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 10 * screenScaleFactor
|
||||||
|
|
||||||
|
onActivated:
|
||||||
|
{
|
||||||
|
UM.Preferences.setValue("layerview/layer_view_type", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
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.SimulationView.compatibilityMode || (type_id == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: compatibilityModeLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: catalog.i18nc("@label","Compatibility Mode")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
visible: UM.SimulationView.compatibilityMode
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: space2Label
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: " "
|
||||||
|
font.pointSize: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: UM.Preferences
|
||||||
|
onPreferenceChanged:
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
view_settings.show_helpers = UM.Preferences.getValue("layerview/show_helpers");
|
||||||
|
view_settings.show_skin = UM.Preferences.getValue("layerview/show_skin");
|
||||||
|
view_settings.show_infill = UM.Preferences.getValue("layerview/show_infill");
|
||||||
|
view_settings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers");
|
||||||
|
view_settings.top_layer_count = UM.Preferences.getValue("view/top_layer_count");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Cura.ExtrudersModel{}
|
||||||
|
CheckBox {
|
||||||
|
id: extrudersModelCheckBox
|
||||||
|
checked: view_settings.extruder_opacities[index] > 0.5 || view_settings.extruder_opacities[index] == undefined || view_settings.extruder_opacities[index] == ""
|
||||||
|
onClicked: {
|
||||||
|
view_settings.extruder_opacities[index] = checked ? 1.0 : 0.0
|
||||||
|
UM.Preferences.setValue("layerview/extruder_opacities", view_settings.extruder_opacities.join("|"));
|
||||||
|
}
|
||||||
|
visible: !UM.SimulationView.compatibilityMode
|
||||||
|
enabled: index + 1 <= 4
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: extrudersModelCheckBox.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
|
color: model.color
|
||||||
|
radius: width / 2
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
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
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: model.name
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: extrudersModelCheckBox.left;
|
||||||
|
anchors.right: extrudersModelCheckBox.right;
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ListModel {
|
||||||
|
id: typesLegendModel
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Travels"),
|
||||||
|
initialValue: view_settings.show_travel_moves,
|
||||||
|
preference: "layerview/show_travel_moves",
|
||||||
|
colorId: "layerview_move_combing"
|
||||||
|
});
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Helpers"),
|
||||||
|
initialValue: view_settings.show_helpers,
|
||||||
|
preference: "layerview/show_helpers",
|
||||||
|
colorId: "layerview_support"
|
||||||
|
});
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Shell"),
|
||||||
|
initialValue: view_settings.show_skin,
|
||||||
|
preference: "layerview/show_skin",
|
||||||
|
colorId: "layerview_inset_0"
|
||||||
|
});
|
||||||
|
typesLegendModel.append({
|
||||||
|
label: catalog.i18nc("@label", "Show Infill"),
|
||||||
|
initialValue: view_settings.show_infill,
|
||||||
|
preference: "layerview/show_infill",
|
||||||
|
colorId: "layerview_infill"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: legendModelCheckBox
|
||||||
|
checked: model.initialValue
|
||||||
|
onClicked: {
|
||||||
|
UM.Preferences.setValue(model.preference, checked);
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: legendModelCheckBox.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
|
color: UM.Theme.getColor(model.colorId)
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
visible: view_settings.show_legend
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: label
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
elide: Text.ElideRight
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: legendModelCheckBox.left;
|
||||||
|
anchors.right: legendModelCheckBox.right;
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("checkbox").width + UM.Theme.getSize("default_margin").width /2
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
checked: view_settings.only_show_top_layers
|
||||||
|
onClicked: {
|
||||||
|
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
|
||||||
|
}
|
||||||
|
text: catalog.i18nc("@label", "Only Show Top Layers")
|
||||||
|
visible: UM.SimulationView.compatibilityMode
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
}
|
||||||
|
CheckBox {
|
||||||
|
checked: view_settings.top_layer_count == 5
|
||||||
|
onClicked: {
|
||||||
|
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
|
||||||
|
}
|
||||||
|
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
|
||||||
|
visible: UM.SimulationView.compatibilityMode
|
||||||
|
style: UM.Theme.styles.checkbox
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ListModel {
|
||||||
|
id: typesLegendModelNoCheck
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
typesLegendModelNoCheck.append({
|
||||||
|
label: catalog.i18nc("@label", "Top / Bottom"),
|
||||||
|
colorId: "layerview_skin",
|
||||||
|
});
|
||||||
|
typesLegendModelNoCheck.append({
|
||||||
|
label: catalog.i18nc("@label", "Inner Wall"),
|
||||||
|
colorId: "layerview_inset_x",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: label
|
||||||
|
visible: view_settings.show_legend
|
||||||
|
id: typesLegendModelLabel
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: typesLegendModelLabel.right
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
width: UM.Theme.getSize("layerview_legend_size").width
|
||||||
|
height: UM.Theme.getSize("layerview_legend_size").height
|
||||||
|
color: UM.Theme.getColor(model.colorId)
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("lining")
|
||||||
|
visible: view_settings.show_legend
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height
|
||||||
|
Layout.preferredWidth: UM.Theme.getSize("layerview_row").width
|
||||||
|
color: UM.Theme.getColor("setting_control_text")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: slidersBox
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.bottom
|
||||||
|
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
||||||
|
left: parent.left
|
||||||
|
}
|
||||||
|
|
||||||
|
PathSlider {
|
||||||
|
id: pathSlider
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// When the user plays the simulation, if the path slider is at the end of this layer, we start
|
||||||
|
// the simulation at the beginning of the current layer.
|
||||||
|
if (playButton.status == 0)
|
||||||
|
{
|
||||||
|
if (currentPath >= numPaths)
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentPath(0)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentPath(currentPath+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the simulation is already playing and we reach the end of a layer, then it automatically
|
||||||
|
// starts at the beginning of the next layer.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (currentPath >= numPaths)
|
||||||
|
{
|
||||||
|
// At the end of the model, the simulation stops
|
||||||
|
if (currentLayer >= numLayers)
|
||||||
|
{
|
||||||
|
playButton.pauseSimulation()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentLayer(currentLayer+1)
|
||||||
|
UM.SimulationView.setCurrentPath(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UM.SimulationView.setCurrentPath(currentPath+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playButton.status = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: fontMetrics
|
||||||
|
font: UM.Theme.getFont("default")
|
||||||
|
}
|
||||||
|
}
|
259
plugins/SimulationView/SimulationViewProxy.py
Normal file
259
plugins/SimulationView/SimulationViewProxy.py
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
# 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()
|
||||||
|
self.is_simulationView_selected = False
|
||||||
|
|
||||||
|
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):
|
||||||
|
# remove other connection if once the SimulationView was created.
|
||||||
|
if self.is_simulationView_selected:
|
||||||
|
active_view.currentLayerNumChanged.disconnect(self._onLayerChanged)
|
||||||
|
active_view.currentPathNumChanged.disconnect(self._onPathChanged)
|
||||||
|
active_view.maxLayersChanged.disconnect(self._onMaxLayersChanged)
|
||||||
|
active_view.maxPathsChanged.disconnect(self._onMaxPathsChanged)
|
||||||
|
active_view.busyChanged.disconnect(self._onBusyChanged)
|
||||||
|
active_view.activityChanged.disconnect(self._onActivityChanged)
|
||||||
|
active_view.globalStackChanged.disconnect(self._onGlobalStackChanged)
|
||||||
|
active_view.preferencesChanged.disconnect(self._onPreferencesChanged)
|
||||||
|
|
||||||
|
self.is_simulationView_selected = True
|
||||||
|
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)
|
26
plugins/SimulationView/__init__.py
Normal file
26
plugins/SimulationView/__init__.py
Normal file
@ -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()}
|
156
plugins/SimulationView/layers.shader
Normal file
156
plugins/SimulationView/layers.shader
Normal file
@ -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 = a_color;
|
||||||
|
// 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 = a_color;
|
||||||
|
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
|
293
plugins/SimulationView/layers3d.shader
Normal file
293
plugins/SimulationView/layers3d.shader
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
[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 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
|
||||||
|
|
||||||
|
uniform highp mat4 u_normalMatrix;
|
||||||
|
|
||||||
|
in highp vec4 a_vertex;
|
||||||
|
in lowp vec4 a_color;
|
||||||
|
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;
|
||||||
|
in highp float a_feedrate;
|
||||||
|
in highp float a_thickness;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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
|
||||||
|
|
||||||
|
switch (u_layer_view_type) {
|
||||||
|
case 0: // "Material color"
|
||||||
|
v_color = a_material_color;
|
||||||
|
break;
|
||||||
|
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;
|
||||||
|
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_layer_view_type = 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
|
||||||
|
|
||||||
|
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
|
||||||
|
u_viewProjectionMatrix = view_projection_matrix
|
||||||
|
u_normalMatrix = normal_matrix
|
||||||
|
u_lightPosition = light_0_position
|
||||||
|
|
||||||
|
[attributes]
|
||||||
|
a_vertex = vertex
|
||||||
|
a_color = color
|
||||||
|
a_normal = normal
|
||||||
|
a_line_dim = line_dim
|
||||||
|
a_extruder = extruder
|
||||||
|
a_material_color = material_color
|
||||||
|
a_line_type = line_type
|
||||||
|
a_feedrate = feedrate
|
||||||
|
a_thickness = thickness
|
256
plugins/SimulationView/layers3d_shadow.shader
Normal file
256
plugins/SimulationView/layers3d_shadow.shader
Normal file
@ -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
|
156
plugins/SimulationView/layers_shadow.shader
Normal file
156
plugins/SimulationView/layers_shadow.shader
Normal file
@ -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
|
8
plugins/SimulationView/plugin.json
Normal file
8
plugins/SimulationView/plugin.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "Simulation View",
|
||||||
|
"author": "Ultimaker B.V.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Provides the Simulation view.",
|
||||||
|
"api": 4,
|
||||||
|
"i18n-catalog": "cura"
|
||||||
|
}
|
BIN
plugins/SimulationView/resources/nozzle.stl
Normal file
BIN
plugins/SimulationView/resources/nozzle.stl
Normal file
Binary file not shown.
79
plugins/SimulationView/resources/simulation_pause.svg
Normal file
79
plugins/SimulationView/resources/simulation_pause.svg
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4620"
|
||||||
|
sodipodi:docname="simulation_pause.svg"
|
||||||
|
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
|
||||||
|
<metadata
|
||||||
|
id="metadata4626">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs4624" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1137"
|
||||||
|
id="namedview4622"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="22.250293"
|
||||||
|
inkscape:cx="8.1879003"
|
||||||
|
inkscape:cy="12.643765"
|
||||||
|
inkscape:window-x="2872"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4620">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide4628"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4630"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5192"
|
||||||
|
width="2"
|
||||||
|
height="20"
|
||||||
|
x="19"
|
||||||
|
y="5" />
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect5192-5"
|
||||||
|
width="2"
|
||||||
|
height="20"
|
||||||
|
x="9"
|
||||||
|
y="5" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
82
plugins/SimulationView/resources/simulation_resume.svg
Normal file
82
plugins/SimulationView/resources/simulation_resume.svg
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
version="1.1"
|
||||||
|
id="svg3765"
|
||||||
|
sodipodi:docname="simulation_resume.svg"
|
||||||
|
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
|
||||||
|
<metadata
|
||||||
|
id="metadata3771">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs3769" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1137"
|
||||||
|
id="namedview3767"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="23.327047"
|
||||||
|
inkscape:cx="10.788646"
|
||||||
|
inkscape:cy="14.67951"
|
||||||
|
inkscape:window-x="2872"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg3765">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide4592"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="15,15"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide4594"
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
id="path3783"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="12.732001"
|
||||||
|
sodipodi:cy="14.695877"
|
||||||
|
sodipodi:r1="13.891838"
|
||||||
|
sodipodi:r2="6.945919"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 26.623839,14.695877 -20.8377567,12.030685 0,-24.0613696 z"
|
||||||
|
inkscape:transform-center-x="-2.9211205"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.32790732;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
transform="matrix(0.84110413,0,0,0.87756418,1.775541,2.1034247)" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
148
plugins/SimulationView/simulationview_composite.shader
Normal file
148
plugins/SimulationView/simulationview_composite.shader
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
[shaders]
|
||||||
|
vertex =
|
||||||
|
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||||
|
attribute highp vec4 a_vertex;
|
||||||
|
attribute highp vec2 a_uvs;
|
||||||
|
|
||||||
|
varying highp vec2 v_uvs;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||||
|
v_uvs = a_uvs;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment =
|
||||||
|
uniform sampler2D u_layer0;
|
||||||
|
uniform sampler2D u_layer1;
|
||||||
|
uniform sampler2D u_layer2;
|
||||||
|
|
||||||
|
uniform vec2 u_offset[9];
|
||||||
|
|
||||||
|
uniform vec4 u_background_color;
|
||||||
|
uniform float u_outline_strength;
|
||||||
|
uniform vec4 u_outline_color;
|
||||||
|
|
||||||
|
varying vec2 v_uvs;
|
||||||
|
|
||||||
|
float kernel[9];
|
||||||
|
|
||||||
|
const vec3 x_axis = vec3(1.0, 0.0, 0.0);
|
||||||
|
const vec3 y_axis = vec3(0.0, 1.0, 0.0);
|
||||||
|
const vec3 z_axis = vec3(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// blur kernel
|
||||||
|
kernel[0] = 0.0; kernel[1] = 1.0; kernel[2] = 0.0;
|
||||||
|
kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0;
|
||||||
|
kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 0.0;
|
||||||
|
|
||||||
|
vec4 result = u_background_color;
|
||||||
|
|
||||||
|
vec4 main_layer = texture2D(u_layer0, v_uvs);
|
||||||
|
vec4 selection_layer = texture2D(u_layer1, v_uvs);
|
||||||
|
vec4 layerview_layer = texture2D(u_layer2, v_uvs);
|
||||||
|
|
||||||
|
result = main_layer * main_layer.a + result * (1.0 - main_layer.a);
|
||||||
|
result = layerview_layer * layerview_layer.a + result * (1.0 - layerview_layer.a);
|
||||||
|
|
||||||
|
vec4 sum = vec4(0.0);
|
||||||
|
for (int i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
vec4 color = vec4(texture2D(u_layer1, v_uvs.xy + u_offset[i]).a);
|
||||||
|
sum += color * (kernel[i] / u_outline_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((selection_layer.rgb == x_axis || selection_layer.rgb == y_axis || selection_layer.rgb == z_axis))
|
||||||
|
{
|
||||||
|
gl_FragColor = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gl_FragColor = mix(result, u_outline_color, abs(sum.a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex41core =
|
||||||
|
#version 410
|
||||||
|
uniform highp mat4 u_modelViewProjectionMatrix;
|
||||||
|
in highp vec4 a_vertex;
|
||||||
|
in highp vec2 a_uvs;
|
||||||
|
|
||||||
|
out highp vec2 v_uvs;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = u_modelViewProjectionMatrix * a_vertex;
|
||||||
|
v_uvs = a_uvs;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment41core =
|
||||||
|
#version 410
|
||||||
|
uniform sampler2D u_layer0;
|
||||||
|
uniform sampler2D u_layer1;
|
||||||
|
uniform sampler2D u_layer2;
|
||||||
|
|
||||||
|
uniform vec2 u_offset[9];
|
||||||
|
|
||||||
|
uniform vec4 u_background_color;
|
||||||
|
uniform float u_outline_strength;
|
||||||
|
uniform vec4 u_outline_color;
|
||||||
|
|
||||||
|
in vec2 v_uvs;
|
||||||
|
|
||||||
|
float kernel[9];
|
||||||
|
|
||||||
|
const vec3 x_axis = vec3(1.0, 0.0, 0.0);
|
||||||
|
const vec3 y_axis = vec3(0.0, 1.0, 0.0);
|
||||||
|
const vec3 z_axis = vec3(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// blur kernel
|
||||||
|
kernel[0] = 0.0; kernel[1] = 1.0; kernel[2] = 0.0;
|
||||||
|
kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0;
|
||||||
|
kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 0.0;
|
||||||
|
|
||||||
|
vec4 result = u_background_color;
|
||||||
|
|
||||||
|
vec4 main_layer = texture(u_layer0, v_uvs);
|
||||||
|
vec4 selection_layer = texture(u_layer1, v_uvs);
|
||||||
|
vec4 layerview_layer = texture(u_layer2, v_uvs);
|
||||||
|
|
||||||
|
result = main_layer * main_layer.a + result * (1.0 - main_layer.a);
|
||||||
|
result = layerview_layer * layerview_layer.a + result * (1.0 - layerview_layer.a);
|
||||||
|
|
||||||
|
vec4 sum = vec4(0.0);
|
||||||
|
for (int i = 0; i < 9; i++)
|
||||||
|
{
|
||||||
|
vec4 color = vec4(texture(u_layer1, v_uvs.xy + u_offset[i]).a);
|
||||||
|
sum += color * (kernel[i] / u_outline_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((selection_layer.rgb == x_axis || selection_layer.rgb == y_axis || selection_layer.rgb == z_axis))
|
||||||
|
{
|
||||||
|
frag_color = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frag_color = mix(result, u_outline_color, abs(sum.a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[defaults]
|
||||||
|
u_layer0 = 0
|
||||||
|
u_layer1 = 1
|
||||||
|
u_layer2 = 2
|
||||||
|
u_background_color = [0.965, 0.965, 0.965, 1.0]
|
||||||
|
u_outline_strength = 1.0
|
||||||
|
u_outline_color = [0.05, 0.66, 0.89, 1.0]
|
||||||
|
|
||||||
|
[bindings]
|
||||||
|
|
||||||
|
[attributes]
|
||||||
|
a_vertex = vertex
|
||||||
|
a_uvs = uv
|
Loading…
x
Reference in New Issue
Block a user