line-rider-splits.js

total 0
used 0
limit 0
/* --- title: Split categories: split curve files: ../point_src/math.js ../point_src/core/head.js ../point_src/pointpen.js ../point_src/pointdraw.js ../point_src/point-content.js ../point_src/pointlistpen.js ../point_src/pointlist.js ../point_src/point.js ../point_src/events.js ../point_src/automouse.js ../point_src/stage.js ../point_src/extras.js ../point_src/random.js ../point_src/distances.js ../point_src/dragging.js ../point_src/setunset.js ../point_src/stroke.js ../point_src/split.js ../point_src/curve-extras.js */ class MainStage extends Stage { canvas='playspace' live = true mounted(){ this.count = 80 let lpoints4 = [new Point(200, 300, 300, 90), new Point(800, 400, 200, 100)] this.curve2 = new BezierCurve(...lpoints4) this.dragging.add( ...lpoints4) // Ball setup this.ball = new Point({ x: 400, y: 100, radius: 15, vx: 0, vy: 0 }) // Physics constants this.gravity = 0.05 this.damping = 0.7 // Reduce bounce by 30% each collision this.dragging.add(this.ball) } draw(ctx){ this.clear(ctx) // Apply gravity to ball this.ball.vy += this.gravity this.ball.x += this.ball.vx this.ball.y += this.ball.vy this.curve2.render(ctx, {color: '#777'}) let normals = this.curve2.split(this.count, 0) normals.each.radius = 10 normals.pen.lines(ctx, 'green', 2) // Create and draw ray projecting in direction of velocity let rayLength = 100 let rayStart = new Point(this.ball.x, this.ball.y) // Calculate ray direction from velocity (or default to downward if no velocity) let velocityMag = Math.sqrt(this.ball.vx * this.ball.vx + this.ball.vy * this.ball.vy) let rayDirX, rayDirY if (velocityMag > 0.01) { // Ray points in direction of velocity rayDirX = this.ball.vx / velocityMag rayDirY = this.ball.vy / velocityMag } else { // Default to pointing downward if ball is stationary rayDirX = 0 rayDirY = 1 } let rayEnd = new Point( this.ball.x + rayDirX * rayLength, this.ball.y + rayDirY * rayLength ) ctx.strokeStyle = 'red' ctx.lineWidth = 2 // ctx.beginPath() // ctx.moveTo(rayStart.x, rayStart.y) // ctx.lineTo(rayEnd.x, rayEnd.y) // ctx.stroke() // Detect nearby normals along the ray path // First, find the closest normal point to use as the origin for sampling let closestNormal = null let minDist = Infinity normals.forEach(point => { let rayDx = rayEnd.x - rayStart.x let rayDy = rayEnd.y - rayStart.y let rayLengthSquared = rayDx * rayDx + rayDy * rayDy if (rayLengthSquared === 0) { let dist = Math.sqrt( Math.pow(point.x - rayStart.x, 2) + Math.pow(point.y - rayStart.y, 2) ) if (dist < minDist) { minDist = dist closestNormal = point } } else { // Project point onto ray line let t = ((point.x - rayStart.x) * rayDx + (point.y - rayStart.y) * rayDy) / rayLengthSquared t = Math.max(0, Math.min(1, t)) let closestX = rayStart.x + t * rayDx let closestY = rayStart.y + t * rayDy let distance = Math.sqrt( Math.pow(point.x - closestX, 2) + Math.pow(point.y - closestY, 2) ) if (distance < minDist) { minDist = distance closestNormal = point } } }) // Now find nearby normals evenly sampled around the closest normal let searchRadius = this.ball.radius let nearbyNormals = [] let minNormals = 3 if (closestNormal) { while (nearbyNormals.length < minNormals && searchRadius < 200) { nearbyNormals = normals.filter(point => { // Distance from the closest normal point (our origin) let distance = Math.sqrt( Math.pow(point.x - closestNormal.x, 2) + Math.pow(point.y - closestNormal.y, 2) ) return distance <= searchRadius }) if (nearbyNormals.length < minNormals) { searchRadius *= 1.5 } } } // Calculate average collision point from nearby normals let collisionPoint = null if (nearbyNormals.length > 0) { // Find the two closest points to rayEnd let sortedByDistance = nearbyNormals.slice().sort((a, b) => { let distA = Math.abs(a.x - rayEnd.x) let distB = Math.abs(b.x - rayEnd.x) return distA - distB }) let point1 = sortedByDistance[0] let point2 = sortedByDistance.length > 1 ? sortedByDistance[1] : sortedByDistance[0] // Project rayEnd onto the line segment between point1 and point2 let segmentDx = point2.x - point1.x let segmentDy = point2.y - point1.y let segmentLengthSquared = segmentDx * segmentDx + segmentDy * segmentDy let avgX, avgY, avgRadians if (segmentLengthSquared > 0) { // Calculate projection parameter t let t = ((rayEnd.x - point1.x) * segmentDx + (rayEnd.y - point1.y) * segmentDy) / segmentLengthSquared t = Math.max(0, Math.min(1, t)) // Clamp to [0, 1] // Calculate projected point on line segment avgX = point1.x + t * segmentDx avgY = point1.y + t * segmentDy // Interpolate the normal angle avgRadians = point1.radians + t * (point2.radians - point1.radians) } else { // Points are the same, use point1 avgX = point1.x avgY = point1.y avgRadians = point1.radians } // Calculate impact strength from current velocity let impactStrength = Math.sqrt( Math.pow(this.ball.vx, 2) + Math.pow(this.ball.vy, 2) ) collisionPoint = new Point({ x: avgX, y: avgY, radians: avgRadians, radius: impactStrength * 5 // Set radius to reflect incoming impact }) } // Check if ball is touching any nearby normals if (collisionPoint) { let distance = Math.sqrt( Math.pow(collisionPoint.x - this.ball.x, 2) + Math.pow(collisionPoint.y - this.ball.y, 2) ) // If distance is less than or equal to ball radius, collision detected if (distance <= this.ball.radius) { // Use collision point's normal vector for reflection let normalX = Math.cos(collisionPoint.radians) let normalY = Math.sin(collisionPoint.radians) // Ensure ball is never below the normal - position it exactly at ball.radius distance this.ball.x = collisionPoint.x + normalX * this.ball.radius this.ball.y = collisionPoint.y + normalY * this.ball.radius // Calculate dot product of velocity and normal let dotProduct = this.ball.vx * normalX + this.ball.vy * normalY // Reflect velocity along the normal this.ball.vx = (this.ball.vx - 2 * dotProduct * normalX) * this.damping this.ball.vy = (this.ball.vy - 2 * dotProduct * normalY) * this.damping - this.gravity } } // Draw nearby normals in a different color to show detection nearbyNormals.forEach(point => { ctx.fillStyle = '#00FFFFAA' ctx.beginPath() ctx.arc(point.x, point.y, 5, 0, Math.PI * 2) ctx.fill() }) // Draw collision point if it exists if (collisionPoint) { ctx.fillStyle = 'red' // ctx.beginPath() // ctx.arc(collisionPoint.x, collisionPoint.y, 8, 0, Math.PI * 2) collisionPoint.pen.indicator(ctx) // ctx.fill() } // Draw ball this.ball.pen.circle(ctx, 2) } } ;stage = MainStage.go();
Run
Meta Data
title Split
imports ()
files ('../point_src/math.js', '../point_src/core/head.js', '../point_src/pointpen.js', '../point_src/pointdraw.js', '../point_src/point-content.js', '../point_src/pointlistpen.js', '../point_src/pointlist.js', '../point_src/point.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/stage.js', '../point_src/extras.js', '../point_src/random.js', '../point_src/distances.js', '../point_src/dragging.js', '../point_src/setunset.js', '../point_src/stroke.js', '../point_src/split.js', '../point_src/curve-extras.js')
unused_keys ()
unknown_keys ('categories',)
categories ['split', 'curve']
filepath_exists True
path line-rider-splits.js
filepath line-rider-splits.js
clean_files ('../point_src/math.js', '../point_src/core/head.js', '../point_src/pointpen.js', '../point_src/pointdraw.js', '../point_src/compass.js', '../point_src/center.js', '../point_src/point-content.js', '../point_src/pointlistpen.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/relative-xy.js', '../point_src/pointcast.js', '../point_src/point.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/stage-resize.js', '../point_src/functions/resolve.js', '../point_src/stage.js', '../point_src/extras.js', '../point_src/random.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/functions/clamp.js', '../point_src/split.js', '../point_src/curve-extras.js')
markdown {'html': '', 'content': '---\ntitle: Split\ncategories: split\n curve\nfiles:\n ../point_src/math.js\n ../point_src/core/head.js\n ../point_src/pointpen.js\n ../point_src/pointdraw.js\n ../point_src/point-content.js\n ../point_src/pointlistpen.js\n ../point_src/pointlist.js\n ../point_src/point.js\n ../point_src/events.js\n ../point_src/automouse.js\n ../point_src/stage.js\n ../point_src/extras.js\n ../point_src/random.js\n ../point_src/distances.js\n ../point_src/dragging.js\n ../point_src/setunset.js\n ../point_src/stroke.js\n ../point_src/split.js\n ../point_src/curve-extras.js'}