const { render } = ReactDOM;
const RangeControl = ({
name,
label,
value,
update,
className = '',
...props
}) => (
<div className={`mb4 ${className}`}>
<label htmlFor={name} className="f5 b db mb2 dark-gray">
{label} <span className="gray">({value})</span>
</label>
<input
name={name}
type="range"
className="input-range-rv flex-auto dark-gray w-100"
value={value}
onChange={e => update(name, e.target.value)}
{...props}
/>
</div>
);
const Controls = ({ className, controls, values, update }) => (
<div className={className}>
{controls.map(({ type, ...props }) =>
React.createElement(type, {
value: values[props.name],
key: props.name,
update,
...props,
}),
)}
</div>
);
const ReuleauxPolygon = ({ reuleauxPolygonD, polygonD, className }) => (
<g>
<defs>
<pattern
id="diagonal-stripe"
patternUnits="userSpaceOnUse"
width="10"
height="10"
>
<rect width="10" height="10" stroke="none" fill="#fff" />
<path
d="M-1,1 l2,-2 M0,10 l10,-10 M9,11 l2,-2"
stroke="#333"
strokeWidth="1"
/>
</pattern>
</defs>
<path fill="url(#diagonal-stripe)" d={reuleauxPolygonD} />
<path stroke="#0f0" strokeWidth="4" d={polygonD} />
<path fill="none" strokeWidth="4" d={reuleauxPolygonD} />
</g>
);
const ReuleauxPolygonConstruction = ({ sideCount = 3, className }) => {
const { r, pts, polygon, reuleaux } = reuleauxPolygonConstruction(
[270, 270],
90,
sideCount,
);
return (
<div className={className}>
<svg
viewBox="0 0 540 540"
className="w-100"
fill="none"
stroke="#333"
strokeLinejoin="round"
strokeLinecap="round"
strokeWidth="2"
>
{pts.map(p => (
<circle key={p.join(',')} stroke="#ccc" cx={p[0]} cy={p[1]} r={r} />
))}
<ReuleauxPolygon reuleauxPolygonD={reuleaux} polygonD={polygon} />
{pts.map(p => (
<circle key={p.join(',')} stroke="none" fill="#f0f" cx={p[0]} cy={p[1]} r={4} />
))}
</svg>
</div>
);
};
class App extends React.Component {
state = {
values: {
'side-count': 3,
},
controls: [
{
name: 'side-count',
label: 'Side Count',
type: RangeControl,
min: 3,
max: 11,
step: 2,
},
],
};
update = (name, value) => {
this.setState(state => ({
values: {
...state.values,
[name]: +value,
},
}));
};
render() {
const { controls, values } = this.state;
return (
<div className="code dark-gray mw6 center ph3 vh-100 flex items-center justify-center">
<div className="w-100">
<ReuleauxPolygonConstruction
className="mh3 mb3"
sideCount={values['side-count']}
/>
<Controls
controls={controls}
values={values}
update={this.update}
className=""
/>
</div>
</div>
);
}
}
render(<App />, document.querySelector('#app'));
/**
* Polygon Math
*/
function pts(sideCount, radius) {
const angle = 360 / sideCount;
const vertexIndices = range(sideCount);
const offsetDeg = 90 - (180 - angle) / 2;
const offset = degreesToRadians(offsetDeg);
return vertexIndices.map(index => {
return {
theta: offset + degreesToRadians(angle * index),
r: radius,
};
});
}
function range(count) {
return Array.from(Array(count).keys());
}
function degreesToRadians(angleInDegrees) {
return Math.PI * angleInDegrees / 180;
}
function polygon([cx, cy], sideCount, radius) {
return pts(sideCount, radius).map(({ r, theta }) => [
cx + r * Math.cos(theta),
cy + r * Math.sin(theta),
]);
}
function dist([x1, y1], [x2, y2]) {
return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5;
}
function reuleauxPolygonConstruction([cx, cy], radius, sideCount) {
const pts = polygon([cx, cy], sideCount, radius);
const l = dist(pts[0], pts[2]);
const [o, ...rest] = pts;
return {
r: l,
pts,
polygon: ['M', o[0], o[1], ...rest.map(p => `L ${p[0]} ${p[1]}`), 'Z'].join(
' ',
),
reuleaux: [
'M',
o[0],
o[1],
...rest.map(p => `A ${l} ${l} 0 0 1 ${p[0]} ${p[1]}`),
`A ${l} ${l} 0 0 1 ${o[0]} ${o[1]}`,
'Z',
].join(' '),
};
}
View Compiled