Skip to content

Conversation

@mateuszJS
Copy link
Owner

@mateuszJS mateuszJS commented Jun 11, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a transformation UI for assets, enabling intuitive resizing and rotation through on-canvas handles.
    • Added support for precise asset manipulation using a new 3x3 matrix system for 2D transformations.
    • Implemented improved picking logic for both textures and transformation handles, enhancing selection accuracy.
  • Improvements

    • Adopted a mathematically consistent y-axis orientation (upwards) for more predictable transformations and calculations.
    • Enhanced geometric methods for points, including angle, distance, and midpoint calculations.
    • Refined asset border rendering and interaction feedback.
    • Updated default point ordering and texture loading to align with coordinate system changes.
  • Bug Fixes

    • Corrected parameter naming inconsistencies and improved type declarations for texture handling.
  • Documentation

    • Added detailed design decision documents outlining transformation storage and coordinate system choices.
  • Tests

    • Expanded integration and UI tests to cover new transformation interactions and UI states.

@coderabbitai
Copy link

coderabbitai bot commented Jun 11, 2025

Walkthrough

This update introduces a transformation UI for graphical assets, enabling resizing and rotation through interactive handles. It adds new Zig modules for 2D matrix math and transform UI logic, updates picking and rendering to support these features, and revises coordinate conventions. Tests and architectural decision records document these design choices.

Changes

File(s) Change Summary
ADRs/points vs matrix, ADRs/y-axis coords Added architectural decision records documenting asset transform data representation and y-axis orientation.
integration-tests/index.ts, integration-tests/tests/creator.spec.ts Enhanced integration tests to support and verify transform UI interactions, including rotation and scaling.
src/WebGPU/pointer.ts, src/getCanvasMatrix.ts, src/index.ts Updated pointer event handling and coordinate systems to use upward-growing y-axis; enabled texture flipping on load.
src/WebGPU/programs/initPrograms.ts, src/WebGPU/programs/pickTexture/getProgram.ts, src/WebGPU/programs/pickTexture/shader.wgsl Refactored and extended picking programs; updated pick texture shader to output IDs as unsigned integers.
src/WebGPU/programs/pickTriangle/getProgram.ts, src/WebGPU/programs/pickTriangle/shader.wgsl Added new triangle picking program and corresponding WGSL shader for transform UI hit detection.
src/logic/index.d.ts, src/logic/index.zig Modified Zig interface: added support for transform actions, picking triangles, and updated function signatures.
src/logic/line.zig Refactored line vertex data generation to support picking and direct buffer writing.
src/logic/matrix.zig Introduced a 3x3 matrix struct for 2D affine transformations with construction, manipulation, and transformation methods.
src/logic/transform_ui.zig Added module for transform UI: defines handles, interaction logic, and vertex data generation for resizing/rotation.
src/logic/types.zig Extended Point and PointUV types with geometric utility methods (angle, midpoint, distance).
src/run.ts Connected new triangle picking logic to the main Zig-WebGPU bridge.
src/utils/getDefaultPoints.ts Changed default point order to match new coordinate conventions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Canvas
    participant ZigLogic
    participant WebGPU
    participant TransformUI

    User->>Canvas: Pointer down on transform handle
    Canvas->>ZigLogic: on_pointer_down(x, y)
    ZigLogic->>TransformUI: is_transform_ui(hovered_id)
    alt Transform UI handle
        ZigLogic->>ZigLogic: Set ongoing_action = transform
    else Not a handle
        ZigLogic->>ZigLogic: Set ongoing_action = move/none
    end

    User->>Canvas: Pointer move
    Canvas->>ZigLogic: on_pointer_move(x, y)
    alt ongoing_action == transform
        ZigLogic->>TransformUI: tranform_points(handle_id, points, x, y)
        TransformUI-->>ZigLogic: Updated points
    else
        ZigLogic->>ZigLogic: Move logic
    end

    User->>Canvas: Pointer up
    Canvas->>ZigLogic: on_pointer_up()
    ZigLogic->>ZigLogic: Reset ongoing_action

    ZigLogic->>WebGPU: Render transform UI (get_transform_ui)
    WebGPU->>Canvas: Draw handles and borders
Loading

Suggested labels

released on @next

Poem

In the garden of code, a canvas anew,
Handles for corners, and rotation too!
Matrices dance, points now align,
Transforming with grace on each design.
Rabbits rejoice, for assets can spin—
With a hop, a resize, let creation begin!
🐇✨


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@mateuszJS mateuszJS changed the title Resize and rotation matrix feat: Resize and rotation Jun 11, 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: 9

🧹 Nitpick comments (14)
ADRs/y-axis coords (1)

1-7: Minor typos in ADR

missunderstandingmisunderstanding
(1,1) i top right corner(1,1) is top-right corner

Polish the ADR for future readers.

ADRs/points vs matrix (1)

1-10: Fix typos & unclear wording in ADR

Several small language issues make the ADR harder to follow (e.g., positon, 3v3, matricies, transition). A quick sweep keeps it professional and unambiguous.

-We have two options how we can store assets positon/scale/rotation.
+We have two options for storing an asset’s position / scale / rotation.

-a) four points, each represents one corner
-b) width, height + 3v3 matrix
+a) four points – each represents one corner  
+b) width, height + 3×3 matrix

-Seems like matrix could simplify a lot of transformations but at the same time
-UV coordinates would be to be stored somewhere as well.
+Using a matrix would simplify many transformations, but UV coordinates would still need to be stored somewhere.

-Thats why for now we stick with storing assets as four points, each point is x,y,u,v.
-For transformations like all scaling, rotations we are going to use matricies.
-For transition is not useful since it's super easy to just add cursor position to points.
+Therefore, for now we store assets as four points (x, y, u, v each).  
+We will apply scaling and rotation via matrices, while translation is handled by simply adding the cursor delta to the points.
integration-tests/index.ts (1)

4-8: Remove stale comment & tighten naming

The comment “// is the string "Jonathan"” is unrelated and confusing; also new_asset_id could be shortened to nextAssetId.

-const isSampleParam = params.has('sample') // is the string "Jonathan"
+const isSampleParam = params.has('sample')
src/WebGPU/programs/initPrograms.ts (1)

1-10: Consistent camel-case for factory names

getDrawtexture deviates from the getDrawX pattern used elsewhere. Renaming to getDrawTexture keeps the surface API clean.

integration-tests/tests/creator.spec.ts (1)

28-31: Drop console-spam helper in test suite

Permanent mousemove logging makes local runs noisy and can flood Playwright output when the CI-skip guard is removed later.

-  await page.evaluate(() =>
-    window.addEventListener('mousemove', (e) => console.log(e.clientX, e.clientY))
-  ) // to display cursor position during debugging
-  // helps to copy position here
+  // Debug helper removed – re-enable locally if needed.
src/logic/types.zig (1)

7-11: anytype may be wider than needed.

angle_to accepts other: anytype. Unless you intentionally want duck-typing, constraining this to Point (or a comptime interface) will give clearer errors and better inlining.

-    pub fn angle_to(self: Point, other: anytype) f32 {
+    pub fn angle_to(self: Point, other: Point) f32 {
src/WebGPU/programs/pickTriangle/getProgram.ts (3)

69-76: Vertex buffer recreated every draw – consider re-use or a staging buffer.

Creating & writing a GPUBuffer each frame is expensive and incurs validation overhead. Reuse a pre-allocated buffer or keep one per mesh and update with queue.writeBuffer/copyBuffer.


77-82: BindGroup churn hurts performance.

device.createBindGroup inside the render loop allocates & validates every frame. Cache one bind group per pipeline layout (or per canvas) and reuse it unless the uniform layout changes.


64-68: Matrix length not validated.

worldProjectionMatrix must have 16 floats; an assert (dev-only) would prevent subtle GPU garbage writes.

if (import.meta.env?.DEV && worldProjectionMatrix.length !== 16)
  throw new Error('worldProjectionMatrix must be length-16 Float32Array')
src/WebGPU/programs/pickTriangle/shader.wgsl (1)

25-27: Casting can be removed after switching to u32.

After changing the attribute type, the fragment shader can simply return in.id;.

src/WebGPU/programs/pickTexture/getProgram.ts (2)

79-88: Avoid per-frame BindGroup creation.

See comment in pickTriangle. The same caching applies here.


54-55: Minor: use std140-aligned byte size constant for readability.

64 bytes is a magic number; a named constant clarifies intent.

const MAT4_SIZE = 16 * 4; // 64 bytes
const uniformBufferSize = MAT4_SIZE;
src/logic/matrix.zig (1)

129-137: Consider adding type constraints or documentation for the point parameter.

Using anytype provides flexibility but lacks compile-time type safety. Consider either:

  1. Using a specific type or union type
  2. Adding a doc comment specifying the expected interface (must have .x and .y fields)
src/logic/index.zig (1)

152-156: Remove commented-out code.

                Line.get_vertex_data(
-                    // buffer[0..LINE_NUM_VERTICIES],
                    buffer[0..][0..LINE_NUM_VERTICIES],
                    point,
                    next_point,
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ac0d8e4 and ea62b5c.

⛔ Files ignored due to path filters (13)
  • integration-tests/tests/creator.spec.ts-snapshots/after-hover-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/after-move-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/after-selection-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/hover-image-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/hover-rotation-ui-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/hover-top-left-scale-ui-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/hover-top-scale-ui-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/move-image-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/rotate-image-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/select-image-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/upload-image-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/use-top-left-scale-ui-chromium.png is excluded by !**/*.png
  • integration-tests/tests/creator.spec.ts-snapshots/use-top-scale-ui-chromium.png is excluded by !**/*.png
📒 Files selected for processing (20)
  • ADRs/points vs matrix (1 hunks)
  • ADRs/y-axis coords (1 hunks)
  • integration-tests/index.ts (1 hunks)
  • integration-tests/tests/creator.spec.ts (2 hunks)
  • src/WebGPU/pointer.ts (2 hunks)
  • src/WebGPU/programs/initPrograms.ts (2 hunks)
  • src/WebGPU/programs/pickTexture/getProgram.ts (6 hunks)
  • src/WebGPU/programs/pickTexture/shader.wgsl (0 hunks)
  • src/WebGPU/programs/pickTriangle/getProgram.ts (1 hunks)
  • src/WebGPU/programs/pickTriangle/shader.wgsl (1 hunks)
  • src/getCanvasMatrix.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/logic/index.d.ts (2 hunks)
  • src/logic/index.zig (6 hunks)
  • src/logic/line.zig (2 hunks)
  • src/logic/matrix.zig (1 hunks)
  • src/logic/transform_ui.zig (1 hunks)
  • src/logic/types.zig (1 hunks)
  • src/run.ts (2 hunks)
  • src/utils/getDefaultPoints.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/WebGPU/programs/pickTexture/shader.wgsl
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/WebGPU/pointer.ts (1)
src/logic/index.d.ts (2)
  • on_pointer_move (24-24)
  • on_pointer_down (22-22)
src/run.ts (1)
src/WebGPU/programs/initPrograms.ts (1)
  • pickTriangle (19-19)
src/index.ts (3)
src/WebGPU/getTexture/index.ts (1)
  • createTextureFromSource (106-117)
src/logic/index.d.ts (1)
  • add_texture (18-18)
src/utils/getDefaultPoints.ts (1)
  • getDefaultPoints (3-19)
integration-tests/index.ts (1)
src/index.ts (1)
  • initCreator (29-102)
src/WebGPU/programs/pickTriangle/getProgram.ts (2)
src/WebGPU/programs/pickTexture/getProgram.ts (1)
  • getProgram (5-101)
src/WebGPU/programs/initPrograms.ts (1)
  • pickTriangle (19-19)
🔇 Additional comments (21)
src/index.ts (1)

78-82: Possible missing flipY option support + silent failure risk

createTextureFromSource is now called with { flipY: true }.
The helper in src/WebGPU/getTexture/index.ts forwards options to copySourceToTexture, but the latter must actually honour flipY; if that util silently ignores unknown keys, textures will appear upside-down and the bug will be hard to trace.

Verify that:

  1. copySourceToTexture respects the flipY flag.
  2. Call-sites that relied on the old default (false) are not unintentionally flipped.

If the flag is not yet implemented, add explicit handling or at least a runtime assertion to surface the mistake.

src/run.ts (1)

31-32: Looks good – integration point is clear

The new pick_triangle plumbing cleanly mirrors existing texture picking and keeps the connectWebGPUPrograms contract symmetrical. No issues spotted.

src/utils/getDefaultPoints.ts (1)

14-18: Point order may contradict the new up-growing Y axis

ADR states (0,0) is bottom-left, yet the first entry here is (paddingX, paddingY + scaledHeight) – the top-left corner.
If downstream code assumes the array starts with bottom-left (e.g., for winding order or hit-tests) this mismatch will break transformations.

Please confirm the expected ordering after the coordinate-system flip and adjust if necessary.

Likely an incorrect or invalid review comment.

src/getCanvasMatrix.ts (1)

4-11:

✅ Verification successful

Confirm near/far ordering for mat4.ortho

mat4.ortho(left, right, bottom, top, near, far) now receives near = 1, far = -1.
Some matrix helpers expect near < far; others flip internally. A mismatch silently inverts depth.


🏁 Script executed:

#!/bin/bash
# Grep the implementation of mat4.ortho to double-check the expected ordering
fd --type f mat4 | xargs rg -n 'function .*ortho'

Length of output: 51


🏁 Script executed:

#!/bin/bash
# Show how mat4 is imported in getCanvasMatrix.ts
grep -n "import.*mat4" src/getCanvasMatrix.ts

# Install gl-matrix to inspect its implementation
npm install gl-matrix

# Locate the ortho function definition in gl-matrix
grep -R "function[[:space:]]*ortho" -n node_modules/gl-matrix

Length of output: 2152


🏁 Script executed:

#!/bin/bash
# Locate the project’s mat4 wrapper module
echo "Searching for utils/mat4 module..."
fd --type f mat4.ts | grep -v node_modules

# If found, inspect the file for how ortho is implemented or forwarded
FILE=$(fd --type f mat4.ts | grep -v node_modules | head -n1)
echo "Inspecting $FILE"
grep -n "ortho" "$FILE" && sed -n '1,200p' "$FILE"

Length of output: 6780


Near/Far ordering is correct in mat4.ortho

The custom ortho implementation uses 1/(near – far), so supplying near = 1 and far = –1 (yielding 1/(1−(−1)) > 0) produces the intended depth range. This matches the existing projection helper (which passes depth, –depth). No changes required.

src/WebGPU/programs/initPrograms.ts (1)

29-31:

✅ Verification successful

Verify updated signature of getPickTexture

The second argument (presentationFormat) was dropped. Make sure every other call site — especially in shader-setup modules — was adjusted; otherwise you’ll get a run-time bind-group mismatch.


🏁 Script executed:

#!/bin/bash
# Find any remaining calls that still pass two arguments.
rg --line-number 'getPickTexture\([^,]*,[^)]*\)'

Length of output: 50


No remaining two-argument calls to getPickTexture

I ran a global search for any calls still passing two arguments to getPickTexture and found none. All call sites—including in shader-setup modules—have been updated to the new single-parameter signature, so there’s no risk of a bind-group mismatch at runtime.

src/logic/index.d.ts (2)

18-18: Rename looks good – check downstream TS/JS callers.

add_texture now expects texture_id. Make sure every caller (e.g. src/index.ts, tests) was updated or compilation will break.


26-31: Declaration matches runtime code – no concerns.
The new pick_triangle callback mirrors the snake-case naming used for the other GPU programs and the function type is consistent.

src/logic/types.zig (2)

20-25: Return type inconsistency – UV info discarded.

mid returns plain Point; callers that expect .u/.v will silently lose those components. If you ever need mid-UV coordinates, consider returning PointUV or providing a second helper.


33-35: Nice addition.
distance wraps std.math.hypot cleanly.

src/logic/matrix.zig (3)

1-48: Matrix constructors are well-implemented.

The basic matrix constructors (identity, scaling, translation, rotation) are correctly implemented for 2D affine transformations.


50-60: Verify the transformation order is as intended.

The current implementation applies transformations in local space (post-multiplication). This means transformations are applied relative to the current orientation. Ensure this matches your intended behavior, as pre-multiplication would apply transformations in global space.


62-127: Matrix operations are correctly implemented.

The transpose, inverse, multiply, and approxEq methods are all mathematically correct. Good practice checking for singular matrices in the inverse method.

src/logic/line.zig (2)

1-40: Good refactoring to use caller-provided buffers.

The change from dynamic allocation to stack-based buffers improves performance and memory safety. The use of fixed-size array types ensures buffer size correctness at compile time.


41-70: Picking vertex data method is well-implemented.

The new get_vertex_data_pick method correctly generates vertex data for GPU-based picking with the appropriate vertex format (5 floats per vertex).

src/logic/index.zig (4)

1-35: Transform UI integration setup looks good.

The new imports, function pointer, and action type are properly integrated for the transform UI feature.


62-64: Good parameter naming consistency.

Changing from texture_index to texture_id improves consistency with the rest of the codebase.


79-140: Transform UI interaction handling is well-implemented.

The pointer event handlers correctly detect and handle transform UI elements, maintaining proper state transitions between move and transform actions.


214-218: Transform UI picking integration looks good.

The addition of transform UI picking for the active asset is properly implemented using the new pick_triangle GPU program.

src/logic/transform_ui.zig (3)

1-41: Transform UI structure is well-designed.

The TransformLine struct and line configurations provide a clean representation of the transform UI elements (corners, edges, and rotation handle).


44-145: Transformation logic is well-implemented.

The approach of transforming to unrotated space, applying modifications, and transforming back is mathematically correct. Each UI component is handled appropriately for its intended behavior.


147-238: Rendering functions are well-structured.

The functions correctly calculate line endpoints and generate vertex data for both rendering and picking. The hover state highlighting is a nice touch for user feedback.

Comment on lines 18 to 20
updatePointer(e)
on_pointer_move(pointer.x, pointer.y)
})

canvas.addEventListener('click', () => {
on_pointer_click()
on_pointer_move(pointer.x, canvas.height - pointer.y)
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Y-flip uses mismatching spaces

pointer.y is measured in CSS pixels (rect.height) whereas canvas.height is in device pixels.
On HiDPI screens or if the canvas is scaled via CSS, canvas.height - pointer.y yields incorrect coordinates.

Compute everything in the same space, e.g.:

-    on_pointer_move(pointer.x, canvas.height - pointer.y)
+    const scale = canvas.height / rect.height
+    on_pointer_move(pointer.x * scale, canvas.height - pointer.y * scale)

This keeps math consistent regardless of DPR or CSS zoom.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/WebGPU/pointer.ts around lines 18 to 20, the calculation for the Y-flip
uses mismatched units by subtracting pointer.y in CSS pixels from canvas.height
in device pixels, causing incorrect coordinates on HiDPI or scaled canvases. Fix
this by converting pointer.y to device pixels before subtracting, typically by
multiplying pointer.y by the device pixel ratio or scaling factor so both values
are in the same coordinate space before performing the subtraction.

Comment on lines +19 to +23
arrayStride: STRIDE * 4,
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
{ shaderLocation: 1, offset: 16, format: 'float32' }, // id
] as const,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

ID should be uint32, not float32, to avoid precision loss.

Large IDs (>2²⁴) cannot be represented exactly as float32.
Use a uint32 vertex attribute and store u32 in the shader.

-            { shaderLocation: 1, offset: 16, format: 'float32' }, // id
+            { shaderLocation: 1, offset: 16, format: 'uint32' },  // id
📝 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
arrayStride: STRIDE * 4,
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
{ shaderLocation: 1, offset: 16, format: 'float32' }, // id
] as const,
arrayStride: STRIDE * 4,
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
{ shaderLocation: 1, offset: 16, format: 'uint32' }, // id
] as const,
🤖 Prompt for AI Agents
In src/WebGPU/programs/pickTriangle/getProgram.ts around lines 19 to 23, the
vertex attribute for the ID is incorrectly set as 'float32', which can cause
precision loss for large IDs. Change the format of the ID attribute from
'float32' to 'uint32' to ensure accurate representation of large integer IDs.
Also, update the shader code to expect a 'u32' type for this attribute.

Comment on lines +1 to +4
struct Vertex {
@location(0) position: vec4f,
@location(1) id: f32,
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use u32 for the vertex ID.

Storing the ID as f32 and casting to u32 wastes precision and ALU. Define the field as u32 in both vertex buffer and shader:

struct Vertex {
  @location(0) position: vec4f,
  @location(1) id: u32,
};

Make the corresponding change in the TS pipeline vertex attribute (uint32) and remove the cast in fs.

🤖 Prompt for AI Agents
In src/WebGPU/programs/pickTriangle/shader.wgsl lines 1 to 4, change the Vertex
struct's id field type from f32 to u32 to avoid precision loss and unnecessary
casting. Also update the TypeScript pipeline vertex attribute to use uint32
instead of float32 for the id, and remove any casting from u32 to float in the
fragment shader code to keep types consistent and efficient.

Comment on lines +26 to 29
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
{ shaderLocation: 1, offset: 16, format: 'float32x2' }, // source position
{ shaderLocation: 2, offset: 16 + 8, format: 'float32' }, // id
// {shaderLocation: 3, offset: 16 + 8, format: 'float32x3'}, // id
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Same precision issue as pick-triangle – store IDs as uint32.

-            { shaderLocation: 2, offset: 16 + 8, format: 'float32' }, // id
+            { shaderLocation: 2, offset: 16 + 8, format: 'uint32' }, // id

Remember to update the WGSL shader and the CPU-side vertex packing.

📝 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
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
{ shaderLocation: 1, offset: 16, format: 'float32x2' }, // source position
{ shaderLocation: 2, offset: 16 + 8, format: 'float32' }, // id
// {shaderLocation: 3, offset: 16 + 8, format: 'float32x3'}, // id
{ shaderLocation: 0, offset: 0, format: 'float32x4' }, // destination position
{ shaderLocation: 1, offset: 16, format: 'float32x2' }, // source position
{ shaderLocation: 2, offset: 16 + 8, format: 'uint32' }, // id
// { shaderLocation: 3, offset: 16 + 8, format: 'float32x3'}, // id
🤖 Prompt for AI Agents
In src/WebGPU/programs/pickTexture/getProgram.ts around lines 26 to 29, the ID
attribute is currently stored as a 'float32', which can cause precision issues.
Change the format of the ID attribute to 'uint32' to ensure correct precision.
Additionally, update the corresponding WGSL shader code and the CPU-side vertex
packing logic to handle the ID as a uint32 instead of a float32.

return id >= 1 and id <= 9;
}

pub fn tranform_points(ui_component_id: u32, points: *[4]PointUV, raw_x: f32, raw_y: f32) void {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in function name.

-pub fn tranform_points(ui_component_id: u32, points: *[4]PointUV, raw_x: f32, raw_y: f32) void {
+pub fn transform_points(ui_component_id: u32, points: *[4]PointUV, raw_x: f32, raw_y: f32) void {

This typo also needs to be fixed in src/logic/index.zig line 136 where this function is called.

📝 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
pub fn tranform_points(ui_component_id: u32, points: *[4]PointUV, raw_x: f32, raw_y: f32) void {
pub fn transform_points(ui_component_id: u32, points: *[4]PointUV, raw_x: f32, raw_y: f32) void {
🤖 Prompt for AI Agents
In src/logic/transform_ui.zig at line 43, correct the typo in the function name
from "tranform_points" to "transform_points". Then, in src/logic/index.zig at
line 136, update the function call to use the corrected name "transform_points"
to ensure consistency and proper linkage.

Repository owner deleted a comment from coderabbitai bot Jun 11, 2025
Repository owner deleted a comment from coderabbitai bot Jun 11, 2025
Repository owner deleted a comment from coderabbitai bot Jun 11, 2025
Repository owner deleted a comment from coderabbitai bot Jun 11, 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: 0

🧹 Nitpick comments (4)
src/logic/matrix.zig (4)

71-101: Extract magic epsilon and re-use across the file

inverse hard-codes 1e-6 for singularity detection, while approxEq also accepts eps.
Expose a single constant (e.g. pub const EPS:f32 = 1e-6;) or pass the tolerance as a parameter to avoid divergent thresholds in the future.

-        if (@abs(det) < 1e-6) return null;
+        if (@abs(det) < EPS) return null;

This keeps numerical heuristics in one place.


121-127: Micro-nit: use for for clarity

The while … : (i += 1) idiom is perfectly valid, but the conventional for loop is a tad clearer and removes the explicit counter:

-var i: usize = 0;
-while (i < 9) : (i += 1) {
-    if (@abs(a.values[i] - b.values[i]) > eps) return false;
-}
+for (a.values, b.values) |lhs, rhs| {
+    if (@abs(lhs - rhs) > eps) return false;
+}

No behaviour change, just readability.


129-137: Narrow the generic parameter of transform_point

Accepting anytype allows passing in a struct that merely happens to have .x and .y, but:

  1. Compilation errors will be harder to read if a field is misspelled.
  2. It hides intent from the call-site.

Consider either:

- pub fn transform_point(self: Matrix3x3, p: anytype) Point {
+ pub fn transform_point(self: Matrix3x3, p: Point) Point {

or making the function generic with an interface guarantee:

fn transform_point(self: Matrix3x3, p: anytype) @TypeOf(p) 
    comptime { _ = .{ p.x, p.y }; } // compile-time field check

Explicit is safer here.


8-16: Expose a compile-time identity constant

identity() constructs a new array every call. Declaring:

pub const IDENTITY = Matrix3x3{
    .values = [_]f32{ 1,0,0, 0,1,0, 0,0,1 },
};

and returning IDENTITY avoids repeated initialisation and conveys immutability.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ea62b5c and 6b48292.

📒 Files selected for processing (4)
  • ADRs/points vs matrix (1 hunks)
  • ADRs/y-axis coords (1 hunks)
  • src/logic/index.zig (6 hunks)
  • src/logic/matrix.zig (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • ADRs/points vs matrix
🚧 Files skipped from review as they are similar to previous changes (2)
  • ADRs/y-axis coords
  • src/logic/index.zig
🔇 Additional comments (1)
src/logic/matrix.zig (1)

50-60:

❓ Verification inconclusive

Check intended transform-composition order

self.* = Matrix3x3.multiply(self.*, Matrix3x3.{scale/rotation/translation}(...)) post-multiplies the new transform.
With the current row-major, row-vector-on-left convention this means:

p' = p * self * T   // after self.translate(...)

Many APIs (e.g. CSS, SVG, OpenGL column-major) expect the opposite order (T * self).
If the rest of the engine assumes intuitive “last call is applied last” semantics, the current order is correct; otherwise every high-level call will appear reversed. Please double-check the calling sites before this lands.


🏁 Script executed:

#!/bin/bash
# Inspect definition of multiply, scaling, rotation, translation to confirm composition semantics
rg -n "pub fn multiply" -A5 -B2 src/logic/matrix.zig
rg -n "pub fn scaling" -A5 -B2 src/logic/matrix.zig
rg -n "pub fn rotation" -A5 -B2 src/logic/matrix.zig
rg -n "pub fn translation" -A5 -B2 src/logic/matrix.zig

Length of output: 1201


Verify transform-composition order

The scale, rotate, and translate methods all do:

self.* = Matrix3x3.multiply(self.*, T)

which post-multiplies the new transform. Under our row-major, row-vector-on-left convention this yields:

p' = p * self * T

so the last call is applied last. If elsewhere your code uses column-vector (M * v) or expects T * self, the transform order will be reversed.

Please confirm that all matrix–vector multiplications and rendering code use the same convention and that this ordering matches your intended “last call is last applied” semantics.

• File: src/logic/matrix.zig
– Methods: scale, rotate, translate

@mateuszJS mateuszJS merged commit 8efc548 into next Jun 12, 2025
4 checks passed
@github-actions
Copy link

🎉 This PR is included in version 0.1.0-next.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants