Skip to main content
OCCT category icon with a stylized logo representation

Drawing Outlines With a Pen

bitbybit.occt.sketch.pen draws a flat outline the way you would describe it in words: start here, go 40 to the right, go up 10, round that corner, curve back smoothly, close. Each command draws on from where the previous one ended. The result is one exact wire, or a face when the outline closes, ready to extrude, revolve or sweep.

Straight pieces come out as true lines and arcs as true circles, so an extruded sketch has planar and cylindrical faces that fillets, selectors and measurements treat exactly.

A first sketch​

const plate = await bitbybit.occt.sketch.pen({
commands: [
{ type: "hLine", id: "base", length: 40 },
{ type: "vLine", id: "right", length: 10 },
{ type: "filletCorner", radius: 2 },
{ type: "tangentArc", id: "nose", to: [-10, 10], relative: true },
{ type: "line", id: "top", to: [0, 20] },
{ type: "close", id: "left" },
],
makeFace: true,
});

const block = await bitbybit.occt.operations.extrude({ shape: plate, direction: [0, 5, 0] });
bitbybit.draw.drawAnyAsync({ entity: block });

makeFace turns the closed outline into a face whose normal is the frame's normal, whichever way the outline runs, so extruding along that normal always builds the solid on the same side. An outline that crosses itself is refused with a message naming the problem, as is any command that cannot be drawn.

The commands​

Every command is a small object with a type, and an optional id that names it in the segments report described below. Points are in the sketch's own x and y. A point is absolute unless relative is true, in which case it is an offset from where the pen is.

typeFieldsDraws
lineto, relativea straight segment to a point
hLinelengthalong the sketch's x axis; negative goes left
vLinelengthalong the sketch's y axis; negative goes down
polarLinelength, angleat an angle in degrees from the x axis, counterclockwise
tangentLinelengthstraight on, in the direction the previous segment ended in
threePointArcthrough, to, relativea circular arc through a middle point
tangentArcto, relativea circular arc leaving tangent to the previous segment
sagittaArcto, sagitta, relativean arc whose middle stands sagitta off the chord; positive bows to the left of travel
bulgeArcto, bulge, relativethe DXF way: bulge is the tangent of a quarter of the swept angle, positive counterclockwise
quadraticcontrol, to, relativea quadratic Bezier curve
cubiccontrol1, control2, to, relativea cubic Bezier curve
closea straight segment back to the start point
filletCornerradiusrounds the corner between the segments before and after it
chamferCornerdistancebevels that corner, cutting distance off each segment

Corner commands sit between the two segments they join. Placed after close, a corner command rounds or bevels the corner at the start point. Corners work between lines and circular arcs in any combination.

In the visual editors, bitbybit.occt.sketch.commands has one builder per command (line, hLine, tangentArc, filletCorner and the rest); collect their outputs into a list and feed it to the pen.

Sketching on a plane​

A sketch lies in the plane of a frame: the frame's direction is the sketch's x axis, and its normal crossed with the direction is the sketch's y axis. Without a frame, the sketch lies on the ground, facing up, with its y axis running along -Z, which reads naturally when you look down from above.

const wallProfile = await bitbybit.occt.sketch.pen({
start: [0, 0],
commands: [
{ type: "hLine", length: 30 },
{ type: "vLine", length: 20 },
{ type: "sagittaArc", to: [0, 20], sagitta: -4 },
{ type: "close" },
],
frame: { origin: [0, 0, 0], normal: [0, 0, 1], direction: [1, 0, 0] },
makeFace: true,
});

Finding the edges a command drew​

bitbybit.occt.sketch.penWithSegments draws the same outline and also reports which edges each command drew, numbered as bitbybit.occt.shapes.edge.getEdges numbers them. Give commands an id to find their edges by name rather than by position, which shifts when a command is added.

const drawn = await bitbybit.occt.sketch.penWithSegments({
commands: [
{ type: "hLine", id: "base", length: 40 },
{ type: "vLine", id: "side", length: 10 },
{ type: "close", id: "slope" },
],
makeFace: true,
});
const slopeEdges = drawn.segments.find(segment => segment.id === "slope")?.edges;

Outlining a wire with a width​

bitbybit.occt.sketch.stroke outlines a wire as if it were drawn with a pen of a given width: a slot from a path, a trace from a centre line. cap finishes the two ends with a flat cut, a half circle or a square end, and join sets how the outline goes around corners. A closed wire gives a ring.

const path = await bitbybit.occt.sketch.pen({
commands: [{ type: "hLine", length: 30 }, { type: "tangentArc", to: [10, 10], relative: true }],
});
const slot = await bitbybit.occt.sketch.stroke({
shape: path,
width: 4,
cap: Bit.Inputs.OCCT.strokeCapEnum.round,
});

Wrapping shapes in a hull​

bitbybit.occt.sketch.hull wraps vertices, lines, circles and circular arcs in the tightest convex outline around them. It is exact: straight where it spans between shapes, following a circle where a circle bulges out. The hull of a few circles is the classic way to draw a lever, a link or a rounded plate.

const pivot = await bitbybit.occt.shapes.wire.createCircleWire({ radius: 8, center: [0, 0, 0], direction: [0, 1, 0] });
const tip = await bitbybit.occt.shapes.wire.createCircleWire({ radius: 4, center: [40, 0, 0], direction: [0, 1, 0] });
const lever = await bitbybit.occt.sketch.hull({ shapes: [pivot, tip] });