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) <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-09-30 08:42:12 +13:00
parent f15736bff7
commit 721bd155f5
2 changed files with 17 additions and 8 deletions
+8 -3
View File
@@ -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;
@@ -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;
}
}