linkage.js

total 0
used 0
limit 0
/* --- title: Semi Circle. categories: arc angles files: head stroke ../point_src/point-content.js pointlist point ../point_src/protractor.js mouse dragging ../point_src/functions/clamp.js stage ../point_src/angle.js ../point_src/text/label.js ../point_src/arc.js ../point_src/tethers-vec.js ../point_src/protractor.js --- Draw a semi-circle to another point _through_ a third point. */ // const canvas = document.getElementById('myCanvas'); // const ctx = canvas.getContext('2d'); // --------------------------------------------------------------------------- // 1) Initial positions // M is fixed in place. A and B define a bar that passes through M. // --------------------------------------------------------------------------- let M = new Point({ x: 300, y: 200 }); // The pinned point, DOES NOT MOVE let A = new Point({ x: 200, y: 200 }); // Red endpoint let B = new Point({ x: 400, y: 200 }); // Blue endpoint // The bar length L const L = 500// distance(A, B); // We'll store direction factors for each end. They determine on which // "side" of M the bar extends. We get them from the initial configuration. const dirA = getDirectionFactor(A, B, M); // if we drag A, how do we find B? const dirB = getDirectionFactor(B, A, M); // if we drag B, how do we find A? // Currently dragging? "A" | "B" | null let dragging = null; // stop dragging if mouse leaves // Draw initially // draw(); // --------------------------------------------------------------------------- // MOUSE HANDLERS // --------------------------------------------------------------------------- function onMouseDown(e) { const { x, y } = getMousePos(e); if (distance({x, y}, A) < 10) { dragging = "A"; } else if (distance({x, y}, B) < 10) { dragging = "B"; } else { dragging = null; } } function onMouseMove(e) { if (!dragging) return; const { x, y } = getMousePos(e); let pair = { A: B, B: A } let nodes = { A, B } let n = nodes[dragging] n.x = x n.y = y repositionXfromX(n, pair[dragging], M, dirA); } function onMouseUp(e) { dragging = null; } function repositionXfromX(a, b, m, directionFactor) { const Mx = m.x - a.x; const My = m.y - a.y; const distM = Math.hypot(Mx, My); if (distM < 1e-9) { b.x = a.x; b.y = a.y; return; } const s = directionFactor * (L / distM); b.x = a.x + s * Mx; b.y = a.y + s * My; } /** * Figure out whether s = +L/|AM| or -L/|AM| for the initial configuration. * * We'll solve B - A = s*(M - A), and check sign of 's' from the current positions. * s = ((B - A) dot (M - A)) / |M - A|^2 */ function getDirectionFactor(A, B, M) { const AM = { x: M.x - A.x, y: M.y - A.y }; const AB = { x: B.x - A.x, y: B.y - A.y }; const dot = AM.x*AB.x + AM.y*AB.y; const magAM2 = AM.x*AM.x + AM.y*AM.y; if (magAM2 < 1e-9) return +1; // degenerate const s = dot / magAM2; // could be pos or neg return (s >= 0) ? +1 : -1; } function distance(p1, p2) { return Math.hypot(p2.x - p1.x, p2.y - p1.y); } function getMousePos(e) { const rect = canvas.getBoundingClientRect(); return { x: e.clientX - rect.left, y: e.clientY - rect.top }; } class TrackPoint extends Point { xSet(value) { if(this._prevX == value) { return } // console.log('x', value) this.dirty = true; this._prevX = value; } ySet(value) { if(this._prevY == value) { return } // console.log('y', value) this.dirty = true; this._prevY = value; } step() { if(this.dirty) { // console.log('step') this.dirty = false this.lookAt(this.pin) if(this.other.dirty == false) { this.other.update(projectFrom(this, 300)) } } } } class MainStage extends Stage { canvas='playspace' mounted(){ this.spinner = new Point({x:200, y:150, radius: 100, color: '#880000'}) this.pinPoint = new Point({x:420, y:150, radius: 100, color: '#880000'}) this.aPoint = new TrackPoint({ x:200, y:300, pin: this.pinPoint, color: 'cyan' }) this.bPoint = new TrackPoint({ x:230, y:190, pin: this.pinPoint, color: 'purple', }) this.aPoint.other = this.bPoint this.bPoint.other = this.aPoint let cp = this.spinner.tethers.add(this.bPoint) this.dragging.addPoints( this.spinner, this.pinPoint, this.aPoint, this.bPoint ) // --------------------------------------------------------------------------- // 2) Event listeners const canvas = this.canvas window.canvas = canvas // --------------------------------------------------------------------------- canvas.addEventListener('mousedown', onMouseDown); canvas.addEventListener('mousemove', onMouseMove); canvas.addEventListener('mouseup', onMouseUp); canvas.addEventListener('mouseleave', onMouseUp); } draw(ctx){ this.clear(ctx) ctx.fillStyle = '#555' ctx.strokeStyle = 'orange' // this.pinPoint.step() this.spinner.tethers.step() this.bPoint.dirty = true this.aPoint.step() this.bPoint.step() // Draw the bar (line from A to B) ctx.beginPath(); ctx.moveTo(A.x, A.y); ctx.lineTo(B.x, B.y); ctx.lineWidth = 2; ctx.strokeStyle = "#333"; ctx.stroke(); // Draw A (red) this.drawPoint(ctx, A.x, A.y, "red"); // Draw B (blue) this.drawPoint(ctx, B.x, B.y, "blue"); // Draw M (green) - pinned/fixed this.drawPoint(ctx, M.x, M.y, "green"); this.pinPoint.pen.circle(ctx, {color: 'purple'}) this.pinPoint.pen.fill(ctx, '#990290', 3) this.aPoint.pen.indicator(ctx) this.bPoint.pen.indicator(ctx) this.aPoint.pen.line(ctx, this.bPoint, 'pink') this.spinner.pen.indicator(ctx) this.spinner.rotation += 1 } drawPoint(ctx, x, y, color) { ctx.beginPath(); ctx.arc(x, y, 6, 0, 2*Math.PI); ctx.fillStyle = color; ctx.fill(); ctx.strokeStyle = "#000"; ctx.stroke(); } } ;stage = MainStage.go();
Run
Meta Data
title Semi Circle.
imports ()
files ('head', 'stroke', '../point_src/point-content.js', 'pointlist', 'point', '../point_src/protractor.js', 'mouse', 'dragging', '../point_src/functions/clamp.js', 'stage', '../point_src/angle.js', '../point_src/text/label.js', '../point_src/arc.js', '../point_src/tethers-vec.js', '../point_src/protractor.js')
unused_keys ()
unknown_keys ('categories',)
categories ['', 'arc', 'angles']
filepath_exists True
path linkage.js
filepath linkage.js
clean_files ('../point_src/core/head.js', '../point_src/setunset.js', '../point_src/stroke.js', '../point_src/compass.js', '../point_src/center.js', '../point_src/point-content.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/pointdraw.js', '../point_src/relative-xy.js', '../point_src/pointcast.js', '../point_src/point.js', '../point_src/protractor.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/functions/clamp.js', '../point_src/distances.js', '../point_src/text/beta.js', '../point_src/dragging.js', '../point_src/stage-hooks.js', '../point_src/stage-resize.js', '../point_src/functions/resolve.js', '../point_src/stage.js', '../point_src/angle.js', '../point_src/text/label.js', '../point_src/arc.js', '../point_src/./windings.js', '../point_src/tethers-vec.js')
markdown {'html': '<p>Draw a semi-circle to another point <em>through</em> a third point.</p>', 'content': '---\ntitle: Semi Circle.\ncategories:\n arc\n angles\nfiles:\n head\n stroke\n ../point_src/point-content.js\n pointlist\n point\n ../point_src/protractor.js\n mouse\n dragging\n ../point_src/functions/clamp.js\n stage\n ../point_src/angle.js\n ../point_src/text/label.js\n ../point_src/arc.js\n ../point_src/tethers-vec.js\n ../point_src/protractor.js\n---\n\nDraw a semi-circle to another point _through_ a third point.'}