import * as React from 'react';
import { CanvasPath, Point } from '../types';
export type SvgPathProps = {
// List of points to create the stroke
paths: Point[];
// Unique ID
id: string;
// Width of the stroke
strokeWidth: number;
// Color of the stroke
strokeColor: string;
// Bezier command to smoothen the line
command?: (point: Point, i: number, a: Point[]) => string;
};
/**
* Generate SVG Path tag from the given points
*/
export const SvgPath = ({
paths,
id,
strokeWidth,
strokeColor,
command = bezierCommand,
}: SvgPathProps): JSX.Element => {
if (paths.length === 1) {
const { x, y } = paths[0];
const radius = strokeWidth / 2;
return (
);
}
const d = paths.reduce(
(acc, point, i, a) =>
i === 0 ? `M ${point.x},${point.y}` : `${acc} ${command(point, i, a)}`,
''
);
return (
);
};
export const line = (pointA: Point, pointB: Point) => {
const lengthX = pointB.x - pointA.x;
const lengthY = pointB.y - pointA.y;
return {
length: Math.sqrt(lengthX ** 2 + lengthY ** 2),
angle: Math.atan2(lengthY, lengthX),
};
};
type ControlPoints = {
current: Point;
previous?: Point;
next?: Point;
reverse?: boolean;
};
const controlPoint = (controlPoints: ControlPoints): [number, number] => {
const { current, next, previous, reverse } = controlPoints;
const p = previous || current;
const n = next || current;
const smoothing = 0.2;
const o = line(p, n);
const angle = o.angle + (reverse ? Math.PI : 0);
const length = o.length * smoothing;
const x = current.x + Math.cos(angle) * length;
const y = current.y + Math.sin(angle) * length;
return [x, y];
};
export const bezierCommand = (point: Point, i: number, a: Point[]): string => {
let cpsX = null;
let cpsY = null;
switch (i) {
case 0:
[cpsX, cpsY] = controlPoint({
current: point,
});
break;
case 1:
[cpsX, cpsY] = controlPoint({
current: a[i - 1],
next: point,
});
break;
default:
[cpsX, cpsY] = controlPoint({
current: a[i - 1],
previous: a[i - 2],
next: point,
});
break;
}
const [cpeX, cpeY] = controlPoint({
current: point,
previous: a[i - 1],
next: a[i + 1],
reverse: true,
});
return `C ${cpsX},${cpsY} ${cpeX},${cpeY} ${point.x}, ${point.y}`;
};
type PathProps = {
id: string;
paths: CanvasPath[];
};
const Paths = ({ id, paths }: PathProps): JSX.Element => (
{paths.map((path: CanvasPath, index: number) => (
))}
);
export default Paths;