chord-example.js

total 0
used 0
limit 0
/* title: Circle Chords category: chords files: head ../point_src/math.js ../point_src/extras.js ../point_src/point-content.js pointlist point stage mouse dragging stroke ../point_src/random.js --- There are a range of elements to consider for a chord. + chord: A line drawn within a circle, edge to edge, equidistant from the center. + _segment_: A chord - but non-equidistant. where the points are _anywhere_ around the edge. + sagitta: The midpoint project to the edge of the (inside) circle. + secant: An extended (to infinite) chord, through two points + tangent: A line extended to infinite, touching the outside the circle */ class MainStage extends Stage { // canvas = document.getElementById('playspace'); canvas = 'playspace' mounted(){ this.point = new Point({x: 250, y: 150 , radius: 100}) this.iPoint = new Point({x: 250, y: 150 , radius: 10, rotation: 33}) this.iPoint2 = new Point({x: 250, y: 250 , radius: 10, rotation: 3}) this.dragging.add(this.point, this.iPoint, this.iPoint2) } onMousedown(ev) { this.iPoint.rotation = random.int(180) } draw(ctx){ this.clear(ctx) let p = this.point; let chord = this.iPoint let chord2 = this.iPoint2 // chord.xy = this.mouse.point.xy chord.pen.indicator(ctx) chord2.pen.indicator(ctx) let r = chordEndpoints2(p, chord)//, chord2) // let r = chordEndpoints(p, chord) // let r = chordEndpointsRaw(p.x, p.y, p.radius, chord.x, chord.y, chord.radians) if(r) { r.forEach(d=>{ (new Point(d)).pen.indicator(ctx) }); (new Point(r[0])).pen.line(ctx, r[1], 'red') } let spacing = chord.radius let count = 10 gOffsetValue += .2; if(gOffsetValue >= spacing) { gOffsetValue = 0 } let offset = {x: -(gOffsetValue % spacing), y: -(gOffsetValue % spacing)}; let pls = generateParallelChordsAutoCountParallel(p, chord, chord2, spacing, offset, true) // let pls = generateParallelChordsAutoCountParallel(p, chord, undefined, spacing, offset, true) // let pls = generateParallelChordsAutoCountParallel(p, chord, chord2, spacing, offset, false) // let pls = generateParallelChordsAutoCountParallel(p, chord, undefined, spacing, offset, false) // let pls = generateParallelChordsAutoCountParallel(p, chord, chord2, 10, offset, true, false) // let pls = generateParallelChordsAutoCountParallel(p, chord, chord2, spacing, offset, true, false) /* _within_ the control point */ // let pls = generateParallelChordsAutoCountOffset(p, chord, undefined, spacing * .3) // let pls = generateParallelChordsAutoCountOffset(p, chord, undefined, 10) // let pls = generateParallelChordsAutoCountOffset(p, chord, chord2, 10) // let pls = generateParallelChordsAutoCountOffset(p, chord, chord2, 10, false) /* manual count*/ // let pls = generateParallelChords(p, chord, undefined, spacing, count) // let pls = generateParallelChords(p, chord, chord2, spacing, count) pls.forEach(pair=>{ (new Point(pair[0])).pen.line(ctx, pair[1], 'green') // pair.forEach(d=>{ // }) }); this.point.pen.indicator(ctx) } } /** * Generates parallel chords automatically covering the circle. * * @param {Object} circleCenter - { x, y, radius } * @param {Object} chordPoint - { x, y, radians, ... } * @param {Object|null} chordPoint2 - Optional second chord reference * @param {number} spacing - Distance between parallel chords * * @returns {Array>} * A list of chord endpoints. Each chord is an array of two points. */ console.log('chord'); /** * Generates parallel chords that always include a chord through `chordPoint`. * The count is automatically derived from (circle radius / spacing). * * @param {Object} circle - { x, y, radius } Center + radius * @param {Object} chordPoint - { x, y, radians, directionTo, radius } * - `chordPoint.radius` is assumed to be the circle radius here. * - `chordPoint.radians` is the base chord angle. * @param {Object|null} chordPoint2 * - If provided, we take the chord angle from chordPoint.directionTo(chordPoint2). * @param {number} spacing - The distance between parallel chords * @returns {Array>} - Array of chords (each chord = two points) */ function generateParallelChordsAutoCountOffset(circle, chordPoint, chordPoint2, spacing, parallel=true) { // 1) Extract center + radius const { x: px, y: py } = circle; const R = chordPoint.radius; // or circle.radius if needed // 2) Determine the chord's base angle and its perpendicular const baseAngle = chordPoint2 ? chordPoint.directionTo(chordPoint2) : chordPoint.radians; const perpAngle = baseAngle + Math.PI / 2; // 3) Compute how many parallel lines we want on each side of chordPoint // so that we cover from the center out to ± the circle radius. // (You may choose Math.ceil if you want to ensure coverage out to edges) const halfCount = Math.floor(R / spacing); // 4) The offset of chordPoint from the circle center, **projected** onto // the perpendicular direction. This ensures the chord at i=0 // lines up exactly with chordPoint’s own chord. const dx = chordPoint.x - px; const dy = chordPoint.y - py; const offsetFromCenter = dx * Math.cos(perpAngle) + dy * Math.sin(perpAngle); // 5) Build chords in the range i = -halfCount..+halfCount // so that i=0 corresponds exactly to chordPoint const chords = []; for (let i = -halfCount; i <= halfCount; i++) { // offset for this i const offset = offsetFromCenter + i * spacing; // compute the chord reference point for this offset const refX = px + offset * Math.cos(perpAngle); const refY = py + offset * Math.sin(perpAngle); const offsetChordPoint = { x: refX, y: refY, radians: baseAngle, directionTo: chordPoint.directionTo, // if needed // If your chordEndpoints2 depends on chordPoint.radius, pass it: radius: chordPoint.radius }; let parallelAngle = undefined if(parallel) { parallelAngle = baseAngle } // 6) Use chordEndpoints2 to get chord endpoints // If it returns null, that offset doesn't intersect the circle. const chord = chordEndpoints2(circle, offsetChordPoint, chordPoint2, parallelAngle); if (chord) { chords.push(chord); } } return chords; } var gOffsetValue = 0 function generateParallelChordsAutoCountParallel(circleCenter, chordPoint, chordPoint2, spacing, offset, forceOverlay=false, parallel=true) { // 1) Compute how many chords needed to cover the circle const R = circleCenter.radius; const halfCount = Math.ceil(R / spacing); // const halfCount = Math.floor(R / spacing); const count = 2 * halfCount + 1; // 2) Determine the base chord direction const baseAngle = chordPoint2 ? chordPoint.directionTo(chordPoint2) : chordPoint.radians; const perpAngle = baseAngle + Math.PI / 2; let offsetAddX = offset.x * Math.cos(perpAngle) let offsetAddY = offset.y * Math.sin(perpAngle) // 3) Generate chords for offsets from -halfCount to +halfCount const chords = []; for (let i = -halfCount; i <= halfCount; i++) { const offsetX = (i * spacing * Math.cos(perpAngle)) + offsetAddX; const offsetY = (i * spacing * Math.sin(perpAngle)) + offsetAddY; let cox = forceOverlay? circleCenter.x: chordPoint.x let coy = forceOverlay? circleCenter.y: chordPoint.y // "Shifted" chord reference point // const { x: px, y: py } = circleCenter; const offsetChordPoint = { x: cox + offsetX, y: coy + offsetY, // radians: chordPoint.radians, // Or keep the same orientation directionTo: chordPoint.directionTo // (Assuming your chordPoint has this) }; let parallelAngle = undefined if(parallel) { parallelAngle = baseAngle } // Use your existing chordEndpoints2 function const chord = chordEndpoints2(circleCenter, offsetChordPoint, chordPoint2, parallelAngle); if (chord) { chords.push(chord); } } return chords; } function chordEndpoints2(point, chordPoint, chordPoint2, radians) { // Circle center: (px, py) // Radius: chordPoint.radius // Chord reference point: (cx, cy) // Angle of chord direction: ca (in radians) let px = point.x , py = point.y , R = point.radius , cx = chordPoint.x , cy = chordPoint.y , ca = chordPoint2? chordPoint.directionTo(chordPoint2): chordPoint.radians ; /* Remove for covergent chords*/ if(radians != undefined) { ca = radians } // Step 1: direction from angle const dxAngle = Math.cos(ca); const dyAngle = Math.sin(ca); // Step 2: define d_x, d_y const dx = cx - px; const dy = cy - py; // Quadratic coefficients for t^2 + B t + C = 0 const A = 1; // because dxAngle^2 + dyAngle^2 = 1 const B = 2 * (dx * dxAngle + dy * dyAngle); const C = (dx * dx) + (dy * dy) - (R * R); // Discriminant const discriminant = B*B - 4*A*C; if (discriminant < 0) { // no real intersections -> no chord return null; // or [] } // solve t values const sqrtD = Math.sqrt(discriminant); const t1 = (-B + sqrtD) / (2 * A); const t2 = (-B - sqrtD) / (2 * A); // endpoints const x1 = cx + t1 * dxAngle; const y1 = cy + t1 * dyAngle; const x2 = cx + t2 * dxAngle; const y2 = cy + t2 * dyAngle; return [ { x: x1, y: y1 }, { x: x2, y: y2 } ]; } /** * Returns an array of chords (each chord is a two-point array). * * @param {Object} circleCenter * The circle's "point" object with { x, y, radius }. * * @param {Object} chordPoint * An object with { x, y, radians, directionTo(...) } * or a similar structure. This is the *base* chord reference. * * @param {Object|null} chordPoint2 * Optional second chord reference used to find chord direction. * If chordPoint2 is given, direction = chordPoint.directionTo(chordPoint2), * else it uses `chordPoint.radians`. * * @param {number} spacing * Distance between parallel chords * * @param {number} count * How many chords total to attempt (the function will produce up * to `count` chords, but some might fall completely outside the circle * and be skipped if `chordEndpoints2` returns null). * * @returns {Array>} * A list of chords, where each chord is an array of two points: * [ {x1, y1}, {x2, y2} ] */ function generateParallelChords(circleCenter, chordPoint, chordPoint2, spacing, count) { const chords = []; // 1) Determine the base chord direction in radians const baseAngle = chordPoint2? chordPoint.directionTo(chordPoint2): chordPoint.radians; // 2) The perpendicular direction const perpAngle = baseAngle + Math.PI / 2; // We'll offset around 0. E.g. if count=11, i goes from -5..5 // so that we have a symmetrical distribution around the base chord const half = Math.floor(count / 2); for (let i = -half; i <= half; i++) { // 3) Calculate the offset in the perpendicular direction const offsetX = i * spacing * Math.cos(perpAngle); const offsetY = i * spacing * Math.sin(perpAngle); // 4) Build a "shifted" chordPoint const offsetChordPoint = { // Original chordPoint’s x,y plus offset x: chordPoint.x + offsetX, y: chordPoint.y + offsetY, // Retain the other relevant fields radians: chordPoint.radians, directionTo: chordPoint.directionTo, // or replicate logic // Not strictly necessary if chordEndpoints2 just uses point.radius, // but if your chordPoint also has a radius or other fields, pass them along radius: chordPoint.radius }; // 5) Use chordEndpoints2 to get chord endpoints const chord = chordEndpoints2(circleCenter, offsetChordPoint, chordPoint2, baseAngle); // chordEndpoints2 can return null if there's no real intersection if (chord) { chords.push(chord); } } return chords; } stage = MainStage.go(/*{ loop: true }*/)
Run
Meta Data
title Circle Chords
imports ()
files ('head', '../point_src/math.js', '../point_src/extras.js', '../point_src/point-content.js', 'pointlist', 'point', 'stage', 'mouse', 'dragging', 'stroke', '../point_src/random.js')
unused_keys ()
unknown_keys ('category',)
category ['chords']
filepath_exists True
path chord-example.js
filepath chord-example.js
clean_files ('../point_src/core/head.js', '../point_src/math.js', '../point_src/extras.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/stage-resize.js', '../point_src/functions/resolve.js', '../point_src/stage.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/setunset.js', '../point_src/stroke.js', '../point_src/random.js')
markdown {'html': '<p>There are a range of elements to consider for a chord.</p>\n<ul>\n<li>chord: A line drawn within a circle, edge to edge, equidistant from the center.</li>\n<li><em>segment</em>: A chord - but non-equidistant. where the points are <em>anywhere</em> around the edge.</li>\n<li>sagitta: The midpoint project to the edge of the (inside) circle.</li>\n<li>secant: An extended (to infinite) chord, through two points</li>\n<li>tangent: A line extended to infinite, touching the outside the circle</li>\n</ul>', 'content': 'title: Circle Chords\ncategory: chords\nfiles:\n head\n ../point_src/math.js\n ../point_src/extras.js\n ../point_src/point-content.js\n pointlist\n point\n stage\n mouse\n dragging\n stroke\n ../point_src/random.js\n---\n\n\nThere are a range of elements to consider for a chord.\n\n+ chord: A line drawn within a circle, edge to edge, equidistant from the center.\n+ _segment_: A chord - but non-equidistant. where the points are _anywhere_ around the edge.\n+ sagitta: The midpoint project to the edge of the (inside) circle.\n+ secant: An extended (to infinite) chord, through two points\n+ tangent: A line extended to infinite, touching the outside the circle'}