Browse Source

feat: use logarithmic conversion for lithophanes

Tim Kuipers 5 years ago
parent
commit
88b424d36a

+ 22 - 0
plugins/ImageReader/ConfigUI.qml

@@ -143,6 +143,28 @@ UM.Dialog
             }
         }
 
+        UM.TooltipArea {
+            Layout.fillWidth:true
+            height: childrenRect.height
+            text: catalog.i18nc("@info:tooltip","For lithophanes a logarithmic function is more appropriate for most materials. For height maps the pixel values correspond to heights linearly.")
+            Row {
+                width: parent.width
+
+                Label {
+                    text: "Conversion"
+                    width: 150 * screenScaleFactor
+                    anchors.verticalCenter: parent.verticalCenter
+                }
+                ComboBox {
+                    id: conversion
+                    objectName: "Conversion"
+                    model: [ catalog.i18nc("@item:inlistbox","Logarithmic"), catalog.i18nc("@item:inlistbox","Linear") ]
+                    width: 180 * screenScaleFactor
+                    onCurrentIndexChanged: { manager.onConvertFunctionChanged(currentIndex) }
+                }
+            }
+        }
+
         UM.TooltipArea {
             Layout.fillWidth:true
             height: childrenRect.height

+ 12 - 4
plugins/ImageReader/ImageReader.py

@@ -3,6 +3,8 @@
 
 import numpy
 
+import math
+
 from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
 from PyQt5.QtCore import Qt
 
@@ -46,9 +48,9 @@ class ImageReader(MeshReader):
 
     def _read(self, file_name):
         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_logarithmic_function)
 
-    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_logarithmic_function):
         scene_node = SceneNode()
 
         mesh = MeshBuilder()
@@ -124,8 +126,14 @@ class ImageReader(MeshReader):
 
             Job.yieldThread()
 
-        height_data *= scale_vector.y
-        height_data += base_height
+        if use_logarithmic_function:
+            min_luminance = 2.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] = peak_height - math.log(mapped_luminance, 2)
+        else:
+            height_data *= scale_vector.y
+            height_data += base_height
 
         heightmap_face_count = 2 * height_minus_one * width_minus_one
         total_face_count = heightmap_face_count + (width_minus_one * 2) * (height_minus_one * 2) + 2

+ 6 - 0
plugins/ImageReader/ImageReaderUI.py

@@ -34,6 +34,7 @@ class ImageReaderUI(QObject):
         self.peak_height = 2.5
         self.smoothing = 1
         self.lighter_is_higher = False;
+        self.use_logarithmic_function = False;
 
         self._ui_lock = threading.Lock()
         self._cancelled = False
@@ -144,3 +145,8 @@ class ImageReaderUI(QObject):
     @pyqtSlot(int)
     def onImageColorInvertChanged(self, value):
         self.lighter_is_higher = (value == 1)
+
+    @pyqtSlot(int)
+    def onConvertFunctionChanged(self, value):
+        self.use_logarithmic_function = (value == 0)
+