Skip to content

Commit 6650229

Browse files
author
etet100
committed
Fix heightmap arc segmentation and G-code modal state bug
- segmentArc: prepend start point so push() generates the first chord correctly (arc points exclude start by convention) - generateGCodeLine: emit explicit G0/G1 for motion/rapid moves to prevent G2/G3 modal state from carrying over to linearized segments after arc conversion - simplifyViewTransform: replace cumulative-length threshold with perpendicular-distance (Ramer-Douglas-Peucker style) for geometrically correct simplification; break early on arcs - applyLoaderGCode: allow program reload in Alarm state
1 parent 7fce1bc commit 6650229

3 files changed

Lines changed: 42 additions & 12 deletions

File tree

src/gpilot/core/gcode/converter/applyheightmap.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ QList<QVector3D> ApplyHeightmap::segmentArc(const QVector3D &start, const QVecto
225225
bool clockwise, PointSegment::planes plane)
226226
{
227227
QList<QVector3D> points;
228+
// Include the raw start so the loop in push() generates the first chord
229+
// (startPoint → arc[0]) correctly, matching segmentLine's convention.
230+
// generatePointsAlongArcBDring does NOT include the start point.
231+
points.append(start);
228232

229233
QList<QVector3D> arcPoints = GcodePreprocessorUtils::generatePointsAlongArcBDring(
230234
plane, start, end, center, clockwise, radius,
@@ -330,11 +334,20 @@ QString ApplyHeightmap::generateGCodeLine(const QVector3D &start, const QVector3
330334
if (!commandOverride.isEmpty()) {
331335
command = commandOverride;
332336
} else {
333-
// Take only the G/M code prefix (first token) because later we append
334-
// fresh X/Y/Z/F values; the original args would duplicate coordinates.
335-
const QString full = originalItem.command();
336-
const int sp = full.indexOf(' ');
337-
command = (sp < 0) ? full : full.left(sp);
337+
// Always emit an explicit motion code so that the machine's modal state
338+
// from a preceding G2/G3 (preserved arc) does not carry over into what
339+
// should be a G1/G0 move. A modal G1 line ("X10 Y5" without a G-word)
340+
// would otherwise be interpreted as G2/G3 by the controller.
341+
switch (originalItem.group) {
342+
case GCodeItemGroup::Movement: command = "G1"; break;
343+
case GCodeItemGroup::RapidMovement: command = "G0"; break;
344+
default: {
345+
const QString full = originalItem.command();
346+
const int sp = full.indexOf(' ');
347+
command = (sp < 0) ? full : full.left(sp);
348+
break;
349+
}
350+
}
338351
}
339352
if (command.isEmpty() && !originalItem.args.empty()) {
340353
command = QString::fromStdString(originalItem.args.front());

src/gpilot/core/gcode/parser/viewtransform/simplifyviewtransform.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ bool areCollinear(const QVector3D& refDir, const LineSegment& s, float tolerance
2323
return cross.lengthSquared() < tolerance * tolerance;
2424
}
2525

26+
float perpendicularDistance(const QVector3D& p, const QVector3D& lineStart, const QVector3D& lineEnd)
27+
{
28+
QVector3D chord = lineEnd - lineStart;
29+
float chordLen = chord.length();
30+
if (chordLen < 1e-7f) {
31+
return (p - lineStart).length();
32+
}
33+
34+
return QVector3D::crossProduct(p - lineStart, chord / chordLen).length();
35+
}
36+
2637
}
2738

2839
SimplifyViewTransform::SimplifyViewTransform(double precision, double collinearTolerance)
@@ -48,19 +59,25 @@ QList<LineSegment> SimplifyViewTransform::apply(const QList<LineSegment>& input)
4859
float refLen = refVec.length();
4960
QVector3D refDir = (refLen > 1e-7f) ? (refVec / refLen) : QVector3D();
5061
bool hasRefDir = refLen > 1e-7f && !input[j].isArc();
51-
double length = refLen;
5262

5363
while (i < input.count() - 1
5464
&& segmentType(input[i + 1]) == segmentType(input[j])) {
55-
bool collinear = hasRefDir && !input[i + 1].isArc()
56-
&& areCollinear(refDir, input[i + 1], (float)m_collinearTolerance);
57-
bool withinPrecision = length < m_precision;
65+
if (input[i + 1].isArc() || !hasRefDir) {
66+
break;
67+
}
68+
69+
bool collinear = areCollinear(refDir, input[i + 1], (float)m_collinearTolerance);
70+
bool withinDeviation = m_precision > 0.0
71+
&& perpendicularDistance(
72+
input[i].getEnd(),
73+
input[j].getStart(),
74+
input[i + 1].getEnd()
75+
) <= (float)m_precision;
5876

59-
if (!collinear && !withinPrecision) {
77+
if (!collinear && !withinDeviation) {
6078
break;
6179
}
6280

63-
length += (input[i + 1].getEnd() - input[i + 1].getStart()).length();
6481
i++;
6582
}
6683
}

src/gpilot/ui/forms/frmmain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2683,7 +2683,7 @@ void FrmMain::applyLoaderGCode(GCodeLoaderData *data)
26832683
// pendant or scripted actions could still push the machine into Running
26842684
// between load start and load finish. Replacing program() while the
26852685
// RunningBehavior is iterating it crashes the sender.
2686-
if (!communicator()->stateBehavior()->is(AbstractStateBehavior::Type::Idle)) {
2686+
if (!communicator()->stateBehavior()->isOneOf(AbstractStateBehavior::Type::Idle, AbstractStateBehavior::Type::Alarm)) {
26872687
qWarning() << "[FrmMain] applyLoaderGCode: machine is not idle, discarding loaded data";
26882688
ui->console->appendSystem(tr("Cannot replace program: machine is not idle"));
26892689
return;

0 commit comments

Comments
 (0)