- 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
This commit is contained in:
Marius Stanciu 2020-09-03 03:45:58 +03:00
parent 75be9d7604
commit 743885cf0f
4 changed files with 158 additions and 12 deletions

View File

@ -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
appCommon/bilinear.py Normal file
View File

@ -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))

View File

@ -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')

View File

@ -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()