Introduction
This project was created during my studies at EPFL in Switzerland 🇨🇭, and I was inspired by the diagrams look from stanli by Jürgen Hackl. Bone is also inspired from standards and courses I got during my studies.
Example
The following example demonstrates the capabilities of this library. The code is available on the Codeberg repository, and the original drawing in LaTeX made using stanli is available on CTAN (TeX Archive Network).
Getting started
Bone has been thought from the beginning to be as easy to use as stanli was for LaTeX, except you can just import it in your documents using the Typst Universe registry. No need to download it manually.
#import "@preview/bone:0.1.0"
#bone.diagram({
import bone: *
beam("d", (0, 0), (0, 2))
frame("f1", (0, 0))
})Note that you are free to include bone directly into any CeTZ drawing you would like. The diagram here is just a cover for the native canvas.
Supports
You have multiple supports at your disposal: Fixed, Pinned, and Roller.
#bone.diagram({
import bone: *
beam((2, 0), (2, 2), dx: .5)
support((2, 0))
support2((4, 0.6))
support3((6, 0.6))
})Beams
Beams can be used to represent anything from a segment of a robot arm to a simply supported bridge beam or column with any given profile (I or T shaped). They are drawn between two points, and support bending as well as custom bending positions
#bone.diagram({
import bone: *
beam((2, 0), (2, 2), dx: .5)
support((2, 0))
beam((4, 0), (4.05, 1), (4,2), dx: .5)
support((4, 0))
})#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("b", (2, 0))
beam("a","b")
support2("a")
support3("b")
})Joints
Bone supports out of the box three kinds of rotational joints, including 1 DOF (simple pivot), 2 DOF (cardan/standard pivot) and 3 DOF pivots (spherical pivot).
#bone.diagram({
import bone: *
hinge((0, 0))
spherical((1, 0))
})It also supports linear joints, such as 1 DOF (prismatic joints).
Forces and moments
Forces are represented by arrows displayed at specific points, and moments are represented by semicircles. Reaction forces and moments are also supported.
force(p,length:0.5,distance 0.5, direction:0deg)
moment(p, start: 0deg, delta: 270deg, radius: 0.5, ccw: true)
momentr(p, start: 0deg, delta: 270deg, radius: 0.5, ccw: true)#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("b", (3, 0))
support("a", angle: -90deg)
beam("a", "b")
forcer("a", length: 1, distance:0.8, direction:-90deg)
force("b", direction: 90deg)#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("am", (-1, 0))
draw.anchor("b", (3, 0))
draw.anchor("al", (-0.95, -0.4))
draw.anchor("bl", (3.85, 0))
support("a", angle: -90deg)
beam("a", "b")
moment("b")
momentr("am",start:-90deg,delta:-270deg,radius:0.5,ccw:false)
label("al",[$M_R$])
label("bl",[$M$])
})Distributed forces
#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("b", (2, 1))
draw.anchor("c", (5, 1))
beam("a", "b", "c")
pdforce("a", "b")
tdforce("b", "c", length: 0.5, length2: 2, distance: 0)
})Dimensioning
You will often want to add different dimensions to some parts of your diagrams. To do this, just use xdim/ydim, it will add a line with the given value projected onto the corresponding axis.
#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("b", (2, 1))
draw.anchor("c", (5, 0))
beam("a", "b", "c")
xdim("a", "b")[$L_1$]
xdim("b", "c")[$L_2$]
ydim("a", "b")[$h$]
dim("a", "c", distance: -0.4)[$Delta x$]
})Annotations
Bone provides a few functions adapted from cetz, to display any information of any kind (content, text, image, etc.).
#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
hinge("a")
label("a", padding: 9pt)[f]
})Examples
#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("b", (3, 0))
draw.anchor("bl", (3, -0.25))
draw.anchor("bfx", (3, -0.25))
draw.anchor("c", (7, 0))
draw.anchor("cl", (7, -0.25))
draw.anchor("d", (9, 0))
label("bl", anchor: "west", padding: 15pt)[$A$]
label("cl", anchor: "west", padding: 15pt)[$B$]
beam("a","b","c","d", thickness: 10pt)
dforce("a","d", distance:0)
support2("b")
support3("c")
xdim("a", "b", distance: -1.5)[$3m$]
xdim("b", "c", distance: -1.5)[$4m$]
xdim("c", "d", distance: -1.5)[$2m$]
forcer("b",distance: 2, direction: -90deg)
label((3,-2.5),anchor: "west")[$A_y$]
forcer("bfx",distance: 0.5, direction: 180deg)
label((2,-0.5),anchor: "east")[$A_x$]
forcer("c",distance: 2, direction: -90deg)
label((7,-2.5),anchor: "west")[$B_y$]
})#let sc = 0.667
#let pad = 10pt
#bone.diagram({
import bone: *
draw.anchor("a", (0, 0))
draw.anchor("b", (6*sc, 0))
draw.anchor("c", (12*sc, 0))
draw.anchor("d", (3*sc, 4*sc))
draw.anchor("e", (9*sc, 4*sc))
//label("bl", anchor: "west", padding: 15pt)[$A$]
//label("cl", anchor: "west", padding: 15pt)[$B$]
beam("a","b","c","e","b","d","a","d","e", thickness: 10pt)
support2("a")
support3("c")
forcer("a", direction: -90deg, distance: 1.2)
forcer("a", direction: 0deg, distance: -1.1)
forcer("c", direction: -90deg, distance: 1.2)
force("b",distance:-1)
force("e",direction: 180deg, distance:-1)
force("d",distance:0.2)
label("a",[$A$],anchor: "south-east", padding: pad)
label("a",[$A_x$],anchor:"east", padding: pad*3.5)
label("a",[$A_y$],anchor:"north", padding: pad*6.5)
label("b",[$B$],anchor: "north-west", padding: pad)
label("c",[$C$],anchor: "west", padding: pad)
label("c",[$C_y$],anchor:"north", padding: pad*6.5)
label("d",[$D$],anchor: "south-west", padding: pad)
label("e",[$E$],anchor: "south-west", padding: pad)
hinge("b")
hinge("d")
hinge("e")
})