claude-sonnet-5-pop-edge-directional-tangent-5-2-again.js

total 0
used 0
limit 0
/* --- title: Directional Tangent 5 categories: tangents bisector raw files: head stroke ../point_src/point-content.js pointlist point ../point_src/tangents.js ../point_src/bisector.js ../point_src/math.js ../point_src/split.js mouse dragging ../point_src/functions/clamp.js stage ../point_src/tangents.js ../point_src/text/beta.js --- This version presents a more complete version of tangent lines. It builds a line by plotting the tangent points to the siblings through a psuedo center (a "pin" projected out from each point along its bisector direction). This ensures the arc is drawn _through_ the center rather than arcing around the outside, and - importantly - keeps the belt's tangent side selection changing continuously as a point is dragged, rather than snapping between the two inbound tangent sides whenever a point crosses the center-line of its neighbours. The pin is projected a distance scaled by how sharp the local bend actually is (`bendMagnitude`, 0 for a point sitting in a near straight line between its neighbours, up to 2 for a sharp reversal). Projecting a full radius regardless of bend sharpness caused the pin offset and the tangent formula's own radius offset to compound on gentle/near-straight bends, landing the plotted meeting tips up to a full diameter away from the real point. The arc sweep direction (`resolveArcDirection`) still needs a guard for the near-collinear case: when a point sits (almost) in a straight line between its neighbours, the two touch points collapse onto nearly the same spot, so floating point jitter alone shouldn't be allowed to pick the reflex/near-full loop over the intended near-zero arc. */ const isOuterPoint = function(a,b,c) { return calculateAngleWithRef(a,b,c) > 180 // return obtuseBisect(previousPoint, p, nextPoint) > -1 } class MainStage extends Stage { canvas='playspace' mounted(){ this.points = new PointList( {x:130, y:190, radius: 30} ,{x:270, y:360, radius: 20, isFlipped: true, name: "TARGET"} ,{x:390, y:580, radius: 30} ,{x:540, y:140, radius: 30} // ,{x:440, y:440, radius: 30} // ,{x:400, y:40, radius: 30} // ,{x:420, y:140, radius: 30} ).cast(); this.dragging.addPoints(...this.points) } regen() { this.twistAll(this.points) this.biPoints = this.generateBiPoints(this.points) this.tangentPoints = this.generateTangents(this.biPoints) this.arcPointPairs = this.orderArcPoints(this.tangentPoints) } orderArcPoints(tangentPoints) { let orders = tangentPoints const rearranged = orders.map((_, i) => { const current = orders[i]; const next = orders[(i + 1) % orders.length]; return [current ? current[1]: next[0], next ? next[0]: current[0]]; }); return rearranged } generateTangents(biPoints) { /* Built tangent lines to later plot*/ let res = []; biPoints.siblings(1).forEach((pairs, i, items)=>{ let p = pairs[0] let next = pairs[1] let typ = (p.isOuter || p.isFlipped) let [da,db] = typ? ['bb', 'ab']: ['ba', 'aa'] let fname = (next?.isOuter || next?.isFlipped)? da: db res.push(pairs[0].tangent[fname](pairs[1])) }) return res; } generateBiPoints(points) { let res = new PointList; points.forEach(p=>{ /* Project the pseudo-center pin only as far as the sharpness of the turn requires. `bendMagnitude` is 0 for a point sitting in a straight line between its neighbours and up to 2 for a sharp reversal (see twistAll). Scaling the projection distance by this prevents a fixed full-radius projection from compounding with the tangent formula's own radius offset and overshooting past the real point (previously up to a full diameter away) on gentle/near-straight bends. */ let pinDistance = p.radius * Math.min(1, (p.bendMagnitude ?? 2) / 2) let pin = p.project(pinDistance) pin.radius = p.radius pin.color = p.isOuter? 'red': 'yellow' pin.isOuter = p.isOuter pin.isFlipped = p.isFlipped res.push(pin) }) return res } twistAll(points) { // triples(this.points).forEach((t)=>{ points.triples().forEach((t)=>{ let bisector = calculateBisector(t[0], t[1], t[2]) let outer = t[1].directionTo(t[1].add(bisector)) let isOuter = isOuterPoint(t[0], t[1], t[2]) t[1].radians = outer t[1].isOuter = isOuter t[1].bendMagnitude = bisector.magnitude() t[1].color = isOuter? 'red': 'yellow' }); } draw(ctx){ this.clear(ctx) this.regen() /* The indicators */ // this.biPoints.pen.indicator(ctx, {color: "#222"}) /* pull point for each corner */ this.points.pen.circle(ctx, {color:'pink'}) // this.biPoints.forEach(pair=>{ // pair.pen.indicator(ctx) // }) // this.points.pen.line(ctx, {color:'green'}) // this.points[0].pen.line(ctx, this.points.last()) ctx.fillStyle = '#DDD' ctx.font = '400 22px sans-serif' ctx.textAlign = 'center' ctx.textBaseline = 'middle' // this.points.forEach((p, i)=>{ // p.text.fill(ctx, i) // }) this.tangentPoints.forEach((pair, i)=>{ if(!pair) { return } let [a,b] = pair; /* The primary line*/ new Point(a).pen.line(ctx, b, '#5544EE', 3) /* tiny tangent drop points.*/ let color = i==0? 'red': '#555' pair.forEach(p=>{ // new Point(p).pen.circle(ctx, {color}) }) }) ctx.strokeStyle = 'white' this.arcPointPairs.forEach((pair, i, all)=>{ if(!pair) { return } let [a,b] = pair; // if(i != 0) { // return // } /* Draw an arc from A to B, from the pseudo-center pin.*/ let color = i==1? 'red': '#555' let p = this.biPoints[i+1]//.pen.arc(ctx) if(p==undefined) { p = this.biPoints[0]//.pen.arc(ctx) } let isOuterPoint = p.isFlipped || p.isOuter let ang = p.radians // let start =(Math.PI2 + p.directionTo(new Point(a)) + ang) % Math.PI2 // let end =(Math.PI2 + p.directionTo(new Point(b)) + ang) % Math.PI2 let pa = new Point(a) let pb = new Point(b) let start = p.directionTo(pa) //- ang let end = p.directionTo(pb) //+ ang // pa.pen.fill(ctx) // pb.pen.indicator(ctx, {color: 'red'}) // let pp = new Point(p) // pp.pen.indicator(ctx, {color: p.isOuter? 'red': '#333'}) let direction = this.resolveArcDirection(p, pa, pb, start, end, isOuterPoint) ctx.beginPath() // ctx.arcTo(a.x, a.y, b.x, b.y, p.radius) // this.drawArc(ctx, p, pa, pb) p.draw.arc(ctx, p.radius, start, end, direction) ctx.stroke() // pair.forEach(p=>{ // new Point(p).pen.circle(ctx, {color}) // }) }) } drawArc(ctx, a, pa, pb) { ctx.arc(a.x, a.y, a.radius, a.directionTo(pa), a.directionTo(pb)) } resolveArcDirection(p, pa, pb, start, end, isOuterPoint) { /* Fix for the "popping"/looping arc bug: When the two neighbour points align with `p` to form a (near) straight line, the two tangent touch points `pa`/`pb` collapse onto (almost) the same spot on the circle. At that singularity the raw `start`/`end` angles are nearly identical, so tiny floating point jitter can flip which side of the circle is "shorter". Since the arc direction is otherwise fixed by `isOuterPoint`, that jitter can suddenly make the *reflex* (near full-circle) arc get drawn instead of the intended near-zero arc, producing a visible pop/loop right at the straight-line alignment. When `pa`/`pb` are this close together, ignore `isOuterPoint` and always choose whichever direction produces the smaller sweep - both options are visually a no-op at that scale, so this keeps the transition continuous. */ let touchDistance = pa.distanceTo(pb) let isNearSingularity = touchDistance < p.radius * 0.5 if(!isNearSingularity) { return isOuterPoint } let clockwiseSweep = ((end - start) + Math.PI2) % Math.PI2 return clockwiseSweep > Math.PI } } ;stage = MainStage.go();
Run
Meta Data
title Directional Tangent 5
imports ()
files ('head', 'stroke', '../point_src/point-content.js', 'pointlist', 'point', '../point_src/tangents.js', '../point_src/bisector.js', '../point_src/math.js', '../point_src/split.js', 'mouse', 'dragging', '../point_src/functions/clamp.js', 'stage', '../point_src/tangents.js', '../point_src/text/beta.js')
unused_keys ()
unknown_keys ('categories',)
categories ['tangents', 'bisector', 'raw']
filepath_exists True
path claude-sonnet-5-pop-edge-directional-tangent-5-2-again.js
filepath claude-sonnet-5-pop-edge-directional-tangent-5-2-again.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/tangents.js', '../point_src/bisector.js', '../point_src/math.js', '../point_src/functions/clamp.js', '../point_src/split.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/distances.js', '../point_src/protractor.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')
markdown {'html': '<p>This version presents a more complete version of tangent lines.</p>\n<p>It builds a line by plotting the tangent points to the siblings through\na psuedo center (a "pin" projected out from each point along its bisector\ndirection). This ensures the arc is drawn <em>through</em> the center rather than\narcing around the outside, and - importantly - keeps the belt\'s tangent\nside selection changing continuously as a point is dragged, rather than\nsnapping between the two inbound tangent sides whenever a point crosses the\ncenter-line of its neighbours.</p>\n<p>The pin is projected a distance scaled by how sharp the local bend actually\nis (<code>bendMagnitude</code>, 0 for a point sitting in a near straight line between\nits neighbours, up to 2 for a sharp reversal). Projecting a full radius\nregardless of bend sharpness caused the pin offset and the tangent formula\'s\nown radius offset to compound on gentle/near-straight bends, landing the\nplotted meeting tips up to a full diameter away from the real point.</p>\n<p>The arc sweep direction (<code>resolveArcDirection</code>) still needs a guard for the\nnear-collinear case: when a point sits (almost) in a straight line between\nits neighbours, the two touch points collapse onto nearly the same spot, so\nfloating point jitter alone shouldn\'t be allowed to pick the reflex/near-full\nloop over the intended near-zero arc.</p>', 'content': '---\ntitle: Directional Tangent 5\ncategories: tangents\n bisector\n raw\nfiles:\n head\n stroke\n ../point_src/point-content.js\n pointlist\n point\n ../point_src/tangents.js\n ../point_src/bisector.js\n ../point_src/math.js\n ../point_src/split.js\n mouse\n dragging\n ../point_src/functions/clamp.js\n stage\n ../point_src/tangents.js\n ../point_src/text/beta.js\n---\n\nThis version presents a more complete version of tangent lines.\n\nIt builds a line by plotting the tangent points to the siblings through\na psuedo center (a "pin" projected out from each point along its bisector\ndirection). This ensures the arc is drawn _through_ the center rather than\narcing around the outside, and - importantly - keeps the belt\'s tangent\nside selection changing continuously as a point is dragged, rather than\nsnapping between the two inbound tangent sides whenever a point crosses the\ncenter-line of its neighbours.\n\nThe pin is projected a distance scaled by how sharp the local bend actually\nis (`bendMagnitude`, 0 for a point sitting in a near straight line between\nits neighbours, up to 2 for a sharp reversal). Projecting a full radius\nregardless of bend sharpness caused the pin offset and the tangent formula\'s\nown radius offset to compound on gentle/near-straight bends, landing the\nplotted meeting tips up to a full diameter away from the real point.\n\nThe arc sweep direction (`resolveArcDirection`) still needs a guard for the\nnear-collinear case: when a point sits (almost) in a straight line between\nits neighbours, the two touch points collapse onto nearly the same spot, so\nfloating point jitter alone shouldn\'t be allowed to pick the reflex/near-full\nloop over the intended near-zero arc.'}