Skip to main content

Rib

Rib turns an open sketch line (the spine) into a wall of a given thickness that grows until it meets the surrounding solid. It is the tool for stiffening webs inside shelled parts: draw one line where the web goes, and the rib finds the walls by itself.

In the viewport

  1. Sketch the spine — an open line or chain, usually on the face the rib should stand on, or on a plane through the cavity — and click Rib on the toolbar.
  2. Fill in the dialog. A green ghost shows the wall while you type.
  3. Click Apply.
The Rib dialog with a spine sketch, a thickness of 5 and a 2° draft
  1. 1
    Add / Remove / New
    Add fuses the rib with the solid it grows into — the usual choice. New keeps it separate; pair that with a Scope so the rib still has walls to conform to.
  2. 2
    Spine sketch
    The open line or chain the wall is built on, filled with the last sketch. One line is enough — the rib finds the surrounding walls by itself.
  3. 3
    Thickness
    The wall thickness, centred on the spine. The sign picks the growth direction: positive grows opposite the sketch normal (into the cavity when the sketch sits on a top face), negative grows along it.
  4. 4
    Parallel to sketch plane
    Flips the growth from "perpendicular to the sketch plane" to "in the plane, perpendicular to the spine" — the natural way to draw a rib from its side profile.
  5. 5
    Extend to walls
    Pushes both ends of the spine outward until they blend into the surrounding walls, so a short line still gives a rib that terminates cleanly.
  6. 6
    Draft angle
    Tapers the rib — positive narrows it toward its tip. Only the long wall faces taper; the caps at the spine ends stay vertical.
  7. 7
    Scope
    Which solids the rib conforms to and fuses with. All considers every solid in the scene.
  8. 8
    Apply
    Writes the statement and adds the timeline row; Exit writes nothing.

From one line to a wall

The rib is the only feature that takes an open sketch and works out its own extent:

BeforeBefore modea spine line

A shelled housing and a single 30 mm line drawn on its top face, from the wall inward. That line is the whole input.

AfterAfter moderib(5).draft(2)

A 5-thick wall centred on the line, grown down into the cavity until it meets the floor and the side wall, with a 2° draft.

The housing and the spine
tray.part.js
import { sketch, extrude, shell, rib, fillet, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

// The housing: a 100 x 50 rectangle, extruded 30, shelled from the top and
// rounded inside — the cavity the rib will grow into.
sketch("top", () => {
const sg1 = line([-50, -25], [50, -25]);
const sg2 = line([50, -25], [50, 25]);
const sg3 = line([50, 25], [-50, 25]);
const sg4 = line([-50, 25], [-50, -25]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -25]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 50);
})

const box = extrude(30)
const s = shell(-4, box.endFaces())
fillet(2, s.internalEdges())

// The spine: one open line on the top face, from the wall inward. That single
// line is everything the rib needs.
sketch(box.endFaces(), () => {
const sg5 = line([-46, 0], [-16, 0]);
horizontal(sg5);
});
The complete file
import { sketch, extrude, shell, rib, fillet, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

// The housing: a 100 x 50 rectangle, extruded 30, shelled from the top and
// rounded inside — the cavity the rib will fit.
sketch("top", () => {
const sg1 = line([-50, -25], [50, -25]);
const sg2 = line([50, -25], [50, 25]);
const sg3 = line([50, 25], [-50, 25]);
const sg4 = line([-50, 25], [-50, -25]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -25]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 50);
})

const box = extrude(30)
const s = shell(-4, box.endFaces())
fillet(2, s.internalEdges())

// The spine: one open line on the top face, from the wall inward.
sketch(box.endFaces(), () => {
const sg5 = line([-46, 0], [-16, 0]);
horizontal(sg5);
});

// A 5-thick wall centred on the spine, grown down into the cavity until
// it meets the floor and the side walls, with a 2° draft.
rib(5).draft(2);

The first argument is the wall thickness. The rib is centred on the spine, extruded perpendicular to the sketch plane, and trimmed to the surrounding cavity.

Thickness direction

SignResult
Positive, e.g. 5Grows opposite the sketch normal — into the cavity when the sketch sits on a top face
Negative, e.g. -5Grows along the sketch normal

Extending the spine

By default the rib's length matches the sketch line exactly. Extend pushes both ends outward until they blend into the surrounding walls:

Rib with .extend()

The code behind it
import { sketch, extrude, shell, rib, fillet, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

sketch("top", () => {
const sg1 = line([-50, -25], [50, -25]);
const sg2 = line([50, -25], [50, 25]);
const sg3 = line([50, 25], [-50, 25]);
const sg4 = line([-50, 25], [-50, -25]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -25]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 50);
})

const box = extrude(30)
const s = shell(-4, box.endFaces())
fillet(2, s.internalEdges())

sketch(box.endFaces(), () => {
const sg5 = line([-50, 0], [-20, 0]);
horizontal(sg5);
});

rib(5).extend().draft(2);

Draw a short spine, and the rib still terminates cleanly against the cavity wall.

Parallel mode

Parallel flips the growth direction from "perpendicular to the sketch plane" to "in the plane, perpendicular to the spine". The sketch line itself becomes one face of the wall. This is the natural way to draw a rib from its side profile — a slanted line on the front plane, growing down to meet the shell:

Parallel rib

The code behind it
import { sketch, extrude, shell, rib, fillet, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

sketch("top", () => {
const sg1 = line([-50, -25], [50, -25]);
const sg2 = line([50, -25], [50, 25]);
const sg3 = line([50, 25], [-50, 25]);
const sg4 = line([-50, 25], [-50, -25]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -25]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 50);
})

const box = extrude(30)
const s = shell(-4, box.endFaces())
fillet(2, s.internalEdges())

sketch("front", () => {
// rib guide: a 45° line falling from just inside the shell wall
line([-46, 20], [-31.857864, 5.857864]);
});

rib(5).parallel();

Combine Parallel with Extend to reach the cavity walls at both ends:

Parallel rib with extend and draft

The code behind it
import { sketch, extrude, shell, rib, fillet, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

sketch("top", () => {
const sg1 = line([-50, -50], [50, -50]);
const sg2 = line([50, -50], [50, 50]);
const sg3 = line([50, 50], [-50, 50]);
const sg4 = line([-50, 50], [-50, -50]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -50]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 100);
})

const box = extrude(30)
const s = shell(-4, box.endFaces())
fillet(2, s.internalEdges())

sketch("front", () => {
// rib guide: a 45° line falling from [-40, 20]
line([-40, 20], [-25.857864, 5.857864]);
});

rib(5).parallel().extend().draft(-3);

Draft

A Draft angle tapers the rib. Positive angles narrow it toward its tip, negative angles widen it:

rib(5).draft(2) // 2° taper, narrowing
rib(5).draft(-3) // 3° taper, widening

Only the long wall faces taper — the small cap faces at the spine endpoints stay vertical.

Scoping the rib

By default a rib considers every solid in the scene. Scope limits which solids it conforms to and fuses with — it works on its own, without New:

rib(5).scope(target) // only consider `target` when conforming and fusing

The New tab keeps the rib as a separate solid. Pair it with a scope so the rib still has a cavity to conform to:

Rib scoped to a different solid

The code behind it
import { sketch, extrude, shell, rib, fillet, circle, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

sketch("top", () => {
const sg1 = line([-50, -25], [50, -25]);
const sg2 = line([50, -25], [50, 25]);
const sg3 = line([50, 25], [-50, 25]);
const sg4 = line([-50, 25], [-50, -25]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -25]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 50);
})

const box = extrude(30)
const sh = shell(-4, box.endFaces())
fillet(2, sh.internalEdges())

sketch("top", () => {
circle([0, 0], 30);
});

const s = extrude(50).draft(-5)

sketch("front", () => {
// rib guide: a 45° line rising from [-40, 20]
line([-40, 20], [-25.857864, 34.142136]);
});

rib(5).parallel().extend().new().scope(s).draft(-4);

Here the rib is attached to a tapered cylinder rather than the outer box — the scope tells the rib which walls to conform to.

Curved enclosures

The rib conforms to whatever cavity surrounds it, so it works inside curved shells as well as rectangular ones:

Rib inside a circular cavity

The code behind it
import { sketch, extrude, shell, rib, fillet, circle, line } from 'fluidcad/core';

sketch("top", () => {
circle([0, 0], 80);
})

const box = extrude(30)
const sh = shell(-4, box.endFaces())
const s = fillet(2, sh.internalEdges())

sketch("front", () => {
// rib guide: a 45° line falling from [-40, 20]
line([-40, 20], [-25.857864, 5.857864]);
});

rib(5).parallel().extend().draft(3).new().scope(s);

Patterning ribs

Pass the rib to Repeat to pattern it around an axis:

Circular pattern of ribs

The code behind it
import { sketch, extrude, shell, rib, fillet, circle, repeat, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

sketch("top", () => {
const sg1 = line([-50, -50], [50, -50]);
const sg2 = line([50, -50], [50, 50]);
const sg3 = line([50, 50], [-50, 50]);
const sg4 = line([-50, 50], [-50, -50]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-50, -50]);
distance(sg1.start(), sg1.end(), 100);
distance(sg2.start(), sg2.end(), 100);
})

const box = extrude(30)
const sh = shell(-4, box.endFaces())
fillet(2, sh.internalEdges())

sketch("top", () => {
circle([0, 0], 30);
});

const s = extrude(50).draft(-5)

sketch("front", () => {
// rib guide: a 45° line rising from [-40, 20]
line([-40, 20], [-25.857864, 34.142136]);
});

const r = rib(5).parallel().extend().new().scope(s).draft(-4);

repeat("circular", "z", {
count: 6,
angle: 360
}, r)

The sketch plane can be tilted too — a rotated plane with a circular pattern fans the ribs out at an angle:

Pattern of ribs on a rotated plane

The code behind it
import { sketch, extrude, shell, rib, fillet, repeat, plane, line } from 'fluidcad/core';
import { coincident, distance, fix, horizontal, vertical } from "fluidcad/constraints";

sketch("top", () => {
const sg1 = line([-40, -40], [40, -40]);
const sg2 = line([40, -40], [40, 40]);
const sg3 = line([40, 40], [-40, 40]);
const sg4 = line([-40, 40], [-40, -40]);
coincident(sg1.end(), sg2.start());
coincident(sg2.end(), sg3.start());
coincident(sg3.end(), sg4.start());
coincident(sg4.end(), sg1.start());
horizontal(sg1);
vertical(sg2);
horizontal(sg3);
vertical(sg4);
fix(sg1.start(), [-40, -40]);
distance(sg1.start(), sg1.end(), 80);
distance(sg2.start(), sg2.end(), 80);
})

const box = extrude(30)
const s = shell(-4, box.endFaces())
fillet(2, s.internalEdges())

sketch("top", () => {
const sg5 = line([-15, -15], [15, -15]);
const sg6 = line([15, -15], [15, 15]);
const sg7 = line([15, 15], [-15, 15]);
const sg8 = line([-15, 15], [-15, -15]);
coincident(sg5.end(), sg6.start());
coincident(sg6.end(), sg7.start());
coincident(sg7.end(), sg8.start());
coincident(sg8.end(), sg5.start());
horizontal(sg5);
vertical(sg6);
horizontal(sg7);
vertical(sg8);
fix(sg5.start(), [-15, -15]);
distance(sg5.start(), sg5.end(), 30);
distance(sg6.start(), sg6.end(), 30);
});

extrude(50)

const p = plane("front", { rotateY: 45 })

sketch(p, () => {
// rib guide: a 45° line rising from [-40, 18]
line([-40, 18], [-25.857864, 32.142136]);
});

const r = rib(6).parallel().extend().draft(-1);

repeat("circular", "z", {
count: 4,
angle: 360
}, r)

Accessing geometry

const r = rib(5).extend()

r.startFaces() // profile face at the sketch plane
r.endFaces() // face where the rib meets the cavity wall
r.sideFaces() // long wall faces of the rib
r.capFaces() // small cap faces at the spine endpoints
r.startEdges() // edges on the start faces
r.endEdges() // edges on the end faces
r.sideEdges() // edges along the side walls
r.capEdges() // edges on the cap faces

Fusion scope

rib(5).add() // fuse with all touching solids (default)
rib(5).scope(target) // limit conform + fuse to `target`
rib(5).new().scope(target) // keep as separate solid, conform to `target`