-
Notifications
You must be signed in to change notification settings - Fork 113
[stale] Improve off-screen waypoint visibility and clarity #416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
[stale] Improve off-screen waypoint visibility and clarity #416
Conversation
Co-authored-by: vital2580 <[email protected]>
Cursor Agent can help with this pull request. Just |
WalkthroughIntroduces 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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.
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. Comment |
There was a problem hiding this 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 helpersIf 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 issuesContent 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 itBaking 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 toTARGET_SCREEN_PX
-based sizing. Consider removing it to avoid confusion.
128-166
: Fix ESLint: trailing spacesStrip 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 phrasingYou 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.
📒 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 variablebot
directly in any file under therenderer/
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 referencingbot
directly.
In renderer code (such as inWorldRendererThree
), use theplayerState
property (e.g.,worldRenderer.playerState.gameMode
) to access player state. The implementation forplayerState
lives insrc/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 arrowsClarifies intent and aligns with the new default. No functional concerns here.
Co-authored-by: vital2580 <[email protected]>
There was a problem hiding this 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 |
There was a problem hiding this comment.
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)
There was a problem hiding this 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 / 2Also 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
📒 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 variablebot
directly in any file under therenderer/
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 referencingbot
directly.
In renderer code (such as inWorldRendererThree
), use theplayerState
property (e.g.,worldRenderer.playerState.gameMode
) to access player state. The implementation forplayerState
lives insrc/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
// 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 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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')! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
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.
Enhance waypoint visibility by adding directional arrows for off-screen waypoints and improving text clarity with higher resolution and larger fonts.
Summary by CodeRabbit
New Features
Improvements
Chores