Skip to content

Instantly share code, notes, and snippets.

@josephg
Created April 7, 2019 08:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josephg/0cd4764581b6276d8003508da266a451 to your computer and use it in GitHub Desktop.
Save josephg/0cd4764581b6276d8003508da266a451 to your computer and use it in GitHub Desktop.
sammy arms
// Avaliable globals:
// ctx (rendering context)
// width, height (the width and height of the window)
const TAU = Math.PI * 2
const circle = (cx, cy, r) => {
ctx.arc(cx, cy, r, 0, TAU)
}
const mouse = {x:0, y:0}
const dist2 = (x, y) => (x*x) + (y*y)
const dist = (x, y) => Math.sqrt(dist2(x, y))
// The shoulders are at y=0.
const shoulders = [-300, 300]
const armLengths = 250
const armWidth = 4
const xyToAngle = (x, y) => { // xy relative to shoulder
// First we figure out the distance from the hand to the shoulder
const s = dist(x, y)
if (s > 2*armLengths) return NaN
// Get the inner angle of the right angled triangle with a=s/2, hyp=armLengths
const a = Math.acos(s / 2 / armLengths)
return a
}
const xyToArms = (x, y) => {
const a1 = xyToAngle(x-shoulders[0], y)
const a2 = xyToAngle(x-shoulders[1], y)
// Its impossible to find shoulder angles
if (isNaN(a1) || isNaN(a2)) return null
return [a1, a2]
}
const offset = [width/2, height * 3/4]
const drawArm = (s, a, m) => {
// To draw we need the shoulder inner angles from shoudler to shoulder to hand.
const ss = Math.atan2(mouse.y, s - mouse.x)
ctx.save()
ctx.translate(s, 0)
ctx.rotate(Math.PI - (ss + a * m))
ctx.fillStyle = 'aquamarine'
ctx.fillRect(0, -armWidth/2, armLengths, armWidth)
ctx.translate(armLengths, 0)
ctx.beginPath()
circle(0, 0, armWidth/2)
ctx.fill()
ctx.rotate(a*2 * m)
ctx.fillRect(0, -armWidth/2, armLengths, armWidth)
ctx.restore()
}
function frame() {
ctx.fillStyle = 'cadetblue';
ctx.fillRect(0, 0, width, height);
ctx.save()
ctx.translate(offset[0], offset[1])
ctx.strokeStyle = 'white'
ctx.beginPath()
circle(shoulders[0], 0, 10)
ctx.stroke()
ctx.beginPath()
circle(shoulders[1], 0, 10)
ctx.stroke()
const arms = xyToArms(mouse.x, mouse.y)
if (arms != null) {
[a1, a2] = arms
drawArm(shoulders[0], a1, 1)
drawArm(shoulders[1], a2, -1)
}
for (const s of shoulders) {
const r = dist(mouse.x - s, mouse.y)
ctx.beginPath()
circle(s, 0, r)
ctx.strokeStyle = r > 2*armLengths ? 'red' : 'blue'
ctx.stroke()
}
//ctx.fillStyle = 'black'
//if (mouse) ctx.fillRect(mouse.x-5, mouse.y-5, 100, 100)
ctx.restore()
}
canvas.onmousemove = (e) => {
e.preventDefault()
mouse.x = e.offsetX - offset[0]
mouse.y = e.offsetY - offset[1]
requestAnimationFrame(frame)
}
canvas.ontouchmove = e => {
e.preventDefault()
//console.log(e)
mouse.x = e.layerX - offset[0]
mouse.y = e.layerY - offset[1]
requestAnimationFrame(frame)
}
frame();
<!DOCTYPE html>
<title>Sammy arms</title>
<meta charset='utf-8'>
<style>
html { height: 100%; }
body {
margin: 0;
background-color: #eee;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
canvas {
width: 800px;
height: 600px;
border-radius: 30px;
box-shadow: 0 0 10px #555;
}
</style>
<canvas></canvas>
<script>
var canvas = document.getElementsByTagName('canvas')[0];
var width, height;
canvas.width = (width = canvas.clientWidth) * devicePixelRatio;
canvas.height = (height = canvas.clientHeight) * devicePixelRatio;
var ctx = canvas.getContext('2d');
ctx.scale(devicePixelRatio, devicePixelRatio); // For high dpi display support
</script>
<script src="game.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment