Skip to content

Instantly share code, notes, and snippets.

@jzting
Created February 3, 2021 14:26
Show Gist options
  • Save jzting/dbf964cc7d0726d3f032cae1e5a3fa64 to your computer and use it in GitHub Desktop.
Save jzting/dbf964cc7d0726d3f032cae1e5a3fa64 to your computer and use it in GitHub Desktop.
const canvasSketch = require('canvas-sketch');
const {
renderPaths,
createPath,
} = require('canvas-sketch-util/penplot');
const Matter = require('matter-js');
const Engine = Matter.Engine,
Body = Matter.Body,
Composite = Matter.Composite,
Composites = Matter.Composites,
Constraint = Matter.Constraint,
World = Matter.World,
Bodies = Matter.Bodies;
const MatterAttractors = require('matter-attractors');
Matter.use(MatterAttractors);
const dimension = 3000;
const settings = {
dimensions: [dimension, dimension],
orientation: 'landscape',
pixelsPerInch: 300,
scaleToView: true,
units: 'px',
animate: true
};
var paths = [];
const radiansPerDegree = Math.PI / 180.0;
const degreesPerRadian = 180.0 / Math.PI;
const epsilon = 0.0001;
var engine = Engine.create();
engine.world.gravity.x = 0.0;
engine.world.gravity.y = 0.0;
var bodies = [];
var group = Body.nextGroup(true);
for (var i = 0; i < 5; i++) {
var rope = Composites.stack(dimension / 2, dimension / 2, 1, 1, 0, 0, function(x, y) {
return Bodies.circle(x, y, 150 * 1.75, {
plugin: {
attractors: [
function(bodyA, bodyB) {
return {
x: (bodyA.position.x - bodyB.position.x) * -3e-6,
y: (bodyA.position.y - bodyB.position.y) * -3e-6,
};
}
]
}
});
});
Composites.chain(rope, 0.5, 0, -0.5, 0, {
stiffness: 0.0,
length: 0
});
Composite.add(rope, Constraint.create({
bodyB: rope.bodies[0],
pointA: {
x: dimension / 2,
y: (dimension / 2) * 1.2
},
stiffness: 0.0,
length: 300 * 1.75
}));
bodies.push(rope);
}
World.add(engine.world, bodies);
Engine.run(engine);
function Vec2(x, y) {
this.x = x;
this.y = y;
}
function sqr(val) {
return val * val;
}
function isEqualEps(lhs, rhs) {
return Math.abs(lhs - rhs) <= epsilon;
}
function toNumberSafe(input) {
var output = Number(input);
return isNaN(output) ? 0 : output;
}
function add(lhs, rhs) {
return new Vec2(lhs.x + rhs.x, lhs.y + rhs.y);
}
function sub(lhs, rhs) {
return new Vec2(lhs.x - rhs.x, lhs.y - rhs.y);
}
function scale(lhs, scale) {
return new Vec2(lhs.x * scale, lhs.y * scale);
}
function addScaled(lhs, rhs, scale) {
return new Vec2(lhs.x + rhs.x * scale, lhs.y + rhs.y * scale);
}
function dot(lhs, rhs) {
return lhs.x * rhs.x + lhs.y * rhs.y;
}
function magSqr(val) {
return val.x * val.x + val.y * val.y;
}
function mag(val) {
return Math.sqrt(val.x * val.x + val.y * val.y);
}
function normalize(val) {
var mag = mag(val);
return (mag > epsilon) ? scale(val, 1.0 / mag) : val;
}
function pointInCircle(point, center, radius) {
return magSqr(sub(point, center)) <= sqr(radius);
}
function drawArc(center, radius, startAng, endAng, width, color, positiveRotation) {
const p = createPath();
p.arc(center.x, center.y, radius, startAng, endAng, positiveRotation);
paths.push(p);
}
function drawArcFromEdge(p1, t1, p2, arcWidth, color, fromP1) {
var chord = sub(p2, p1);
var n1 = new Vec2(-t1.y, t1.x);
var chordDotN1 = dot(chord, n1);
if (isEqualEps(chordDotN1, 0)) {} else {
var radius = magSqr(chord) / (2 * chordDotN1);
var center = addScaled(p1, n1, radius);
var p1Offset = sub(p1, center);
var p2Offset = sub(p2, center);
var p1Ang1 = Math.atan2(p1Offset.y, p1Offset.x);
var p2Ang1 = Math.atan2(p2Offset.y, p2Offset.x);
if (p1Offset.x * t1.y - p1Offset.y * t1.x > 0) {
drawArc(center, Math.abs(radius), p1Ang1, p2Ang1, arcWidth, color, !fromP1);
} else {
drawArc(center, Math.abs(radius), p1Ang1, p2Ang1, arcWidth, color, fromP1);
}
}
}
function drawBiArc(p1, p2, a1, a2) {
var arcWidth = 5;
var arcColor = 'rgb(0, 0, 0)';
var angle1 = radiansPerDegree * toNumberSafe(0);
var angle2 = radiansPerDegree * toNumberSafe(0);
var t1 = new Vec2(Math.cos(angle1), Math.sin(angle1));
var t2 = new Vec2(Math.cos(angle2), Math.sin(angle2));
var d1_min = toNumberSafe(-500);
var d1_max = toNumberSafe(500);
var d1 = d1_min + (d1_max - d1_min) * toNumberSafe(60) / 100.0;
{
var v = sub(p2, p1);
var vMagSqr = magSqr(v);
var vDotT1 = dot(v, t1);
var t = add(t1, t2);
var tMagSqr = magSqr(t);
var equalTangents = isEqualEps(tMagSqr, 4.0);
var perpT1 = isEqualEps(vDotT1, 0.0);
if (equalTangents && perpT1) {
joint = addScaled(p1, v, 0.5);
var angle = Math.atan2(v.y, v.x);
var center1 = addScaled(p1, v, 0.25);
var center2 = addScaled(p1, v, 0.75);
var radius = Math.sqrt(vMagSqr) * 0.25;
var cross = v.x * t1.y - v.y * t1.x;
drawArc(center1, radius, angle, angle + Math.PI, arcWidth, arcColor, cross < 0);
drawArc(center2, radius, angle, angle + Math.PI, arcWidth, arcColor, cross > 0);
} else {
var vDotT = dot(v, t);
var perpT1 = isEqualEps(vDotT1, 0.0);
if (equalTangents) {
d1 = vMagSqr / (4 * vDotT1);
} else {
var denominator = 2 - 2 * dot(t1, t2);
var discriminant = sqr(vDotT) + denominator * vMagSqr;
d1 = (Math.sqrt(discriminant) - vDotT) / denominator
}
var joint = scale(sub(t1, t2), d1);
joint = add(joint, p1);
joint = add(joint, p2);
joint = scale(joint, 0.5);
drawArcFromEdge(p1, t1, joint, arcWidth, arcColor, true);
drawArcFromEdge(p2, t2, joint, arcWidth, arcColor, false);
}
}
}
function mapValue(value, min1, max1, min2, max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
function doRender(props) {
const {
context,
width,
height,
playhead
} = props;
Engine.update(engine);
paths = [];
var bodies = Composite.allBodies(engine.world);
for (var i = 0; i < bodies.length; i += 1) {
var b1 = bodies[i];
var b2 = bodies[(i + 1) % bodies.length];
var p1 = new Vec2(toNumberSafe(b1.position.x), toNumberSafe(b1.position.y));
var p2 = new Vec2(toNumberSafe(b2.position.x), toNumberSafe(b2.position.y));
var spacing = 5 * 2;
for (var j = 0; j < 7 ; j++) {
var adjustedP1 = p1;
var adjustedP2 = p2;
if (i == 0) {
adjustedP2.x += spacing;
} else if (i == bodies.length - 1) {
adjustedP1.x += spacing;
} else {
adjustedP1.x += spacing;
adjustedP2.x += spacing;
}
drawBiArc(adjustedP1, adjustedP2, b1.angle, b2.angle);
}
}
return renderPaths(paths, {
...props,
optimize: false
});
}
function sketch() {
return (props) => {
return doRender(props);
}
}
canvasSketch(sketch, settings);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment