Resources Numerical accuracy Calculation policy

Drawing views of the exact solid

Produces the visible and hidden lines of the front, top, right, isometric and section views as 2D segments in model millimeters.

How the precision is kept

Hidden-line removal is done by OpenCascade on the exact solid. This file then turns each resulting edge into straight segments: a straight edge by its two ends, a curve by sampling. Curved lines in a view are therefore polylines, and their accuracy is set by the sampling step. Coordinates are 64-bit.

Open item. Keep circles and arcs in drawing views as arcs instead of 3° polylines, so curved lines on the sheet are not inside the true curve by up to 0.034 % of the radius.

Calculations under this policy

5 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.

brep-draw.js PartCraft 0.8.1 64-bit 5 calculations Reviewed

Tests bin/test-partcraft.mjs, tools/partcraft-test/test-holetable.mjs

Points along a projected edge

64-bit

Turns one edge of a view into the points its line segments join.

circle: n = max(8, ceil(|t₁ − t₀| ÷ (π ÷ 60))); other curves: n = 96; drop a sample within 1e-7 × size of the chord

Sample parameters are t₀ + (t₁ − t₀) × i ÷ n, computed from the ends each time. A sample is tested against the chord from the last kept point to the following sample. The drawn curve is a polyline; for a circle at 3° steps the chord's sagitta is r × (1 − cos 1.5°).

  • brep-draw.js
  • lines 36 to 58
  • PartCraft 0.8.1
/**
 * Points along an edge: the two ends of a straight one; for curves, even steps in the parameter
 * (at least every 3°-ish of a circle) with the points that sit on a straight run dropped again.
 */
function edgePoints(oc, T, e, size) {
  const c = T(new oc.BRepAdaptor_Curve(e));
  const t0 = c.FirstParameter(), t1 = c.LastParameter();
  const P = (t) => { const p = T(c.EvalD0(t)); return [p.X(), p.Y(), p.Z()]; };
  if (c.GetType() === 'GeomAbs_Line') return [P(t0), P(t1)];
  const n = c.GetType() === 'GeomAbs_Circle' ? Math.max(8, Math.ceil(Math.abs(t1 - t0) / (Math.PI / 60))) : 96;
  const pts = [];
  for (let i = 0; i <= n; i++) pts.push(P(t0 + (t1 - t0) * i / n));
  const tol = size * 1e-7;
  const out = [pts[0]];
  for (let i = 1; i < pts.length - 1; i++) {
    const a = out[out.length - 1], b = pts[i + 1], p = pts[i];
    const ab = V.sub(b, a), l = V.len(ab);
    const off = l > 0 ? V.len(V.cross(V.sub(p, a), ab)) / l : V.len(V.sub(p, a));
    if (off > tol) out.push(p);
  }
  out.push(pts[pts.length - 1]);
  return out;
}

Extent of a view

64-bit

The smallest box around a view's segments, used to place and scale it.

u₀ = min, u₁ = max over segment ends (same for v)

Minimum and maximum of the segment end coordinates; no arithmetic on them. Because curves are polylines, the box of a curved outline can be smaller than the true outline by the sagitta. An empty view gets a 1 × 1 box.

  • brep-draw.js
  • lines 86 to 92
  • PartCraft 0.8.1
/** Extents of a shape seen along a view (from its exact bounding box corners, then the drawn segments). */
function boxOf(segs) {
  let u0 = Infinity, v0 = Infinity, u1 = -Infinity, v1 = -Infinity;
  for (const s of segs) { u0 = Math.min(u0, s[0], s[2]); u1 = Math.max(u1, s[0], s[2]); v0 = Math.min(v0, s[1], s[3]); v1 = Math.max(v1, s[1], s[3]); }
  if (u0 === Infinity) return { u0: 0, v0: 0, u1: 1, v1: 1 };
  return { u0, v0, u1, v1 };
}

Section plane position

64-bit

Places the cutting plane square to a view's direction and builds the box that removes the near half.

o = center − d (center · d) + d × at; box side = 2 × (2 × diagonal + 10)

The plane's point is the bounding-box center moved along the view direction to the stored offset. The cutting box is oversized (twice the diagonal plus 10 mm each way), so its other faces stay clear of the part.

  • brep-draw.js
  • lines 113 to 119
  • PartCraft 0.8.1
const centre = V.mul(V.add(min, max), 0.5);
const at = Number.isFinite(sec.at) ? sec.at : V.dot(centre, view.dir);
const o = V.add(V.sub(centre, V.mul(view.dir, V.dot(centre, view.dir))), V.mul(view.dir, at));
// a box on the viewer's side of the plane, larger than the part
const big = size * 2 + 10;
const ax = T(new oc.gp_Ax2(T(new oc.gp_Pnt(...V.sub(V.sub(o, V.mul(view.right, big)), V.mul(view.up, big)))), T(new oc.gp_Dir(...view.dir)), T(new oc.gp_Dir(...view.right))));
const box = T(T(new oc.BRepPrimAPI_MakeBox(ax, 2 * big, 2 * big, big)).Shape());

Faces in the cutting plane

64-bit

Finds the faces created by the cut, whose outlines bound the hatched area.

||n · d| − 1| ≤ 1e-9 and |(p − o) · d| ≤ 1e-6 × diagonal

The direction test is absolute on unit vectors; the position test scales with the part's diagonal. A face of the part that happens to lie in the cutting plane passes the same test.

  • brep-draw.js
  • lines 134 to 135
  • PartCraft 0.8.1
if (Math.abs(Math.abs(V.dot([n.X(), n.Y(), n.Z()], view.dir)) - 1) > 1e-9) continue;
if (Math.abs(V.dot(V.sub([p.X(), p.Y(), p.Z()], o), view.dir)) > 1e-6 * size) continue;

Cutting-plane line in the neighboring view

64-bit

The point and direction of the line that shows where the section was cut.

along = unit(d_section × d_view); p = (o · right, o · up); dir = (along · right, along · up)

Dot products of 64-bit vectors with the view's axes. The cross product is normalized without a guard; the two view directions are fixed constants in drawing.js.

  • brep-draw.js
  • lines 146 to 149
  • PartCraft 0.8.1
const along = V.norm(V.cross(view.dir, tv.dir));
const p0 = [V.dot(o, tv.right), V.dot(o, tv.up)];
const d2 = [V.dot(along, tv.right), V.dot(along, tv.up)];
const sight = [V.dot(V.mul(view.dir, -1), tv.right), V.dot(V.mul(view.dir, -1), tv.up)];

In detail

Method
OpenCascade HLRBRep with a parallel projector per view; circles sampled at steps of at most π ÷ 60 (3°), other curves at 96 equal parameter steps; samples on a straight run removed.
Number types
64-bit numbers from OpenCascade; segments kept in plain arrays.
Tolerances
A sample is dropped when it lies within 1e-7 × the part's bounding-box diagonal of the chord. A face counts as in the cutting plane when its normal is within 1e-9 of the view direction (as a dot product) and its origin within 1e-6 × the diagonal of the plane.
Limits
A circle sampled every 3° has chords up to r × (1 − cos 1.5°) ≈ 3.4e-4 × r inside the true arc. The view box is taken from the drawn segments.

Approach: 64-bit, stated limit

The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.

Every policy that takes this approach