From af367fef47336f9d3fa6020fb0f9db77581917e4 Mon Sep 17 00:00:00 2001 From: Thomas Vreys Date: Thu, 6 Nov 2025 11:54:12 +0100 Subject: [PATCH] fix(render): correct multi-lane field rendering Fields spanning multiple lanes duplicated bits and labels. Solution: Clip fields per lane, slice and shift attributes per lane and use lane bounds for edge labels to render accurately across lanes. This work was sponsored by OIP Sensor Systems Signed-off-by: Thomas Vreys --- lib/render.js | 59 ++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/lib/render.js b/lib/render.js index bf7dcc9..9f9fc51 100644 --- a/lib/render.js +++ b/lib/render.js @@ -86,15 +86,25 @@ const getAttr = (e, opt, step, lsbm, msbm) => { const x = opt.vflip ? step * ((msbm + lsbm) / 2) : step * (opt.mod - ((msbm + lsbm) / 2) - 1); + + const sliceLen = msbm - lsbm + 1; + const laneStart = opt.index * opt.mod; + const sliceLsbGlobal = Math.max(e.lsb, laneStart); + const shift = sliceLsbGlobal - e.lsb; if (!Array.isArray(e.attr)) { - return getLabel(e.attr, x, 0, step, e.bits); + const val = typeof e.attr === 'number' ? (e.attr >> shift) : e.attr; + return getLabel(val, x, 0, step, sliceLen); } - return e.attr.reduce((prev, a, i) => - (a === undefined || a === null) - ? prev - : prev.concat([getLabel(a, x, opt.fontsize * i, step, e.bits)]), - ['g', {}]); + return e.attr.reduce( + (prev, a, i) => { + if (a === undefined || a === null) return prev; + const val = typeof a === 'number' ? (a >> shift) : a; + return prev.concat([ + getLabel(val, x, opt.fontsize * i, step, sliceLen), + ]); + }, + ['g', {}]); }; const labelArr = (desc, opt) => { @@ -107,29 +117,26 @@ const labelArr = (desc, opt) => { const names = ['g', tt(round(step / 2), round(0.5 * height + 0.4 * fontsize - 6))]; const attrs = ['g', tt(round(step / 2), round(height + 0.7 * fontsize - 2))]; desc.map(e => { - let lsbm = 0; - let msbm = mod - 1; - let lsb = index * mod; - let msb = (index + 1) * mod - 1; - if (((e.lsb / mod) >> 0) === index) { - lsbm = e.lsbm; - lsb = e.lsb; - if (((e.msb / mod) >> 0) === index) { - msb = e.msb; - msbm = e.msbm; - } - } else { - if (((e.msb / mod) >> 0) === index) { - msb = e.msb; - msbm = e.msbm; - } else if (!(lsb > e.lsb && msb < e.msb)) { - return; - } + const laneStart = index * mod; + const laneEnd = (index + 1) * mod - 1; + + // Skip if no overlap + if (e.msb < laneStart || e.lsb >= laneEnd + 1) { + return; } + + let lsbm = Math.max(0, e.lsb - laneStart); + let msbm = Math.min(mod - 1, e.msb - laneStart); + + const startsInLane = e.lsb >= laneStart && e.lsb <= laneEnd; + const endsInLane = e.msb >= laneStart && e.msb <= laneEnd; + const lsbLabel = (startsInLane ? e.lsb : laneStart) + offset; + const msbLabel = (endsInLane ? e.msb : laneEnd) + offset; + if (!compact) { - bits.push(text(lsb + offset, step * (vflip ? lsbm : (mod - lsbm - 1)))); + bits.push(text(lsbLabel, step * (vflip ? lsbm : (mod - lsbm - 1)))); if (lsbm !== msbm) { - bits.push(text(msb + offset, step * (vflip ? msbm : (mod - msbm - 1)))); + bits.push(text(msbLabel, step * (vflip ? msbm : (mod - msbm - 1)))); } } if (e.name !== undefined) {