rope-and-text.js

total 0
used 0
limit 0
/* title: Pinnable Rope categories: chain rope constraints files: head pointlist point stage stroke mouse dragging ../point_src/random.js --- A very pinnable verlet constraint chain for a rope-like catenary solution. */ // const distance = 5; const gravity2D = {x:0, y:1}; const gravity = 0.35; const friction = 0.9; class MainStage extends Stage { canvas='playspace' mounted(){ this.points = PointList.generate.random(10, [100, 200], [0,0, 5, 0]) this.points.forEach((p)=>{ p.update({vx: 0, vy:0 }) }) } /* GPT 4.5 absolutely knocked it out the park.*/ mounted() { this.numPoints = 10; this.segmentLength = 20; this.gravity = .31; this.gravity2D = gravity2D this.points = Array.from({ length: this.numPoints }, () => ({ x: this.mouse.point.x, y: this.mouse.point.y, oldX: this.mouse.point.x, oldY: this.mouse.point.y, })); // this.points[9].invMass = .01 this.points = new PointList(...this.points).cast() this.endPin = new Point(100, 200) this.midPin = new Point(250, 200) this.points.last().invMass = 2 this.midPin.color = 'red' this.dragging.add(this.midPin, this.endPin) } applyPhysics(points, gravity) { points.forEach((p, index) => { if (index === 0) { // first point follows mouse p.x = this.mouse.point.x; p.y = this.mouse.point.y; } else { const vx = (p.x - p.oldX) * 0.98; const vy = (p.y - p.oldY) * 0.98; p.oldX = p.x; p.oldY = p.y; p.x += vx; p.y += vy + gravity; } }); } applyPhysics2(points, gravity2D, pinnedIndices){ points.forEach((p, index) => { if (!pinnedIndices.includes(index) && (p.invMass ?? 1) !== 0) { p.x += gravity2D.x * (p.invMass ?? 1) * this.gravity; p.y += gravity2D.y * (p.invMass ?? 1) * this.gravity; } }); } solveConstraints1(points, segmentLength, iterations = 5) { for (let j = 0; j < iterations; j++) { for (let i = 0; i < points.length - 1; i++) { const p1 = points[i]; const p2 = points[i + 1]; let dx = p2.x - p1.x; let dy = p2.y - p1.y; const distance = Math.sqrt(dx * dx + dy * dy); const difference = segmentLength - distance; const percent = difference / distance / 2; const offsetX = dx * percent; const offsetY = dy * percent; if (i !== 0) { p1.x -= offsetX; p1.y -= offsetY; } p2.x += offsetX; p2.y += offsetY; } } } draw(ctx) { this.clear(ctx); /* A viscous fluid, applied through 2d invMass*/ // this.applyPhysics2(this.points, this.gravity2D, [1, this.points.length-1]); /* Rope like mass physics.*/ this.applyPhysics(this.points, this.gravity); /* Different solving methods */ // this.solveConstraints1(this.points, this.segmentLength); // this.solveConstraints2(this.points, this.segmentLength); this.solveConstraints3(this.points, this.segmentLength); ctx.fillStyle = '#666' ctx.font = 'normal 30px lexend deca' ctx.textAlign = 'center' // this.points.pen.indicator(ctx); this.points.pen.quadCurve(ctx); let penUlt = this.points[this.points.length - 2] let last = this.points.last() last.lookAt(penUlt, Math.PI *.5) // last.pen.indicator(ctx) penUlt.pen.indicator(ctx) last.text.label(ctx, 'polypoint') // ctx.beginPath(); // ctx.moveTo(this.points[0].x, this.points[0].y); // this.points.forEach(p => ctx.lineTo(p.x, p.y)); // ctx.strokeStyle = "#333"; // ctx.lineWidth = 4; // ctx.lineCap = "round"; // ctx.stroke(); } solveConstraints2(points, segmentLength, iterations = 5) { const pl = points.last() let endPin = this.endPin const pinnedPositions = { 0: this.mouse.point // , [~~(points.length *.5)]: this.midPin // , [points.length - 1]: endPin }; const pinnedIndices = Object.keys(pinnedPositions) for (let j = 0; j < iterations; j++) { for (let i = 0; i < points.length - 1; i++) { const p1 = points[i]; const p2 = points[i + 1]; let dx = p2.x - p1.x; let dy = p2.y - p1.y; const distance = Math.sqrt(dx * dx + dy * dy); const difference = segmentLength - distance; const percent = difference / distance / 2; const offsetX = dx * percent; const offsetY = dy * percent; if (!pinnedIndices.includes(i)) { p1.x -= offsetX; p1.y -= offsetY; } if (!pinnedIndices.includes(i + 1)) { p2.x += offsetX; p2.y += offsetY; } } // Reinforce pinned positions after constraint adjustment pinnedIndices.forEach(index => { points[index].x = pinnedPositions[index].x; points[index].y = pinnedPositions[index].y; }); } } solveConstraints3(points, segmentLength, iterations = 5) { const pl = points.last(); let endPin = this.endPin; const pinnedPositions = { 0: this.mouse.point, // [~~(points.length *.5)]: this.midPin, // [points.length - 1]: endPin }; const pinnedIndices = Object.keys(pinnedPositions).map(Number); for (let j = 0; j < iterations; j++) { for (let i = 0; i < points.length - 1; i++) { const p1 = points[i]; const p2 = points[i + 1]; let dx = p2.x - p1.x; let dy = p2.y - p1.y; const distance = Math.sqrt(dx * dx + dy * dy) || 0.0001; // prevent NaN const difference = segmentLength - distance; const invMass1 = p1.invMass ?? 1; // Default to 1 if undefined const invMass2 = p2.invMass ?? 1; const totalInvMass = invMass1 + invMass2 || 0.0001; // prevent division by zero const percent1 = (invMass1 / totalInvMass) * difference / distance; const percent2 = (invMass2 / totalInvMass) * difference / distance; if (!pinnedIndices.includes(i)) { p1.x -= dx * percent1; p1.y -= dy * percent1; } if (!pinnedIndices.includes(i + 1)) { p2.x += dx * percent2; p2.y += dy * percent2; } } // Re-pin positions pinnedIndices.forEach(index => { points[index].x = pinnedPositions[index].x; points[index].y = pinnedPositions[index].y; }); } } } ;stage = MainStage.go();
Run
Meta Data
title Pinnable Rope
imports ()
files ('head', 'pointlist', 'point', 'stage', 'stroke', 'mouse', 'dragging', '../point_src/random.js')
unused_keys ()
unknown_keys ('categories',)
categories ['chain', 'rope', 'constraints']
filepath_exists True
path rope-and-text.js
filepath rope-and-text.js
clean_files ('../point_src/core/head.js', '../point_src/pointlistdraw.js', '../point_src/pointlistgradient.js', '../point_src/pointlistshape.js', '../point_src/pointlistgenerator.js', '../point_src/unpack.js', '../point_src/pointlist.js', '../point_src/pointlistpen.js', '../point_src/pointpen.js', '../point_src/compass.js', '../point_src/center.js', '../point_src/point-content.js', '../point_src/pointdraw.js', '../point_src/relative-xy.js', '../point_src/pointcast.js', '../point_src/point.js', '../point_src/stage-resize.js', '../point_src/functions/resolve.js', '../point_src/stage.js', '../point_src/setunset.js', '../point_src/stroke.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/functions/clamp.js', '../point_src/distances.js', '../point_src/protractor.js', '../point_src/text/beta.js', '../point_src/dragging.js', '../point_src/random.js')
markdown {'html': '<p>A very pinnable verlet constraint chain for a rope-like catenary solution.</p>', 'content': 'title: Pinnable Rope\ncategories: chain\n rope\n constraints\nfiles:\n head\n pointlist\n point\n stage\n stroke\n mouse\n dragging\n ../point_src/random.js\n---\n\nA very pinnable verlet constraint chain for a rope-like catenary solution.'}