-
-
Save sufianrhazi/385727c0300d177d8d411cd6bb18e50d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Gooey, { calc, field, mount, ref } from '@srhazi/gooey'; | |
import type { Component, Field } from '@srhazi/gooey'; | |
const NumericInput: Component<{ | |
value: Field<number>; | |
id?: string; | |
type: string; | |
min: string; | |
max: string; | |
}> = ({ value, ...rest }) => ( | |
<input | |
{...rest} | |
value={value} | |
on:input={(e, el) => value.set(el.valueAsNumber)} | |
/> | |
); | |
const Visualization: Component = (props, { onMount }) => { | |
const canvasRef = ref<HTMLCanvasElement>(); | |
let ctx: CanvasRenderingContext2D | null = null; | |
// Input data, just X & Y coordinates | |
const x = field(40); | |
const y = field(30); | |
// Derived data, calculations that show angle & degrees | |
const angle = calc(() => { | |
const baseAngle = Math.atan(y.get() / x.get()); | |
if (x.get() < 0) { | |
return baseAngle + Math.PI; | |
} | |
if (y.get() < 0) { | |
return baseAngle + 2 * Math.PI; | |
} | |
return baseAngle; | |
}); | |
const degrees = calc(() => (angle.get() * 360) / (2 * Math.PI)); | |
// A calculation which is re-evaluated when dependencies change, but doesn't return anything. | |
// This can be used to subscribe to multiple things at once. | |
const render = calc(() => { | |
update({ | |
x: x.get(), | |
y: y.get(), | |
angle: angle.get(), | |
}); | |
}); | |
onMount(() => { | |
if (canvasRef.current) { | |
ctx = canvasRef.current.getContext('2d'); | |
} | |
// By subscribing to our render calculation, it becomes "alive" and is recalculated when the x/y/angle changes | |
return render.subscribe(() => {}); | |
}); | |
const update = ({ | |
x, | |
y, | |
angle, | |
}: { | |
x: number; | |
y: number; | |
angle: number; | |
}) => { | |
if (!ctx) { | |
return; | |
} | |
ctx.fillStyle = '#F4F2F4'; | |
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height); | |
ctx.fill(); | |
ctx.save(); | |
ctx.scale(ctx.canvas.width / 200, ctx.canvas.height / 200); | |
ctx.translate(100, 100); | |
// Draw outline for angle | |
ctx.beginPath(); | |
ctx.save(); | |
ctx.strokeStyle = '#C0C000'; | |
ctx.setLineDash([2.5, 2.5]); | |
ctx.arc(0, 0, 20, 0, 2 * Math.PI, angle > 0); | |
ctx.stroke(); | |
ctx.restore(); | |
// Draw orange arc for angle | |
ctx.beginPath(); | |
ctx.strokeStyle = '#C0C000'; | |
ctx.fillStyle = '#808000'; | |
ctx.arc(0, 0, 20, 0, -angle, angle > 0); | |
ctx.stroke(); | |
ctx.lineTo(0, 0); | |
ctx.fill(); | |
// Draw red line for X axis | |
ctx.beginPath(); | |
ctx.strokeStyle = '#800000'; | |
ctx.fillStyle = '#C00000'; | |
ctx.moveTo(0, 0); | |
ctx.lineTo(0 + x, 0); | |
ctx.stroke(); | |
// Draw green line for Y axis | |
ctx.beginPath(); | |
ctx.strokeStyle = '#008000'; | |
ctx.fillStyle = '#00C000'; | |
ctx.moveTo(0 + x, 0); | |
ctx.lineTo(0 + x, 0 - y); | |
ctx.stroke(); | |
// Draw blue line for hypotenuse | |
ctx.beginPath(); | |
ctx.strokeStyle = '#0000C0'; | |
ctx.fillStyle = '#000080'; | |
ctx.moveTo(0, 0); | |
ctx.lineTo(0 + x, 0 - y); | |
ctx.stroke(); | |
// Red X point | |
ctx.beginPath(); | |
ctx.strokeStyle = '#800000'; | |
ctx.fillStyle = '#C00000'; | |
ctx.arc(0 + x, 0, 4, 0, 2 * Math.PI); | |
ctx.stroke(); | |
ctx.fill(); | |
// Green Y point | |
ctx.beginPath(); | |
ctx.strokeStyle = '#008000'; | |
ctx.fillStyle = '#00C000'; | |
ctx.arc(0 + x, 0 - y, 4, 0, 2 * Math.PI); | |
ctx.stroke(); | |
ctx.fill(); | |
// Blue center point | |
ctx.beginPath(); | |
ctx.strokeStyle = '#0000C0'; | |
ctx.fillStyle = '#000080'; | |
ctx.arc(0, 0, 4, 0, 2 * Math.PI); | |
ctx.stroke(); | |
ctx.fill(); | |
ctx.restore(); | |
}; | |
return ( | |
<div> | |
<style> | |
{` | |
.triangle-canvas { | |
position: relative; | |
cursor: crosshair; | |
width: 400px; | |
height: auto; | |
} | |
.triangle-canvas-controls { | |
display: grid; | |
padding: 16px; | |
gap: 16px; | |
grid-template-columns: auto 1fr; | |
} | |
.triangle-angle-input { | |
text-align: right; | |
} | |
`} | |
</style> | |
<canvas | |
class="triangle-canvas" | |
ref={canvasRef} | |
width="800" | |
height="800" | |
on:mousemove={(e, el) => { | |
const box = el.getBoundingClientRect(); | |
const mouseX = | |
2 * ((e.clientX - box.left) / box.width - 0.5); | |
const mouseY = | |
2 * ((e.clientY - box.top) / box.height - 0.5); | |
x.set(Math.round(mouseX * 1000) / 10); | |
y.set(Math.round(-mouseY * 1000) / 10); | |
}} | |
on:touchmove={(e: TouchEvent, el) => { | |
if (e.targetTouches[0]) { | |
e.preventDefault(); | |
const box = el.getBoundingClientRect(); | |
const touchX = | |
2 * | |
((e.targetTouches[0].clientX - box.left) / | |
box.width - | |
0.5); | |
const touchY = | |
2 * | |
((e.targetTouches[0].clientY - box.top) / | |
box.height - | |
0.5); | |
x.set(Math.round(touchX * 10000) / 10); | |
y.set(Math.round(-touchY * 1000) / 10); | |
} | |
}} | |
/> | |
<fieldset class="triangle-canvas-controls"> | |
<legend>Controls</legend> | |
<label for="x-value">X Value</label> | |
<NumericInput | |
id="x-value" | |
type="number" | |
value={x} | |
min="-100" | |
max="100" | |
/> | |
<span /> | |
<NumericInput type="range" value={x} min="-100" max="100" /> | |
<label for="y-value">Y Value</label> | |
<NumericInput | |
id="y-value" | |
type="number" | |
value={y} | |
min="-100" | |
max="100" | |
/> | |
<span /> | |
<NumericInput type="range" value={y} min="-100" max="100" /> | |
<label for="angle">Angle</label> | |
<input | |
class="triangle-angle-input" | |
id="angle" | |
type="text" | |
value={calc(() => `${angle.get().toFixed(3)} radians`)} | |
readonly | |
/> | |
<label for="degrees">Degrees</label> | |
<input | |
class="triangle-angle-input" | |
id="degrees" | |
type="text" | |
value={calc(() => `${degrees.get().toFixed(3)} degrees`)} | |
readonly | |
/> | |
</fieldset> | |
</div> | |
); | |
}; | |
const angleViz = document.getElementById('angleViz'); | |
if (angleViz) { | |
mount(angleViz, <Visualization />); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment