You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.1 KiB
JavaScript
121 lines
3.1 KiB
JavaScript
import { useEffect, useRef, useState } from 'preact/hooks'
|
|
import { simplifyCurve } from '../../lib/math/curves.js'
|
|
|
|
class KnotSimulation {
|
|
constructor(knotRef) {
|
|
this.knotRef = knotRef
|
|
this.ghostPath = null
|
|
}
|
|
|
|
onMouseDrag(x, y) {
|
|
if (!this.ghostPath) {
|
|
this.ghostPath = []
|
|
}
|
|
|
|
this.ghostPath.push([x, y])
|
|
}
|
|
|
|
onMouseUp() {
|
|
this.knotRef.current.points = simplifyCurve(this.ghostPath, 15)
|
|
this.ghostPath = null
|
|
}
|
|
|
|
update() {}
|
|
|
|
/** @param {CanvasRenderingContext2D} g */
|
|
render(g) {
|
|
const w = g.canvas.width
|
|
const h = g.canvas.height
|
|
const knot = this.knotRef.current
|
|
|
|
g.clearRect(0, 0, w, h)
|
|
g.lineWidth = 3
|
|
g.lineCap = 'round'
|
|
g.lineJoin = 'round'
|
|
|
|
g.strokeStyle = '#333'
|
|
if (this.ghostPath && this.ghostPath.length > 0) {
|
|
g.strokeStyle = '#888'
|
|
g.beginPath()
|
|
{
|
|
const [x0, y0] = this.ghostPath[0]
|
|
g.moveTo(x0, y0)
|
|
for (const [x, y] of this.ghostPath) {
|
|
g.lineTo(x, y)
|
|
}
|
|
}
|
|
g.stroke()
|
|
} else if (knot.points.length > 0) {
|
|
g.beginPath()
|
|
const [x0, y0] = knot.points[0]
|
|
g.moveTo(x0, y0)
|
|
|
|
for (const [x, y] of knot.points) {
|
|
g.lineTo(x, y)
|
|
}
|
|
g.stroke()
|
|
|
|
g.fillStyle = '#080'
|
|
for (const [x, y] of knot.points) {
|
|
g.beginPath()
|
|
g.ellipse(x, y, 3, 3, 0, 0, Math.PI * 2)
|
|
g.fill()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export const KnotLayer = ({ knotRef }) => {
|
|
const canvasRef = useRef(null)
|
|
const [knotSim] = useState(() => new KnotSimulation(knotRef))
|
|
|
|
useEffect(() => {
|
|
window.addEventListener('resize', () => {
|
|
// trigger repaint and get a new context
|
|
})
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
let simTimerHandle
|
|
|
|
if (canvasRef.current) {
|
|
canvasRef.current.width = canvasRef.current.offsetWidth
|
|
canvasRef.current.height = canvasRef.current.offsetHeight
|
|
|
|
const g = canvasRef.current.getContext('2d')
|
|
|
|
simTimerHandle = setInterval(() => {
|
|
knotSim.update()
|
|
requestAnimationFrame(() => knotSim.render(g))
|
|
|
|
console.log('Prova')
|
|
}, 1000 / 30)
|
|
}
|
|
|
|
return () => {
|
|
if (simTimerHandle) {
|
|
clearInterval(simTimerHandle)
|
|
}
|
|
}
|
|
}, [canvasRef.current])
|
|
|
|
return (
|
|
<canvas
|
|
ref={canvasRef}
|
|
onMouseDown={e => {
|
|
if (e.buttons === 1) {
|
|
knotSim.onMouseDrag(e.offsetX, e.offsetY)
|
|
}
|
|
}}
|
|
onMouseMove={e => {
|
|
if (e.buttons === 1) {
|
|
knotSim.onMouseDrag(e.offsetX, e.offsetY)
|
|
}
|
|
}}
|
|
onMouseUp={e => {
|
|
knotSim.onMouseUp()
|
|
}}
|
|
/>
|
|
)
|
|
}
|