Skip to content

Commit f888d16

Browse files
author
etet100
committed
Heightmap: position cells, grid sync, panel toggles
- Heightmap table cells now show XY position (small text) above the measured height value - HeightmapTableModel subscribes to Heightmap::changed and resets automatically; uses setHeightAt() instead of direct array access - Grid parameters (size, Z range, probe feed, interpolation step) sync to Heightmap model on change - Program panel gains Program/Heightmap toggle buttons, replacing the old external visibility API - Visualizer auto-updates on heightmap change and adds a "Toggle grid" toolbar button - Default grid size changed from 1×1 to 5×5 - Cell colors adapt to dark/light theme - Fix saveChanges() to check both gcode and heightmap changes on window close - Add heightmap user documentation page
1 parent c8e2469 commit f888d16

22 files changed

Lines changed: 406 additions & 76 deletions

docs/heightmap.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
title: Heightmap / Surface Leveling
3+
---
4+
5+
# Heightmap / Surface Leveling
6+
7+
AstroCore can scan the surface of the CNC table by probing at multiple points and building a height map. This is useful when the table or workpiece is not perfectly flat — the height map allows the application to compensate for any unevenness by adjusting the Z axis in real time while running a program.
8+
9+
## Setting up the grid
10+
11+
Before scanning, you need to define a grid — a mesh of probe points placed over the working area. The grid is configured in the heightmap panel: you set its position, size, and the number of probe points in each direction.
12+
13+
## Scanning
14+
15+
Once the grid is configured, the **Scan table** function moves the tool to each grid vertex in sequence and probes the surface height at that point. The machine must be connected and the probe must be set up correctly before starting.
16+
17+
![Heightmap scan in progress](screenshots/screenshot_main_dark_heightmap.png)
18+
19+
## Viewing the result
20+
21+
The scanned surface can be viewed in two ways:
22+
23+
- **Visualizer** — the 3D view shows the heightmap as a colored surface overlaid on the working area. Colors represent height — from blue (low) to red (high). Height markers can be displayed at each probe point by enabling the marker overlay in the visualizer.
24+
- **Heightmap table** — switching to the heightmap view in the program panel shows the probed values as a grid table. Each cell displays the X, Y position of the probe point and the measured height.
25+
26+
![Heightmap visualizer with markers](screenshots/screenshot_main_heightmap_markers_2d.png)
27+
28+
## Manual editing
29+
30+
Any individual height value can be edited manually:
31+
32+
- In the **visualizer** — click a height marker to edit its value directly.
33+
- In the **table** — click any cell and type the new value.
34+
35+
This is useful for correcting a bad probe reading or filling in a point that was not probed.
36+
37+
## Interpolation
38+
39+
Between the probed grid points, the application interpolates the surface to compute the Z correction for any position in the working area. Several interpolation methods are available (Bicubic, Bilinear, Linear, Nearest Neighbour). Bicubic produces the smoothest result.
40+
41+
![Bicubic interpolation](screenshots/screenshot_heightmap_bicubic.png)

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Main documentation pages available in this folder:
3838
- [User interface](ui.md)
3939
- [Jogging](jogging.md)
4040
- [G-code editing](gcode-editing.md)
41+
- [Heightmap / Surface leveling](heightmap.md)
4142
- [Connection and virtual modes](connection-modes.md)
4243
- [Log browser](log-browser.md)
4344
- [Architecture overview](architecture.md)
-176 KB
Loading

src/gpilot/core/heightmap/configurationheightmap.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ const QMap<QString,QVariant> DEFAULTS = {
1111
{"heightmapAreaX2", 100},
1212
{"heightmapAreaY2", 100},
1313
{"heightmapAreaShow", false},
14-
{"heightmapGridX", 1},
15-
{"heightmapGridY", 1},
14+
{"heightmapGridX", 5},
15+
{"heightmapGridY", 5},
1616
{"heightmapGridZTop", 1},
1717
{"heightmapGridZBottom", -1},
1818
{"heightmapProbeFeed", 10},

src/gpilot/core/heightmap/heightmap.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,51 @@ void Heightmap::setArea(QRectF area)
122122
// reset();
123123
}
124124

125+
void Heightmap::setGridSize(QSize size)
126+
{
127+
if (size == m_size) {
128+
return;
129+
}
130+
131+
QSizeF currentArea(m_endPos.x() - m_startPos.x(), m_endPos.y() - m_startPos.y());
132+
m_size = size;
133+
m_stepSize = QSizeF(
134+
size.width() > 1 ? currentArea.width() / (size.width() - 1) : 0,
135+
size.height() > 1 ? currentArea.height() / (size.height() - 1) : 0
136+
);
137+
m_data = QList<double>(size.width() * size.height(), NAN);
138+
updateEndPos();
139+
updateMinMax();
140+
notifyChanged();
141+
}
142+
143+
void Heightmap::setProbeFeed(int probeFeed)
144+
{
145+
if (probeFeed == m_probeFeed) {
146+
return;
147+
}
148+
m_probeFeed = probeFeed;
149+
notifyChanged();
150+
}
151+
152+
void Heightmap::setZBottomTop(BottomTop zBottomTop)
153+
{
154+
if (zBottomTop.bottom == m_zBottomTop.bottom && zBottomTop.top == m_zBottomTop.top) {
155+
return;
156+
}
157+
m_zBottomTop = zBottomTop;
158+
notifyChanged();
159+
}
160+
161+
void Heightmap::setInterpolationStepSize(QSizeF stepSize)
162+
{
163+
if (stepSize == m_interpolationStepSize) {
164+
return;
165+
}
166+
m_interpolationStepSize = stepSize;
167+
notifyChanged();
168+
}
169+
125170
QPair<int, int> Heightmap::gridIndices(const QPointF &ptMm) const
126171
{
127172
int i = static_cast<int>((ptMm.x() - m_startPos.x()) / m_stepSize.width());

src/gpilot/core/heightmap/heightmap.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Heightmap : public QObject
2929
};
3030

3131
Heightmap(
32-
QSize size = QSize(11, 11),
32+
QSize size = QSize(5, 5),
3333
QPointF startPos = QPointF(0.0, 0.0),
3434
QSizeF stepSize = QSizeF(10.0, 10.0),
3535
InterpolationMode interpolationMode = InterpolationMode::Bicubic,
@@ -60,13 +60,18 @@ class Heightmap : public QObject
6060
double stepWidth() const { return m_stepSize.width(); }
6161
double stepHeight() const { return m_stepSize.height(); }
6262
QSizeF interpolationStepSize() const { return m_interpolationStepSize; }
63+
void setInterpolationStepSize(QSizeF stepSize);
6364
InterpolationMode interpolationMode() const { return m_interpolationMode; }
6465
void setInterpolationMode(InterpolationMode mode);
6566
QRectF area() const { return QRectF(m_startPos, m_endPos); }
6667
void setArea(QRectF area);
68+
// Resize grid keeping the current area; recomputes stepSize and resets data to NaN.
69+
void setGridSize(QSize size);
6770
MinMax valuesMinMax() const { return m_valuesMinMax; }
6871
BottomTop zBottomTop() const { return m_zBottomTop; }
72+
void setZBottomTop(BottomTop zBottomTop);
6973
int probeFeed() const { return m_probeFeed; }
74+
void setProbeFeed(int probeFeed);
7075
QPair<int, int> gridIndices(const QPointF& pt_mm) const;
7176
QList<QPointF> probePoints(QPointF currentPos, ScanMode mode) const;
7277
double& at(int x, int y);

src/gpilot/ui/forms/frmmain.cpp

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ void FrmMain::initializeHeightmapPanel()
463463
ui->visualizer->updateHeightmap();
464464
}
465465
});
466+
connect(ui->heightmap, &PartMainHeightmap::gridParametersChanged, this, &FrmMain::onGridParametersChanged);
466467
connect(ui->heightmap, &PartMainHeightmap::interpolationModeChanged, this, [this](Heightmap::InterpolationMode mode) {
467468
ui->visualizer->setHeightmapInterpolationMode(mode);
468469
heightmap().setInterpolationMode(mode);
@@ -715,7 +716,7 @@ void FrmMain::closeEvent(QCloseEvent *ce)
715716
bool mode = m_heightmapMode;
716717
m_heightmapMode = false;
717718

718-
if (!saveChanges(m_heightmapMode)) {
719+
if (!saveChanges(false) || !saveChanges(true)) {
719720
ce->ignore();
720721
m_heightmapMode = mode;
721722
return;
@@ -2182,6 +2183,28 @@ void FrmMain::onHeightmapDataChangedByUser()
21822183
// updateHeightmapInterpolationDrawer();
21832184
}
21842185

2186+
void FrmMain::onGridParametersChanged(QSize gridSize, PartMainHeightmap::MinMax zMinMax, int probeFeed, QSize interpolationStep)
2187+
{
2188+
auto& cfg = ConfigurationHeightmap::instance();
2189+
cfg.setProperty("gridX", gridSize.width());
2190+
cfg.setProperty("gridY", gridSize.height());
2191+
cfg.setProperty("gridZBottom", zMinMax.min);
2192+
cfg.setProperty("gridZTop", zMinMax.max);
2193+
cfg.setProperty("probeFeed", probeFeed);
2194+
cfg.setProperty("interpolationStepX", interpolationStep.width());
2195+
cfg.setProperty("interpolationStepY", interpolationStep.height());
2196+
2197+
Heightmap& hm = heightmap();
2198+
hm.beginUpdate();
2199+
hm.setGridSize(gridSize);
2200+
hm.setZBottomTop({ zMinMax.min, zMinMax.max });
2201+
hm.setProbeFeed(probeFeed);
2202+
hm.setInterpolationStepSize(QSizeF(interpolationStep.width(), interpolationStep.height()));
2203+
hm.endUpdate();
2204+
2205+
updateHeightmapGrid();
2206+
}
2207+
21852208
void FrmMain::preloadSettings()
21862209
{
21872210
ConfigurationUI &uiConfiguration = UiConfigs::instance().ui();
@@ -2719,7 +2742,6 @@ void FrmMain::applyLoaderGCode(GCodeLoaderData *data)
27192742

27202743
ui->program->selectFirstRow();
27212744

2722-
resetHeightmap();
27232745
updateControlsState();
27242746
}
27252747

@@ -2808,7 +2830,7 @@ bool FrmMain::saveChanges(bool heightMapMode)
28082830
{
28092831
FilesManager& fm = FilesManager::instance();
28102832

2811-
if ((!heightMapMode && fm.gcodeModified())) {
2833+
if (!heightMapMode && fm.gcodeModified()) {
28122834
int res = QMessageBox::warning(this, this->windowTitle(), tr("G-code program file was changed. Save?"),
28132835
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
28142836
if (res == QMessageBox::Cancel) return false;
@@ -2817,7 +2839,7 @@ bool FrmMain::saveChanges(bool heightMapMode)
28172839
fm.setGcodeModified(false);
28182840
}
28192841

2820-
if (fm.heightmapModified()) {
2842+
if (heightMapMode && fm.heightmapModified()) {
28212843
int res = QMessageBox::warning(this, this->windowTitle(), tr("Heightmap file was changed. Save?"),
28222844
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
28232845
if (res == QMessageBox::Cancel) return false;
@@ -2991,8 +3013,8 @@ void FrmMain::updateControlsState()
29913013
// ui->cboJogStep->setStyleSheet(QString("font-size: %1").arg(UiConfigs::instance().ui().fontSize()));
29923014
// ui->cboJogFeed->setStyleSheet(ui->cboJogStep->styleSheet());
29933015

2994-
ui->program->setHeightMapVisible(m_heightmapMode);
2995-
ui->program->setProgramVisible(!m_heightmapMode);
3016+
// ui->program->setHeightMapVisible(m_heightmapMode);
3017+
// ui->program->setProgramVisible(!m_heightmapMode);
29963018

29973019
ui->program->setSendButtonText(m_heightmapMode ? tr("Probe") : tr("Send"));
29983020

@@ -3066,26 +3088,11 @@ void FrmMain::updateJogTitle()
30663088

30673089
bool FrmMain::updateHeightmapGrid()
30683090
{
3069-
if (!heightmap().anyHeightSet()) {
3070-
if (QMessageBox::warning(this, this->windowTitle(), tr("Changing grid settings will reset probe data. Continue?"),
3071-
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return false;
3072-
}
3073-
3074-
// Update grid drawer
30753091
QRectF borderRect = ui->heightmap->areaRectFromTextboxes();
3076-
// ui->visualizer->heightmapGridDrawer()->setBorderRect(borderRect);
3077-
// ui->visualizer->heightmapGridDrawer()->setGridSize(QPointF(ui->txtHeightMapGridX->value(), ui->txtHeightMapGridY->value()));
3078-
// ui->visualizer->heightmapGridDrawer()->setZBottom(ui->txtHeightMapGridZBottom->value());
3079-
// ui->visualizer->heightmapGridDrawer()->setZTop(ui->txtHeightMapGridZTop->value());
30803092

3081-
// Reset model
30823093
int gridPointsX = heightmap().gridSize().width();
30833094
int gridPointsY = heightmap().gridSize().height();
30843095

3085-
ui->program->resizeHeightmapModel(gridPointsX, gridPointsY);
3086-
ui->program->setHeightmap(nullptr);
3087-
ui->program->setHeightmap(&heightmap());
3088-
30893096
// Update interpolation
30903097
ui->visualizer->updateHeightmapInterpolation(true);
30913098

@@ -3426,7 +3433,7 @@ bool FrmMain::actionTextLessThan(const QAction *a1, const QAction *a2)
34263433
void FrmMain::setHeightmapPoint(QPoint point, double height)
34273434
{
34283435
heightmap().setHeightAt(point, height);
3429-
ui->visualizer->updateHeightmap();
3436+
// ui->visualizer->updateHeightmap();
34303437

34313438
ui->console->append(QString("[Heightmap] Point (%1, %2) set to %3").arg(point.x()).arg(point.y()).arg(height));
34323439
}

src/gpilot/ui/forms/frmmain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "ui/forms/partials/main/partmainstate.h"
3434
#include "ui/forms/partials/main/partmainconsole.h"
3535
#include "ui/forms/partials/main/partmainvisualizer.h"
36+
#include "ui/forms/partials/main/partmainheightmap.h"
3637
#include "ui/forms/partials/main/partmainoverride.h"
3738
#include "ui/forms/frmgrblconfigurator.h"
3839
#include "ui/forms/frmlog.h"
@@ -182,6 +183,7 @@ private slots:
182183
// void onProgramLinesUpdated(int from, int to);
183184
// void updateHeightmapInterpolationDrawer(bool reset = false);
184185
void onHeightmapDataChangedByUser();
186+
void onGridParametersChanged(QSize gridSize, PartMainHeightmap::MinMax zMinMax, int probeFeed, QSize interpolationStep);
185187
void centralWidgetActionTriggered(bool checked);
186188

187189
protected:

src/gpilot/ui/forms/frmmain.ui

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<bool>true</bool>
1515
</property>
1616
<property name="windowTitle">
17-
<string>G-Pilot GCode Sender</string>
17+
<string>AstroCore GCode Sender</string>
1818
</property>
1919
<property name="windowIcon">
2020
<iconset resource="../resources/images.qrc">
@@ -384,13 +384,13 @@ QGroupBox::title {
384384
<x>0</x>
385385
<y>0</y>
386386
<width>105</width>
387-
<height>525</height>
387+
<height>565</height>
388388
</rect>
389389
</property>
390390
<property name="acceptDrops">
391391
<bool>true</bool>
392392
</property>
393-
<layout class="QVBoxLayout" name="verticalLayout_13" stretch="0,0,0,0,0">
393+
<layout class="QVBoxLayout" name="verticalLayout_13" stretch="0,0,0,0,0,0">
394394
<property name="sizeConstraint">
395395
<enum>QLayout::SizeConstraint::SetMinimumSize</enum>
396396
</property>
@@ -532,7 +532,6 @@ QGroupBox::title {
532532
</layout>
533533
</widget>
534534
</item>
535-
536535
</layout>
537536
</widget>
538537
</widget>
@@ -742,7 +741,7 @@ QGroupBox::title {
742741
<x>0</x>
743742
<y>0</y>
744743
<width>105</width>
745-
<height>335</height>
744+
<height>295</height>
746745
</rect>
747746
</property>
748747
<property name="acceptDrops">

src/gpilot/ui/forms/partials/main/partmainheightmap.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ QRectF PartMainHeightmap::areaRectFromTextboxes()
7474

7575
void PartMainHeightmap::applyHeightmapConfiguration(ConfigurationHeightmap &configurationHeightmap)
7676
{
77+
QSignalBlocker blocker(this);
78+
7779
ui->txtAreaX->setValue(configurationHeightmap.areaX1());
7880
ui->txtAreaX1->setValue(configurationHeightmap.areaX1());
7981
ui->txtAreaY->setValue(configurationHeightmap.areaY1());
@@ -203,10 +205,10 @@ void PartMainHeightmap::emitShowVisualizationChanged()
203205
void PartMainHeightmap::emitGridParametersChanged()
204206
{
205207
emit gridParametersChanged(
206-
QPoint(ui->txtGridX->value(), ui->txtGridY->value()),
208+
QSize(ui->txtGridX->value(), ui->txtGridY->value()),
207209
{ ui->txtGridZBottom->value(), ui->txtGridZTop->value() },
208210
ui->txtProbeFeed->value(),
209-
QPoint(ui->txtInterpolationStepX->value(), ui->txtInterpolationStepY->value())
211+
QSize(ui->txtInterpolationStepX->value(), ui->txtInterpolationStepY->value())
210212
);
211213
}
212214

@@ -241,10 +243,10 @@ void PartMainHeightmap::onAreaChanged()
241243
void PartMainHeightmap::onGridParametersChanged()
242244
{
243245
emit gridParametersChanged(
244-
QPoint(ui->txtGridX->value(), ui->txtGridY->value()),
246+
QSize(ui->txtGridX->value(), ui->txtGridY->value()),
245247
{ ui->txtGridZBottom->value(), ui->txtGridZTop->value() },
246248
ui->txtProbeFeed->value(),
247-
QPoint(ui->txtInterpolationStepX->value(), ui->txtInterpolationStepY->value())
249+
QSize(ui->txtInterpolationStepX->value(), ui->txtInterpolationStepY->value())
248250
);
249251
}
250252

0 commit comments

Comments
 (0)