Resources Numerical accuracy Calculation policy

Symbols imported from another drawing

Turns a block of a DWG or DXF drawing into an electrical symbol in millimeters, with its connection points.

How the precision is kept

The block's geometry is moved to its base point and multiplied by one unit factor in 64-bit numbers. The factors for inches and feet are the exact 25.4 and 304.8. Connection points are stored rounded to 0.001 mm, while the geometry they sit on is not rounded.

Calculations under this policy

4 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

addons/electrical/lib/symlib.js CadCraft 0.8.2 64-bit 4 calculations Reviewed

Tests None found by the review

Millimeters per drawing unit

Exact

The factor that takes the source drawing's unit to millimeters.

inches 25.4; feet 304.8; centimeters 10; meters 1000; anything else 1

All five factors are exact by definition. An unknown or missing unit silently gives 1.

  • addons/electrical/lib/symlib.js
  • lines 62 to 64
  • CadCraft 0.8.2
export function unitScale(units) {
  return { inches: 25.4, feet: 304.8, millimeters: 1, centimeters: 10, meters: 1000 }[String(units || '').toLowerCase()] || 1;
}

Geometry scaled to millimeters

64-bit

Every line, polyline, circle, arc, ellipse, hatch and text of the block moved to its base point and scaled.

x′ = (x − base x) × s; r′ = r × s

The base point is subtracted before the multiplication, so the scaled symbol keeps the absolute rounding of the source coordinates: a block drawn far from the origin gives a symbol with fewer good digits. Nothing is rounded.

  • addons/electrical/lib/symlib.js
  • lines 112 to 126
  • CadCraft 0.8.2
function scaled(e, s, base) {
  const o = clone(e);
  delete o.id;
  const X = (x) => (x - base.x) * s, Y = (y) => (y - base.y) * s;
  switch (o.type) {
    case 'line': Object.assign(o, { x1: X(o.x1), y1: Y(o.y1), x2: X(o.x2), y2: Y(o.y2) }); break;
    case 'poly': o.pts = o.pts.map((p) => ({ ...p, x: X(p.x), y: Y(p.y) })); break;
    case 'circle': case 'arc': Object.assign(o, { cx: X(o.cx), cy: Y(o.cy), r: o.r * s }); break;
    case 'ellipse': Object.assign(o, { cx: X(o.cx), cy: Y(o.cy), mx: o.mx * s, my: o.my * s }); break;
    case 'hatch': o.loops = o.loops.map((l) => l.map((v, i) => (i % 2 ? Y(v) : X(v)))); break;
    case 'text': case 'point': case 'insert': Object.assign(o, { x: X(o.x), y: Y(o.y) }); if (o.h) o.h *= s; if (o.type === 'insert') { o.sx = (o.sx ?? 1) * s; o.sy = (o.sy ?? 1) * s; } break;
    default: return null;
  }
  return o;
}

Connection points found on the outline

64-bit

The points where wires connect: terminal attributes, or the ends of leads that leave the symbol's outline straight.

tol = max(width, height) × 0.001 + 1e-9

The tolerance scales with the symbol's size. It decides whether an end is on the outline, whether a lead is vertical or horizontal, and whether two ends are the same point.

  • addons/electrical/lib/symlib.js
  • lines 81 to 107
  • CadCraft 0.8.2
/** Points where wires connect, in the block's own coordinates relative to its base, times scale. */
export function findPins(blk, scale = 1) {
  const base = blk.base || { x: 0, y: 0 };
  const rel = (x, y) => ({ x: (x - base.x) * scale, y: (y - base.y) * scale });
  const byAttr = (blk.attdefs || []).filter((a) => PIN_ATTR.test(String(a.tag).toUpperCase()) && fin(a.x) && fin(a.y))
    .sort((a, b) => String(a.tag).localeCompare(String(b.tag), 'en', { numeric: true })).map((a) => rel(a.x, a.y));
  if (byAttr.length) return byAttr.slice(0, LIMITS.pins);
  // line ends lying on the drawing's outline: where a symbol's leads stop
  const ents = blockEntities(blk);
  const bb = boundsOf(ents.filter((e) => e.type !== 'text'), []);   // the drawing's outline, not its labels
  const w = bb.x1 - bb.x0, h = bb.y1 - bb.y0;
  const tol = Math.max(w, h) * 1e-3 + 1e-9;
  const ends = [];
  for (const e of ents) {
    const pts = e.type === 'line' ? [[e.x1, e.y1], [e.x2, e.y2]] : e.type === 'poly' && !e.closed && e.pts?.length ? [[e.pts[0].x, e.pts[0].y], [e.pts.at(-1).x, e.pts.at(-1).y]] : [];
    for (const [x, y] of pts) {
      const onTB = h > tol && (Math.abs(y - bb.y0) < tol || Math.abs(y - bb.y1) < tol);
      const onLR = w > tol && (Math.abs(x - bb.x0) < tol || Math.abs(x - bb.x1) < tol);
      // a lead: the end of a line that leaves the outline straight (vertical leads on top/bottom, horizontal on the sides)
      if ((onTB && e.type === 'line' && Math.abs(e.x1 - e.x2) < tol) || (onLR && e.type === 'line' && Math.abs(e.y1 - e.y2) < tol)) {
        if (!ends.some((p) => Math.abs(p.x - x) < tol && Math.abs(p.y - y) < tol)) ends.push({ x, y });
      }
    }
  }
  ends.sort((a, b) => b.y - a.y || a.x - b.x);   // top first, then left to right (current flows top to bottom)
  return ends.slice(0, LIMITS.pins).map((p) => rel(p.x, p.y));
}

Stored pin coordinates

64-bit

The connection points written into the symbol's data.

x = round(x × 1000) ÷ 1000

Pins are rounded to 0.001 mm and the scaled geometry is not, so a pin can sit up to 0.0005 mm from the end of the lead it was found on.

  • addons/electrical/lib/symlib.js
  • lines 138 to 140
  • CadCraft 0.8.2
const found = findPins(blk, scale);
const pinTags = found.length === s.pins.length ? s.pins.map(([tag]) => tag) : found.map((_, i) => 'P' + (i + 1));
const pins = found.map((p, i) => ({ tag: pinTags[i], x: Math.round(p.x * 1000) / 1000, y: Math.round(p.y * 1000) / 1000 }));

In detail

Method
new coordinate = (coordinate − base) × scale. Connection points are attributes named like terminals, or else the ends of straight leads on the symbol's outline.
Number types
64-bit numbers.
Tolerances
A lead end lies on the outline within 0.001 of the symbol's larger side plus 1e-9 (relative to the symbol). Pin coordinates are rounded to 0.001 mm.
Limits
A drawing whose units are not inches, feet, millimeters, centimeters or meters is taken as millimeters.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach