bone

v0.1.0

A package to draw structural analysis and kinematic diagrams

engineeringphysicsdrawing
RepositoryCodeberg
LicenseMPL-2.0
Typst Universebone
Version0.1.0
Last update25 Sep 2026

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.

WarningThis project is still in early stage. Only the 2D representation and basic functionalities are working, but work for 3D schematics will soon be on its way. You can follow #4 for more information on the progress.

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")

})
Made with Manifesto from Typst Universe