@moyotsukai/bezier
A tool for drawing bezier curves.
This library is for React projects in local environments.
Quick Start
npm i @moyotsukai/bezier
import Bezier from '@moyotsukai/bezier'
const App: React.FC = () => {
const path = Bezier.spline({
start: { x: 100, y: 400 },
points: [
{
end: { eaa: 15, eal: 400 },
controls: { sca: 30, scl: 100, eca: 45, ecl: 120 }
}
]
})
return (
<Bezier.Root>
<Bezier.Svg
splines={[path]}
/>
</Bezier.Root>
)
}
export default App
Drawing Bezier Curves
A bezier curve consists of the following four points.
Defining the Start Point
Give the coordinate to define the start point.
const CENTER: Bezier.Vec2 = { x: 400, y: 300 }
const path = Bezier.spline({
start: CENTER,
...
})
Defining the End Point
Option 1: Give the coordinate.
const path = Bezier.spline({
start: CENTER,
points: [
{
end: { x: 600, y: 100 },
...
}
]
})
Option 2: Define the end point using end-anchor-angle (eaa
) and end-anchor-length (eal
).
const path = Bezier.spline({
start: CENTER,
points: [
{
end: { eaa: 35, eal: 300 },
...
}
]
})
Defining Control Points
Option 1: Give the coordinate for start-control (sc
) and end-control (ec
).
const path = Bezier.spline({
start: CENTER,
points: [
{
end: { eaa: 35, eal: 300 },
controls: { sc: { x: 450, y: 120 }, ec: { x: 550, y: 90 } }
}
]
})
Option 2: Define the control points using start-control-angle (sca
), start-control-length (scl
), end-control-angle (eca
), and end-control-length (ecl
).
const path = Bezier.spline({
start: CENTER,
points: [
{
end: { eaa: 35, eal: 300 },
controls: { sca: 30, scl: 200, eca: 50, ecl: 100 }
}
]
})
Option 3: Define the control points using control-midpoint-angle (cma
), control-midpoint-length (cml
), ratio of control-distance to end-anchor-length (cdr
), and control-distance-angle (cda
).
const path = Bezier.spline({
start: CENTER,
points: [
{
end: { eaa: 35, eal: 800 * SCALE },
controls: { cma: 30, cml: 120, cdr: 0.3, cda: -15 }
}
]
})
API Reference
Bezier.Vec2
type Vec2 = {
x: number
y: number
}
Bezier.Points
type Points = BezierPoints
type BezierPoints = {
startAnchor: Vec2
startControl: Vec2,
endControl: Vec2,
endAnchor: Vec2,
anchorMidpoint: Vec2,
controlMidpoint: Vec2
}
Bezier.Spline
type Spline = BezierSpline
class BezierSpline {
private _paths
private _stroke
private _fill
constructor(paths: BezierPoints[], style?: BezierStyleProps)
get paths(): BezierPoints[]
get stroke(): string | null
get fill(): string | null
get startAnchor(): Vec2
get endAnchor(): Vec2
rotate({ center, angle }: {
center: Vec2
angle: number
}): BezierSpline
mirror({ center, angle }: {
center: Vec2
angle: number
}): BezierSpline
translate({ angle, distance }: {
angle: number
distance: number
}): BezierSpline
}
Bezier.spline()
function spline(props: ControlPointsProps, style?: StyleProps): BezierSpline
Bezier.ControlPointsProps
type ControlPointsProps = BezierControlPointsProps
type BezierControlPointsProps = {
start: Vec2,
points: {
end: EndPointAngleParameters | Vec2,
controls: ControlPointsAngleParameters | ControlPointsMidpointParameters | ControlPointsVec2Parameters
}[]
}
//eaa: end anchor angle
//eal: end anchor length
type EndPointAngleParameters = {
eaa: number,
eal: number
}
//sca: start control angle
//scl: start control length
//eca: end control angle
//ecl: end control length
type ControlPointsAngleParameters = {
sca: number | "smooth"
scl: number
eca: number
ecl: number
}
//cma: control midpoint angle
//cml: control midpoint length
//cdr: ratio of control distance to end anchor length
//cda: control distance angle
type ControlPointsMidpointParameters = {
cma: number,
cml: number,
cdr: number | "smooth",
cda: number
}
//sc: start control point
//ec: end control point
type ControlPointsVec2Parameters = {
sc: Vec2,
ec: Vec2
}
Bezier.StyleProps
type StyleProps = BezierStyleProps
type BezierStyleProps = {
stroke?: string | null
fill?: string | null
}
<Bezier.Root>
Props
{
fileName?: string
children: React.ReactNode
}
<Bezier.Svg />
Props
{
splines: BezierSpline[],
width?: number,
height?: number,
expandCanvasToEdge?: boolean,
shouGuide?: boolean
}
Bezier.getStartAnchor()
function getStartAnchor(paths: BezierPoints[]): Vec2
Bezier.getEndAnchor()
function getEndAnchor(paths: BezierPoints[]): Vec2
Bezier.mirrorPath()
function mirrorPath({
center: Vec2,
angle: number,
path: BezierPoints[]
}): BezierPoints[]
Bezier.rotatePath()
function rotatePath({
center: Vec2,
angle: number,
path: BezierPoints[]
}): BezierPoints[]
Bezier.translatePath()
function translatePath({
angle: number,
distance: number,
path: BezierPoints[]
}): BezierPoints[]
Bezier.absoluteAngle()
function absoluteAngle({
start: Vec2,
end: Vec2
}): number
Bezier.distance()
function distance(a: Vec2, b: Vec2): number
Bezier.inferLine()
function inferLine({
point: Vec2,
angle: number
}): [Vec2, Vec2]
Bezier.intersection()
function intersection(ab: [Vec2, Vec2], cd: [Vec2, Vec2]): Vec2 | null
Bezier.midpoint()
function midpoint(a: Vec2, b: Vec2): Vec2
Example
import Bezier from '@moyotsukai/bezier'
const NUM = 8
const CENTER: Bezier.Vec2 = { x: 400, y: 300 }
const SCALE = 0.3
const Example: React.FC = () => {
const path_1 = Bezier.spline({
start: { x: CENTER.x + 5, y: CENTER.y - 25 },
points: [
{
end: { eaa: 90, eal: 800 * SCALE },
controls: { cma: -60, cml: -200 * SCALE, cdr: 0.3, cda: 60 }
}
]
}, {
stroke: "red"
})
const path_2 = Bezier.spline({
start: { x: CENTER.x - 5, y: CENTER.y - 25 },
points: [
{
end: { eaa: 85, eal: 500 * SCALE },
controls: { cma: -30, cml: -50 * SCALE, cdr: 0.5, cda: 30 }
},
{
end: { eaa: 130, eal: 300 * SCALE },
controls: { sca: "smooth", scl: 120 * SCALE, eca: -30, ecl: 80 * SCALE }
}
]
}, {
stroke: "blue"
})
const path_3 = Bezier.spline({
start: path_1.endAnchor,
points: [
{
end: path_2.endAnchor,
controls: { sca: 0, scl: 0, eca: 0, ecl: 0 }
}
]
})
const splines: Bezier.Spline[] = Array(NUM).fill(0).map((_, index) => {
return [
path_1.rotate({ center: CENTER, angle: 360 / NUM * index }),
path_2.rotate({ center: CENTER, angle: 360 / NUM * index }),
path_3.rotate({ center: CENTER, angle: 360 / NUM * index }),
Bezier.spline({
start: path_2.rotate({ center: CENTER, angle: 360 / NUM * index }).endAnchor,
points: [
{
end: path_1.rotate({ center: CENTER, angle: 360 / NUM * (index + 1) }).endAnchor,
controls: { sca: 0, scl: 0, eca: 0, ecl: 0 }
}
]
})
]
}).flat()
return (
<Bezier.Svg
splines={splines}
expandCanvasToEdge={true}
/>
)
}
export default Example