Преглед на файлове

- in CNCJob UI Autolevelling: changed the UI a bit
- added a bilinear interpolation calculation class from: https://github.com/pmav99/interpolation
- in CNCJob UI Autolevelling: made sure that the grid can't have less than 2 rows and 2 columns when using the bilinear interpolation or 1 row and 1 column when using the Voronoi polygons

Marius Stanciu преди 5 години
родител
ревизия
743885cf0f
променени са 4 файла, в които са добавени 158 реда и са изтрити 12 реда
  1. 7 0
      CHANGELOG.md
  2. 119 0
      appCommon/bilinear.py
  3. 15 4
      appGUI/ObjectUI.py
  4. 17 8
      appObjects/FlatCAMCNCJob.py

+ 7 - 0
CHANGELOG.md

@@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta
 
 =================================================
 
+3.09.2020
+
+- in CNCJob UI Autolevelling: changed the UI a bit
+- added a bilinear interpolation calculation class from: https://github.com/pmav99/interpolation
+- in CNCJob UI Autolevelling: made sure that the grid can't have less than 2 rows and 2 columns when using the bilinear interpolation or 1 row and 1 column when using the Voronoi polygons
+
+
 2.09.2020
 
 - in CNCJob UI Autolevelling: solved some small errors: when manual adding probe points dragging the mouse with left button pressed created selection rectangles; detection of click inside the solid geometry was failing

+ 119 - 0
appCommon/bilinear.py

@@ -0,0 +1,119 @@
+#############################################################################
+# Copyright (c) 2013 by Panagiotis Mavrogiorgos
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+# * Neither the name(s) of the copyright holders nor the names of its
+#   contributors may be used to endorse or promote products derived from this
+#   software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AS IS AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#############################################################################
+#
+# @license: http://opensource.org/licenses/BSD-3-Clause
+
+from bisect import bisect_left
+import logging
+
+log = logging.getLogger('base')
+
+
+class BilinearInterpolation(object):
+    """
+    Bilinear interpolation with optional extrapolation.
+    Usage:
+    table = BilinearInterpolation(
+        x_index=(1, 2, 3),
+        y_index=(1, 2, 3),
+        values=((110, 120, 130),
+                (210, 220, 230),
+                (310, 320, 330)),
+        extrapolate=True)
+
+    assert table(1, 1) == 110
+    assert table(2.5, 2.5) == 275
+
+    """
+    def __init__(self, x_index, y_index, values):
+        # sanity check
+        x_length = len(x_index)
+        y_length = len(y_index)
+
+        if x_length < 2 or y_length < 2:
+            raise ValueError("Table must be at least 2x2.")
+        if y_length != len(values):
+            raise ValueError("Table must have equal number of rows to y_index.")
+        if any(x2 - x1 <= 0 for x1, x2 in zip(x_index, x_index[1:])):
+            raise ValueError("x_index must be in strictly ascending order!")
+        if any(y2 - y1 <= 0 for y1, y2 in zip(y_index, y_index[1:])):
+            raise ValueError("y_index must be in strictly ascending order!")
+
+        self.x_index = x_index
+        self.y_index = y_index
+        self.values = values
+        self.x_length = x_length
+        self.y_length = y_length
+        self.extrapolate = True
+
+        #slopes = self.slopes = []
+        #for j in range(y_length):
+            #intervals = zip(x_index, x_index[1:], values[j], values[j][1:])
+            #slopes.append([(y2 - y1) / (x2 - x1) for x1, x2, y1, y2 in intervals])
+
+    def __call__(self, x, y):
+        # local lookups
+        x_index, y_index, values = self.x_index, self.y_index, self.values
+
+        i = bisect_left(x_index, x) - 1
+        j = bisect_left(y_index, y) - 1
+
+        if self.extrapolate:
+            # fix x index
+            if i == -1:
+                x_slice = slice(None, 2)
+            elif i == self.x_length - 1:
+                x_slice = slice(-2, None)
+            else:
+                x_slice = slice(i, i + 2)
+
+            # fix y index
+            if j == -1:
+                j = 0
+                y_slice = slice(None, 2)
+            elif j == self.y_length - 1:
+                j = -2
+                y_slice = slice(-2, None)
+            else:
+                y_slice = slice(j, j + 2)
+        else:
+            if i == -1 or i == self.x_length - 1:
+                raise ValueError("Extrapolation not allowed!")
+            if j == -1 or j == self.y_length - 1:
+                raise ValueError("Extrapolation not allowed!")
+
+        # if the extrapolations is False this will fail
+        x1, x2 = x_index[x_slice]
+        y1, y2 = y_index[y_slice]
+        z11, z12 = values[j][x_slice]
+        z21, z22 = values[j + 1][x_slice]
+
+        return (z11 * (x2 - x) * (y2 - y) +
+                z21 * (x - x1) * (y2 - y) +
+                z12 * (x2 - x) * (y - y1) +
+                z22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))

+ 15 - 4
appGUI/ObjectUI.py

@@ -1923,11 +1923,13 @@ class CNCObjectUI(ObjectUI):
 
         grid0.addWidget(self.al_probe_points_table, 1, 0, 1, 2)
 
-        self.voronoi_cb = FCCheckBox(_("Show Voronoi diagram"))
-        self.voronoi_cb.setToolTip(
-            _("Display Voronoi diagram if there are probe points in the table.")
+        self.plot_probing_pts_cb = FCCheckBox(_("Plot probing points"))
+        self.plot_probing_pts_cb.setToolTip(
+            _("Plot the probing points in the table.\n"
+              "If a Voronoi method is used then\n"
+              "the Voronoi areas are also plotted.")
         )
-        grid0.addWidget(self.voronoi_cb, 3, 0, 1, 2)
+        grid0.addWidget(self.plot_probing_pts_cb, 3, 0, 1, 2)
 
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -2022,6 +2024,7 @@ class CNCObjectUI(ObjectUI):
 
         # ## Columns
         self.al_columns_entry = FCSpinner()
+        self.al_columns_entry.setMinimum(2)
 
         self.al_columns_label = QtWidgets.QLabel('%s:' % _("Columns"))
         self.al_columns_label.setToolTip(
@@ -2032,6 +2035,7 @@ class CNCObjectUI(ObjectUI):
 
         # ## Rows
         self.al_rows_entry = FCSpinner()
+        self.al_rows_entry.setMinimum(2)
 
         self.al_rows_label = QtWidgets.QLabel('%s:' % _("Rows"))
         self.al_rows_label.setToolTip(
@@ -2465,6 +2469,13 @@ class CNCObjectUI(ObjectUI):
 
         # Set initial UI
         self.al_frame.hide()
+        self.al_rows_entry.setDisabled(True)
+        self.al_rows_label.setDisabled(True)
+        self.al_columns_entry.setDisabled(True)
+        self.al_columns_label.setDisabled(True)
+        self.al_method_lbl.setDisabled(True)
+        self.al_method_radio.setDisabled(True)
+        self.al_method_radio.set_value('v')
         # self.on_mode_radio(val='grid')
 
 

+ 17 - 8
appObjects/FlatCAMCNCJob.py

@@ -466,13 +466,11 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.ui.al_probe_points_table.setMaximumHeight(self.ui.al_probe_points_table.getHeight())
 
         if self.ui.al_probe_points_table.model().rowCount():
-            self.ui.voronoi_cb.setDisabled(False)
             self.ui.grbl_get_heightmap_button.setDisabled(False)
             self.ui.grbl_save_height_map_button.setDisabled(False)
             self.ui.h_gcode_button.setDisabled(False)
             self.ui.view_h_gcode_button.setDisabled(False)
         else:
-            self.ui.voronoi_cb.setDisabled(True)
             self.ui.grbl_get_heightmap_button.setDisabled(True)
             self.ui.grbl_save_height_map_button.setDisabled(True)
             self.ui.h_gcode_button.setDisabled(True)
@@ -573,8 +571,9 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         # autolevelling signals
         self.ui.sal_cb.stateChanged.connect(self.on_autolevelling)
         self.ui.al_mode_radio.activated_custom.connect(self.on_mode_radio)
+        self.ui.al_method_radio.activated_custom.connect(self.on_method_radio)
         self.ui.al_controller_combo.currentIndexChanged.connect(self.on_controller_change)
-        self.ui.voronoi_cb.stateChanged.connect(self.show_voronoi_diagram)
+        self.ui.plot_probing_pts_cb.stateChanged.connect(self.show_voronoi_diagram)
         # GRBL
         self.ui.com_search_button.clicked.connect(self.on_grbl_search_ports)
         self.ui.add_bd_button.clicked.connect(self.on_grbl_add_baudrate)
@@ -645,6 +644,8 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.ui.al_mode_radio.set_value(self.options['al_mode'])
         self.on_controller_change()
 
+        self.on_method_radio(val=self.options['al_method'])
+
     # def on_cnc_custom_parameters(self, signal_text):
     #     if signal_text == 'Parameters':
     #         return
@@ -708,8 +709,8 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             cols = self.ui.al_columns_entry.get_value()
             rows = self.ui.al_rows_entry.get_value()
 
-            dx = width / (cols - 1)
-            dy = height / (rows - 1)
+            dx = 0 if cols == 1 else width / (cols - 1)
+            dy = 0 if rows == 1 else height / (rows - 1)
 
             points = []
             new_y = ymin
@@ -742,7 +743,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             self.probing_gcode_text = self.probing_gcode()
 
             self.build_al_table_sig.emit()
-            if self.ui.voronoi_cb.get_value():
+            if self.ui.plot_probing_pts_cb.get_value():
                 self.show_voronoi_diagram(state=True, reset=True)
             else:
                 # clear probe shapes
@@ -780,7 +781,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             self.mouse_events_connected = True
 
             self.build_al_table_sig.emit()
-            if self.ui.voronoi_cb.get_value():
+            if self.ui.plot_probing_pts_cb.get_value():
                 self.show_voronoi_diagram(state=True, reset=True)
             else:
                 # clear probe shapes
@@ -990,7 +991,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             # rebuild the al table
             self.build_al_table_sig.emit()
 
-            if self.ui.voronoi_cb.get_value():
+            if self.ui.plot_probing_pts_cb.get_value():
                 self.show_voronoi_diagram(state=True, reset=True)
             else:
                 # clear probe shapes
@@ -1095,6 +1096,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             self.ui.al_method_radio.setDisabled(False)
             self.ui.al_method_radio.set_value(self.app.defaults['cncjob_al_method'])
 
+    def on_method_radio(self, val):
+        if val == 'b':
+            self.ui.al_columns_entry.setMinimum(2)
+            self.ui.al_rows_entry.setMinimum(2)
+        else:
+            self.ui.al_columns_entry.setMinimum(1)
+            self.ui.al_rows_entry.setMinimum(1)
+
     def on_controller_change(self):
         if self.ui.al_controller_combo.get_value() == 'GRBL':
             self.ui.h_gcode_button.hide()