Merge branch 'master' of github.com:Ultimaker/Cura

This commit is contained in:
Jaime van Kessel 2019-11-01 15:15:31 +01:00
commit b3bf77524b
No known key found for this signature in database
GPG Key ID: 3710727397403C91
31 changed files with 267 additions and 273 deletions

View File

@ -41,11 +41,11 @@ class IntentCategoryModel(ListModel):
} }
_translations["engineering"] = { _translations["engineering"] = {
"name": catalog.i18nc("@label", "Engineering"), "name": catalog.i18nc("@label", "Engineering"),
"description": catalog.i18nc("@text", "Suitable for engineering work") "description": catalog.i18nc("@text", "Optimized for higher accuracy")
} }
_translations["smooth"] = { _translations["quick"] = {
"name": catalog.i18nc("@label", "Smooth"), "name": catalog.i18nc("@label", "Draft"),
"description": catalog.i18nc("@text", "Optimized for a smooth surfaces") "description": catalog.i18nc("@text", "Optimized for fast results")
} }

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import numpy import numpy
@ -72,7 +72,7 @@ class GcodeStartEndFormatter(Formatter):
value = default_value_str value = default_value_str
# "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value. # "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value.
if key in kwargs["-1"]: if key in kwargs["-1"]:
value = kwargs["-1"] value = kwargs["-1"][key]
if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]: if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]:
value = kwargs[str(extruder_nr)][key] value = kwargs[str(extruder_nr)][key]

View File

@ -143,6 +143,52 @@ UM.Dialog
} }
} }
UM.TooltipArea {
Layout.fillWidth:true
height: childrenRect.height
text: catalog.i18nc("@info:tooltip","For lithophanes a simple logarithmic model for translucency is available. For height maps the pixel values correspond to heights linearly.")
Row {
width: parent.width
Label {
text: "Color Model"
width: 150 * screenScaleFactor
anchors.verticalCenter: parent.verticalCenter
}
ComboBox {
id: color_model
objectName: "ColorModel"
model: [ catalog.i18nc("@item:inlistbox","Linear"), catalog.i18nc("@item:inlistbox","Translucency") ]
width: 180 * screenScaleFactor
onCurrentIndexChanged: { manager.onColorModelChanged(currentIndex) }
}
}
}
UM.TooltipArea {
Layout.fillWidth:true
height: childrenRect.height
text: catalog.i18nc("@info:tooltip","The percentage of light penetrating a print with a thickness of 1 millimeter. Lowering this value increases the contrast in dark regions and decreases the contrast in light regions of the image.")
visible: color_model.currentText == catalog.i18nc("@item:inlistbox","Translucency")
Row {
width: parent.width
Label {
text: catalog.i18nc("@action:label", "1mm Transmittance (%)")
width: 150 * screenScaleFactor
anchors.verticalCenter: parent.verticalCenter
}
TextField {
id: transmittance
objectName: "Transmittance"
focus: true
validator: RegExpValidator {regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/}
width: 180 * screenScaleFactor
onTextChanged: { manager.onTransmittanceChanged(text) }
}
}
}
UM.TooltipArea { UM.TooltipArea {
Layout.fillWidth:true Layout.fillWidth:true
height: childrenRect.height height: childrenRect.height

View File

@ -3,6 +3,8 @@
import numpy import numpy
import math
from PyQt5.QtGui import QImage, qRed, qGreen, qBlue from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
@ -46,9 +48,9 @@ class ImageReader(MeshReader):
def _read(self, file_name): def _read(self, file_name):
size = max(self._ui.getWidth(), self._ui.getDepth()) size = max(self._ui.getWidth(), self._ui.getDepth())
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.lighter_is_higher) return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.lighter_is_higher, self._ui.use_transparency_model, self._ui.transmittance_1mm)
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, lighter_is_higher): def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, lighter_is_higher, use_transparency_model, transmittance_1mm):
scene_node = SceneNode() scene_node = SceneNode()
mesh = MeshBuilder() mesh = MeshBuilder()
@ -99,12 +101,14 @@ class ImageReader(MeshReader):
for x in range(0, width): for x in range(0, width):
for y in range(0, height): for y in range(0, height):
qrgb = img.pixel(x, y) qrgb = img.pixel(x, y)
avg = float(qRed(qrgb) + qGreen(qrgb) + qBlue(qrgb)) / (3 * 255) if use_transparency_model:
height_data[y, x] = avg height_data[y, x] = (0.299 * math.pow(qRed(qrgb) / 255.0, 2.2) + 0.587 * math.pow(qGreen(qrgb) / 255.0, 2.2) + 0.114 * math.pow(qBlue(qrgb) / 255.0, 2.2))
else:
height_data[y, x] = (0.212655 * qRed(qrgb) + 0.715158 * qGreen(qrgb) + 0.072187 * qBlue(qrgb)) / 255 # fast computation ignoring gamma and degamma
Job.yieldThread() Job.yieldThread()
if not lighter_is_higher: if lighter_is_higher == use_transparency_model:
height_data = 1 - height_data height_data = 1 - height_data
for _ in range(0, blur_iterations): for _ in range(0, blur_iterations):
@ -124,6 +128,13 @@ class ImageReader(MeshReader):
Job.yieldThread() Job.yieldThread()
if use_transparency_model:
divisor = 1.0 / math.log(transmittance_1mm / 100.0) # log-base doesn't matter here. Precompute this value for faster computation of each pixel.
min_luminance = (transmittance_1mm / 100.0) ** (peak_height - base_height)
for (y, x) in numpy.ndindex(height_data.shape):
mapped_luminance = min_luminance + (1.0 - min_luminance) * height_data[y, x]
height_data[y, x] = base_height + divisor * math.log(mapped_luminance) # use same base as a couple lines above this
else:
height_data *= scale_vector.y height_data *= scale_vector.y
height_data += base_height height_data += base_height

View File

@ -34,6 +34,8 @@ class ImageReaderUI(QObject):
self.peak_height = 2.5 self.peak_height = 2.5
self.smoothing = 1 self.smoothing = 1
self.lighter_is_higher = False; self.lighter_is_higher = False;
self.use_transparency_model = True;
self.transmittance_1mm = 50.0; # based on pearl PLA
self._ui_lock = threading.Lock() self._ui_lock = threading.Lock()
self._cancelled = False self._cancelled = False
@ -75,6 +77,7 @@ class ImageReaderUI(QObject):
self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height)) self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height))
self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height)) self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height))
self._ui_view.findChild(QObject, "Transmittance").setProperty("text", str(self.transmittance_1mm))
self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing) self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing)
def _createConfigUI(self): def _createConfigUI(self):
@ -144,3 +147,11 @@ class ImageReaderUI(QObject):
@pyqtSlot(int) @pyqtSlot(int)
def onImageColorInvertChanged(self, value): def onImageColorInvertChanged(self, value):
self.lighter_is_higher = (value == 1) self.lighter_is_higher = (value == 1)
@pyqtSlot(int)
def onColorModelChanged(self, value):
self.use_transparency_model = (value == 0)
@pyqtSlot(int)
def onTransmittanceChanged(self, value):
self.transmittance_1mm = value

View File

@ -66,6 +66,13 @@ class VersionUpgrade43to44(VersionUpgrade):
# Alternate skin rotation should be translated to top/bottom line directions. # Alternate skin rotation should be translated to top/bottom line directions.
if "skin_alternate_rotation" in parser["values"] and parseBool(parser["values"]["skin_alternate_rotation"]): if "skin_alternate_rotation" in parser["values"] and parseBool(parser["values"]["skin_alternate_rotation"]):
parser["values"]["skin_angles"] = "[45, 135, 0, 90]" parser["values"]["skin_angles"] = "[45, 135, 0, 90]"
# Unit of adaptive layers topography size changed.
if "adaptive_layer_height_threshold" in parser["values"]:
val = parser["values"]["adaptive_layer_height_threshold"]
if val.startswith("="):
val = val[1:]
val = "=({val}) / 1000".format(val = val) # Convert microns to millimetres. Works even if the profile contained a formula.
parser["values"]["adaptive_layer_height_threshold"] = val
result = io.StringIO() result = io.StringIO()
parser.write(result) parser.write(result)

View File

@ -7074,11 +7074,12 @@
}, },
"adaptive_layer_height_threshold": "adaptive_layer_height_threshold":
{ {
"label": "Adaptive Layers Threshold", "label": "Adaptive Layers Topography Size",
"description": "Threshold whether to use a smaller layer or not. This number is compared to the tan of the steepest slope in a layer.", "description": "Target horizontal distance between two adjacent layers. Reducing this setting causes thinner layers to be used to bring the edges of the layers closer together.",
"type": "float", "type": "float",
"enabled": "adaptive_layer_height_enabled", "enabled": "adaptive_layer_height_enabled",
"default_value": 200.0, "default_value": 0.2,
"unit": "mm",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": false, "settable_per_extruder": false,
"settable_per_meshgroup": false "settable_per_meshgroup": false

View File

@ -18,12 +18,12 @@
"supports_usb_connection": false, "supports_usb_connection": false,
"machine_extruder_trains": "machine_extruder_trains":
{ {
"0": "alya3dp_extruder_0" "0": "kupido_extruder_0"
} }
}, },
"overrides": { "overrides": {
"machine_name": { "default_value": "ALYA 3DP" }, "machine_name": { "default_value": "KUPIDO" },
"machine_heated_bed": { "default_value": true }, "machine_heated_bed": { "default_value": true },
"machine_width": { "default_value": 195 }, "machine_width": { "default_value": 195 },
"machine_height": { "default_value": 190 }, "machine_height": { "default_value": 190 },

View File

@ -67,8 +67,6 @@
"extruder_prime_pos_abs": { "default_value": true }, "extruder_prime_pos_abs": { "default_value": true },
"machine_start_gcode": { "default_value": "" }, "machine_start_gcode": { "default_value": "" },
"machine_end_gcode": { "default_value": "" }, "machine_end_gcode": { "default_value": "" },
"prime_tower_position_x": { "value": "345" },
"prime_tower_position_y": { "value": "222.5" },
"prime_blob_enable": { "enabled": true, "default_value": false }, "prime_blob_enable": { "enabled": true, "default_value": false },
"speed_travel": "speed_travel":

View File

@ -3,7 +3,7 @@
"name": "Extruder 1", "name": "Extruder 1",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "BIBO2 dual", "machine": "bibo2_dual",
"position": "0" "position": "0"
}, },
"overrides": { "overrides": {

View File

@ -3,7 +3,7 @@
"name": "Extruder 2", "name": "Extruder 2",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "BIBO2 dual", "machine": "bibo2_dual",
"position": "1" "position": "1"
}, },
"overrides": { "overrides": {

View File

@ -3,7 +3,7 @@
"name": "Left Extruder", "name": "Left Extruder",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Creality CR-X", "machine": "creality_cr-x",
"position": "0" "position": "0"
}, },

View File

@ -3,7 +3,7 @@
"name": "Right Extruder", "name": "Right Extruder",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Creality CR-X", "machine": "creality_cr-x",
"position": "1" "position": "1"
}, },

View File

@ -3,7 +3,7 @@
"name": "Extruder 1", "name": "Extruder 1",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Creatable_D3", "machine": "creatable_d3",
"position": "0" "position": "0"
}, },

View File

@ -3,7 +3,7 @@
"name": "Extruder 1", "name": "Extruder 1",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "hellbot_magna_i", "machine": "hellbot_magna_I",
"position": "0" "position": "0"
}, },

View File

@ -3,7 +3,7 @@
"name": "0.4mm Nozzle", "name": "0.4mm Nozzle",
"inherits": "fdmextruder", "inherits": "fdmextruder",
"metadata": { "metadata": {
"machine": "Tyro", "machine": "key3d_tyro",
"position": "0" "position": "0"
}, },

View File

@ -6,7 +6,7 @@ definition = ultimaker_s3
[metadata] [metadata]
setting_version = 10 setting_version = 10
type = intent type = intent
intent_category = smooth intent_category = quick
quality_type = draft quality_type = draft
material = generic_abs material = generic_abs
variant = AA 0.4 variant = AA 0.4

View File

@ -1,17 +0,0 @@
[general]
version = 4
name = Visual
definition = ultimaker_s3
[metadata]
setting_version = 10
type = intent
intent_category = visual
quality_type = draft
material = generic_abs
variant = AA 0.4
[values]
speed_infill = 50
wall_thickness = =wall_line_width * 3
top_bottom_thickness = =wall_thickness

View File

@ -6,7 +6,7 @@ definition = ultimaker_s3
[metadata] [metadata]
setting_version = 10 setting_version = 10
type = intent type = intent
intent_category = smooth intent_category = quick
quality_type = draft quality_type = draft
material = generic_pla material = generic_pla
variant = AA 0.4 variant = AA 0.4

View File

@ -1,17 +0,0 @@
[general]
version = 4
name = Visual
definition = ultimaker_s3
[metadata]
setting_version = 10
type = intent
quality_type = draft
intent_category = visual
material = generic_pla
variant = AA 0.4
[values]
speed_infill = 50
wall_thickness = =wall_line_width * 3
top_bottom_thickness = =wall_thickness

View File

@ -6,7 +6,7 @@ definition = ultimaker_s3
[metadata] [metadata]
setting_version = 10 setting_version = 10
type = intent type = intent
intent_category = smooth intent_category = quick
quality_type = draft quality_type = draft
material = generic_tough_pla material = generic_tough_pla
variant = AA 0.4 variant = AA 0.4

View File

@ -1,17 +0,0 @@
[general]
version = 4
name = Visual
definition = ultimaker_s3
[metadata]
setting_version = 10
type = intent
quality_type = draft
intent_category = visual
material = generic_tough_pla
variant = AA 0.4
[values]
speed_infill = 50
wall_thickness = =wall_line_width * 3
top_bottom_thickness = =wall_thickness

View File

@ -6,7 +6,7 @@ definition = ultimaker_s5
[metadata] [metadata]
setting_version = 10 setting_version = 10
type = intent type = intent
intent_category = smooth intent_category = quick
quality_type = draft quality_type = draft
material = generic_abs material = generic_abs
variant = AA 0.4 variant = AA 0.4

View File

@ -1,16 +0,0 @@
[general]
version = 4
name = Visual
definition = ultimaker_s5
[metadata]
setting_version = 10
type = intent
intent_category = visualquality_type = draft
material = generic_abs
variant = AA 0.4
[values]
speed_infill = 50
wall_thickness = =wall_line_width * 3
top_bottom_thickness = =wall_thickness

View File

@ -6,7 +6,7 @@ definition = ultimaker_s5
[metadata] [metadata]
setting_version = 10 setting_version = 10
type = intent type = intent
intent_category = smooth intent_category = quick
quality_type = draft quality_type = draft
material = generic_pla material = generic_pla
variant = AA 0.4 variant = AA 0.4

View File

@ -1,17 +0,0 @@
[general]
version = 4
name = Visual
definition = ultimaker_s5
[metadata]
setting_version = 10
type = intent
quality_type = draft
intent_category = visual
material = generic_pla
variant = AA 0.4
[values]
speed_infill = 50
wall_thickness = =wall_line_width * 3
top_bottom_thickness = =wall_thickness

View File

@ -6,7 +6,7 @@ definition = ultimaker_s5
[metadata] [metadata]
setting_version = 10 setting_version = 10
type = intent type = intent
intent_category = smooth intent_category = quick
quality_type = draft quality_type = draft
material = generic_tough_pla material = generic_tough_pla
variant = AA 0.4 variant = AA 0.4

View File

@ -1,16 +0,0 @@
[general]
version = 4
name = Visual
definition = ultimaker_s5
[metadata]
setting_version = 10
type = intent
intent_category = visual
material = generic_tough_pla
variant = AA 0.4
[values]
speed_infill = 50
wall_thickness = =wall_line_width * 3
top_bottom_thickness = =wall_thickness

View File

@ -124,18 +124,6 @@ Item
elide: Text.ElideRight elide: Text.ElideRight
} }
NoIntentIcon
{
affected_extruders: Cura.MachineManager.extruderPositionsWithNonActiveIntent
intent_type: model.name
anchors.right: intentCategoryLabel.right
anchors.rightMargin: UM.Theme.getSize("narrow_margin").width
width: intentCategoryLabel.height * 0.75
anchors.verticalCenter: parent.verticalCenter
height: width
visible: Cura.MachineManager.activeIntentCategory == model.intent_category && affected_extruders.length
}
Cura.RadioCheckbar Cura.RadioCheckbar
{ {
anchors anchors
@ -164,8 +152,9 @@ Item
isCheckedFunction: checkedFunction isCheckedFunction: checkedFunction
} }
MouseArea // tooltip hover area MouseArea // Intent description tooltip hover area
{ {
id: intentDescriptionHoverArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
enabled: model.description !== undefined enabled: model.description !== undefined
@ -181,6 +170,20 @@ Item
} }
onExited: base.hideTooltip() onExited: base.hideTooltip()
} }
NoIntentIcon // This icon has hover priority over intentDescriptionHoverArea, so draw it above it.
{
affected_extruders: Cura.MachineManager.extruderPositionsWithNonActiveIntent
intent_type: model.name
anchors.right: intentCategoryLabel.right
anchors.rightMargin: UM.Theme.getSize("narrow_margin").width
width: intentCategoryLabel.height * 0.75
anchors.verticalCenter: parent.verticalCenter
height: width
visible: Cura.MachineManager.activeIntentCategory == model.intent_category && affected_extruders.length
}
} }
} }

View File

@ -137,38 +137,17 @@ def test_materialAdded_update(container_registry, machine_node, metadata, change
def test_preferredMaterialExactMatch(empty_variant_node): def test_preferredMaterialExactMatch(empty_variant_node):
empty_variant_node.materials = { empty_variant_node.materials = {
"some_different_material": MagicMock(getMetaDataEntry = lambda x: 3), "some_different_material": MagicMock(getMetaDataEntry = lambda x: 3),
"preferred_material_base_file": MagicMock(getMetaDataEntry = lambda x: 3) # Exact match. "preferred_material": MagicMock(getMetaDataEntry = lambda x: 3) # Exact match.
} }
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material_base_file"], "It should match exactly on this one since it's the preferred material." assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material"], "It should match exactly on this one since it's the preferred material."
## Tests the preferred material when a submaterial is available in the
# materials list for this node.
def test_preferredMaterialSubmaterial(empty_variant_node):
empty_variant_node.materials = {
"some_different_material": MagicMock(getMetaDataEntry = lambda x: 3),
"preferred_material_base_file_aa04": MagicMock(getMetaDataEntry = lambda x: 3) # This is a submaterial of the preferred material.
}
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material_base_file_aa04"], "It should match on the submaterial just as well."
## Tests the preferred material matching on the approximate diameter of the
# filament.
def test_preferredMaterialDiameter(empty_variant_node):
empty_variant_node.materials = {
"some_different_material": MagicMock(getMetaDataEntry = lambda x: 3),
"preferred_material_wrong_diameter": MagicMock(getMetaDataEntry = lambda x: 2), # Approximate diameter is 2 instead of 3.
"preferred_material_correct_diameter": MagicMock(getMetaDataEntry = lambda x: 3) # Correct approximate diameter.
}
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material_correct_diameter"], "It should match only on the material with correct diameter."
## Tests the preferred material matching on a different material if the ## Tests the preferred material matching on a different material if the
# diameter is wrong. # diameter is wrong.
def test_preferredMaterialDiameterNoMatch(empty_variant_node): def test_preferredMaterialDiameterNoMatch(empty_variant_node):
empty_variant_node.materials = collections.OrderedDict() empty_variant_node.materials = collections.OrderedDict()
empty_variant_node.materials["some_different_material"] = MagicMock(getMetaDataEntry = lambda x: 3) # This one first so that it gets iterated over first. empty_variant_node.materials["some_different_material"] = MagicMock(getMetaDataEntry = lambda x: 3) # This one first so that it gets iterated over first.
empty_variant_node.materials["preferred_material_wrong_diameter"] = MagicMock(getMetaDataEntry = lambda x: 2) empty_variant_node.materials["preferred_material"] = MagicMock(getMetaDataEntry = lambda x: 2) # Wrong diameter.
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["some_different_material"], "It should match on another material with the correct diameter if the preferred one is unavailable." assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["some_different_material"], "It should match on another material with the correct diameter if the preferred one is unavailable."
@ -177,7 +156,7 @@ def test_preferredMaterialDiameterNoMatch(empty_variant_node):
def test_preferredMaterialDiameterPreference(empty_variant_node): def test_preferredMaterialDiameterPreference(empty_variant_node):
empty_variant_node.materials = collections.OrderedDict() empty_variant_node.materials = collections.OrderedDict()
empty_variant_node.materials["some_different_material"] = MagicMock(getMetaDataEntry = lambda x: 2) # This one first so that it gets iterated over first. empty_variant_node.materials["some_different_material"] = MagicMock(getMetaDataEntry = lambda x: 2) # This one first so that it gets iterated over first.
empty_variant_node.materials["preferred_material_wrong_diameter"] = MagicMock(getMetaDataEntry = lambda x: 2) # Matches on ID but not diameter. empty_variant_node.materials["preferred_material"] = MagicMock(getMetaDataEntry = lambda x: 2) # Matches on ID but not diameter.
empty_variant_node.materials["not_preferred_but_correct_diameter"] = MagicMock(getMetaDataEntry = lambda x: 3) # Matches diameter but not ID. empty_variant_node.materials["not_preferred_but_correct_diameter"] = MagicMock(getMetaDataEntry = lambda x: 3) # Matches diameter but not ID.
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["not_preferred_but_correct_diameter"], "It should match on the correct diameter even if it's not the preferred one." assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["not_preferred_but_correct_diameter"], "It should match on the correct diameter even if it's not the preferred one."

View File

@ -17,6 +17,10 @@ Resources.addSearchPath(os.path.abspath(os.path.join(os.path.dirname(__file__),
machine_filepaths = sorted(os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "definitions"))) machine_filepaths = sorted(os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "definitions")))
machine_filepaths = [os.path.join(os.path.dirname(__file__), "..", "..", "resources", "definitions", filename) for filename in machine_filepaths]
extruder_filepaths = sorted(os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "extruders")))
extruder_filepaths = [os.path.join(os.path.dirname(__file__), "..", "..", "resources", "extruders", filename) for filename in extruder_filepaths]
definition_filepaths = machine_filepaths + extruder_filepaths
all_meshes = os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "meshes")) all_meshes = os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "meshes"))
all_images = os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "images")) all_images = os.listdir(os.path.join(os.path.dirname(__file__), "..", "..", "resources", "images"))
@ -29,17 +33,16 @@ def definition_container():
## Tests all definition containers ## Tests all definition containers
@pytest.mark.parametrize("file_name", machine_filepaths) @pytest.mark.parametrize("file_path", machine_filepaths)
def test_validateMachineDefinitionContainer(file_name, definition_container): def test_validateMachineDefinitionContainer(file_path, definition_container):
file_name = os.path.basename(file_path)
if file_name == "fdmprinter.def.json" or file_name == "fdmextruder.def.json": if file_name == "fdmprinter.def.json" or file_name == "fdmextruder.def.json":
return # Stop checking, these are root files. return # Stop checking, these are root files.
definition_path = os.path.join(os.path.dirname(__file__), "..", "..", "resources", "definitions") assertIsDefinitionValid(definition_container, file_path)
assertIsDefinitionValid(definition_container, definition_path, file_name)
def assertIsDefinitionValid(definition_container, file_path):
def assertIsDefinitionValid(definition_container, path, file_name): with open(file_path, encoding = "utf-8") as data:
with open(os.path.join(path, file_name), encoding = "utf-8") as data:
json = data.read() json = data.read()
parser, is_valid = definition_container.readAndValidateSerialized(json) parser, is_valid = definition_container.readAndValidateSerialized(json)
assert is_valid #The definition has invalid JSON structure. assert is_valid #The definition has invalid JSON structure.
@ -57,10 +60,9 @@ def assertIsDefinitionValid(definition_container, path, file_name):
# When a definition container defines a "default_value" but inherits from a # When a definition container defines a "default_value" but inherits from a
# definition that defines a "value", the "default_value" is ineffective. This # definition that defines a "value", the "default_value" is ineffective. This
# test fails on those things. # test fails on those things.
@pytest.mark.parametrize("file_name", machine_filepaths) @pytest.mark.parametrize("file_path", definition_filepaths)
def test_validateOverridingDefaultValue(file_name: str): def test_validateOverridingDefaultValue(file_path: str):
definition_path = os.path.join(os.path.dirname(__file__), "..", "..", "resources", "definitions", file_name) with open(file_path, encoding = "utf-8") as f:
with open(definition_path, encoding = "utf-8") as f:
doc = json.load(f) doc = json.load(f)
if "inherits" not in doc: if "inherits" not in doc:
@ -69,8 +71,8 @@ def test_validateOverridingDefaultValue(file_name: str):
return # No settings are being overridden. No need to check anything. return # No settings are being overridden. No need to check anything.
parent_settings = getInheritedSettings(doc["inherits"]) parent_settings = getInheritedSettings(doc["inherits"])
for key, val in doc["overrides"].items(): for key, val in doc["overrides"].items():
if "value" in parent_settings[key]: if key in parent_settings and "value" in parent_settings[key]:
assert "default_value" not in val, "Unnecessary default_value for {key} in {file_name}".format(key = key, file_name = file_name) # If there is a value in the parent settings, then the default_value is not effective. assert "default_value" not in val, "Unnecessary default_value for {key} in {file_name}".format(key = key, file_name = file_path) # If there is a value in the parent settings, then the default_value is not effective.
## Get all settings and their properties from a definition we're inheriting ## Get all settings and their properties from a definition we're inheriting
# from. # from.
@ -130,10 +132,46 @@ def merge_dicts(base: Dict[str, Any], overrides: Dict[str, Any]) -> Dict[str, An
# #
# ID fields are legacy. They should not be used any more. This is legacy that # ID fields are legacy. They should not be used any more. This is legacy that
# people don't seem to be able to get used to. # people don't seem to be able to get used to.
@pytest.mark.parametrize("file_name", machine_filepaths) @pytest.mark.parametrize("file_path", definition_filepaths)
def test_noId(file_name): def test_noId(file_path: str):
definition_path = os.path.join(os.path.dirname(__file__), "..", "..", "resources", "definitions", file_name) with open(file_path, encoding = "utf-8") as f:
with open(definition_path, encoding = "utf-8") as f:
doc = json.load(f) doc = json.load(f)
assert "id" not in doc, "Definitions should not have an ID field." assert "id" not in doc, "Definitions should not have an ID field."
## Verifies that extruders say that they work on the same extruder_nr as what
# is listed in their machine definition.
@pytest.mark.parametrize("file_path", extruder_filepaths)
def test_extruderMatch(file_path: str):
extruder_id = os.path.basename(file_path).split(".")[0]
with open(file_path, encoding = "utf-8") as f:
doc = json.load(f)
if "metadata" not in doc:
return # May not be desirable either, but it's probably unfinished then.
if "machine" not in doc["metadata"] or "position" not in doc["metadata"]:
return # FDMextruder doesn't have this since it's not linked to a particular printer.
machine = doc["metadata"]["machine"]
position = doc["metadata"]["position"]
# Find the machine definition.
for machine_filepath in machine_filepaths:
machine_id = os.path.basename(machine_filepath).split(".")[0]
if machine_id == machine:
break
else:
assert False, "The machine ID {machine} is not found.".format(machine = machine)
with open(machine_filepath, encoding = "utf-8") as f:
machine_doc = json.load(f)
# Make sure that the two match up.
assert "metadata" in machine_doc, "Machine definition missing metadata entry."
assert "machine_extruder_trains" in machine_doc["metadata"], "Machine must define extruder trains."
extruder_trains = machine_doc["metadata"]["machine_extruder_trains"]
assert position in extruder_trains, "There must be a reference to the extruder in the machine definition."
assert extruder_trains[position] == extruder_id, "The extruder referenced in the machine definition must match up."
# Also test if the extruder_nr setting is properly overridden.
if "overrides" not in doc or "extruder_nr" not in doc["overrides"] or "default_value" not in doc["overrides"]["extruder_nr"]:
assert position == "0" # Default to 0 is allowed.
assert doc["overrides"]["extruder_nr"]["default_value"] == int(position)