Skip to content

Conversation

zardoy
Copy link
Owner

@zardoy zardoy commented Aug 15, 2025

Enhance waypoint visibility by adding directional arrows for off-screen waypoints and improving text clarity with higher resolution and larger fonts.


Open in Cursor Open in Web

Summary by CodeRabbit

  • New Features

    • Offscreen waypoint arrows now display when a waypoint is outside the view, guiding you toward targets.
    • Added an option to hide offscreen waypoint arrows.
  • Improvements

    • Sharper waypoint visuals and text via higher-resolution rendering.
    • Waypoint arrows are enabled by default.
    • Smoother visibility transitions between on-screen and offscreen indicators for clearer navigation.
  • Chores

    • Improved resource cleanup for waypoint visuals to maintain performance.

Copy link

cursor bot commented Aug 15, 2025

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

Introduces an offscreen waypoint arrow sprite with configurable visibility, positioning, and texture updates. Expands rendering configuration (canvas size/scale, layout). Updates drawing pipeline to support scaled canvases and optional contexts. Ensures both on-screen and offscreen sprites synchronize color, label, and distance, and disposes related resources.

Changes

Cohort / File(s) Summary
Waypoint sprite rendering & offscreen overlay
renderer/viewer/three/waypointSprite.ts
Adds offscreen arrow sprite with creation/management via ensureOffscreenSprite; extends visibility logic with ARROW.hideOffscreenSprite; propagates updates (color/label/distance) to offscreen texture; positions/scales offscreen sprite toward screen center; increases CANVAS_SIZE 256→512 and CANVAS_SCALE 2→3; adjusts LAYOUT.DISTANCE_Y 0.55→0.62; enables ARROW.enabledDefault true; drawCombinedCanvas now accepts optional ctx/size for high-res rendering; dispose cleans offscreen resources.
Waypoints usage note
renderer/viewer/three/waypoints.ts
Adds a clarifying inline comment about always enabling offscreen arrows; no functional change.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App
  participant Waypoints as Waypoints
  participant Sprite as WaypointSprite
  participant Scene

  App->>Waypoints: addWaypoint(data)
  Waypoints->>Sprite: new WaypointSprite(data)
  Waypoints->>Sprite: enableOffscreenArrow(true)
  Sprite->>Sprite: ensureOffscreenSprite() [lazy create]
  Sprite->>Scene: attach main sprite
  Sprite->>Scene: attach offscreen sprite

  rect rgba(200,240,255,0.3)
  note over Sprite: On update (position/color/label/distance)
  Sprite->>Sprite: drawCombinedCanvas(ctx?, size?)<br/>update textures (main + offscreen)
  end

  alt Waypoint off-screen
    Sprite->>Sprite: compute offset toward screen center
    Sprite->>Sprite: show offscreen sprite
    Sprite->>Sprite: toggle main visibility per hideOffscreenSprite
  else Waypoint on-screen
    Sprite->>Sprite: show main sprite
    Sprite->>Sprite: hide offscreen sprite
  end

  App-->>Sprite: dispose()
  Sprite->>Scene: detach sprites (main + offscreen)
  Sprite->>Sprite: release textures/resources
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I hop between the screen and sky,
An arrow twin to guide your eye.
One stays near, one drifts afar—
Both painted bright on higher char.
With bigger canvases we glide,
Offscreen, onscreen—side by side. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the PR's primary intent—improving off-screen waypoint visibility and label clarity—which matches the changes adding offscreen directional arrows and higher-resolution label rendering; it is concise and relevant to the changeset. The only issue is the extraneous "[stale]" prefix which may confuse reviewers about PR status.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cursor/improve-off-screen-waypoint-visibility-and-clarity-3e0e

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zardoy zardoy marked this pull request as ready for review August 15, 2025 22:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
renderer/viewer/three/graphicsBackend.ts (1)

56-110: Optional: Type augment the Window interface for the new helpers

If you want stronger typing (and to avoid future TS complaints about unknown window.* props), add a global declaration:

declare global {
  interface Window {
    testWaypoints: () => void
    addTestWaypoint: (id: string, x: number, y: number, z: number, color?: number, label?: string) => void
    removeWaypoint: (id: string) => void
    listWaypoints: () => void
    world?: WorldRendererThree
  }
}
waypoint-test.html (1)

1-145: Test page reads well; no blocking issues

Content is clear and aligns with the PR’s goals. Consider linking to docs or including a short “when these commands are available” note (i.e., only after window.world is set) to reduce confusion, but it’s already implied in the “fully loaded” note.

If this is meant to live in the repo long-term, think about where it’s hosted/served and whether it should be excluded from production builds.

renderer/viewer/three/waypointSprite.ts (4)

126-174: Avoid baking the arrow into the same texture if you plan to rotate it

Baking the arrow overlay and the text into a single texture forces you to rotate both together. Consider either:

  • Two-sprite approach: unrotated base sprite + small arrow-only sprite on top, rotated per frame, or
  • Dynamic canvas redraw per frame: redraw base (once or cached) and overlay the arrow with ctx.rotate(angle); update the texture only if the angle meaningfully changed.

This will keep labels readable off-screen and opens the door for animating/skinable arrows later.


345-345: Comment mismatch: 5% radius, not ~12%

The code uses 5% of canvas height. Update the comment or adjust the constant.

-  const radius = Math.round(sizeCanvas * 0.05) // Dot takes up ~12% of canvas height
+  const radius = Math.round(sizeCanvas * 0.05) // Dot takes up ~5% of canvas height

18-21: Unused config field: ARROW.pixelSize

ARROW.pixelSize appears unused after switching to TARGET_SCREEN_PX-based sizing. Consider removing it to avoid confusion.


128-166: Fix ESLint: trailing spaces

Strip trailing spaces on the flagged lines to satisfy @stylistic/no-trailing-spaces.

-    if (arrowSprite) return
-    
+    if (arrowSprite) return
...
-    const ctx = canvas.getContext('2d')!
-    
+    const ctx = canvas.getContext('2d')!
...
-    drawCombinedCanvas(color, currentLabel, '0m', ctx, size)
-    
+    drawCombinedCanvas(color, currentLabel, '0m', ctx, size)
...
-    const centerY = size / 2
-    
+    const centerY = size / 2
...
-    ctx.translate(centerX, centerY)
-    
+    ctx.translate(centerX, centerY)
...
-    ctx.closePath()
-    
+    ctx.closePath()
...
-    ctx.stroke()
-    
+    ctx.stroke()
...
-    ctx.restore()
-    
+    ctx.restore()
...
-    // Calculate the angle in the XZ plane (horizontal direction)
-    const horizontalAngle = Math.atan2(direction.x, direction.z)
-    
+    // Calculate the angle in the XZ plane (horizontal direction)
+    const horizontalAngle = Math.atan2(direction.x, direction.z)

Also applies to: 268-271

WAYPOINT_IMPROVEMENTS.md (1)

1-137: Minor editorial: keep “off-screen” wording consistent and tighten phrasing

You mix “offscreen” and “off-screen”. Pick one and use consistently (project seems to prefer “off-screen” in most places). Also a few sentences can be tightened, but nothing blocking.

If desired, I can submit a quick pass normalizing “off-screen” and polishing a few lines.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cc4f705 and e4541cd.

📒 Files selected for processing (5)
  • WAYPOINT_IMPROVEMENTS.md (1 hunks)
  • renderer/viewer/three/graphicsBackend.ts (1 hunks)
  • renderer/viewer/three/waypointSprite.ts (4 hunks)
  • renderer/viewer/three/waypoints.ts (1 hunks)
  • waypoint-test.html (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
renderer/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/vars-usage.mdc)

renderer/**/*.ts: Do not use the global variable bot directly in any file under the renderer/ directory or its subfolders (e.g., renderer/viewer/three/worldrendererThree.ts).
In renderer code, all bot/player state and events must be accessed via explicit interfaces, state managers, or passed-in objects, never by referencing bot directly.
In renderer code (such as in WorldRendererThree), use the playerState property (e.g., worldRenderer.playerState.gameMode) to access player state. The implementation for playerState lives in src/mineflayer/playerState.ts.

Files:

  • renderer/viewer/three/waypoints.ts
  • renderer/viewer/three/graphicsBackend.ts
  • renderer/viewer/three/waypointSprite.ts
🧬 Code Graph Analysis (2)
renderer/viewer/three/graphicsBackend.ts (2)
src/react/RendererDebugMenu.tsx (1)
  • worldRenderer (10-13)
renderer/viewer/three/primitives.js (3)
  • color (8-8)
  • color (21-21)
  • color (37-37)
renderer/viewer/three/waypointSprite.ts (1)
renderer/viewer/three/primitives.js (1)
  • THREE (3-3)
🪛 GitHub Check: build-and-deploy
renderer/viewer/three/graphicsBackend.ts

[failure] 90-90:
Cannot find name 'worldRenderer'.


[failure] 89-89:
Cannot find name 'worldRenderer'.


[failure] 81-81:
Cannot find name 'worldRenderer'.


[failure] 80-80:
Cannot find name 'worldRenderer'.


[failure] 69-69:
Cannot find name 'worldRenderer'.


[failure] 68-68:
Cannot find name 'worldRenderer'.


[failure] 60-60:
Cannot find name 'worldRenderer'.


[failure] 59-59:
Cannot find name 'worldRenderer'.

🪛 ESLint
renderer/viewer/three/graphicsBackend.ts

[error] 66-66: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 69-69: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 70-70: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 71-71: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 78-78: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 87-87: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 95-95: Use for…of instead of .forEach(…).

(unicorn/no-array-for-each)


[error] 103-103: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)

renderer/viewer/three/waypointSprite.ts

[error] 128-128: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 136-136: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 139-139: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 144-144: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 148-148: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 157-157: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 164-164: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 166-166: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 268-268: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)


[error] 271-271: Trailing spaces not allowed.

(@stylistic/no-trailing-spaces)

🪛 GitHub Actions: CI
renderer/viewer/three/graphicsBackend.ts

[error] 59-59: TS2304: Cannot find name 'worldRenderer'.

🪛 LanguageTool
WAYPOINT_IMPROVEMENTS.md

[grammar] ~1-~1: There might be a mistake here.
Context: # 🎯 Waypoint System Improvements ## Overview This document outlines the impro...

(QB_NEW_EN)


[grammar] ~3-~3: There might be a mistake here.
Context: ...aypoint System Improvements ## Overview This document outlines the improvements ...

(QB_NEW_EN)


[grammar] ~8-~8: There might be a mistake here.
Context: ... 1. Directional Arrows When Out of Sight - Before: Simple triangle arrows when wa...

(QB_NEW_EN)


[grammar] ~9-~9: There might be a mistake here.
Context: ...le arrows when waypoints were off-screen - After: Full waypoint sprites with dire...

(QB_NEW_EN)


[grammar] ~10-~10: There might be a mistake here.
Context: ...ow overlays pointing toward the waypoint - Benefit: Players can see the complete ...

(QB_NEW_EN)


[grammar] ~13-~13: There might be a mistake here.
Context: ...l guidance ### 2. Enhanced Text Clarity - Font Sizes: - Title font increased ...

(QB_NEW_EN)


[grammar] ~14-~14: There might be a mistake here.
Context: ... Enhanced Text Clarity - Font Sizes: - Title font increased from 8% to 12% of c...

(QB_NEW_EN)


[grammar] ~21-~21: There might be a mistake here.
Context: ...and contrast ### 3. Improved Resolution - Canvas Size: Doubled from 256x256 to 5...

(QB_NEW_EN)


[grammar] ~22-~22: There might be a mistake here.
Context: ...: Doubled from 256x256 to 512x512 pixels - Scale Factor: Increased from 2x to 3x ...

(QB_NEW_EN)


[grammar] ~23-~23: There might be a mistake here.
Context: ...from 2x to 3x for sharper text rendering - Result: Much crisper, more readable te...

(QB_NEW_EN)


[grammar] ~26-~26: There might be a mistake here.
Context: ... on all devices ## 🔧 Technical Changes ### Files Modified 1. **`renderer/viewer/thre...

(QB_NEW_EN)


[uncategorized] ~36-~36: Do not mix variants of the same word (‘offscreen’ and ‘off-screen’) within a single text.
Context: ...ewer/three/waypoints.ts`** - Enabled offscreen arrows by default for all waypoints 3....

(EN_WORD_COHERENCY)


[grammar] ~67-~67: There might be a mistake here.
Context: .../ 9% of canvas height ``` ## 🧪 Testing ### Console Commands The following functions ...

(QB_NEW_EN)


[grammar] ~69-~69: There might be a mistake here.
Context: ...``` ## 🧪 Testing ### Console Commands The following functions are now availabl...

(QB_NEW_EN)


[grammar] ~86-~86: There might be a mistake here.
Context: ...stWaypoints() ``` ### Test Instructions 1. Open the Minecraft Web Client 2. Wait fo...

(QB_NEW_EN)


[grammar] ~89-~89: There might be a mistake here.
Context: ...ait for the world to load completely 3. Open browser console (F12 → Console) 4. Use ...

(QB_NEW_EN)


[grammar] ~94-~94: There might be a mistake here.
Context: ... text clarity ## 🎨 Visual Improvements ### Before vs After - Visibility: Waypoin...

(QB_NEW_EN)


[grammar] ~96-~96: There might be a mistake here.
Context: ...Visual Improvements ### Before vs After - Visibility: Waypoints now maintain ful...

(QB_NEW_EN)


[grammar] ~103-~103: There might be a mistake here.
Context: ...or improved visibility ### Arrow Design - Shows the complete waypoint sprite (dot,...

(QB_NEW_EN)


[grammar] ~104-~104: There might be a mistake here.
Context: ...te waypoint sprite (dot, name, distance) - Overlays a white directional arrow with ...

(QB_NEW_EN)


[grammar] ~105-~105: There might be a mistake here.
Context: ...e) - Overlays a white directional arrow with black outline - Arrow size is 15% of ca...

(QB_NEW_EN)


[grammar] ~105-~105: There might be a mistake here.
Context: ...ite directional arrow with black outline - Arrow size is 15% of canvas for good vis...

(QB_NEW_EN)


[grammar] ~106-~106: There might be a mistake here.
Context: ... with black outline - Arrow size is 15% of canvas for good visibility - Automatica...

(QB_NEW_EN)


[grammar] ~106-~106: There might be a mistake here.
Context: ...ize is 15% of canvas for good visibility - Automatically rotates to point toward th...

(QB_NEW_EN)


[grammar] ~110-~110: There might be a mistake here.
Context: ... waypoint ## 📱 Device Compatibility - Higher resolution ensures crisp rendering...

(QB_NEW_EN)


[grammar] ~111-~111: There might be a mistake here.
Context: ... crisp rendering on high-DPI displays - Improved scaling handles various device p...

(QB_NEW_EN)


[grammar] ~113-~113: There might be a mistake here.
Context: ...tter contrast in all lighting conditions ## 🚀 Performance Impact - Minimal performan...

(QB_NEW_EN)


[grammar] ~116-~116: There might be a mistake here.
Context: ... conditions ## 🚀 Performance Impact - Minimal performance impact from increased...

(QB_NEW_EN)


[grammar] ~117-~117: There might be a mistake here.
Context: ...nce impact from increased canvas size - Efficient texture management with proper ...

(QB_NEW_EN)


[grammar] ~118-~118: There might be a mistake here.
Context: ...xture management with proper disposal - Optimized rendering with consistent scali...

(QB_NEW_EN)


[grammar] ~119-~119: There might be a mistake here.
Context: ...zed rendering with consistent scaling - No additional memory overhead for arrow o...

(QB_NEW_EN)


[grammar] ~119-~119: There might be a mistake here.
Context: ...ional memory overhead for arrow overlays ## 🔮 Future Enhancements Potential areas fo...

(QB_NEW_EN)


[grammar] ~121-~121: There might be a mistake here.
Context: ...row overlays ## 🔮 Future Enhancements Potential areas for further improvement: ...

(QB_NEW_EN)


[grammar] ~123-~123: There might be a mistake here.
Context: ...ential areas for further improvement: - Configurable arrow styles and colors - An...

(QB_NEW_EN)


[grammar] ~124-~124: There might be a mistake here.
Context: ... Configurable arrow styles and colors - Animated directional indicators - Distanc...

(QB_NEW_EN)


[grammar] ~125-~125: There might be a mistake here.
Context: ...ors - Animated directional indicators - Distance-based arrow sizing - Custom wayp...

(QB_NEW_EN)


[grammar] ~126-~126: There might be a mistake here.
Context: ...icators - Distance-based arrow sizing - Custom waypoint icons - Waypoint grouping...

(QB_NEW_EN)


[grammar] ~127-~127: There might be a mistake here.
Context: ... arrow sizing - Custom waypoint icons - Waypoint grouping and organization ## 📋...

(QB_NEW_EN)


[grammar] ~127-~127: There might be a mistake here.
Context: ...ons - Waypoint grouping and organization ## 📋 Summary The waypoint system now provid...

(QB_NEW_EN)


[grammar] ~129-~129: There might be a mistake here.
Context: ...rouping and organization ## 📋 Summary The waypoint system now provides: 1. **Be...

(QB_NEW_EN)


[grammar] ~131-~131: There might be a mistake here.
Context: ... The waypoint system now provides: 1. Better Navigation: Clear directional gu...

(QB_NEW_EN)


[grammar] ~132-~132: There might be a mistake here.
Context: ...ctional guidance when out of sight 2. Improved Readability: Larger, sharper t...

(QB_NEW_EN)


[grammar] ~133-~133: There might be a mistake here.
Context: ... sharper text with better contrast 3. Enhanced Quality: Higher resolution ren...

(QB_NEW_EN)


[grammar] ~134-~134: There might be a mistake here.
Context: ...solution rendering for all devices 4. Consistent Experience: Same visual styl...

(QB_NEW_EN)


[grammar] ~135-~135: There might be a mistake here.
Context: ...tyle whether visible or off-screen 5. Easy Testing: Built-in console commands...

(QB_NEW_EN)


[grammar] ~135-~135: There might be a mistake here.
Context: ...ole commands for development and testing These improvements make waypoints much mo...

(QB_NEW_EN)

🔇 Additional comments (1)
renderer/viewer/three/waypoints.ts (1)

82-82: LGTM: Explicitly enabling offscreen arrows

Clarifies intent and aligns with the new default. No functional concerns here.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

const offscreenMat = offscreenSprite.material
offscreenMat.map?.dispose()
offscreenMat.map = offscreenTexture
offscreenMat.needsUpdate = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Canvas Cloning Fails for Offscreen Textures

Using canvas.cloneNode(true) to create textures for offscreen sprites is incorrect. cloneNode only copies the DOM element structure, not its drawn content, causing offscreen sprites to display blank textures instead of the waypoint graphics when updated in setColor, setLabel, and updateDistanceText.

Additional Locations (2)

Fix in Cursor Fix in Web

@zardoy zardoy changed the title Improve off-screen waypoint visibility and clarity [stale] Improve off-screen waypoint visibility and clarity Sep 19, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
renderer/viewer/three/waypointSprite.ts (1)

269-272: Screen-space projection math and scaling look solid.

Using camera basis (forward/right/upCam), clamping to padded NDC edges, and scaling via worldUnitsPerScreenHeightAtDist yields stable placement and pixel-consistent sizing. Nice.

Also applies to: 291-303

🧹 Nitpick comments (4)
renderer/viewer/three/waypointSprite.ts (4)

296-298: Rotate arrow by the actual clamped on-screen direction to avoid slight orientation mismatch.

Use finalNdcX/Y (after padding clamp) so the arrow points exactly toward its placed position:

-    const angle = Math.atan2(ry, rx)
-    arrowSprite.material.rotation = angle - Math.PI / 2
+    const angle = Math.atan2(finalNdcY, finalNdcX)
+    ;(arrowSprite.material as THREE.SpriteMaterial).rotation = angle - Math.PI / 2

Also applies to: 269-272, 279-281


341-343: Avoid per-frame CanvasTexture churn for distance text; reuse a persistent canvas/texture and throttle updates.

Right now every frame allocates a new CanvasTexture and disposes the old one (for both sprites). This is a hot path and will fragment GPU memory. Keep one canvas/texture per sprite and only redraw when the rounded meters change (or on a timer).

I can draft a targeted refactor that:

  • creates mainCanvas/mainCtx and offscreenCanvas/offscreenCtx once,
  • updates existing textures via texture.needsUpdate = true,
  • updates at most every 100–200ms or when value changes.

183-199: Unify sprite creation to avoid subtle material/filters drift.

ensureOffscreenSprite reimplements createCombinedSprite and omits filter setup. Prefer reusing createCombinedSprite or mirror its filters:

-    const material = new THREE.SpriteMaterial({
+    const material = new THREE.SpriteMaterial({
       map: texture,
       transparent: true,
       depthTest: false,
       depthWrite: false
     })
+    texture.anisotropy = 1
+    texture.magFilter = THREE.LinearFilter
+    texture.minFilter = THREE.LinearFilter

403-404: Comment nit: 0.05 radius ≈ 10% diameter, not ~12%.

-  const radius = Math.round(sizeCanvas * 0.05) // Dot takes up ~12% of canvas height
+  const radius = Math.round(sizeCanvas * 0.05) // Dot diameter ~10% of canvas height
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4541cd and 50a5a38.

📒 Files selected for processing (1)
  • renderer/viewer/three/waypointSprite.ts (12 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
renderer/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/vars-usage.mdc)

renderer/**/*.ts: Do not use the global variable bot directly in any file under the renderer/ directory or its subfolders (e.g., renderer/viewer/three/worldrendererThree.ts).
In renderer code, all bot/player state and events must be accessed via explicit interfaces, state managers, or passed-in objects, never by referencing bot directly.
In renderer code (such as in WorldRendererThree), use the playerState property (e.g., worldRenderer.playerState.gameMode) to access player state. The implementation for playerState lives in src/mineflayer/playerState.ts.

Files:

  • renderer/viewer/three/waypointSprite.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-and-deploy

Comment on lines +88 to 96
// Update offscreen sprite if it exists
if (offscreenSprite) {
const offscreenTexture = new THREE.CanvasTexture(canvas.cloneNode(true) as HTMLCanvasElement)
const offscreenMat = offscreenSprite.material
offscreenMat.map?.dispose()
offscreenMat.map = offscreenTexture
offscreenMat.needsUpdate = true
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Offscreen texture updates use canvas.cloneNode() — results in blank textures. Use the same drawn canvas and keep filter parity.

cloneNode on a canvas doesn’t copy the drawing buffer, so the offscreen sprite will intermittently render empty after updates. Also, new textures created here don’t match the filters set in createCombinedSprite, causing inconsistent appearance.

Apply the following diffs in all three update sites (setColor, setLabel, updateDistanceText):

@@
-    // Update offscreen sprite if it exists
-    if (offscreenSprite) {
-      const offscreenTexture = new THREE.CanvasTexture(canvas.cloneNode(true) as HTMLCanvasElement)
-      const offscreenMat = offscreenSprite.material
-      offscreenMat.map?.dispose()
-      offscreenMat.map = offscreenTexture
-      offscreenMat.needsUpdate = true
-    }
+    // Update offscreen sprite if it exists
+    if (offscreenSprite) {
+      const offscreenTexture = new THREE.CanvasTexture(canvas)
+      offscreenTexture.anisotropy = 1
+      offscreenTexture.magFilter = THREE.LinearFilter
+      offscreenTexture.minFilter = THREE.LinearFilter
+      const offscreenMat = offscreenSprite.material as THREE.SpriteMaterial
+      offscreenMat.map?.dispose()
+      offscreenMat.map = offscreenTexture
+      offscreenMat.needsUpdate = true
+    }

And ensure main-sprite textures also keep the same filters:

@@
-    const texture = new THREE.CanvasTexture(canvas)
+    const texture = new THREE.CanvasTexture(canvas)
+    texture.anisotropy = 1
+    texture.magFilter = THREE.LinearFilter
+    texture.minFilter = THREE.LinearFilter
@@
-    const texture = new THREE.CanvasTexture(canvas)
+    const texture = new THREE.CanvasTexture(canvas)
+    texture.anisotropy = 1
+    texture.magFilter = THREE.LinearFilter
+    texture.minFilter = THREE.LinearFilter
@@
-    const texture = new THREE.CanvasTexture(canvas)
+    const texture = new THREE.CanvasTexture(canvas)
+    texture.anisotropy = 1
+    texture.magFilter = THREE.LinearFilter
+    texture.minFilter = THREE.LinearFilter

Also applies to: 107-114, 125-132

🤖 Prompt for AI Agents
In renderer/viewer/three/waypointSprite.ts around lines 88-96 (and similarly at
107-114 and 125-132), the update logic uses canvas.cloneNode(true) which does
not copy the canvas drawing buffer and creates a blank/offscreen texture;
replace cloning with reusing the same drawn HTMLCanvasElement (pass the original
canvas instance) when creating the new THREE.CanvasTexture, and copy the
texture/filter properties used in createCombinedSprite (e.g., magFilter,
minFilter, generateMipmaps, and any other sampler settings) onto the newly
created texture (and the main sprite texture where updated) so the offscreen and
main sprites keep identical filtering and rendering parity, then call
.needsUpdate = true and dispose the old map as already done.

Comment on lines +316 to +323
offscreenSprite.visible = true
offscreenSprite.position.copy(spritePos)
offscreenSprite.material.rotation = 0 // No rotation for the sprite

// Scale offscreen sprite to match the main sprite size
const spritePx = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.TARGET_SCREEN_PX / viewportHeightPx)
offscreenSprite.scale.set(spritePx, spritePx, 1)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Respect ARROW.hideOffscreenSprite: don’t show the offscreen full sprite when configured to “arrow only.”

Currently, offscreenSprite is forced visible offscreen regardless of the flag. Gate it:

-    offscreenSprite.visible = true
+    offscreenSprite.visible = !WAYPOINT_CONFIG.ARROW.hideOffscreenSprite
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
offscreenSprite.visible = true
offscreenSprite.position.copy(spritePos)
offscreenSprite.material.rotation = 0 // No rotation for the sprite
// Scale offscreen sprite to match the main sprite size
const spritePx = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.TARGET_SCREEN_PX / viewportHeightPx)
offscreenSprite.scale.set(spritePx, spritePx, 1)
offscreenSprite.visible = !WAYPOINT_CONFIG.ARROW.hideOffscreenSprite
offscreenSprite.position.copy(spritePos)
offscreenSprite.material.rotation = 0 // No rotation for the sprite
// Scale offscreen sprite to match the main sprite size
const spritePx = worldUnitsPerScreenHeightAtDist * (WAYPOINT_CONFIG.TARGET_SCREEN_PX / viewportHeightPx)
offscreenSprite.scale.set(spritePx, spritePx, 1)
🤖 Prompt for AI Agents
In renderer/viewer/three/waypointSprite.ts around lines 316 to 323, the
offscreen full sprite is always made visible even when ARROW.hideOffscreenSprite
(or the equivalent config flag) requests "arrow only"; change the logic so
offscreenSprite.visible is set conditionally: if ARROW.hideOffscreenSprite is
truthy then do not make the full offscreen sprite visible (leave it hidden and
rely on the arrow rendering path), otherwise set offscreenSprite.visible = true
and proceed to copy position, reset rotation, and scale. Ensure the conditional
checks the correct config variable and preserves existing
position/scale/rotation updates only when the sprite will be shown.

Comment on lines +389 to +395
function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
const scale = WAYPOINT_CONFIG.CANVAS_SCALE * (globalThis.devicePixelRatio || 1)
const size = WAYPOINT_CONFIG.CANVAS_SIZE * scale
const sizeCanvas = size || WAYPOINT_CONFIG.CANVAS_SIZE * scale
const canvas = document.createElement('canvas')
canvas.width = size
canvas.height = size
const ctx = canvas.getContext('2d')!
canvas.width = sizeCanvas
canvas.height = sizeCanvas
const ctxCanvas = ctx || canvas.getContext('2d')!
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Texture size can exceed common GPU limits (DPR-multiplied 512×3 → 4608 on DPR=3). Clamp and cap DPR to prevent perf/alloc spikes.

Multiplying CANVAS_SIZE (512) by CANVAS_SCALE (3) and devicePixelRatio produces >4K canvases on high-DPR displays, risking stalls and OOM on some GPUs. Cap DPR and clamp to a safe max size:

-function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
-  const scale = WAYPOINT_CONFIG.CANVAS_SCALE * (globalThis.devicePixelRatio || 1)
-  const sizeCanvas = size || WAYPOINT_CONFIG.CANVAS_SIZE * scale
+function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
+  const deviceScale = Math.min(globalThis.devicePixelRatio || 1, 2) // cap DPR impact
+  const scale = WAYPOINT_CONFIG.CANVAS_SCALE * deviceScale
+  const baseSize = WAYPOINT_CONFIG.CANVAS_SIZE * scale
+  const MAX_TEX = 2048
+  const sizeCanvas = size || Math.min(baseSize, MAX_TEX)

Optionally consider reducing CANVAS_SCALE to 2 for mobile profiles.

Also applies to: 8-8, 16-16, 21-23


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Searching repository for drawCombinedCanvas, WAYPOINT_CONFIG, CANVAS_SIZE, CANVAS_SCALE, devicePixelRatio..."

if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore-vcs -S "drawCombinedCanvas|WAYPOINT_CONFIG|CANVAS_SIZE|CANVAS_SCALE|devicePixelRatio" || true
else
  echo "rg (ripgrep) not found; using find+grep fallback"
  find . -type f -name '*.ts' -o -name '*.js' -o -name '*.tsx' -o -name '*.jsx' | xargs grep -nH -E "drawCombinedCanvas|WAYPOINT_CONFIG|CANVAS_SIZE|CANVAS_SCALE|devicePixelRatio" || true
fi

# Show the waypointSprite file if present
WSP=$(rg -n --hidden --no-ignore-vcs -S "waypointSprite.ts" -l || true)
if [ -n "$WSP" ]; then
  echo "Found waypointSprite file(s):"
  echo "$WSP"
  for f in $WSP; do
    echo "---- $f (first 400 lines) ----"
    sed -n '1,400p' "$f" | nl -ba
  done
else
  echo "No waypointSprite.ts file found by name; trying to locate files containing drawCombinedCanvas..."
  FILES=$(rg -n --hidden --no-ignore-vcs -S "drawCombinedCanvas" -l || true)
  if [ -n "$FILES" ]; then
    echo "Files with drawCombinedCanvas:"
    echo "$FILES"
    for f in $FILES; do
      echo "---- $f (first 400 lines) ----"
      sed -n '1,400p' "$f" | nl -ba
    done
  else
    echo "No files contain drawCombinedCanvas."
  fi
fi

echo "Searching for definitions/values of CANVAS_SIZE / CANVAS_SCALE / WAYPOINT_CONFIG..."
CFGFILES=$(rg -n --hidden --no-ignore-vcs -S "CANVAS_SIZE|CANVAS_SCALE|WAYPOINT_CONFIG" -l || true)
if [ -n "$CFGFILES" ]; then
  for f in $CFGFILES; do
    echo "---- $f ----"
    rg -n --hidden --no-ignore-vcs -S "CANVAS_SIZE|CANVAS_SCALE|WAYPOINT_CONFIG" "$f" -n -C3 || true
    echo "File head:"
    sed -n '1,200p' "$f" | nl -ba
  done
else
  echo "No files found defining CANVAS_SIZE/CANVAS_SCALE/WAYPOINT_CONFIG."
fi

echo "Searching for raw uses of canvas.width/height and getContext..."
rg -n --hidden --no-ignore-vcs -S "canvas.width|canvas.height|getContext\\(" || true

echo "Done."

Length of output: 3105


Clamp DPR and cap canvas size to avoid >4K textures

WAYPOINT_CONFIG.CANVAS_SIZE = 512 and CANVAS_SCALE = 3 are multiplied by devicePixelRatio in drawCombinedCanvas — DPR=3 → 51233 = 4608px, which can cause GPU stalls/OOM. Cap DPR and clamp the computed canvas size.

Location: renderer/viewer/three/waypointSprite.ts (drawCombinedCanvas at ~lines 389–395; WAYPOINT_CONFIG at top ~lines 4–16)

-function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
-  const scale = WAYPOINT_CONFIG.CANVAS_SCALE * (globalThis.devicePixelRatio || 1)
-  const sizeCanvas = size || WAYPOINT_CONFIG.CANVAS_SIZE * scale
+function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
+  const deviceScale = Math.min(globalThis.devicePixelRatio || 1, 2) // cap DPR impact
+  const scale = WAYPOINT_CONFIG.CANVAS_SCALE * deviceScale
+  const baseSize = WAYPOINT_CONFIG.CANVAS_SIZE * scale
+  const MAX_TEX = 2048
+  const sizeCanvas = size || Math.min(baseSize, MAX_TEX)

Also review other drawCombinedCanvas call sites in this file to ensure they don’t force oversized textures.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
const scale = WAYPOINT_CONFIG.CANVAS_SCALE * (globalThis.devicePixelRatio || 1)
const size = WAYPOINT_CONFIG.CANVAS_SIZE * scale
const sizeCanvas = size || WAYPOINT_CONFIG.CANVAS_SIZE * scale
const canvas = document.createElement('canvas')
canvas.width = size
canvas.height = size
const ctx = canvas.getContext('2d')!
canvas.width = sizeCanvas
canvas.height = sizeCanvas
const ctxCanvas = ctx || canvas.getContext('2d')!
function drawCombinedCanvas (color: number, id: string, distance: string, ctx?: CanvasRenderingContext2D, size?: number): HTMLCanvasElement {
const deviceScale = Math.min(globalThis.devicePixelRatio || 1, 2) // cap DPR impact
const scale = WAYPOINT_CONFIG.CANVAS_SCALE * deviceScale
const baseSize = WAYPOINT_CONFIG.CANVAS_SIZE * scale
const MAX_TEX = 2048
const sizeCanvas = size || Math.min(baseSize, MAX_TEX)
const canvas = document.createElement('canvas')
canvas.width = sizeCanvas
canvas.height = sizeCanvas
const ctxCanvas = ctx || canvas.getContext('2d')!
🤖 Prompt for AI Agents
In renderer/viewer/three/waypointSprite.ts around lines 389–395, drawing
multiplies WAYPOINT_CONFIG.CANVAS_SCALE by globalThis.devicePixelRatio which can
produce >4K canvases (e.g., 512*3*3=4608) and cause GPU/OOM; fix by clamping DPR
to a safe maximum (e.g., const dpr = Math.min(globalThis.devicePixelRatio || 1,
2)) and capping the computed canvas size to a MAX_CANVAS_SIZE (e.g., 4096)
before assigning canvas.width/height (use Math.min(sizeOrDefault * dpr,
MAX_CANVAS_SIZE) and ensure the result is an integer), and update any other
drawCombinedCanvas call sites in this file to avoid forcing oversized textures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants