Add local pointer field#41
Conversation
|
Follow-up pushed: What changed:
Validation:
|
|
CI update for Checks:
PR #41 is mergeable and clean against Maintenance note: the run emitted GitHub's Node.js 20 JavaScript action deprecation warning for |
Greptile SummaryThis PR introduces a small local pointer-attraction field ( Confidence Score: 4/5Safe to merge; two P2 quality notes in BlobPhysics.ts, no logic errors or regressions found. All findings are P2: magic numbers in the private pointer-field method and a sentinel-based neutral check with a known center-coordinate edge case. No P1/P0 issues identified. src/core/BlobPhysics.ts — pointer field constants and neutral sentinel worth revisiting before the next tuning slice. Important Files Changed
Sequence DiagramsequenceDiagram
participant Browser
participant BlobPhysics
participant ConvexBlob
Browser->>BlobPhysics: updateMousePosition(x, y)
Note over BlobPhysics: stores mouseX/Y,<br/>computes mouseVelX/Y
BlobPhysics->>BlobPhysics: updateScreensaverPhysics(blob, dt, t)
BlobPhysics->>ConvexBlob: blob.mouseDistance = dist(blob, mouse)
BlobPhysics->>BlobPhysics: applyAccelerometerForces(blob)
BlobPhysics->>BlobPhysics: applyPointerField(blob)
Note over BlobPhysics: skip if mouseX==50 && mouseY==50<br/>skip if distance >= 34<br/>apply (1-d/34)² × 0.0014 attraction
BlobPhysics->>ConvexBlob: velocityX += dx * scale
BlobPhysics->>ConvexBlob: velocityY += dy * scale
BlobPhysics->>BlobPhysics: updateMovementWithAccelerometer(blob, t)
BlobPhysics->>ConvexBlob: currentX += velocityX
BlobPhysics->>ConvexBlob: currentY += velocityY
BlobPhysics->>ConvexBlob: velocity *= 0.992 (damping)
Reviews (1): Last reviewed commit: "test(browser): cover CDP pointer deliver..." | Re-trigger Greptile |
| private applyPointerField(blob: ConvexBlob): void { | ||
| if (this.mouseX === 50 && this.mouseY === 50) return; | ||
|
|
||
| const dx = this.mouseX - blob.currentX; | ||
| const dy = this.mouseY - blob.currentY; | ||
| const distance = Math.sqrt(dx * dx + dy * dy); | ||
| if (distance === 0 || distance >= 34) return; | ||
|
|
||
| const normalized = 1 - distance / 34; | ||
| const scale = (normalized * normalized * 0.0014) / distance; | ||
| blob.velocityX += dx * scale; | ||
| blob.velocityY += dy * scale; | ||
| } |
There was a problem hiding this comment.
Magic numbers for pointer radius and strength
The hardcoded 34 (radius) and 0.0014 (strength) are embedded directly in the arithmetic without named constants. When the next tuning slice arrives (per the physics contract, "pointer-locality probes should be the next slice"), it will be easy to miss one of the two sites since the same values also appear implicitly in the test (distance >= 34, velocityX > 0). Extracting them as private named constants keeps the relationship explicit.
| private applyPointerField(blob: ConvexBlob): void { | |
| if (this.mouseX === 50 && this.mouseY === 50) return; | |
| const dx = this.mouseX - blob.currentX; | |
| const dy = this.mouseY - blob.currentY; | |
| const distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance === 0 || distance >= 34) return; | |
| const normalized = 1 - distance / 34; | |
| const scale = (normalized * normalized * 0.0014) / distance; | |
| blob.velocityX += dx * scale; | |
| blob.velocityY += dy * scale; | |
| } | |
| private static readonly POINTER_FIELD_RADIUS = 34; | |
| private static readonly POINTER_FIELD_STRENGTH = 0.0014; | |
| private applyPointerField(blob: ConvexBlob): void { | |
| if (this.mouseX === 50 && this.mouseY === 50) return; | |
| const dx = this.mouseX - blob.currentX; | |
| const dy = this.mouseY - blob.currentY; | |
| const distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance === 0 || distance >= BlobPhysics.POINTER_FIELD_RADIUS) return; | |
| const normalized = 1 - distance / BlobPhysics.POINTER_FIELD_RADIUS; | |
| const scale = (normalized * normalized * BlobPhysics.POINTER_FIELD_STRENGTH) / distance; | |
| blob.velocityX += dx * scale; | |
| blob.velocityY += dy * scale; | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| } | ||
|
|
||
| private applyPointerField(blob: ConvexBlob): void { | ||
| if (this.mouseX === 50 && this.mouseY === 50) return; |
There was a problem hiding this comment.
Neutral sentinel silently disables field at coordinate center
mouseX === 50 && mouseY === 50 is the sentinel for "no pointer input yet / pointer reset to neutral." If the coordinate space is [0, 100] (which mouseX = 50 at init and the mouseDistance formula suggest), a user whose cursor legitimately lands at the exact center will get no pointer attraction. The sentinel conflates "uninitialized" with a valid position. A separate boolean flag (e.g. private pointerActive = false) would eliminate the ambiguity without a performance cost.
Summary
Stacked on PR #39. Adds the first conservative standalone pointer-field slice:
Refs #26 and #40.
Validation
pnpm vitest run tests/unit/core.test.ts tests/unit/interaction-field.test.tspnpm run checkpnpm run test(181 tests)pnpm run buildpnpm run check:bundle-size(11.00 KiBgzip, rounded0.00 KiBtarget overage; below12.00 KiBgate)pnpm run test:browser:motionNotes
This deliberately keeps pointer strength/radius private and low. Browser pointer-locality probes should be the next slice before any stronger tuning.