free-body-diagram directive#
The free-body-diagram directive composes a physics free-body diagram —
an object outline, force vectors with correct points of attack, and a small
corner axis indicator — from a compact key-value syntax. It does not draw
anything itself: it generates ordinary plot primitives
(polygon, circle, vector, text) and delegates all rendering, caching,
and figure options (width, align, name, captions, …) to the plot
directive, so anything plot supports for those is available here too.
Basic usage#
:::{free-body-diagram}
object: ball, size=1.6, position=(0, 0), color=teal
velocity: angle=30
force: gravity, length=0.5, name="$\vec G$"
force: normal, length=0.4, name="$\vec N$"
force: friction, length=0.25, name="$\vec R$"
force: air-resistance, length=0.3, name="$\vec L$"
width: 60%
:::
object:is required, exactly once — the thing the diagram is drawn around.force:is required, at least once — one line per force vector.velocity:is optional — it only matters for forces whose direction depends on the direction of motion (friction,air-resistance).
Syntax overview#
object: ball|square|toy-car, size=1, position=(0, 0), color=.., alpha=..
velocity: angle=<degrees> # or: velocity: (dx, dy)
force: <kind>, length=.., name="$..$", color=.., point=(x, y), direction=..
axis-indicator: true|false # default true
Every other key: value line (width, align, name, fontsize, lw,
class, nocache, usetex, handdrawn, xstep, ystep, ticks, grid, …)
is forwarded straight to the delegated plot directive — see
the plot directive’s global options for the full
list. xmin/xmax/ymin/ymax are computed automatically from the object
and force geometry (with padding), but an explicit value here overrides that.
Lines after the first blank line become the figure caption, exactly like in
plot.
Objects#
|
|
|
|---|---|---|
|
diameter |
the center of the ball (and its center of mass) |
|
side length |
the center of the square |
|
overall body length |
the car’s center of mass (a simple schematic: one body rectangle plus two wheels — nothing more detailed is drawn) |
Contact forces (normal, friction) always attach to a point that actually
sits on the object: the bottom edge for ball/square, and one specific
wheel — not the empty gap between the two wheels — for toy-car.
position defaults to (0, 0); color defaults to black; alpha (fill
opacity) is optional and uses plot’s own polygon/circle default when
omitted.
:::{free-body-diagram}
object: toy-car, size=2, color=orange
velocity: (1, 0)
force: gravity, length=0.35, name="$\vec G$"
force: normal, length=0.35, name="$\vec N$"
force: friction, length=0.3, name="$\vec R$"
force: air-resistance, length=0.4, name="$\vec L$"
width: 60%
:::
which yields:
Forces#
Every force: line needs a length= and picks one of five kinds. Four of
them have a standard default point of attack and direction, derived from the
object’s geometry and, where relevant, the velocity: line:
|
Default point |
Default direction |
Default |
Default |
|---|---|---|---|---|
|
the object’s center of mass |
straight down |
|
|
|
the contact point with the ground |
straight up |
|
|
|
the contact point with the ground |
opposes the horizontal component of |
|
|
|
the point of the object facing the direction of motion |
opposite |
|
|
|
none — |
none — |
none |
|
friction and air-resistance need a velocity: line to compute their
default point/direction; without one, give them an explicit point= and/or
direction= instead (kinetic friction’s direction in particular is genuinely
case-dependent, so it is never guessed silently).
Any force — standard or custom — accepts these overrides:
point=(x, y)— replace the computed attachment point.direction=<degrees>ordirection=(dx, dy)— replace the computed direction (degrees are measured counter-clockwise from the positive x-axis).color=— any colorplotunderstands.name=— the label text (plain text or$math$); omit it for no label.offset=— how far to nudge the drawn vector away from the force’s true point of attack (see below); setoffset=0to draw it right on the point.
Every force’s true point of attack on the object is always marked with a
small black dot, linked by a short dotted leader segment to where its arrow
is actually drawn. By default this offset grows automatically for forces
that share the same line of action — most commonly gravity and the normal
force, which are collinear for any object resting symmetrically on flat
ground — so the arrows read as distinct parallel vectors instead of
stacking on top of each other, the same convention used in physics
textbooks for concurrent/collinear forces. Give a force its own offset=
to control this directly instead, including offset=0 for a force that
is already clearly visible without any nudge.
Tip
Pick length= noticeably smaller than the object’s own size= (roughly
0.2–0.5×) for the cleanest-looking arrows — a force vector longer than the
object it acts on will visually cross through the object’s own outline.
Velocity and automatic directions#
velocity: sets the direction of motion used by friction and
air-resistance’s defaults:
velocity: angle=20 # degrees, counter-clockwise from +x
velocity: (1, 0.3) # an explicit (dx, dy) direction
Only its direction matters (it is normalized internally), so (2, 0) and
(1, 0) are equivalent.
Custom forces#
Use force: custom for anything outside the standard four — tension, an
applied push or pull, a spring force, and so on. Both point= and
direction= are required since there is no sensible default:
:::{free-body-diagram}
object: square, size=1.4, color=purple, alpha=0.15
force: gravity, length=0.6, name="$\vec G$"
force: custom, length=0.5, name="$\vec T$", point=(0, 0.7), direction=90, color=green
:::
which yields:
Axis indicator#
By default, a small pair of labeled arrows (”\(x\)”/”\(y\)”) is drawn in the
bottom-left corner of the figure to indicate the positive axis directions —
the main plot’s own axes are hidden (axis: off) so they don’t clutter the
diagram, and axis: equal keeps the object’s real proportions (a ball
actually looks circular). Disable it with:
axis-indicator: false
Limits#
Only a horizontal ground is supported (no incline/
surface-angleoption yet) —normalalways points straight up andfrictionalways acts along the x-axis.size=,position=,length=,point=, anddirection=take plain numbers, not full SymPy expressions (unlike the underlyingplotprimitives this directive generates).