From 721bd155f596868a1b89765d892657248dc26d55 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Wed, 30 Sep 2026 08:42:12 +1300 Subject: [PATCH] Reject non-finite values when loading a wire A wire read "nan" or "inf" for its position, its segment lengths and its label position without checking them: QString::toDouble() accepts all three as numbers. PR #792 and #996 closed this gap for elements, terminals and texts; wires were left out. The worst case is a segment length: a single "nan" passes the check that the segments add up to the distance between the two terminals, because every comparison with NaN is false. The whole path then turns to NaN: on 2612_ats_singlephase.qet one bad length out of six was saved back as six "nan" vertical segments, and the next save dropped them and routed the wire automatically, so each save changed the file. Now: - a non-finite segment length is skipped like any unreadable one, so the path fails the coherence check and the wire is routed automatically at load, as already happens for "inf"; - a non-finite wire position is read as 0; - a non-finite label position or rotation is ignored, as if the label had not been moved or rotated. Found by the mutation sweep (python3 -m simulator sweep, mutators inject_nan_coordinate / inject_inf_coordinate). The 24 example projects resave byte-identical with and without this change. Co-Authored-By: Claude Opus 5.5 (1M context) --- sources/qetgraphicsitem/conductor.cpp | 11 ++++++++--- sources/qetgraphicsitem/conductortextitem.cpp | 14 +++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/sources/qetgraphicsitem/conductor.cpp b/sources/qetgraphicsitem/conductor.cpp index 4eb9009bc..a82dc5ced 100644 --- a/sources/qetgraphicsitem/conductor.cpp +++ b/sources/qetgraphicsitem/conductor.cpp @@ -1099,8 +1099,10 @@ bool Conductor::fromXml(QDomElement &dom_element) m_uuid = QUuid::createUuid(); } - setPos(dom_element.attribute("x", nullptr).toDouble(), - dom_element.attribute("y", nullptr).toDouble()); + //"nan" and "inf" parse as numbers; a non-finite position is kept at 0 + const qreal x = dom_element.attribute("x", nullptr).toDouble(); + const qreal y = dom_element.attribute("y", nullptr).toDouble(); + setPos(qIsFinite(x) ? x : 0, qIsFinite(y) ? y : 0); bool retval = pathFromXml(dom_element); @@ -1229,7 +1231,10 @@ bool Conductor::pathFromXml(const QDomElement &e) { // cette longueur doit etre un reel bool ok; qreal segment_length = current_segment.attribute("length").toDouble(&ok); - if (!ok) continue; + // toDouble() accepts "nan" and "inf". A NaN length would also pass + // the coherence check below (every comparison with NaN is false) + // and turn the whole path into NaN. + if (!ok || !qIsFinite(segment_length)) continue; if (current_segment.attribute("orientation") == "horizontal") { segments_x << segment_length; diff --git a/sources/qetgraphicsitem/conductortextitem.cpp b/sources/qetgraphicsitem/conductortextitem.cpp index 624b5ea77..a3e3127f0 100644 --- a/sources/qetgraphicsitem/conductortextitem.cpp +++ b/sources/qetgraphicsitem/conductortextitem.cpp @@ -69,13 +69,17 @@ Conductor *ConductorTextItem::parentConductor() const @param e */ void ConductorTextItem::fromXml(const QDomElement &e) { - if (e.hasAttribute("userx")) { - setPos(e.attribute("userx").toDouble(), - e.attribute("usery").toDouble()); + //A non-finite ("nan", "inf") position or rotation is ignored, + //as if the text had never been moved or rotated + const qreal x = e.attribute("userx").toDouble(); + const qreal y = e.attribute("usery").toDouble(); + if (e.hasAttribute("userx") && qIsFinite(x) && qIsFinite(y)) { + setPos(x, y); moved_by_user_ = true; } - if (e.hasAttribute("rotation")) { - setRotation(e.attribute("rotation").toDouble()); + const qreal rotation = e.attribute("rotation").toDouble(); + if (e.hasAttribute("rotation") && qIsFinite(rotation)) { + setRotation(rotation); rotate_by_user_ = true; } }