Pulley System
The Bluefish gallery pulley example: distributed pulleys, path-based weights, and fractional line anchors for the ropes
import { Graphic } from "@modular-svg/react";
import * as React from "react";
const R = 25;
const W2JUT = 10;
// A pulley: a large wheel with an axle dot, centered together
function PulleyCircle({ id }: { id: string }) {
return (
<align key={id} alignment="center">
<circle r={R} stroke="#828282" stroke-width={3} fill="#C1C1C1" />
<circle r={5} fill="#555555" />
</align>
);
}
// A trapezoid weight with its label centered on it
function Weight({
width,
height,
children,
}: {
width: number;
height: number;
children: string;
}) {
return (
<align alignment="center">
<path
d={`M 10,0 l ${width - 20},0 l 10,${height} l ${-width},0 Z`}
fill="#545454"
stroke="#545454"
/>
{/* effective dims: measured at 14px, height from the 10px prop */}
<text font-size={10} fill="white" width={children.length * 7} height={10}>
{children}
</text>
</align>
);
}
// TypeScript resolves the line tag to SVG's (whose target is a string), so
// ropes go through createElement - the reconciler sees the same line tag
function Rope(props: {
id: string;
from: string;
to: string;
source?: number[];
target?: number[];
}) {
return React.createElement(
"line",
{
key: props.id,
stroke: "#774e32",
source: props.source,
target: props.target,
},
React.createElement("ref", { target: props.from }),
React.createElement("ref", { target: props.to }),
);
}
function RopeLabel({ id, children }: { id: string; children: string }) {
return (
<text key={id} font-size={14}>
{children}
</text>
);
}
export function PulleyDiagram() {
return (
<Graphic
style={{
border: "1px solid #ddd",
borderRadius: "8px",
display: "inline-block",
}}
margin={15}
>
<group>
<rect
key="rect"
height={20}
width={9 * R}
fill="#C9C9C9"
stroke-width={2}
/>
<PulleyCircle id="A" />
<PulleyCircle id="B" />
<PulleyCircle id="C" />
<distribute axis="x" spacing={-R}>
<ref target="A" />
<ref target="B" />
</distribute>
<distribute axis="x" spacing={0}>
<ref target="B" />
<ref target="C" />
</distribute>
<distribute axis="y" spacing={40}>
<ref target="rect" />
<ref target="B" />
</distribute>
<distribute axis="y" spacing={30}>
<ref target="B" />
<ref target="A" />
</distribute>
<distribute axis="y" spacing={50}>
<ref target="B" />
<ref target="C" />
</distribute>
<group key="G">
<ref target="A" />
<ref target="B" />
<ref target="C" />
</group>
<align alignment="centerX">
<ref target="rect" />
<ref target="G" />
</align>
<align alignment="center">
<ref target="B" />
<RopeLabel id="tB">B</RopeLabel>
</align>
<align alignment="center">
<ref target="A" />
<RopeLabel id="tA">A</RopeLabel>
</align>
<align alignment="center">
<ref target="C" />
<RopeLabel id="tC">C</RopeLabel>
</align>
<Rope id="l1" from="B" to="A" source={[0, 0.5]} target={[0.5, 0.5]} />
<Rope id="l2" from="B" to="C" source={[1, 0.5]} target={[0, 0.5]} />
<Rope id="l3" from="rect" to="C" target={[1, 0.5]} />
<stackH spacing={5}>
<ref target="l1" />
<RopeLabel id="t1">x</RopeLabel>
</stackH>
<distribute axis="x" spacing={5}>
<ref target="l2" />
<RopeLabel id="t2">y</RopeLabel>
</distribute>
<distribute axis="x" spacing={5}>
<ref target="l3" />
<RopeLabel id="t3">z</RopeLabel>
</distribute>
<align alignment="centerY">
<ref target="t1" />
<ref target="t2" />
<ref target="t3" />
</align>
<stackH key="w1">
<Weight width={30} height={30}>
W1
</Weight>
<rect fill="transparent" width={R * 2 - 10} stroke-width={0} />
</stackH>
<stackH key="w2">
<rect
fill="transparent"
width={R + (R / 2 - 10) - W2JUT / 2}
stroke-width={0}
/>
<Weight width={R * 3 + W2JUT} height={30}>
W2
</Weight>
</stackH>
<distribute axis="y" spacing={50}>
<ref target="C" />
<ref target="w2" />
</distribute>
<align alignment="left">
<ref target="A" />
<ref target="w2" />
</align>
<align alignment="centerX">
<ref target="A" />
<ref target="w1" />
</align>
{/* w1 rides with w2 (in Bluefish an undefined-height rect
disqualifies w1 as the anchor; here the anchor goes first) */}
<align alignment="centerY">
<ref target="w2" />
<ref target="w1" />
</align>
<Rope id="l4" from="A" to="w1" source={[0, 0.5]} />
<Rope id="l5" from="A" to="w2" source={[1, 0.5]} />
<Rope id="l6" from="C" to="w2" source={[0.5, 0.5]} />
<distribute axis="x" spacing={5}>
<ref target="l4" />
<RopeLabel id="t4">p</RopeLabel>
</distribute>
<distribute axis="x" spacing={5}>
<ref target="l5" />
<RopeLabel id="t5">q</RopeLabel>
</distribute>
<stackH spacing={5}>
<ref target="l6" />
<RopeLabel id="t6">s</RopeLabel>
</stackH>
<align alignment="centerY">
<ref target="t6" />
<ref target="t5" />
<ref target="t4" />
</align>
{/* Overdraws to make the diagram pretty */}
<PulleyCircle id="Acopy" />
<PulleyCircle id="Ccopy" />
<align alignment="center">
<ref target="A" />
<ref target="Acopy" />
</align>
<align alignment="center">
<ref target="C" />
<ref target="Ccopy" />
</align>
<Rope
id="l1copy"
from="B"
to="A"
source={[0, 0.5]}
target={[0.5, 0.5]}
/>
<PulleyCircle id="Bcopy" />
<align alignment="center">
<ref target="B" />
<ref target="Bcopy" />
</align>
<Rope id="l0" from="rect" to="B" target={[0.5, 0.5]} />
<Rope id="l6copy" from="C" to="w2" source={[0.5, 0.5]} />
</group>
</Graphic>
);
}