Skip to content

feat(core): accept readonly vectors in vec2 operations - #52

Draft
RodrigoHamuy wants to merge 1 commit into
pmndrs:mainfrom
RodrigoHamuy:feat/readonly-types
Draft

RodrigoHamuy wants to merge 1 commit into
pmndrs:mainfrom
RodrigoHamuy:feat/readonly-types

Conversation

@RodrigoHamuy

@RodrigoHamuy RodrigoHamuy commented Sep 9, 2026

Copy link
Copy Markdown

Adds RVec2 (Readonly<Vec2>) and uses it for every vec2 parameter that is only read from. Output parameters stay mutable Vec2.

Opening as a draft to check whether you like the approach before I extend it to the rest of the types.

Why: callers holding a readonly vector (a frozen constant, a value from a readonly struct, or anything typed as const) currently have to copy or cast it to pass it to vec2.add, vec2.length, and friends, even though those functions never write to it. Encoding that in the signature also documents which arguments are outputs.

Compatibility: Vec2 is a tuple, so Readonly<Vec2> only widens the input side. Mutable vectors stay assignable to RVec2, so existing callers are unaffected. Passing a readonly vector as out becomes a compile error, which is the intended behaviour.

Scope: vec2 only, 44 functions. create, fromValues, set, zero, and fromBuffer are unchanged since they have no read-only vector params.

Checks: tsc --noEmit clean, all 1154 tests pass. I also typechecked a temporary fixture confirming readonly inputs are accepted, mutable inputs still work, and a readonly out is rejected.

Question for maintainers: happy for me to extend this to Vec3, Vec4, Mat2/Mat2d/Mat3/Mat4, Quat/Quat2, Euler, Polar, and Spherical? Those are more involved than vec2 -- the types are consumed across shapes/, geometry/, and ik/, where internal helpers thread vectors into each other, so the readonly-ness will cascade a fair way. Also worth settling the naming (RVec2 vs ReadonlyVec2) before it's repeated a dozen times -- gl-matrix uses ReadonlyVec2, so that may be the more familiar choice.

next steps

If this seems okay, then we can do the same for all of these:

core --- tuple types that get an R* alias

  1. Vec3RVec3
  2. Vec4RVec4
  3. QuatRQuat
  4. Quat2RQuat2
  5. Mat2RMat2
  6. Mat2dRMat2d
  7. Mat3RMat3
  8. Mat4RMat4
  9. EulerREuler
  10. SphericalRSpherical
  11. PolarRPolar

color
11. ColorRColor
12. HSLRHSL

shapes --- tuple types with an R* alias
13. Box2RBox2
14. Box3RBox3
15. Frustum / FrustumCornersRFrustum / RFrustumCorners

shapes --- object types (readonly via Readonly<...> on the fields, e.g. RSphere = { readonly center: RVec3; readonly radius: number })
16. SphereRSphere
17. CircleRCircle
18. Plane3RPlane3
19. OBB3ROBB3

This is all done in:

Add RVec2, a readonly Vec2, and use it for every vec2 parameter that is
only read from. Output parameters stay mutable Vec2 so the receiving
vector can still be written to.

Mutable vectors remain assignable to RVec2, so existing callers are
unaffected. Callers holding a readonly vector can now pass it directly
instead of copying or casting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6613487

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

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.

1 participant