Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@cptbtptpbcptdtptp
Copy link
Collaborator

@cptbtptpbcptdtptp cptbtptpbcptdtptp commented Jul 9, 2025

Please check if the PR fulfills these requirements

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

What is the current behavior? (You can also link to an open issue here)

What is the new behavior (if this is a feature change)?

Does this PR introduce a breaking change? (What changes might users need to make in their application due to this PR?)

Other information:

Summary by CodeRabbit

  • New Features

    • UI alignment controls: horizontal/vertical modes with alignLeft/Right/Center/Top/Bottom/Middle, stretch behaviors, rect-driven layout, and public getters/setters; re-exports of HorizontalAlignmentMode and VerticalAlignmentMode.
    • Core re-export: TransformModifyFlags now publicly available.
  • Refactor

    • Centralized transform dirty-flag system, parent caching and cascaded update flow with protected helpers.
  • Tests

    • Expanded UITransform test suite covering alignment, stretching, size/pivot, propagation, and world-position interactions.

@coderabbitai
Copy link

coderabbitai bot commented Jul 9, 2025

Walkthrough

Centralizes Transform dirty-flag, parent-caching, and lifecycle logic into protected helpers; extends UITransform with rect-based alignment, insets, stretch modes and alignment enums; adds an extensive UITransform integration test suite and re-exports alignment enums and TransformModifyFlags.

Changes

Cohort / File(s) Summary
Core: Transform implementation
packages/core/src/Transform.ts
Converted several private members to protected; added centralized dirty-flag helpers (_decomposeLocalMatrix, _onWorldMatrixChange, _isContainDirtyFlags, _isContainDirtyFlag, _setDirtyFlagTrue, _setDirtyFlagFalse, _worldAssociatedChange, _getParentTransform); parent caching via _parentTransformCache and _isParentDirty; replaced private position/world-position helpers with protected variants; removed _onDestroy; adjusted matrix/position setters to use new dirty-flag flow.
Core: Public exports
packages/core/src/index.ts
Re-exported TransformModifyFlags alongside Transform.
UI: UITransform component
packages/ui/src/component/UITransform.ts
Added Rect state and alignment insets/state; introduced horizontalAlignment/verticalAlignment getters/setters and six inset properties; wired alignment into lifecycle hooks and Transform overrides; implemented _updatePositionByAlignment, _updateSizeByAlignment, _updateRectByPivot; added propagation helpers _updateWorldFlagWithSelfRectChange and _updateWorldFlagWithParentRectChange.
UI: Alignment enums
packages/ui/src/enums/HorizontalAlignmentMode.ts, packages/ui/src/enums/VerticalAlignmentMode.ts
Added HorizontalAlignmentMode and VerticalAlignmentMode enums (None/edges/center/stretch combinations) with JSDoc describing position/size semantics.
UI: Public index
packages/ui/src/index.ts
Re-exported HorizontalAlignmentMode and VerticalAlignmentMode.
Tests: UITransform suite
tests/src/ui/UITransform.test.ts
Added an integration-style test suite (WebGLEngine + UI APIs) covering size/pivot dispatch, horizontal/vertical alignment, stretch modes, rect dirty propagation, parent-driven updates, manual transform interactions, and world/local transform behavior.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Transform
  participant ParentCache as ParentTransformCache

  Caller->>Transform: set localMatrix / worldMatrix / position / worldPosition
  alt localMatrix set
    Transform->>Transform: _decomposeLocalMatrix(...)
    Transform->>Transform: _setDirtyFlagTrue(...) / _worldAssociatedChange(...)
  else worldMatrix set
    Transform->>Transform: _onWorldMatrixChange()
  else position set
    Transform->>Transform: _onPositionChanged()
    Transform->>Transform: _worldAssociatedChange(WorldPosition)
  else worldPosition set
    Transform->>Transform: _onWorldPositionChanged()
  end
  Transform->>ParentCache: _getParentTransform() (uses cache / _isParentDirty)
  ParentCache-->>Transform: parent (if any)
  Transform->>Transform: propagate / clear dirty flags
Loading
sequenceDiagram
  autonumber
  participant Child as UITransform(child)
  participant Parent as UITransform(parent)
  participant Base as Transform(base)

  Note over Child,Parent: Alignment-driven layout & rect propagation
  Child->>Child: set alignment / insets
  Child->>Child: _updateSizeByAlignment() / _updatePositionByAlignment() / _updateRectByPivot()
  Child->>Child: _updateWorldFlagWithSelfRectChange()
  Child->>Base: alignment-aware overrides (_onPositionChanged / _onWorldMatrixChange)

  Parent->>Parent: size/pivot change
  Parent->>Child: _updateWorldFlagWithParentRectChange()
  Child->>Child: recompute size/position/rect and dispatch flags
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop through matrices, flags in tow,
I nudge rects left and right to grow.
Parents cache, children line the queue,
Tests prance round each pivot true.
A rabbit hums — layouts snug and new. 🥕


📜 Recent 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 baab21a and d15e41e.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/src/component/UITransform.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). (4)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: codecov
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

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: 6

🧹 Nitpick comments (1)
packages/ui/src/component/UITransform.ts (1)

183-185: Improve null safety for child transform updates

The optional chaining on line 184 could skip updates silently. Consider adding explicit null checks.

    for (let i = 0, n = children.length; i < n; i++) {
-      (children[i].transform as unknown as UITransform)?._calAbsoluteByRelative();
+      const childTransform = children[i].transform;
+      if (childTransform instanceof UITransform) {
+        childTransform._calAbsoluteByRelative();
+      }
    }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c50e4f2 and 829dce7.

📒 Files selected for processing (3)
  • packages/core/src/Entity.ts (1 hunks)
  • packages/core/src/Transform.ts (1 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/ui/src/component/UITransform.ts (2)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (1)
  • children (212-214)
⏰ 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). (5)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 1/4)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: codecov
🔇 Additional comments (1)
packages/core/src/Transform.ts (1)

579-608: Good API design for extensibility

The visibility changes from private to protected for _parentChange(), _getParentTransform(), and _onPositionChanged() provide proper extension points for subclasses like UITransform. This follows good OOP principles by allowing controlled access to lifecycle hooks.

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

♻️ Duplicate comments (4)
packages/ui/src/component/UITransform.ts (4)

78-89: Fix property assignment - use private fields instead of setters.

Line 83 incorrectly uses the public setter. Use the private fields directly.

-    this._left = this.right = NaN;
+    this._left = this._right = NaN;

129-140: Fix property assignment - use private fields instead of setters.

Line 134 incorrectly uses the public setter. Use the private fields directly.

-    this._top = this.bottom = NaN;
+    this._top = this._bottom = NaN;

267-269: Critical: Fix incorrect bitshift operator usage.

Line 268 incorrectly uses the bitshift right operator >> instead of division. This will produce incorrect results.

 case UITransformAlignment.Center:
-  position.x = (parentRect.x + (parentRect.width - localRect.width)) >> (1 + localRect.x + this._center);
+  position.x = parentRect.x + (parentRect.width - localRect.width) / 2 + localRect.x + this._center;
   break;

284-286: Critical: Fix incorrect bitshift operator usage.

Line 285 has the same bitshift operator issue as line 268.

 case UITransformAlignment.Middle:
-  position.y = (parentRect.y + (parentRect.height - localRect.height)) >> (1 + localRect.y + this._middle);
+  position.y = parentRect.y + (parentRect.height - localRect.height) / 2 + localRect.y + this._middle;
   break;
🧹 Nitpick comments (6)
packages/ui/src/component/UITransform.ts (6)

22-28: Remove redundant double-negation.

The double-negation is unnecessary since numeric values are automatically coerced to boolean in conditional contexts.

 override get position(): Vector3 {
-  if (this._isContainDirtyFlag(UITransformModifyFlags.LocalPosition) && !!this._alignment) {
+  if (this._isContainDirtyFlag(UITransformModifyFlags.LocalPosition) && this._alignment) {
     this._calAbsoluteByAlignment();
     this._setDirtyFlagFalse(UITransformModifyFlags.LocalPosition);
   }
   return this._position;
 }

30-38: Remove redundant double-negation in matrix setters.

 override set localMatrix(value: Matrix) {
   super.localMatrix = value;
-  !!this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.LpLm);
+  this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.LpLm);
 }

 override set worldMatrix(value: Matrix) {
   super.worldMatrix = value;
-  !!this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.LpLmWm);
+  this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.LpLmWm);
 }

190-202: Remove redundant double-negation.

 protected override _parentChange(): void {
   super._parentChange();
   const alignment = this._alignment;
-  if (!!alignment) {
+  if (alignment) {
     this._setDirtyFlagTrue(UITransformModifyFlags.LocalPosition);
     if (
       (alignment & UITransformAlignment.Horizontal) === UITransformAlignment.LeftAndRight ||
       (alignment & UITransformAlignment.Vertical) === UITransformAlignment.TopAndBottom
     ) {
       this._setLocalRectDirty(UITransformModifyFlags.LsLr);
     }
   }
 }

204-224: Remove redundant double-negation in alignment setters.

 private _setHorizontalAlignment(value: UITransformAlignment): void {
   const alignment = this._alignment;
   if ((alignment & UITransformAlignment.Horizontal) != value) {
     this._alignment = (alignment & UITransformAlignment.Vertical) | value;
-    if (!!value) {
+    if (value) {
       this._onPositionChanged();
       value === UITransformAlignment.LeftAndRight && this._onSizeChanged();
     }
   }
 }

 private _setVerticalAlignment(value: UITransformAlignment): void {
   const alignment = this._alignment;
   if ((alignment & UITransformAlignment.Vertical) != value) {
     this._alignment = (alignment & UITransformAlignment.Horizontal) | value;
-    if (!!value) {
+    if (value) {
       this._onPositionChanged();
       value === UITransformAlignment.TopAndBottom && this._onSizeChanged();
     }
   }
 }

237-254: Remove redundant double-negation.

 private _setLocalRectDirty(flags: UITransformModifyFlags): void {
   if (this._isContainDirtyFlag(flags)) return;
   this._setDirtyFlagTrue(flags);
   const children = this.entity.children;
   for (let i = 0, n = children.length; i < n; i++) {
     const transform = children[i].transform as unknown as UITransform;
     const alignment = transform?._alignment ?? UITransformAlignment.None;
-    if (!!alignment) {
+    if (alignment) {
       transform._setDirtyFlagTrue(UITransformModifyFlags.LocalPosition);
       if (
         (alignment & UITransformAlignment.Horizontal) === UITransformAlignment.LeftAndRight ||
         (alignment & UITransformAlignment.Vertical) === UITransformAlignment.TopAndBottom
       ) {
         transform._setLocalRectDirty(UITransformModifyFlags.LsLr);
       }
     }
   }
 }

304-313: Remove redundant double-negation in position handlers.

 @ignoreClone
 protected override _onPositionChanged(): void {
   super._onPositionChanged();
-  !!this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.LocalPosition);
+  this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.LocalPosition);
 }

 @ignoreClone
 protected override _onWorldPositionChanged(): void {
   super._onWorldPositionChanged();
-  !!this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
+  this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 08de1e3 and a743375.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (4 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🧬 Code Graph Analysis (1)
packages/core/src/Transform.ts (4)
packages/math/src/Vector3.ts (1)
  • Vector3 (11-612)
packages/core/src/clone/CloneManager.ts (2)
  • deepClone (45-47)
  • ignoreClone (9-11)
packages/math/src/Matrix.ts (1)
  • Matrix (11-1288)
packages/core/src/Entity.ts (3)
  • parent (201-203)
  • parent (205-207)
  • transform (155-157)
🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 193-193: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 208-208: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 219-219: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 244-244: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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). (3)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 1/4)
🔇 Additional comments (7)
packages/core/src/Transform.ts (6)

22-24: LGTM! Appropriate visibility changes for inheritance.

The conversion of _position and _worldPosition from private to protected is necessary and well-justified to enable the UITransform subclass to implement alignment features.


42-42: LGTM! Necessary visibility change.

Converting _localMatrix to protected enables proper matrix manipulation in subclasses.


319-343: LGTM! Consistent callback binding pattern.

The addition of _onWorldPositionChanged binding follows the established pattern for other position/rotation callbacks.


579-602: LGTM! Essential visibility changes for subclass functionality.

Converting _parentChange and _getParentTransform to protected enables UITransform to properly handle parent transform updates and implement alignment features.


604-621: LGTM! Well-implemented position change handlers.

The visibility change for _onPositionChanged and the new _onWorldPositionChanged method properly handle position updates with correct parent transform considerations.


741-755: LGTM! Necessary utility method visibility changes.

Converting these dirty flag management methods to protected enables subclasses to properly manage their own dirty flags while maintaining the same robust implementation.

packages/ui/src/component/UITransform.ts (1)

1-21: LGTM! Well-structured field declarations.

The use of NaN for unset alignment values and the bitmask pattern for alignment flags is a clean design choice.

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 (1)
packages/ui/src/component/UITransform.ts (1)

25-25: Remove redundant double-negation for cleaner code.

The static analysis correctly identifies redundant double-negation operators. In boolean contexts (if statements, logical operators), explicit boolean coercion is unnecessary:

-        if (!!parentRect) {
+        if (parentRect) {
-        if (!!this._alignment) {
+        if (this._alignment) {

Apply similar changes to all flagged instances for more readable code.

Also applies to: 103-103, 135-135, 304-304, 317-317, 325-325, 349-349, 364-364, 374-374

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a743375 and 8aa37fd.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (9 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 304-304: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 317-317: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 325-325: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 364-364: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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). (5)
  • GitHub Check: e2e (22.x, 1/4)
  • GitHub Check: codecov
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 2/4)
🔇 Additional comments (10)
packages/core/src/Transform.ts (3)

19-19: Well-designed visibility changes to enable UITransform extension.

The strategic change of key transform fields from private to protected (_position, _worldPosition, _rotationQuaternion, _scale, _localMatrix, _worldMatrix, etc.) enables the UITransform subclass to properly override and extend core transformation behavior while maintaining encapsulation. This is a clean foundation for the alignment system.

Also applies to: 22-22, 24-24, 28-28, 30-30, 32-32, 42-42, 44-44


579-582: Protected lifecycle methods enable alignment-aware behavior.

The conversion of _parentChange, _getParentTransform, _onPositionChanged, and _onWorldPositionChanged to protected visibility allows UITransform to override these methods for alignment calculations and dirty flag management. The implementation maintains the original logic while providing extension points.

Also applies to: 584-602, 605-608, 611-621


719-719: Protected dirty flag methods support alignment system.

Making the dirty flag helper methods protected (_updateAllWorldFlag, _isContainDirtyFlags, _isContainDirtyFlag, _setDirtyFlagTrue, _setDirtyFlagFalse) enables UITransform to manage its alignment-specific dirty flags while leveraging the existing flag management infrastructure.

Also applies to: 741-756

packages/ui/src/component/UITransform.ts (7)

11-21: Well-structured alignment system fields.

The new fields provide a clean foundation for the alignment system:

  • Individual alignment offset values (_left, _right, _center, etc.)
  • Bitmask for tracking active alignments (_alignment)
  • Cached local rectangle for efficient bounds calculation (_localRect)

22-64: Sophisticated and correct alignment position calculation.

The position override properly implements alignment-based positioning:

  • Correctly handles all alignment combinations (Left/Center/Right × Top/Middle/Bottom)
  • Properly accounts for parent bounds and local rectangle offsets
  • Maintains performance by temporarily disabling change callbacks during calculation
  • Uses appropriate dirty flag management

The mathematical logic is sound for all alignment cases.


72-78: Well-implemented matrix overrides with alignment support.

The localMatrix and worldMatrix overrides properly extend the base Transform behavior:

  • Maintain correct matrix calculation logic from the parent class
  • Add alignment-aware dirty flag management (LpLm flags when alignment is active)
  • Properly handle callback management during matrix decomposition
  • Use conditional flag setting based on alignment state

The implementations correctly balance performance and functionality.

Also applies to: 80-108, 110-121, 123-140


142-157: Excellent alignment property implementation.

The alignment property setters demonstrate sophisticated logic:

  • Proper conflict resolution (center clears left/right, middle clears top/bottom)
  • Correct bitmask manipulation for tracking active alignments
  • Consistent pattern across all six alignment properties
  • Efficient early return on unchanged values using MathUtil.equals

The mutual exclusion logic for conflicting alignments (e.g., center vs left/right) is correctly implemented.

Also applies to: 159-174, 176-191, 193-208, 210-225, 227-242


247-266: Correct stretching calculation in size getter.

The size override properly implements automatic stretching when aligned to opposite edges:

  • LeftAndRight alignment: width = parentWidth - left - right
  • TopAndBottom alignment: height = parentHeight - top - bottom
  • Only recalculates when dirty flag is set
  • Maintains proper callback management during calculation

This enables flexible responsive UI layouts.


315-329: Well-architected helper methods for alignment system.

The private helper methods provide clean abstractions:

  • _setHorizontalAlignment/_setVerticalAlignment: Proper bitmask management with position/size recalculation
  • _getLocalRect: Efficient cached rectangle calculation based on size and pivot
  • _setLocalRectDirty: Recursive propagation of layout changes to aligned children
  • Lifecycle method overrides: Clean integration with parent transform behavior

The recursive propagation in _setLocalRectDirty correctly handles nested UI hierarchies where child elements depend on parent layout changes.

Also applies to: 331-340, 342-359, 361-376, 378-389, 392-396


403-429: Well-designed enum extensions for UI alignment system.

The new enums provide proper flag management:

UITransformModifyFlags:

  • Extends base flags without conflicts
  • Logical bit flag values (powers of 2)
  • Useful combined flags (LsLr, LpLm) for common operations
  • Clear separation of UI-specific flags

UITransformAlignment:

  • Proper bit flag design for horizontal/vertical separation
  • Combined flags (LeftAndRight, TopAndBottom) for stretching behavior
  • Efficient bitmask operations with Horizontal/Vertical masks

Also applies to: 431-445

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 (10)
packages/ui/src/component/UITransform.ts (10)

25-25: Remove redundant double-negation.

The double-negation operator is unnecessary here since the expression will already be coerced to a boolean in the conditional context.

-      if (!!parentRect) {
+      if (parentRect) {

103-103: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-    if (!!this._alignment) {
+    if (this._alignment) {

135-135: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-    if (!!this._alignment) {
+    if (this._alignment) {

186-186: Fix formatting issue.

There's a formatting issue that needs to be addressed for better readability.

-      this._left = this._right = NaN;
+      this._left = this._right = NaN;

237-237: Fix formatting issue.

There's a formatting issue that needs to be addressed for better readability.

-      this._top = this._bottom = NaN;
+      this._top = this._bottom = NaN;

304-304: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-    if (!!alignment) {
+    if (alignment) {

317-317: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-    if (!!value) {
+    if (value) {

325-325: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-    if (!!value) {
+    if (value) {

349-349: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-      if (!!alignment) {
+      if (alignment) {

364-364: Remove redundant double-negation.

The double-negation operator is unnecessary here.

-    if (!!this._alignment) {
+    if (this._alignment) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa37fd and 0c6df5d.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 304-304: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 317-317: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 325-325: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 364-364: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🪛 GitHub Check: lint
packages/ui/src/component/UITransform.ts

[failure] 237-237:
Insert ⏎·······


[failure] 186-186:
Insert ⏎·······

🪛 ESLint
packages/ui/src/component/UITransform.ts

[error] 186-186: Insert ⏎·······

(prettier/prettier)


[error] 237-237: Insert ⏎·······

(prettier/prettier)

⏰ 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). (5)
  • GitHub Check: e2e (22.x, 1/4)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: codecov
🔇 Additional comments (16)
packages/ui/src/component/UITransform.ts (16)

1-1: Import statement looks good.

The imports are well-organized and all appear to be necessary for the UI alignment functionality.


11-20: Well-structured private fields for alignment system.

The private fields are properly organized with appropriate decorators. The _localRect uses @ignoreClone which is correct for a cached computed value.


22-64: Position getter has robust alignment calculation logic.

The position calculation correctly handles different alignment scenarios with proper parent rectangle checks and temporary callback disabling.


66-70: Position setter correctly avoids infinite loops.

Good implementation that prevents triggering alignment recalculations by directly copying the value instead of using the setter logic.


72-108: Local matrix override handles alignment state properly.

The implementation correctly manages dirty flags and handles the alignment recalculation trigger appropriately.


110-140: World matrix override is well-implemented.

The world matrix calculation and setter properly handle parent transforms and alignment state management.


142-191: Alignment property setters have sound logic.

The horizontal alignment properties (left, right, center) correctly manage mutual exclusivity and update alignment flags appropriately.


193-242: Vertical alignment properties implemented correctly.

The vertical alignment properties (top, bottom, middle) follow the same sound pattern as horizontal alignment with proper flag management.


247-266: Size getter handles stretch alignment correctly.

The implementation properly calculates stretched dimensions based on parent rect and alignment constraints.


290-296: Constructor properly binds event handlers.

The constructor correctly binds the size and pivot change handlers which are essential for the alignment system.


301-313: Parent change override handles alignment updates correctly.

The method properly triggers position and size recalculations when the parent changes and alignment is active.


315-329: Alignment helper methods are well-designed.

The _setHorizontalAlignment and _setVerticalAlignment methods correctly update flags and trigger appropriate recalculations.


331-359: Local rect calculation and dirty propagation are robust.

The _getLocalRect method correctly calculates the local rectangle from size and pivot, while _setLocalRectDirty properly propagates changes to child transforms.


362-375: Position change handlers correctly manage dirty flags.

Both _onPositionChanged and _onWorldPositionChanged properly set appropriate dirty flags based on alignment state.


378-396: Size and pivot change handlers are well-implemented.

The handlers correctly manage dirty flags and dispatch update events for the alignment system.


403-440: Enums are well-designed for the alignment system.

The UITransformModifyFlags and UITransformAlignmentFlags enums provide a clean, efficient way to manage state flags with logical combinations for common operations.

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)
packages/ui/src/component/UITransform.ts (4)

22-71: Fix redundant double-negation and approve the alignment logic

The position calculation logic is well-implemented with proper handling of different alignment cases and performance optimizations. However, there's a redundant double-negation on line 25.

Apply this diff to remove the redundant double-negation:

-      if (!!parentRect) {
+      if (parentRect) {

72-141: Fix redundant double-negations and approve matrix handling

The matrix override implementations are sophisticated and correctly handle the complex relationship between local/world matrices and UI alignment. The dirty flag management and callback handling are well-designed.

Apply these diffs to remove redundant double-negations:

-    if (!!this._alignment) {
+    if (this._alignment) {
-    if (!!this._alignment) {
+    if (this._alignment) {

292-377: Fix redundant double-negations and approve lifecycle management

The constructor and lifecycle method overrides are well-implemented. The parent change handling correctly manages dirty flags for alignment-aware transforms, and the callback binding ensures proper reactivity.

Apply these diffs to remove redundant double-negations:

-    if (!!alignment) {
+    if (alignment) {
-    if (!!this._alignment) {
+    if (this._alignment) {

317-398: Fix redundant double-negations and approve helper methods

The private helper methods are well-designed and implement the core alignment system effectively. The recursive dirty flag propagation and local rect caching are good optimizations.

Apply these diffs to remove redundant double-negations:

-    if (!!value) {
+    if (value) {
-    if (!!value) {
+    if (value) {
-      if (!!alignment) {
+      if (alignment) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0c6df5d and 2481ac0.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🧬 Code Graph Analysis (1)
packages/ui/src/component/UITransform.ts (4)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Transform.ts (7)
  • position (61-63)
  • position (65-69)
  • rotationQuaternion (150-166)
  • rotationQuaternion (168-178)
  • scale (215-217)
  • scale (219-223)
  • Transform (10-863)
packages/core/src/Entity.ts (4)
  • parent (201-203)
  • parent (205-207)
  • children (212-214)
  • transform (155-157)
packages/core/src/2d/sprite/Sprite.ts (2)
  • pivot (167-169)
  • pivot (171-173)
🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 306-306: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 319-319: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 327-327: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 351-351: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 366-366: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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). (3)
  • GitHub Check: e2e (22.x, 1/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: build (22.x, macos-latest)
🔇 Additional comments (4)
packages/ui/src/component/UITransform.ts (4)

1-21: LGTM: Well-structured class foundation

The imports, class declaration, and private field setup are properly implemented. The use of decorators for cloning behavior and the alignment flag system using bit operations is efficient and appropriate.


142-244: LGTM: Consistent and well-designed alignment properties

The alignment property getters and setters are consistently implemented with proper mutual exclusion logic. The use of MathUtil.equals for comparisons and NaN for unset values is appropriate. The delegation to helper methods promotes code reuse and maintainability.


249-268: LGTM: Correct stretch behavior implementation

The size getter correctly implements stretch behavior for UI elements. When both opposing alignment values are set (left+right or top+bottom), the element properly stretches to fill the available space minus the specified margins.


405-442: LGTM: Well-designed flag system

The flag enums are properly designed with clear naming conventions and non-conflicting bit values. The composite flags (LsLr, LpLm, etc.) are useful optimizations for common flag combinations, and the separation of horizontal/vertical alignment flags provides good flexibility.

@codecov
Copy link

codecov bot commented Jul 21, 2025

Codecov Report

Attention: Patch coverage is 60.83333% with 188 lines in your changes missing coverage. Please review.

Project coverage is 79.00%. Comparing base (95135f4) to head (964ed0e).
Report is 27 commits behind head on dev/1.6.

Files with missing lines Patch % Lines
packages/ui/src/component/UITransform.ts 55.79% 183 Missing ⚠️
packages/core/src/Transform.ts 92.30% 5 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           dev/1.6    #2760      +/-   ##
===========================================
+ Coverage    78.88%   79.00%   +0.12%     
===========================================
  Files          843      845       +2     
  Lines        89944    90611     +667     
  Branches      8786     8962     +176     
===========================================
+ Hits         70951    71586     +635     
- Misses       18856    18885      +29     
- Partials       137      140       +3     
Flag Coverage Δ
unittests 79.00% <60.83%> (+0.12%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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 (7)
packages/ui/src/component/UITransform.ts (7)

25-25: Remove redundant double-negation.

The double-negation is unnecessary as the value will be coerced to boolean in the conditional context.

-      if (!!parentRect) {
+      if (parentRect) {

103-103: Remove redundant double-negation.

Simplify the boolean check for better readability.

-    if (!!this._alignment) {
+    if (this._alignment) {

135-135: Remove redundant double-negation.

Simplify the boolean check for consistency.

-    if (!!this._alignment) {
+    if (this._alignment) {

311-311: Remove redundant double-negation.

Simplify the boolean check for better readability.

-      if (!!alignment) {
+      if (alignment) {

338-338: Remove redundant double-negation.

Simplify the boolean check for consistency.

-    if (!!value) {
+    if (value) {

346-346: Remove redundant double-negation.

Simplify the boolean check for consistency.

-    if (!!value) {
+    if (value) {

382-382: Remove redundant double-negation.

Simplify the boolean check for consistency.

-    if (!!this._alignment) {
+    if (this._alignment) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2481ac0 and 9a95c0f.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (10 hunks)
  • packages/ui/src/component/UITransform.ts (3 hunks)
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 338-338: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 346-346: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 382-382: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/Transform.ts
🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 338-338: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 346-346: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 382-382: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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). (1)
  • GitHub Check: codecov
🔇 Additional comments (8)
packages/ui/src/component/UITransform.ts (8)

11-20: Well-structured alignment system foundation.

The private field declarations are well-organized and follow good practices:

  • Proper use of @ignoreClone for the cached _rect field
  • Consistent initialization of alignment values with NaN for optional properties
  • Clean bitmask approach with UITransformAlignmentFlags.None

22-64: Sophisticated position calculation with good performance optimization.

The position getter implementation is well-designed:

  • Proper dirty flag management to avoid unnecessary recalculations
  • Smart callback disabling during internal calculations to prevent recursion
  • Clear separation of horizontal and vertical alignment logic
  • Correct mathematical calculations for each alignment case

72-140: Excellent matrix override implementation.

The matrix getter/setter overrides demonstrate sophisticated handling of:

  • Dirty flag optimization for performance
  • Proper matrix decomposition and recomposition
  • Correct parent-child matrix relationships
  • Smart integration with the alignment system

The callback management during matrix operations prevents unwanted side effects.


142-244: Well-designed alignment property system.

The alignment property implementations demonstrate excellent design:

  • Consistent pattern across all six properties
  • Proper value equality checks to prevent unnecessary updates
  • Correct mutual exclusion logic between conflicting alignments
  • Clean separation between horizontal and vertical alignment management
  • Appropriate use of helper methods for triggering layout updates

The mutual exclusion logic correctly handles edge cases like setting center clearing left/right values.


250-266: Excellent responsive sizing implementation.

The size getter enhancement perfectly handles stretched layouts:

  • Dynamic width calculation for LeftAndRight alignment using parent bounds
  • Dynamic height calculation for TopAndBottom alignment
  • Proper dirty flag management for performance optimization
  • Smart callback disabling to prevent infinite recursion

This enables responsive UI elements that automatically adjust to parent container changes.


295-306: Proper event handling and hierarchy management.

The constructor and _parentChange override are well-implemented:

  • Correct binding of change handlers for size and pivot properties
  • Proper propagation of layout updates when parent transforms change
  • Clean integration with the dirty flag system through _transferFlags

This ensures layout consistency is maintained throughout the transform hierarchy.


308-424: Sophisticated layout management system.

The helper methods implement a comprehensive layout system:

  • Complex but correct dirty flag propagation logic in _transferFlags
  • Proper alignment update handling with position/size recalculations
  • Efficient local rect caching with appropriate invalidation
  • Correct child propagation for maintaining hierarchy consistency

The interdependency management between position, size, alignment, and dirty flags is well-architected.


431-473: Well-architected flag system.

The enum definitions demonstrate excellent design:

  • UITransformModifyFlags appropriately extends base transform flags with UI-specific additions
  • Convenient combined flag constants like LmLp, WmWp for common operations
  • UITransformAlignmentFlags uses efficient bit flag patterns for alignment state management
  • Logical separation between horizontal and vertical alignment flags
  • Clear naming and organization of flag combinations

This flag system enables efficient dirty state tracking and alignment management.

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 (3)
packages/ui/src/component/UITransform.ts (3)

22-70: Excellent alignment-aware position implementation with minor cleanup needed.

The position getter correctly implements sophisticated alignment calculations with proper dirty flag management and callback handling. The position setter is appropriately simplified for the alignment system.

Fix the redundant double-negation flagged by static analysis:

-      if (!!parentRect) {
+      if (parentRect) {

72-140: Robust matrix handling with alignment awareness.

The matrix property overrides correctly implement alignment-aware transform management with proper dirty flag handling and parent-child relationships.

Fix redundant double-negations flagged by static analysis:

-    if (!!this._alignment) {
+    if (this._alignment) {
      this._setDirtyFlagTrue(UITransformModifyFlags.LmLp);
    } else {
      this._setDirtyFlagFalse(UITransformModifyFlags.LmLp);
    }
-    if (!!this._alignment) {
+    if (this._alignment) {
      this._setDirtyFlagTrue(UITransformModifyFlags.WorldMatrix);
    } else {
      this._setDirtyFlagFalse(UITransformModifyFlags.WorldMatrix);
    }

303-396: Solid override implementations for alignment-aware transform lifecycle.

The method overrides correctly handle alignment state changes and dirty flag propagation throughout the transform hierarchy.

Fix redundant double-negations flagged by static analysis:

-      if (!!alignment) {
+      if (alignment) {
-    if (!!value) {
+    if (value) {
-    if (!!value) {
+    if (value) {
-    if (!!this._alignment) {
+    if (this._alignment) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9a95c0f and 1f9538e.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (10 hunks)
  • packages/ui/src/component/UITransform.ts (3 hunks)
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 341-341: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 385-385: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/Transform.ts
🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 341-341: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 385-385: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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: build (22.x, macos-latest)
  • GitHub Check: codecov
🔇 Additional comments (6)
packages/ui/src/component/UITransform.ts (6)

1-1: LGTM! Well-structured foundation for the alignment system.

The import additions and private field declarations properly establish the alignment system infrastructure. The use of @ignoreClone for the cached _rect field and NaN initialization for alignment offsets is appropriate.

Also applies to: 12-20


142-244: Excellent alignment property implementation with consistent patterns.

The alignment property getters and setters are well-designed with proper mutual exclusivity handling, appropriate floating-point comparisons, and consistent bitwise flag management.


249-268: Well-implemented dynamic size calculation for stretched layouts.

The size getter correctly handles dynamic sizing for stretched alignment modes while maintaining proper dirty flag management and callback handling patterns.


295-298: Appropriate constructor enhancements for alignment system.

The constructor properly binds event handlers for size and pivot changes to support the alignment system.


308-428: Comprehensive and well-structured helper method implementations.

The helper methods effectively support the alignment system with proper flag propagation, efficient caching, and consistent state management patterns.


435-477: Well-designed enum definitions for efficient flag management.

The enum extensions and new alignment flags are properly structured with non-conflicting bit patterns and logical groupings that support efficient bitwise operations.

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 (3)
packages/ui/src/component/UITransform.ts (3)

22-64: Remove redundant double-negation.

The position calculation logic is correct, but line 25 has redundant double-negation that can be simplified.

-      if (!!parentRect) {
+      if (parentRect) {

72-140: Remove redundant double-negations in matrix methods.

The matrix override logic is correct, but there are redundant double-negations that can be simplified.

-    if (!!this._alignment) {
+    if (this._alignment) {
      this._setDirtyFlagTrue(UITransformModifyFlags.LmLp);
    } else {
      this._setDirtyFlagFalse(UITransformModifyFlags.LmLp);
    }
-    if (!!this._alignment) {
+    if (this._alignment) {
      this._setDirtyFlagTrue(UITransformModifyFlags.WorldMatrix);
    } else {
      this._setDirtyFlagFalse(UITransformModifyFlags.WorldMatrix);
    }

303-428: Remove redundant double-negations in private methods.

The private method implementations are logically correct, but several instances of redundant double-negation can be simplified for cleaner code.

-      if (!!alignment) {
+      if (alignment) {
-    if (!!value) {
+    if (value) {
-    if (!!value) {
+    if (value) {
-    !!this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
+    this._alignment && this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1f9538e and 78ba4c6.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (3 hunks)
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 341-341: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 385-385: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 341-341: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 385-385: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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: codecov
  • GitHub Check: e2e (22.x, 1/4)
🔇 Additional comments (5)
packages/ui/src/component/UITransform.ts (5)

1-21: LGTM! Well-structured field declarations.

The imports and private field declarations are properly organized with appropriate decorators for the alignment system.


142-244: LGTM! Well-implemented alignment properties.

The alignment property getters and setters are consistently implemented with proper flag management and mutual exclusivity handling between conflicting alignments.


249-268: LGTM! Correct dynamic size calculation.

The size getter properly handles dynamic sizing for stretched UI elements with appropriate dirty flag management.


435-477: LGTM! Well-structured enum definitions.

The enum definitions for modify flags and alignment flags are properly organized with appropriate bitwise values and meaningful combinations.


1-477: Excellent implementation of UI alignment system.

This is a comprehensive and well-architected implementation of relative layout capabilities for UITransform. The alignment calculations are mathematically sound, dirty flag management is proper, and the code follows established patterns. The only improvements needed are removing redundant double-negations flagged by static analysis.

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 (6)
packages/ui/src/component/UITransform.ts (6)

22-64: Remove redundant double-negation in parent rectangle check.

The alignment-aware position calculation logic is well-implemented, but line 25 contains redundant double-negation as flagged by static analysis.

-      if (!!parentRect) {
+      if (parentRect) {

The mathematical calculations for different alignment cases appear correct, and the temporary disabling of value change callbacks prevents recursive updates.


72-108: Remove redundant double-negation in alignment check.

The matrix computation and decomposition logic is correct, but line 103 contains redundant double-negation.

-    if (!!this._alignment) {
+    if (this._alignment) {

The alignment-aware dirty flag management is a good addition to handle layout recalculations when matrices change.


110-140: Remove redundant double-negation in alignment check.

The world matrix computation is correctly implemented with proper parent hierarchy handling, but line 135 contains redundant double-negation.

-    if (!!this._alignment) {
+    if (this._alignment) {

The matrix inversion and local matrix update logic is sound.


303-337: Remove redundant double-negation in alignment check.

The hierarchy change propagation logic is well-implemented but line 311 contains redundant double-negation.

-      if (!!alignment) {
+      if (alignment) {

The _transferFlags method correctly handles:

  • Conditional flag propagation based on alignment state
  • Special handling for stretched alignment modes
  • Efficient dirty flag containment checks to avoid unnecessary work
  • Recursive propagation to child transforms

This is essential for maintaining layout consistency across the UI hierarchy.


339-380: Remove redundant double-negations in alignment checks.

The alignment helper methods are well-structured but contain redundant double-negations flagged by static analysis.

-    if (!!value) {
+    if (value) {

Apply this fix to both lines 341 and 349.

The helper methods provide excellent abstractions:

  • Clean separation of horizontal and vertical alignment management
  • Proper triggering of position and size recalculations when needed
  • Efficient cached rectangle computation in _getLocalRect
  • Sound bitwise operations for flag isolation

383-428: Remove redundant double-negation in alignment check.

The event handler overrides are correctly implemented but line 385 contains redundant double-negation.

-    if (!!this._alignment) {
+    if (this._alignment) {

The event handlers properly integrate with the alignment system:

  • Position change handlers manage alignment-specific dirty flags appropriately
  • Size and pivot handlers trigger necessary recalculations
  • Proper update flag manager dispatching for change notifications
  • Efficient recursive propagation to child transforms
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 78ba4c6 and 964ed0e.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (3 hunks)
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 341-341: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 385-385: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

🧰 Additional context used
🧠 Learnings (1)
packages/ui/src/component/UITransform.ts (2)

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-07-19T03:26:39.344Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

Learnt from: cptbtptpbcptdtptp
PR: #2165
File: packages/xr/src/loader/XRSceneExtendParser.ts:0-0
Timestamp: 2024-10-09T01:11:17.858Z
Learning: The user cptbtptpbcptdtptp resolved the issue regarding the use of this in a static context in the XRSceneExtendParser class by replacing it with the class name.

🪛 Biome (1.9.4)
packages/ui/src/component/UITransform.ts

[error] 25-25: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 103-103: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 135-135: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 311-311: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 341-341: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 349-349: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)


[error] 385-385: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation

(lint/complexity/noExtraBooleanCast)

⏰ 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). (3)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: build (22.x, windows-latest)
  • GitHub Check: codecov
🔇 Additional comments (5)
packages/ui/src/component/UITransform.ts (5)

1-1: Well-structured field declarations for the alignment system.

The new private fields are properly typed and decorated. The use of @ignoreClone on _rect is appropriate since it's a computed cache.

Also applies to: 11-20


142-244: Well-implemented alignment property system.

The alignment property getters and setters are correctly implemented with:

  • Proper value comparison using MathUtil.equals to avoid unnecessary updates
  • Sound bitwise flag manipulation for alignment state management
  • Appropriate mutual exclusion logic (e.g., setting center clears left and right)
  • Consistent pattern across all alignment properties

The logic correctly handles the relationships between conflicting alignment modes.


249-268: Excellent dynamic size calculation for stretched layouts.

The modified size getter correctly implements dynamic dimension calculation for stretched alignment modes:

  • Handles horizontal stretching (LeftAndRight) and vertical stretching (TopAndBottom) independently
  • Uses parent rectangle dimensions minus alignment offsets
  • Proper callback management prevents recursive updates
  • Efficient dirty flag-based recalculation

This is essential functionality for responsive UI layouts.


295-298: Proper event handler binding in constructor.

The constructor correctly binds the size and pivot change handlers to maintain proper this context when called as callbacks.


435-477: Well-designed enum extensions for the alignment system.

The enum extensions are excellently structured:

UITransformModifyFlags:

  • Adds necessary flags for alignment-aware dirty state management
  • Logical flag combinations (e.g., LmLp, WmWp) improve code readability
  • Proper bit patterns that don't conflict with existing flags

UITransformAlignmentFlags:

  • Clean separation of horizontal (0x1-0x7) and vertical (0x8-0x38) bit ranges
  • Logical combinations like LeftAndRight and TopAndBottom correctly defined
  • Efficient bit masking with Horizontal and Vertical group flags

This design enables efficient bitwise operations throughout the alignment system.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/Transform.ts (1)

301-310: Guard Matrix.invert in worldMatrix setter

Matrix.invert may return null for non-invertible parents (e.g., zero scale). Avoid multiplying with null.

-    if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat42);
-      Matrix.multiply(Transform._tempMat42, value, this._localMatrix);
-    } else {
+    if (parent) {
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat42);
+      if (inv) {
+        Matrix.multiply(inv, value, this._localMatrix);
+      } else {
+        // Fallback: treat as root when parent matrix is singular.
+        this._localMatrix.copyFrom(value);
+      }
+    } else {
       this._localMatrix.copyFrom(value);
     }
♻️ Duplicate comments (3)
tests/src/ui/UITransform.test.ts (2)

299-302: Avoid duplicating internal flags; import enum instead

Redefining UITransformModifyFlags is brittle. Import from the package if publicly exported; otherwise assert via public APIs instead of bits.

-enum UITransformModifyFlags {
-  Size = 0x200,
-  Pivot = 0x400
-}
+import { UITransformModifyFlags } from "@galacean/engine-ui";

10-22: Refactor: don’t use async describe; move setup into beforeAll/afterAll

Async describe causes tests to register unreliably in Vitest. Move async work to hooks and clean up the engine to prevent leaks.

-import { describe, expect, it } from "vitest";
+import { beforeAll, afterAll, describe, expect, it } from "vitest";
@@
-describe("UITransform", async () => {
-  const canvas = document.createElement("canvas");
-  const engine = await WebGLEngine.create({ canvas });
-  const webCanvas = engine.canvas;
-  webCanvas.width = 750;
-  webCanvas.height = 1334;
-  const scene = engine.sceneManager.scenes[0];
-  const root = scene.createRootEntity("root");
-
-  // Parent with UITransform (via UICanvas), children created under it will inherit UITransform automatically
-  const canvasEntity = root.createChild("canvas");
-  canvasEntity.addComponent(UICanvas);
+describe("UITransform", () => {
+  let engine: WebGLEngine;
+  let root: any;
+  let canvasEntity: any;
+
+  beforeAll(async () => {
+    const canvas = document.createElement("canvas");
+    engine = await WebGLEngine.create({ canvas });
+    const webCanvas = engine.canvas;
+    webCanvas.width = 750;
+    webCanvas.height = 1334;
+    const scene = engine.sceneManager.scenes[0];
+    root = scene.createRootEntity("root");
+    canvasEntity = root.createChild("canvas");
+    canvasEntity.addComponent(UICanvas);
+  });
+
+  afterAll(async () => {
+    await engine?.destroy?.();
+  });
packages/core/src/Transform.ts (1)

642-651: Guard Matrix.invert in world→local mapping

Same singular-matrix issue when writing worldPosition.

-    if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat41);
-      Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
-    } else {
+    if (parent) {
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat41);
+      if (inv) {
+        Vector3.transformCoordinate(worldPosition, inv, this._position);
+      } else {
+        this._position.copyFrom(worldPosition);
+      }
+    } else {
       this._position.copyFrom(worldPosition);
     }
🧹 Nitpick comments (4)
tests/src/ui/UITransform.test.ts (2)

2-7: Style: fix prettier import formatting

Match repo prettier: collapse named imports to a single line.

-import {
-  HorizontalAlignmentFlags,
-  UICanvas,
-  UITransform,
-  VerticalAlignmentFlags,
-} from "@galacean/engine-ui";
+import { HorizontalAlignmentFlags, UICanvas, UITransform, VerticalAlignmentFlags } from "@galacean/engine-ui";

275-277: Style: remove extra blank lines

Prettier flags these empty lines.

-
-
-
packages/ui/src/component/UITransform.ts (2)

261-266: Re-setting WorldPosition dirty in _onWorldPositionChanged can loop

Calling super clears the flag; setting it true again may cause extra churn. If the goal is to notify dependents, prefer _worldAssociatedChange gating instead of raw dirty bit.

-    if (!!this._horizontalAlignment || !!this._verticalAlignment) {
-      this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
-    }
+    // If notification is needed, gate by containment to avoid churn:
+    // if (!this._isContainDirtyFlag(UITransformModifyFlags.WorldPosition)) {
+    //   this._worldAssociatedChange(UITransformModifyFlags.WorldPosition);
+    // }

403-417: Avoid duplicating core flags in UITransformModifyFlags

These values mirror TransformModifyFlags and can drift. Reuse core flags and only add UI-specific ones (Size, Pivot).

-import {
-  Entity,
-  MathUtil,
-  Matrix,
-  Quaternion,
-  Rect,
-  Transform,
-  Vector2,
-  Vector3,
-  deepClone,
-  ignoreClone
-} from "@galacean/engine";
+import {
+  Entity,
+  MathUtil,
+  Matrix,
+  Quaternion,
+  Rect,
+  Transform,
+  TransformModifyFlags as TMF,
+  Vector2,
+  Vector3,
+  deepClone,
+  ignoreClone
+} from "@galacean/engine";
@@
-export enum UITransformModifyFlags {
-  None = 0x0,
-  LocalEuler = 0x1,
-  LocalQuat = 0x2,
-  WorldPosition = 0x4,
-  LocalMatrix = 0x40,
-  WorldMatrix = 0x80,
-  Size = 0x200,
-  Pivot = 0x400,
-
-  /** World matrix | world position. */
-  WmWp = WorldMatrix | WorldPosition,
-  /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling */
-  WmWpWeWqWsWus = 0x1bc
-}
+export enum UITransformModifyFlags {
+  None = TMF.WmWp & ~TMF.WmWp, // 0
+  // Reuse core bits:
+  LocalEuler = TMF.LocalEuler,
+  LocalQuat = TMF.LocalQuat,
+  WorldPosition = TMF.WorldPosition,
+  LocalMatrix = TMF.LocalMatrix,
+  WorldMatrix = TMF.WorldMatrix,
+  // UI-specific:
+  Size = 0x200,
+  Pivot = 0x400,
+  // Common combos:
+  WmWp = TMF.WmWp,
+  WmWpWeWqWsWus = TMF.WmWpWeWqWsWus
+}
📜 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 6e3c6b9 and 9a78804.

📒 Files selected for processing (3)
  • packages/core/src/Transform.ts (7 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
  • tests/src/ui/UITransform.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/core/src/Transform.ts
  • packages/ui/src/component/UITransform.ts
🧬 Code graph analysis (3)
packages/core/src/Transform.ts (5)
packages/math/src/Vector3.ts (3)
  • Vector3 (11-612)
  • scale (207-212)
  • scale (483-489)
packages/math/src/Matrix.ts (4)
  • Matrix (11-1288)
  • rotationQuaternion (206-242)
  • scale (670-694)
  • scale (1166-1169)
packages/physics-lite/src/LiteTransform.ts (6)
  • position (36-38)
  • position (40-46)
  • rotationQuaternion (52-63)
  • rotationQuaternion (65-72)
  • scale (110-112)
  • scale (114-120)
packages/core/src/Entity.ts (3)
  • parent (201-203)
  • parent (205-207)
  • transform (155-157)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/ui/src/component/UITransform.ts (3)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (2)
  • position (61-63)
  • position (65-69)
tests/src/ui/UITransform.test.ts (1)
packages/ui/src/component/UITransform.ts (1)
  • UITransform (18-397)
🪛 ESLint
tests/src/ui/UITransform.test.ts

[error] 2-7: Replace ⏎··HorizontalAlignmentFlags,⏎··UICanvas,⏎··UITransform,⏎··VerticalAlignmentFlags,⏎ with ·HorizontalAlignmentFlags,·UICanvas,·UITransform,·VerticalAlignmentFlags·

(prettier/prettier)


[error] 275-277: Delete ⏎⏎

(prettier/prettier)


[error] 302-302: Insert

(prettier/prettier)

⏰ 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). (4)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: codecov
🔇 Additional comments (11)
packages/core/src/Transform.ts (2)

74-83: Good: worldPosition uses position when no parent

This aligns world/local coupling with UI overrides (e.g., alignment), avoiding stale _position reads.


249-253: Good: localMatrix builds from position getter

Ensures layout-side adjustments apply before matrix composition.

packages/ui/src/component/UITransform.ts (9)

61-85: Alignment setter behavior looks right

Position-only for Left/Right/Center; position+size for LeftAndRight. Matches intended relative layout priority.


94-119: Margins trigger minimal recompute; good

Finite-checks and conditional size updates avoid unnecessary work.


135-159: Vertical alignment mirrors horizontal correctly

Top/Middle/Bottom vs TopAndBottom handling is symmetric and clear.


238-248: Matrix→local decompose plus alignment recalc

This reasserts alignment after setting local/world matrices. Verify no unexpected overrides when alignment is None.

Would you like a focused test that sets localMatrix under each alignment mode to confirm expected precedence?


250-252: Potential: skipping base _onWorldMatrixChange leaves WorldMatrix dirty

When any alignment is active, base clearing is skipped. Ensure this won’t cause repeated recomputation. Consider always clearing WorldMatrix but keep layout-specific flags separate.

I can propose a patch to clear WorldMatrix while preserving layout recalcs if needed.


268-306: Position formulas are correct

Left/Center/Right and Top/Middle/Bottom computations match rect math and tests.


308-324: Good: stretched size clamped non-negative

Prevents invalid sizes when margins exceed parent size.


355-368: Propagation on self-rect changes looks right

Inverts the contain-check correctly; dispatches WmWp and notifies children.


94-134: Confirm enums are bit-flags

Setters use bitwise checks (value & Flag). Ensure Horizontal/Vertical enums are defined as bit-flags; if they’re plain enums, switch to equality checks.

I can adjust conditionals once you confirm the enum definitions.

Also applies to: 168-207

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/core/src/Transform.ts (1)

297-310: Guard parent inverse in worldMatrix setter

If the parent’s world matrix is singular, the unguarded invert/multiply yields invalid locals.

Apply:

   const parent = this._getParentTransform();
   if (parent) {
-    Matrix.invert(parent.worldMatrix, Transform._tempMat42);
-    Matrix.multiply(Transform._tempMat42, value, this._localMatrix);
+    const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat42);
+    if (inv) {
+      Matrix.multiply(inv, value, this._localMatrix);
+    } else {
+      // Fallback: treat as root if non-invertible
+      this._localMatrix.copyFrom(value);
+    }
   } else {
     this._localMatrix.copyFrom(value);
   }
   this.localMatrix = this._localMatrix;
   this._onWorldMatrixChange();
packages/ui/src/component/UITransform.ts (2)

42-46: Enforce non-negative size in setter

Negative width/height produce inverted rects and break layout. Clamp here to guarantee invariants.

  set size(value: Vector2) {
    const { _size: size } = this;
    if (size === value) return;
-    (size.x !== value.x || size.y !== value.y) && size.copyFrom(value);
+    if (size.x !== value.x || size.y !== value.y) {
+      // Clamp to [0, +∞)
+      const nx = Math.max(0, value.x);
+      const ny = Math.max(0, value.y);
+      size.set(nx, ny);
+    }
  }

230-236: Clone misses alignment and margins

Cloned UITransform loses layout (alignment flags and offsets), causing visual divergence.

  override _cloneTo(target: UITransform, srcRoot: Entity, targetRoot: Entity): void {
    // @ts-ignore
    super._cloneTo(target, srcRoot, targetRoot);
    target.size.copyFrom(this._size);
    target.pivot.copyFrom(this._pivot);
+   // Copy alignment and margins
+   target.horizontalAlignment = this._horizontalAlignment;
+   target.verticalAlignment = this._verticalAlignment;
+   target.left = this._left;
+   target.right = this._right;
+   target.center = this._center;
+   target.top = this._top;
+   target.bottom = this._bottom;
+   target.middle = this._middle;
  }
♻️ Duplicate comments (2)
packages/core/src/Transform.ts (1)

642-653: Also guard invert in world→local position mapping

Unprotected inversion can propagate NaNs when the parent is non-invertible. Same concern previously raised.

Apply:

   const parent = this._getParentTransform();
   if (parent) {
-    Matrix.invert(parent.worldMatrix, Transform._tempMat41);
-    Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
+    const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat41);
+    if (inv) {
+      Vector3.transformCoordinate(worldPosition, inv, this._position);
+    } else {
+      this._position.copyFrom(worldPosition);
+    }
   } else {
     this._position.copyFrom(worldPosition);
   }
   this._setDirtyFlagFalse(TransformModifyFlags.WorldPosition);
packages/ui/src/component/UITransform.ts (1)

1-14: Stop duplicating core Transform flags; reference them

UITransformModifyFlags redefines core bits (LocalMatrix, WorldMatrix, etc.). This is brittle if core values change. Reference core flags directly and reserve UI-only bits for Size/Pivot.

Apply:

-import {
-  Entity,
-  MathUtil,
-  Matrix,
-  Quaternion,
-  Rect,
-  Transform,
-  Vector2,
-  Vector3,
-  deepClone,
-  ignoreClone
-} from "@galacean/engine";
+import {
+  Entity,
+  MathUtil,
+  Matrix,
+  Quaternion,
+  Rect,
+  Transform,
+  TransformModifyFlags as TMF,
+  Vector2,
+  Vector3,
+  deepClone,
+  ignoreClone
+} from "@galacean/engine";
@@
 export enum UITransformModifyFlags {
-  None = 0x0,
-  LocalEuler = 0x1,
-  LocalQuat = 0x2,
-  WorldPosition = 0x4,
-  LocalMatrix = 0x40,
-  WorldMatrix = 0x80,
-  Size = 0x200,
-  Pivot = 0x400,
-
-  /** World matrix | world position. */
-  WmWp = WorldMatrix | WorldPosition,
-  /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling */
-  WmWpWeWqWsWus = 0x1bc
+  None = 0x0,
+  // Reuse core flags
+  LocalEuler = TMF.LocalEuler,
+  LocalQuat = TMF.LocalQuat,
+  WorldPosition = TMF.WorldPosition,
+  LocalMatrix = TMF.LocalMatrix,
+  WorldMatrix = TMF.WorldMatrix,
+  // UI-only flags
+  Size = 0x200,
+  Pivot = 0x400,
+  // Reuse core composites
+  WmWp = TMF.WmWp,
+  WmWpWeWqWsWus = TMF.WmWpWeWqWsWus
 }

Also applies to: 403-417

🧹 Nitpick comments (2)
packages/core/src/Transform.ts (1)

616-634: Minor: simplify parent lookup

Every Entity has a transform; the loop that skips entities without transforms can be simplified for maintainability.

-    let parentCache: Transform = null;
-    let parent = this._entity.parent;
-    while (parent) {
-      const transform = parent.transform;
-      if (transform) {
-        parentCache = transform;
-        break;
-      } else {
-        parent = parent.parent;
-      }
-    }
+    let parentCache: Transform = null;
+    let parent = this._entity.parent;
+    if (parent) parentCache = parent.transform;
packages/ui/src/component/UITransform.ts (1)

55-59: Clamp pivot to [0, 1]

Out-of-range pivots yield surprising rects. Clamp for robustness.

  set pivot(value: Vector2) {
    const { _pivot: pivot } = this;
    if (pivot === value) return;
-    (pivot.x !== value.x || pivot.y !== value.y) && pivot.copyFrom(value);
+    if (pivot.x !== value.x || pivot.y !== value.y) {
+      pivot.set(MathUtil.clamp(value.x, 0, 1), MathUtil.clamp(value.y, 0, 1));
+    }
  }
📜 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 9a78804 and 087aa67.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (6 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
  • packages/core/src/Transform.ts
🧬 Code graph analysis (2)
packages/ui/src/component/UITransform.ts (3)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (2)
  • position (61-63)
  • position (65-69)
packages/core/src/Transform.ts (5)
packages/math/src/Matrix.ts (4)
  • Matrix (11-1288)
  • rotationQuaternion (206-242)
  • scale (670-694)
  • scale (1166-1169)
packages/physics-lite/src/LiteTransform.ts (6)
  • position (36-38)
  • position (40-46)
  • rotationQuaternion (52-63)
  • rotationQuaternion (65-72)
  • scale (110-112)
  • scale (114-120)
packages/math/src/Vector3.ts (3)
  • scale (207-212)
  • scale (483-489)
  • Vector3 (11-612)
packages/core/src/Entity.ts (3)
  • parent (201-203)
  • parent (205-207)
  • transform (155-157)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
⏰ 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). (5)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: e2e (22.x, 1/4)
  • GitHub Check: codecov
🔇 Additional comments (22)
packages/core/src/Transform.ts (4)

53-55: Good change: make parent-dirty visible to subclasses

Promoting _isParentDirty to protected cleanly enables UITransform to hook parent-cache invalidation.


80-83: worldPosition fallback to local is correct

Using worldPosition.copyFrom(this.position) when no parent preserves prior semantics and reduces duplication.


249-253: Compute localMatrix from public accessors

Building the affine from this.position/this.rotationQuaternion/this._scale is consistent with the new dirty-paths and avoids bypassing hooks.


590-593: No-op world-matrix hook is fine

Clearing only the WorldMatrix bit here centralizes behavior and lets subclasses override selectively.

packages/ui/src/component/UITransform.ts (18)

68-85: Alignment setter side-effects look correct

Position invalidation on Left/Right/Center and size+position on LeftAndRight matches expected stretch behavior.


94-103: Left margin updates: OK

Conditional position and stretch-size recalculation are appropriate and avoid redundant work.


111-119: Right margin updates: OK

Symmetric with left; looks good.


128-133: Center offset update: OK

No size adjustments on center-only is correct.


142-159: Vertical alignment setter mirrors horizontal: OK

Middle and TopAndBottom paths mirror center/stretch behavior correctly.


168-176: Top margin path: OK

Triggers position and stretch-size updates only when relevant flags are set.


185-193: Bottom margin path: OK

Matches top logic; good.


202-207: Middle offset update: OK

Correctly limited to Middle alignment.


214-220: Wiring size/pivot change handlers is correct

Binding and assigning _onValueChanged hooks ensures reactive layout.


225-228: Parent change propagation: OK

Marking parent-dirty and kicking rect-dependent world flags downstream is appropriate.


238-248: Override of _decomposeLocalMatrix: OK

Decomposing the provided matrix, then recomputing position under alignment honors “relative > absolute” design.


250-253: World-matrix clearing delegated appropriately

Skipping base clearing when alignment is active prevents premature world-flag resolution; good.


260-266: Ignore world-position writes under active alignment

Re-flagging WorldPosition after the base conversion ensures absolute writes don’t override relative layout. Matches stated design choice.


268-306: Position formulas look correct

Left/Center/Right and Top/Middle/Bottom equations are consistent with rect math (child edge equals parent edge ± margins).


308-324: Stretch size clamped to non-negative

Good safeguard against over-constrained layouts.


333-345: Size-change pipeline is sound

Recomputes stretch, rect, propagates world flags, and dispatches Size; minimal, correct.


355-368: Self-rect change propagation: OK

Conditional world-flag dispatch plus child fan-out avoids redundant updates.


370-396: Parent-rect change handling: OK

Correctly recomputes stretch, rect, and position then propagates, with short-circuiting when flags already set.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/component/UITransform.ts (1)

230-236: Clone misses layout state (alignments and margins).

Cloned UI elements lose alignment/margins, producing different layout.

Apply:

   override _cloneTo(target: UITransform, srcRoot: Entity, targetRoot: Entity): void {
     // @ts-ignore
     super._cloneTo(target, srcRoot, targetRoot);
-    target.size.copyFrom(this._size);
-    target.pivot.copyFrom(this._pivot);
+    target.size.copyFrom(this._size);
+    target.pivot.copyFrom(this._pivot);
+    // Copy layout state without triggering recalcs during clone.
+    (target as any)._horizontalAlignment = this._horizontalAlignment;
+    (target as any)._verticalAlignment = this._verticalAlignment;
+    (target as any)._left = this._left;
+    (target as any)._right = this._right;
+    (target as any)._center = this._center;
+    (target as any)._top = this._top;
+    (target as any)._bottom = this._bottom;
+    (target as any)._middle = this._middle;
+    (target as any)._rect.copyFrom(this._rect);
   }

I can add a focused test asserting cloned nodes preserve alignment and margins.

♻️ Duplicate comments (1)
packages/core/src/Transform.ts (1)

642-653: Also guard inversion in world→local position mapping.

Apply:

-    if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat41);
-      Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
-    } else {
+    if (parent) {
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat41);
+      if (inv) {
+        Vector3.transformCoordinate(worldPosition, inv, this._position);
+      } else {
+        this._position.copyFrom(worldPosition);
+      }
+    } else {
       this._position.copyFrom(worldPosition);
     }
🧹 Nitpick comments (3)
packages/ui/src/component/UITransform.ts (3)

255-261: Short-circuit worldPosition writes under active alignment to reduce churn.

Avoid doing world→local then immediately overriding via alignment and re-dirtying WorldPosition.

Apply:

-  protected override _onWorldPositionChanged(): void {
-    super._onWorldPositionChanged();
-    if (!!this._horizontalAlignment || !!this._verticalAlignment) {
-      this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
-    }
-  }
+  protected override _onWorldPositionChanged(): void {
+    if (this._horizontalAlignment || this._verticalAlignment) {
+      // Ignore external world-position writes when relative layout is active.
+      this._calPosition();
+      this._setDirtyFlagFalse(UITransformModifyFlags.WorldPosition);
+      return;
+    }
+    super._onWorldPositionChanged();
+  }

303-319: Clamp comment vs. behavior mismatch.

Comment says “must be greater than 0” but code clamps to >= 0. Align the comment to “non-negative” or clamp to a tiny epsilon.

Apply either:

-      // The values of size must be greater than 0.
+      // The values of size must be non-negative.

or

-        size.x = Math.max(parentRect.width - this._left - this._right, 0);
+        size.x = Math.max(parentRect.width - this._left - this._right, 1e-6);

(and similarly for size.y)


398-412: Deduplicate flag values by reusing core TransformModifyFlags.

Prevents silent drift if core bits change.

Apply:

@@
-import {
+import {
   Entity,
   MathUtil,
   Matrix,
   Quaternion,
   Rect,
   Transform,
+  TransformModifyFlags as TMF,
   Vector2,
   Vector3,
   deepClone,
   ignoreClone
 } from "@galacean/engine";
@@
 export enum UITransformModifyFlags {
   None = 0x0,
-  LocalEuler = 0x1,
-  LocalQuat = 0x2,
-  WorldPosition = 0x4,
-  LocalMatrix = 0x40,
-  WorldMatrix = 0x80,
+  LocalEuler = TMF.LocalEuler,
+  LocalQuat = TMF.LocalQuat,
+  WorldPosition = TMF.WorldPosition,
+  LocalMatrix = TMF.LocalMatrix,
+  WorldMatrix = TMF.WorldMatrix,
   Size = 0x200,
   Pivot = 0x400,
   /** World matrix | world position. */
-  WmWp = WorldMatrix | WorldPosition,
+  WmWp = TMF.WmWp,
   /** WorldMatrix | WorldPosition | WorldEuler | WorldQuat | WorldScale | WorldUniformScaling */
-  WmWpWeWqWsWus = 0x1bc
+  WmWpWeWqWsWus = TMF.WmWpWeWqWsWus
 }

Also applies to: 1-12

📜 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 5cf9d11 and 74e37b3.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (4 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
  • packages/core/src/Transform.ts
🧬 Code graph analysis (2)
packages/ui/src/component/UITransform.ts (3)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (2)
  • position (61-63)
  • position (65-69)
packages/core/src/Transform.ts (3)
packages/physics-lite/src/LiteTransform.ts (6)
  • position (36-38)
  • position (40-46)
  • rotationQuaternion (52-63)
  • rotationQuaternion (65-72)
  • scale (110-112)
  • scale (114-120)
packages/core/src/Entity.ts (3)
  • parent (201-203)
  • parent (205-207)
  • transform (155-157)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
⏰ 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: e2e (22.x, 1/4)
  • GitHub Check: codecov
🔇 Additional comments (7)
packages/core/src/Transform.ts (3)

584-588: Good fix: now decomposes the passed-in matrix (not this._localMatrix).

This resolves the earlier bug and keeps dirty flags consistent.


610-615: World-associated dispatch helper looks correct.

Only newly added flags are dispatched; avoids redundant events.


297-310: Matrix.invert returns void, not a boolean
Matrix.invert(a, out) mutates out and does not return success/failure (its return value is always undefined). Capturing its return value and branching on it won’t work. To guard against non-invertible parent matrices, compute the determinant (e.g. via a new Matrix.determinant method) and skip inversion on singular matrices, or extend the API with a tryInvert(a, out): boolean variant that returns false on failure.

Likely an incorrect or invalid review comment.

packages/ui/src/component/UITransform.ts (4)

249-253: Relative layout correctly overrides absolute position writes.

Calling _calPosition before delegating to super keeps alignment priority as intended.


263-301: Alignment formulas look correct.

Left/Center/Right and Top/Middle/Bottom computations correctly use parent/local rects and margins.


263-266: Confirm parent is always a UITransform; otherwise, define a fallback.

If a UITransform can be parented under a non-UI Transform, parentRect is undefined and alignment becomes a no-op. Either assert UI parentage or add a documented fallback.

Would you like me to add a dev-assert (only in non-prod) and a graceful fallback?

Also applies to: 303-306


222-228: World-flag propagation for rect changes looks sound.

Self and parent-rect change paths update LocalMatrix and dispatch newly added world flags; child propagation is gated to avoid redundant dispatch.

Also applies to: 350-363, 365-391

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 (7)
packages/ui/src/component/UITransform.ts (7)

68-85: Canonicalize horizontalAlignment to supported combos

Prevent illegal mixes (e.g., Left|Center) from becoming a silent no-op by normalizing to None | Left | Center | Right | LeftAndRight before applying side effects.

   set horizontalAlignment(value: HorizontalAlignmentFlags) {
-    const current = this._horizontalAlignment;
-    if (current === value) return;
-    this._horizontalAlignment = value;
-    switch (value) {
+    // Canonicalize: only allow None | Left | Center | Right | LeftAndRight
+    const LR = HorizontalAlignmentFlags.Left | HorizontalAlignmentFlags.Right;
+    const allowed = HorizontalAlignmentFlags.Left | HorizontalAlignmentFlags.Right | HorizontalAlignmentFlags.Center;
+    let v = value & allowed;
+    if ((v & LR) === LR) v = HorizontalAlignmentFlags.LeftAndRight;
+    else if ((v & HorizontalAlignmentFlags.Center) && (v & LR)) v = HorizontalAlignmentFlags.Center;
+    if (v === 0) v = HorizontalAlignmentFlags.None;
+
+    const current = this._horizontalAlignment;
+    if (current === v) return;
+    this._horizontalAlignment = v;
+    switch (v) {
       case HorizontalAlignmentFlags.Left:
       case HorizontalAlignmentFlags.Right:
       case HorizontalAlignmentFlags.Center:
         this._onPositionChanged();
         break;
       case HorizontalAlignmentFlags.LeftAndRight:
         this._onPositionChanged();
         this._onSizeChanged();
         break;
       default:
         break;
     }
   }

142-159: Canonicalize verticalAlignment to supported combos

Normalize to None | Top | Middle | Bottom | TopAndBottom to avoid undefined behavior with mixed flags.

   set verticalAlignment(value: VerticalAlignmentFlags) {
-    const current = this._verticalAlignment;
-    if (current === value) return;
-    this._verticalAlignment = value;
-    switch (value) {
+    // Canonicalize: only allow None | Top | Middle | Bottom | TopAndBottom
+    const TB = VerticalAlignmentFlags.Top | VerticalAlignmentFlags.Bottom;
+    const allowed = VerticalAlignmentFlags.Top | VerticalAlignmentFlags.Bottom | VerticalAlignmentFlags.Middle;
+    let v = value & allowed;
+    if ((v & TB) === TB) v = VerticalAlignmentFlags.TopAndBottom;
+    else if ((v & VerticalAlignmentFlags.Middle) && (v & TB)) v = VerticalAlignmentFlags.Middle;
+    if (v === 0) v = VerticalAlignmentFlags.None;
+
+    const current = this._verticalAlignment;
+    if (current === v) return;
+    this._verticalAlignment = v;
+    switch (v) {
       case VerticalAlignmentFlags.Top:
       case VerticalAlignmentFlags.Bottom:
       case VerticalAlignmentFlags.Middle:
         this._onPositionChanged();
         break;
       case VerticalAlignmentFlags.TopAndBottom:
         this._onPositionChanged();
         this._onSizeChanged();
         break;
       default:
         break;
     }
   }

238-243: Avoid unnecessary _calPosition when alignment is off

Minor perf/readability: guard the call; behavior unchanged.

   protected override _decomposeLocalMatrix(matrix: Matrix, pos: Vector3, quat: Quaternion, scale: Vector3): void {
     matrix.decompose(pos, quat, scale);
-    this._calPosition();
+    if (this._horizontalAlignment || this._verticalAlignment) {
+      this._calPosition();
+    }
     this._setDirtyFlagTrue(UITransformModifyFlags.LocalEuler | UITransformModifyFlags.LocalMatrix);
     this._setDirtyFlagFalse(UITransformModifyFlags.LocalQuat);
   }

256-261: Redundant world-position dirty flag

Base already handles world-position dirtiness; setting it again under alignment adds noise without benefit.

   protected override _onWorldPositionChanged(): void {
     super._onWorldPositionChanged();
-    if (!!this._horizontalAlignment || !!this._verticalAlignment) {
-      this._setDirtyFlagTrue(UITransformModifyFlags.WorldPosition);
-    }
   }

263-266: Type-narrow parentTransform instead of casting through unknown

Safer narrowing avoids accidental misuse when the parent isn’t a UITransform.

-    const parentRect = (this._getParentTransform() as unknown as UITransform)?._rect;
+    const parent = this._getParentTransform();
+    const parentRect = parent instanceof UITransform ? parent._rect : undefined;

Apply similarly in _calSize.

Also applies to: 303-306


309-315: Comment nit: “greater than 0” → “non-negative”

You clamp to 0, so the comment should reflect non-negativity.

-      // The values of size must be greater than 0.
+      // Size values are clamped to be non-negative.

401-414: Don’t duplicate core Transform flags; ensure parity or reuse

Redefining shared bits (LocalMatrix, WorldMatrix, WorldPosition, etc.) is brittle. Prefer reusing core flags or add a dev-only parity assert to catch drift early.

Option A (preferred): reuse core flags.

  • Import core TransformModifyFlags and reference them where needed, keeping UI-only flags (Size, Pivot, LocalRect, LocalPosition) in this enum or as consts.

Option B: add a parity assert (dev-only) that validates your numeric values match core at startup; keep UI-only bits local.

📜 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 74e37b3 and 73bd148.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
🧬 Code graph analysis (1)
packages/ui/src/component/UITransform.ts (3)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (4)
  • scale (215-217)
  • scale (219-223)
  • position (61-63)
  • position (65-69)
⏰ 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: e2e (22.x, 4/4)
  • GitHub Check: codecov
🔇 Additional comments (2)
packages/ui/src/component/UITransform.ts (2)

270-301: Alignment formulas look correct (left/center/right, top/middle/bottom)

The position equations properly anchor edges/centers relative to parentRect and local rect. Nice touch disabling callbacks during mutation. This matches the intended “relative layout overrides absolute” behavior noted earlier.


328-340: LGTM: Size/rect propagation under stretch

Recomputing size under LeftAndRight/TopAndBottom, then rect and world propagation, aligns with the product decision that relative layout takes precedence over absolute size.

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: 1

♻️ Duplicate comments (1)
packages/ui/src/component/UITransform.ts (1)

400-414: Deduplicate core Transform flags; reuse from core or assert parity

Replicating core flag bit values is brittle. Reuse TransformModifyFlags for shared bits; keep UI-only flags locally. If reuse isn’t possible, add a dev-time parity assertion.

Preferred change:

+import { TransformModifyFlags as TMF } from "@galacean/engine";
@@
 export enum UITransformModifyFlags {
-  None = 0x0,
-  LocalEuler = 0x1,
-  LocalQuat = 0x2,
-  WorldPosition = 0x4,
-  LocalMatrix = 0x40,
-  WorldMatrix = 0x80,
+  None = 0x0,
+  LocalEuler = TMF.LocalEuler,
+  LocalQuat = TMF.LocalQuat,
+  WorldPosition = TMF.WorldPosition,
+  LocalMatrix = TMF.LocalMatrix,
+  WorldMatrix = TMF.WorldMatrix,
   Size = 0x200,
   Pivot = 0x400,
-  WmWp = WorldMatrix | WorldPosition,
-  WmWpWeWqWsWus = 0x1bc
+  WmWp = TMF.WorldMatrix | TMF.WorldPosition,
+  // Keep this composite in sync with core:
+  WmWpWeWqWsWus = TMF.WorldMatrix | TMF.WorldPosition | TMF.WorldEuler | TMF.WorldQuat | TMF.WorldScale | TMF.WorldUniformScaling
 }

Fallback (if importing TMF here causes layering issues): add a one-time dev assert at startup comparing these bit values against core.

🧹 Nitpick comments (3)
packages/ui/src/component/UITransform.ts (3)

245-247: Prefer explicit if over boolean short-circuit for side effects

Improves readability and avoids mixing boolean expressions with a void call.

-  protected override _onWorldMatrixChange() {
-    !this._horizontalAlignment && !this._verticalAlignment && super._onWorldMatrixChange();
-  }
+  protected override _onWorldMatrixChange() {
+    if (!this._horizontalAlignment && !this._verticalAlignment) {
+      super._onWorldMatrixChange();
+    }
+  }

249-253: Remove @ignoreClone on methods

ignoreClone is for fields; applying it to methods is unnecessary and pollutes clone metadata.

-  @ignoreClone
   protected override _onPositionChanged(): void {
@@
-  @ignoreClone
   protected override _onWorldPositionChanged(): void {
@@
-  @ignoreClone
   private _onSizeChanged(): void {
@@
-  @ignoreClone
   private _onPivotChanged(): void {

Also applies to: 255-261, 328-340, 342-348


263-266: Tighten parent rect access (avoid double cast to unknown)

Minor TS hygiene; keeps the cast localized and clearer.

-    const parentRect = (this._getParentTransform() as unknown as UITransform)?._rect;
+    const parent = this._getParentTransform() as UITransform | undefined;
+    const parentRect = parent?._rect;
📜 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 73bd148 and d39c1eb.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
🧬 Code graph analysis (1)
packages/ui/src/component/UITransform.ts (3)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (4)
  • scale (215-217)
  • scale (219-223)
  • position (61-63)
  • position (65-69)
⏰ 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). (1)
  • GitHub Check: codecov
🔇 Additional comments (2)
packages/ui/src/component/UITransform.ts (2)

270-283: Alignment formulas look correct

Left/Center/Right and Top/Middle/Bottom positioning math matches expected rect-edge/center relationships and honors pivot. This also aligns with the intended “relative > absolute” priority noted earlier.

Also applies to: 285-297


303-319: Good: stretched size is clamped

Clamping the stretched size to non-negative prevents invalid widths/heights under over-constrained margins.

Comment on lines 328 to 340
@ignoreClone
private _onSizeChanged(): void {
if (
this._horizontalAlignment === HorizontalAlignmentFlags.LeftAndRight ||
this._verticalAlignment === VerticalAlignmentFlags.TopAndBottom
) {
this._calSize();
}
this._calRect();
this._updateWorldFlagWithSelfRectChange();
// @ts-ignore
this._entity._updateFlagManager.dispatch(UITransformModifyFlags.Size);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clamp size for non-stretch cases too

Direct size changes can still be negative and will corrupt rect/placement math. Clamp here as well (not just in stretch), before recalculating the rect.

Apply:

   private _onSizeChanged(): void {
     if (
       this._horizontalAlignment === HorizontalAlignmentFlags.LeftAndRight ||
       this._verticalAlignment === VerticalAlignmentFlags.TopAndBottom
     ) {
       this._calSize();
     }
+    // Ensure non-negative size in all cases to keep rect math valid.
+    const size = this._size;
+    // @ts-ignore
+    size._onValueChanged = null;
+    size.x = Math.max(0, size.x);
+    size.y = Math.max(0, size.y);
+    // @ts-ignore
+    size._onValueChanged = this._onSizeChanged;
     this._calRect();
     this._updateWorldFlagWithSelfRectChange();
📝 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
@ignoreClone
private _onSizeChanged(): void {
if (
this._horizontalAlignment === HorizontalAlignmentFlags.LeftAndRight ||
this._verticalAlignment === VerticalAlignmentFlags.TopAndBottom
) {
this._calSize();
}
this._calRect();
this._updateWorldFlagWithSelfRectChange();
// @ts-ignore
this._entity._updateFlagManager.dispatch(UITransformModifyFlags.Size);
}
@ignoreClone
private _onSizeChanged(): void {
if (
this._horizontalAlignment === HorizontalAlignmentFlags.LeftAndRight ||
this._verticalAlignment === VerticalAlignmentFlags.TopAndBottom
) {
this._calSize();
}
// Ensure non-negative size in all cases to keep rect math valid.
const size = this._size;
// @ts-ignore
size._onValueChanged = null;
size.x = Math.max(0, size.x);
size.y = Math.max(0, size.y);
// @ts-ignore
size._onValueChanged = this._onSizeChanged;
this._calRect();
this._updateWorldFlagWithSelfRectChange();
// @ts-ignore
this._entity._updateFlagManager.dispatch(UITransformModifyFlags.Size);
}
🤖 Prompt for AI Agents
In packages/ui/src/component/UITransform.ts around lines 328-340, the
_onSizeChanged method needs to clamp the transform's explicit size for
non-stretch cases as well so negative direct size changes don't corrupt rect
math; before calling this._calRect() (and before dispatching the Size flag)
clamp width/height to a non-negative minimum (or call the module's existing
clamp/normalize helper if one exists), e.g. ensure this._width = Math.max(0,
this._width) and this._height = Math.max(0, this._height) (or equivalent), then
proceed to _calRect(), _updateWorldFlagWithSelfRectChange(), and the dispatch.

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/Transform.ts (1)

301-310: Guard non-invertible parent in worldMatrix write path.

If parent.worldMatrix is singular (e.g., zero scale), Matrix.invert can fail; current code proceeds with an invalid inverse.

Apply:

-    if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat42);
-      Matrix.multiply(Transform._tempMat42, value, this._localMatrix);
-    } else {
+    if (parent) {
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat42);
+      if (inv) {
+        Matrix.multiply(inv, value, this._localMatrix);
+      } else {
+        // Parent not invertible; best-effort: treat as root.
+        this._localMatrix.copyFrom(value);
+      }
+    } else {
       this._localMatrix.copyFrom(value);
     }
     this.localMatrix = this._localMatrix;
     this._onWorldMatrixChange();
♻️ Duplicate comments (3)
tests/src/ui/UITransform.test.ts (2)

10-22: Fix async test setup structure.

This is the same issue raised in previous reviews about async describe blocks not registering tests reliably in Vitest.


366-369: Export UITransformModifyFlags instead of redefining.

The enum is being redefined locally instead of importing from the canonical source, which makes it brittle to changes.

packages/core/src/Transform.ts (1)

643-653: Also guard invert in world→local position mapping.

Same singular-parent issue as above when writing worldPosition.

   protected _onWorldPositionChanged(): void {
     const worldPosition = this._worldPosition;
     const parent = this._getParentTransform();
     if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat41);
-      Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat41);
+      if (inv) {
+        Vector3.transformCoordinate(worldPosition, inv, this._position);
+      } else {
+        // Parent not invertible; best-effort: treat as root.
+        this._position.copyFrom(worldPosition);
+      }
     } else {
       this._position.copyFrom(worldPosition);
     }
     this._setDirtyFlagFalse(TransformModifyFlags.WorldPosition);
   }
🧹 Nitpick comments (4)
tests/src/ui/UITransform.test.ts (1)

324-324: Fix prettier formatting issues.

The static analysis indicates formatting problems with line breaks in this section. While the test logic is correct, addressing these formatting issues will ensure consistent code style.

Also applies to: 326-326

packages/ui/src/component/UITransform.ts (1)

398-405: Consider exposing UITransformModifyFlags publicly or using core flags.

Based on previous review feedback, consider either:

  1. Exporting this enum via the public API (in packages/ui/src/index.ts) so tests and external code can import it reliably
  2. Reusing TransformModifyFlags from core for shared flag values and defining only UI-specific additions here

This would reduce maintenance burden and prevent tests from needing to redefine the enum values.

packages/core/src/Transform.ts (1)

610-615: Nit: avoid dispatching when no new bits are added.

Minor micro-opt to skip a no-op dispatch.

   protected _worldAssociatedChange(type: number): void {
-    const dispatchFlags = type & ~this._dirtyFlag;
+    const dispatchFlags = type & ~this._dirtyFlag;
     this._dirtyFlag |= type;
-    this._entity._updateFlagManager.dispatch(dispatchFlags);
+    if (dispatchFlags !== 0) {
+      this._entity._updateFlagManager.dispatch(dispatchFlags);
+    }
   }
packages/ui/src/enums/HorizontalAlignmentMode.ts (1)

1-12: Make flags semantics explicit; document valid combos; tighten JSDoc

Clarify this is a flags enum, encode values with shifts/OR for readability, and state that Center is mutually exclusive with Left/Right. Also note alignment precedence per UITransform behavior and add a since tag.

-/** Horizontal alignment mode. */
-export enum HorizontalAlignmentMode {
-  /** No horizontal alignment. */
-  None = 0,
-  /** Left-aligned, `alignLeft` drives `position.x`. */
-  Left = 0x1,
-  /** Right-aligned, `alignRight` drives `position.x`. */
-  Right = 0x2,
-  /** Horizontal stretch, `alignLeft` and `alignRight` drive `position.x` and `size.x`. */
-  LeftAndRight = 0x3,
-  /** Center-aligned, `alignCenter` drives `position.x`. */
-  Center = 0x4
-}
+/**
+ * Horizontal alignment mode (flags).
+ *
+ * Valid states:
+ * - None
+ * - Left
+ * - Right
+ * - Left | Right (aka LeftAndRight; stretch)
+ * - Center (mutually exclusive with Left/Right)
+ *
+ * Note: When not `None`, alignment overrides absolute `position.x` in UITransform.
+ * @since 1.6
+ */
+export enum HorizontalAlignmentMode {
+  /** No horizontal alignment. */
+  None = 0,
+  /** Left-aligned, `alignLeft` drives `position.x`. */
+  Left = 1 << 0,
+  /** Right-aligned, `alignRight` drives `position.x`. */
+  Right = 1 << 1,
+  /** Horizontal stretch, `alignLeft` and `alignRight` drive `position.x` and `size.x`. */
+  LeftAndRight = Left | Right,
+  /** Center-aligned, `alignCenter` drives `position.x` (do not combine with Left/Right). */
+  Center = 1 << 2
+}
📜 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 d39c1eb and d323db9.

📒 Files selected for processing (7)
  • packages/core/src/Transform.ts (5 hunks)
  • packages/core/src/index.ts (1 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
  • packages/ui/src/enums/HorizontalAlignmentMode.ts (1 hunks)
  • packages/ui/src/enums/VerticalAlignmentMode.ts (1 hunks)
  • packages/ui/src/index.ts (1 hunks)
  • tests/src/ui/UITransform.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/src/index.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
  • packages/core/src/Transform.ts
🧬 Code graph analysis (5)
packages/ui/src/enums/VerticalAlignmentMode.ts (1)
packages/ui/src/index.ts (1)
  • VerticalAlignmentMode (25-25)
packages/ui/src/enums/HorizontalAlignmentMode.ts (1)
packages/ui/src/index.ts (1)
  • HorizontalAlignmentMode (24-24)
tests/src/ui/UITransform.test.ts (1)
packages/ui/src/component/UITransform.ts (1)
  • UITransform (20-396)
packages/ui/src/component/UITransform.ts (5)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/ui/src/index.ts (2)
  • HorizontalAlignmentMode (24-24)
  • VerticalAlignmentMode (25-25)
packages/core/src/index.ts (2)
  • Entity (11-11)
  • TransformModifyFlags (17-17)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (2)
  • position (61-63)
  • position (65-69)
packages/core/src/Transform.ts (5)
packages/physics-lite/src/LiteTransform.ts (6)
  • position (36-38)
  • position (40-46)
  • rotationQuaternion (52-63)
  • rotationQuaternion (65-72)
  • scale (110-112)
  • scale (114-120)
packages/math/src/Matrix.ts (4)
  • rotationQuaternion (206-242)
  • scale (670-694)
  • scale (1166-1169)
  • Matrix (11-1288)
packages/math/src/Vector3.ts (3)
  • scale (207-212)
  • scale (483-489)
  • Vector3 (11-612)
packages/core/src/Entity.ts (3)
  • parent (201-203)
  • parent (205-207)
  • transform (155-157)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
🪛 ESLint
tests/src/ui/UITransform.test.ts

[error] 2-7: Replace ⏎··HorizontalAlignmentMode,⏎··UICanvas,⏎··UITransform,⏎··VerticalAlignmentMode,⏎ with ·HorizontalAlignmentMode,·UICanvas,·UITransform,·VerticalAlignmentMode·

(prettier/prettier)


[error] 324-324: Insert ⏎·······

(prettier/prettier)


[error] 326-326: Insert ⏎·······

(prettier/prettier)


[error] 339-342: Replace ⏎··});⏎⏎ with ··});

(prettier/prettier)


[error] 369-369: Insert

(prettier/prettier)

⏰ 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). (3)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: codecov
🔇 Additional comments (34)
packages/ui/src/enums/VerticalAlignmentMode.ts (1)

1-13: LGTM - Consistent with horizontal alignment design.

The enum maintains parity with HorizontalAlignmentMode using the same hex value pattern. Clear documentation of how each mode drives position and size calculations.

packages/core/src/index.ts (1)

17-17: LGTM - Exposes TransformModifyFlags publicly.

This enables the UI layer and external consumers to access the centralized dirty-flag system, which supports the enhanced transform hierarchy introduced in this PR.

tests/src/ui/UITransform.test.ts (5)

23-60: LGTM - Comprehensive event dispatch testing.

The test properly verifies that size and pivot changes dispatch the correct flags through the entity's update flag manager, which is crucial for the hierarchical dirty propagation system.


62-188: LGTM - Thorough alignment testing coverage.

The tests comprehensively cover both horizontal and vertical alignment modes under default and custom parent configurations, including stretch modes. The expected values properly account for parent size, pivot, and alignment insets.


189-272: LGTM - Excellent dirty propagation testing.

These tests effectively verify that rect changes propagate correctly through the hierarchy, both for self-rect changes (pivot/size) and parent-rect changes. The test cases cover the critical scenarios for layout invalidation and recalculation.


274-339: LGTM - Validates alignment priority over manual transforms.

These tests correctly verify the design decision that relative layout (alignment) takes priority over absolute positioning, as confirmed in the retrieved learnings. The tests ensure layout consistency is maintained even when manual transform operations are attempted.


344-363: LGTM - World transform integration testing.

The test properly verifies that alignment-driven local positions compose correctly with parent world transforms, ensuring the layout system works within the broader transform hierarchy.

packages/ui/src/component/UITransform.ts (19)

1-15: LGTM - Well-structured imports for UI layout system.

The imports bring together core engine primitives with the new alignment enums to support the comprehensive UI layout features being introduced.


21-35: LGTM - Clean state initialization for alignment system.

The private fields are well-organized with logical defaults: rect initialized to match the default size/pivot, alignment values defaulted to zero, and alignment modes set to None. The @ignoreClone decorator on _rect is appropriate since it's computed from size/pivot.


63-87: LGTM - Proper alignment mode handling with selective recalculation.

The setter correctly triggers position recalculation for all alignment modes and additionally triggers size recalculation specifically for stretch mode (LeftAndRight), which is the expected behavior.


89-135: LGTM - Robust alignment inset property implementations.

The alignment property setters properly validate finite values, use MathUtil.equals for floating-point comparison, and intelligently trigger only the necessary recalculations based on the current alignment mode. The bitwise flag checks correctly determine when position and/or size updates are needed.


137-161: LGTM - Consistent vertical alignment implementation.

The vertical alignment setter maintains the same pattern as horizontal alignment, correctly triggering position updates for all modes and adding size recalculation for the TopAndBottom stretch mode.


163-209: LGTM - Consistent vertical inset properties.

The vertical alignment properties follow the same robust pattern as the horizontal ones: finite value validation, proper equality checks, and selective recalculation based on active alignment modes.


214-222: LGTM - Proper event handler binding in constructor.

The constructor correctly binds the size and pivot change handlers and wires them to the Vector2 value change callbacks, establishing the foundation for the reactive layout system.


224-230: LGTM - Parent change propagation.

The _parentChange method properly marks parent as dirty and propagates rect changes through the hierarchy with appropriate transform flags.


240-245: LGTM - Override handles alignment-aware decomposition.

The method correctly decomposes the matrix, recalculates alignment-driven position, and updates the appropriate dirty flags. This ensures alignment is preserved when transforms are applied via matrix operations.


247-249: LGTM - Conditional world matrix change handling.

The override correctly bypasses the base class behavior when alignment is active, preventing conflicts between alignment-driven positioning and world matrix updates.


251-255: LGTM - Alignment-aware position change handling.

The method correctly recalculates position when alignment is active before delegating to the base implementation, ensuring alignment constraints are maintained.


257-263: LGTM - World position dirty flag management.

The override properly sets the WorldPosition dirty flag when alignment is active, ensuring world-space changes are tracked correctly in the alignment system.


265-303: LGTM - Comprehensive position calculation logic.

The _calPosition method correctly calculates local position from parent rect and alignment settings. The logic properly handles all alignment modes with appropriate offset calculations. The temporary disabling of the value change callback prevents infinite recursion during recalculation.


305-321: LGTM - Stretch size calculation with proper clamping.

The _calSize method correctly calculates stretched sizes from parent dimensions and alignment insets, with proper clamping to prevent negative values. The temporary callback disabling prevents recursion.


323-328: LGTM - Efficient rect calculation.

The _calRect method efficiently computes the local rect from size and pivot using standard UI layout formulas.


330-342: LGTM - Comprehensive size change handling.

The method properly recalculates stretch-driven sizes, updates the rect, propagates world flags, and dispatches the Size event. The logic flow ensures all dependent calculations are updated in the correct order.


344-350: LGTM - Pivot change handling.

The method correctly updates the rect, propagates world flags, and dispatches the Pivot event when pivot changes occur.


352-365: LGTM - Self rect change propagation.

The method properly recalculates position when alignment is active, sets appropriate dirty flags, triggers world-associated changes, and propagates rect changes to children. The flag management ensures efficient hierarchy updates.


367-395: LGTM - Parent rect change propagation with stretch handling.

The method correctly handles parent-driven changes by recalculating stretch-driven sizes when needed, updating position for alignment, managing dirty flags appropriately, and propagating changes to children. The conditional Size dispatch for stretch changes is correct.

packages/core/src/Transform.ts (8)

53-55: Good: expose _isParentDirty to subclasses.

This enables UITransform (and others) to invalidate the cached parent transform when layout-related ancestry changes.


257-278: Local-matrix setter decomposition path looks correct.

Using _decomposeLocalMatrix(...) centralizes the split and correctly reattaches value-change hooks and world-flag propagation.


584-588: Fix confirmed: _decomposeLocalMatrix now uses its matrix parameter.

This resolves the previous setter/back-prop corruption.


590-593: Hook _onWorldMatrixChange is a sensible default.

No-op (besides clearing flag) is fine in core; derived classes should super._onWorldMatrixChange() then extend.

Please confirm UITransform overrides this hook (or localMatrix getter) to trigger relative-layout recomputation before consumers read world/local matrices.


594-609: Dirty-flag helpers centralize bit ops.

These reduce duplication and improve readability.


616-634: Parent-transform cache logic looks good; verify invalidation coverage.

Cache is reset only via _parentChange(). Ensure ancestry reparenting triggers _parentChange() for descendants so the nearest-transform cache stays correct.


636-641: Local position change propagation is correct.

Marks LocalMatrix dirty and fans out world-position updates.


877-913: Flag table reads consistently.

Bit groupings (e.g., WmWsWus, WmWpWeWqWs) match their components.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/component/UITransform.ts (1)

232-238: Cloning omits alignment state and insets (breaks layout of clones)

_cloneTo copies only size/pivot; alignment modes and offsets are lost, so cloned UI elements won’t keep their relative layout. Copy these fields too.

Apply this diff:

   override _cloneTo(target: UITransform, srcRoot: Entity, targetRoot: Entity): void {
     // @ts-ignore
     super._cloneTo(target, srcRoot, targetRoot);
-    target.size.copyFrom(this._size);
-    target.pivot.copyFrom(this._pivot);
+    // Preserve alignment modes and insets.
+    target._horizontalAlignment = this._horizontalAlignment;
+    target._verticalAlignment = this._verticalAlignment;
+    target._alignLeft = this._alignLeft;
+    target._alignRight = this._alignRight;
+    target._alignCenter = this._alignCenter;
+    target._alignTop = this._alignTop;
+    target._alignBottom = this._alignBottom;
+    target._alignMiddle = this._alignMiddle;
+    // Then copy size/pivot (hooks will recalc rect/position if needed).
+    target.size.copyFrom(this._size);
+    target.pivot.copyFrom(this._pivot);
   }
♻️ Duplicate comments (1)
packages/ui/src/component/UITransform.ts (1)

330-342: Clamp explicit size to non-negative in non-stretch cases

Negative width/height can corrupt rect math and placements when users set size directly. Clamp here, not only in stretch.

Apply this diff:

   private _onSizeChanged(): void {
     if (
       this._horizontalAlignment === HorizontalAlignmentMode.LeftAndRight ||
       this._verticalAlignment === VerticalAlignmentMode.TopAndBottom
     ) {
       this._calSize();
     }
+    // Ensure non-negative size for direct (non-stretch) edits as well.
+    // @ts-ignore
+    const size = this._size;
+    // @ts-ignore
+    size._onValueChanged = null;
+    size.x = Math.max(0, size.x);
+    size.y = Math.max(0, size.y);
+    // @ts-ignore
+    size._onValueChanged = this._onSizeChanged;
     this._calRect();
     this._updateWorldFlagWithSelfRectChange();
     // @ts-ignore
     this._entity._updateFlagManager.dispatch(UITransformModifyFlags.Size);
   }

Please add/verify a test that sets size to negative values and asserts width/height are clamped to 0 (and that rect/position remain stable).

🧹 Nitpick comments (2)
packages/ui/src/component/UITransform.ts (2)

257-263: Remove redundant WorldPosition dirty set

super._onWorldPositionChanged() already marks world position dirty. The extra set under alignment is unnecessary.

Apply this diff:

   protected override _onWorldPositionChanged(): void {
     super._onWorldPositionChanged();
-    if (!!this._horizontalAlignment || !!this._verticalAlignment) {
-      this._setDirtyFlagTrue(TransformModifyFlags.WorldPosition);
-    }
   }

287-296: Document anchor choice for stretch on Y (TopAndBottom uses bottom for position)

Behavior is fine, but make it explicit in a short comment to avoid confusion: stretch computes size from top+bottom and anchors position to bottom (symmetrically, LeftAndRight anchors to left).

Apply this diff:

       switch (this._verticalAlignment) {
         case VerticalAlignmentMode.Top:
           position.y = parentRect.y + parentRect.height - rect.y - rect.height - this._alignTop;
           break;
         case VerticalAlignmentMode.Middle:
           position.y = parentRect.y + parentRect.height * 0.5 - rect.y - rect.height * 0.5 + this._alignMiddle;
           break;
         case VerticalAlignmentMode.Bottom:
         case VerticalAlignmentMode.TopAndBottom:
+          // For stretch (TopAndBottom), size is from top+bottom; position anchors to bottom.
           position.y = parentRect.y - rect.y + this._alignBottom;
           break;
📜 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 d323db9 and a41a1bc.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
🧬 Code graph analysis (1)
packages/ui/src/component/UITransform.ts (4)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/index.ts (2)
  • Entity (11-11)
  • TransformModifyFlags (17-17)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (2)
  • position (61-63)
  • position (65-69)
⏰ 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). (1)
  • GitHub Check: codecov
🔇 Additional comments (1)
packages/ui/src/component/UITransform.ts (1)

265-303: LGTM: Alignment-driven position calculation is correct and avoids re-entrancy

Formulas for Left/Center/Right and Top/Middle/Bottom are sound, and the temporary nulling of _onValueChanged prevents recursive updates. This also enforces the intended priority of relative layout over absolute positioning.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/component/UITransform.ts (1)

232-238: Cloning loses alignment configuration

_cloneTo() copies size/pivot but not alignment modes or insets. Cloned UI elements will silently drop relative layout, diverging from source behavior.

Apply:

   override _cloneTo(target: UITransform, srcRoot: Entity, targetRoot: Entity): void {
     // @ts-ignore
     super._cloneTo(target, srcRoot, targetRoot);
     target.size.copyFrom(this._size);
     target.pivot.copyFrom(this._pivot);
+    // Preserve alignment configuration.
+    target._horizontalAlignment = this._horizontalAlignment;
+    target._verticalAlignment = this._verticalAlignment;
+    target._alignLeft = this._alignLeft;
+    target._alignRight = this._alignRight;
+    target._alignCenter = this._alignCenter;
+    target._alignTop = this._alignTop;
+    target._alignBottom = this._alignBottom;
+    target._alignMiddle = this._alignMiddle;
+    target._updateRectByPivot();
   }
♻️ Duplicate comments (1)
packages/ui/src/component/UITransform.ts (1)

330-342: Clamp explicit size to non-negative values (non-stretch path)

Direct size edits can set negative width/height and corrupt rect/placement math. Stretch path clamps, but non-stretch path doesn’t.

Apply:

   private _onSizeChanged(): void {
+    // Clamp explicit size to non-negative to keep rect math valid.
+    const size = this._size;
+    // @ts-ignore
+    const prevCb = size._onValueChanged;
+    // @ts-ignore
+    size._onValueChanged = null;
+    size.x = Math.max(0, size.x);
+    size.y = Math.max(0, size.y);
+    // @ts-ignore
+    size._onValueChanged = prevCb;
     if (
       this._horizontalAlignment === HorizontalAlignmentMode.LeftAndRight ||
       this._verticalAlignment === VerticalAlignmentMode.TopAndBottom
     ) {
       this._updateSizeByAlignment();
     }
     this._updateRectByPivot();
     this._updateWorldFlagWithSelfRectChange();
     // @ts-ignore
     this._entity._updateFlagManager.dispatch(UITransformModifyFlags.Size);
   }
🧹 Nitpick comments (3)
packages/ui/src/component/UITransform.ts (3)

240-245: Make LocalPosition dirtiness explicit after alignment override

_decomposeLocalMatrix() overrides position via alignment but doesn’t mark LocalPosition dirty. If any systems listen specifically for LocalPosition, they may miss the change.

Apply (assuming LocalPosition exists on TransformModifyFlags):

   protected override _decomposeLocalMatrix(matrix: Matrix, pos: Vector3, quat: Quaternion, scale: Vector3): void {
     matrix.decompose(pos, quat, scale);
     this._updatePositionByAlignment();
+    this._setDirtyFlagTrue(TransformModifyFlags.LocalPosition);
     this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler | TransformModifyFlags.LocalMatrix);
     this._setDirtyFlagFalse(TransformModifyFlags.LocalQuat);
   }

If LocalPosition isn’t a defined flag, call super._onPositionChanged() after updating the position instead.


258-263: Redundant WorldPosition dirty toggle

super._onWorldPositionChanged() already handles world-position dirtiness. The extra flag set may be redundant.

Apply:

   protected override _onWorldPositionChanged(): void {
     super._onWorldPositionChanged();
-    if (!!this._horizontalAlignment || !!this._verticalAlignment) {
-      this._setDirtyFlagTrue(TransformModifyFlags.WorldPosition);
-    }
   }

311-317: Doc phrasing: “greater than 0” → “non-negative”

You clamp to zero, so zero is valid. Adjust the comment.

Apply:

-      // The values of size must be greater than 0.
+      // The values of size must be non-negative.
📜 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 a41a1bc and a8cffc4.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
🧬 Code graph analysis (1)
packages/ui/src/component/UITransform.ts (2)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
⏰ 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). (3)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 2/4)
  • GitHub Check: codecov
🔇 Additional comments (5)
packages/ui/src/component/UITransform.ts (5)

227-230: Parent-change hook may miss base invariants

_UITransform._parentChange() doesn’t call super._parentChange(). If core Transform relies on this hook to update caches/flags, skipping it risks inconsistent world state.

Consider:

   _parentChange(): void {
-    this._isParentDirty = true;
-    this._updateWorldFlagWithParentRectChange(TransformModifyFlags.WmWpWeWqWsWus);
+    // Preserve base invariants.
+    // @ts-ignore
+    super._parentChange?.();
+    this._isParentDirty = true;
+    this._updateWorldFlagWithParentRectChange(TransformModifyFlags.WmWpWeWqWsWus);
   }

Please confirm whether core Transform defines _parentChange() and if it must be invoked.


265-303: Nice: alignment math is correct and side-effect safe

Formulas for Left/Center/Right and Top/Middle/Bottom are correct, and temporary callback detachment avoids re-entrancy. LGTM.


89-136: Insets setters behave correctly under flags

Bitwise checks and conditional size recompute on stretch are sound; finite-guard avoids NaNs/Infinity. LGTM.

Also applies to: 163-209


398-405: Enum surface looks good

UI-only flags are separate and dispatched appropriately. LGTM.


247-249: Verify rotation/scale propagation under UI alignment
Overriding _onWorldMatrixChange skips the super call when either horizontalAlignment or verticalAlignment is set. Confirm that parent rotations and scales still propagate into the child’s worldQuaternion/scale—add a test that rotates/scales a parent while a child has alignment enabled and assert the child’s world transform updates accordingly.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/component/UITransform.ts (1)

232-238: Clone loses alignment config and insets; copy them in _cloneTo.

Cloned UITransforms won’t preserve alignment mode or offsets, leading to different layout after cloning.

   override _cloneTo(target: UITransform, srcRoot: Entity, targetRoot: Entity): void {
     // @ts-ignore
     super._cloneTo(target, srcRoot, targetRoot);
     target.size.copyFrom(this._size);
     target.pivot.copyFrom(this._pivot);
+    // Preserve alignment configuration and insets.
+    target._horizontalAlignment = this._horizontalAlignment;
+    target._verticalAlignment = this._verticalAlignment;
+    target._alignLeft = this._alignLeft;
+    target._alignRight = this._alignRight;
+    target._alignCenter = this._alignCenter;
+    target._alignTop = this._alignTop;
+    target._alignBottom = this._alignBottom;
+    target._alignMiddle = this._alignMiddle;
+    // Recompute rect for cloned pivot/size.
+    target._updateRectByPivot();
   }
♻️ Duplicate comments (2)
packages/ui/src/component/UITransform.ts (2)

330-337: Relative layout priority over absolute is correctly enforced.

Stretch realigns size on change, aligning with the established behavior for this component.


330-342: Clamp size for non-stretch paths to avoid negative dimensions.

Direct size edits can go negative and break rect/placement math; clamp before recalculating rect.

   private _onSizeChanged(): void {
     if (
       this._horizontalAlignment === HorizontalAlignmentMode.LeftAndRight ||
       this._verticalAlignment === VerticalAlignmentMode.TopAndBottom
     ) {
       this._updateSizeByAlignment();
     }
+    // Clamp explicit size in all cases.
+    const size = this._size;
+    // @ts-ignore
+    const prev = size._onValueChanged;
+    // @ts-ignore
+    size._onValueChanged = null;
+    size.x = Math.max(0, size.x);
+    size.y = Math.max(0, size.y);
+    // @ts-ignore
+    size._onValueChanged = prev;
     this._updateRectByPivot();
     this._updateWorldFlagWithSelfRectChange();
     // @ts-ignore
     this._entity._updateFlagManager.dispatch(UITransformModifyFlags.Size);
   }
🧹 Nitpick comments (3)
packages/ui/src/component/UITransform.ts (3)

311-317: Tweak comment: “greater than 0” → “non-negative”.

Matches the Math.max(..., 0) behavior.

-      // The values of size must be greater than 0.
+      // Size must be non-negative.

352-364: Skip child propagation when no flags changed.

Avoids no-op traversals when worldFlags is 0.

-    const children = this.entity.children;
-    for (let i = 0, n = children.length; i < n; i++) {
-      (children[i].transform as UITransform)?._updateWorldFlagWithParentRectChange?.(worldFlags);
-    }
+    if (worldFlags) {
+      const children = this.entity.children;
+      for (let i = 0, n = children.length; i < n; i++) {
+        (children[i].transform as UITransform)?._updateWorldFlagWithParentRectChange?.(worldFlags);
+      }
+    }

258-263: Possible redundant WorldPosition dirty flag.

If super already sets WorldPosition, this extra set is unnecessary. Keep if alignment path bypasses core updates; otherwise remove.

📜 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 a8cffc4 and 6bd7dc5.

📒 Files selected for processing (1)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
🧬 Code graph analysis (1)
packages/ui/src/component/UITransform.ts (4)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/index.ts (2)
  • Entity (11-11)
  • TransformModifyFlags (17-17)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (2)
  • position (61-63)
  • position (65-69)
⏰ 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). (4)
  • GitHub Check: e2e (22.x, 1/4)
  • GitHub Check: e2e (22.x, 4/4)
  • GitHub Check: e2e (22.x, 3/4)
  • GitHub Check: codecov
🔇 Additional comments (5)
packages/ui/src/component/UITransform.ts (5)

63-88: Alignment setter side effects look right.

Switching modes updates position and stretches update size as expected.

Also applies to: 144-161


265-303: Alignment formulas (Left/Center/Right, Top/Middle/Bottom) look correct.

Center uses parent/child centers; edges use rect edges plus insets. TopAndBottom shares Bottom for Y-position, which is reasonable with stretch. If a different anchor is intended, confirm.


305-321: Stretch size calculation and clamping LGTM.

Non-negative enforcement via Math.max is correct.


247-255: Recursion guard in _onPositionChanged is correct.

Temporarily disabling Vector3._onValueChanged avoids re-entrancy.


240-245: Resolve: UITransform dirty-flag logic is correct
The override must re-dirty LocalMatrix (due to _updatePositionByAlignment) while flagging LocalEuler and clearing LocalQuat. This intentionally diverges from core to handle alignment—no change needed.

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/Transform.ts (1)

306-315: Guard parent inverse in worldMatrix→local mapping

Matrix.invert can fail (non-invertible parent.worldMatrix). Use the return value and fall back gracefully to avoid propagating invalid locals.

Apply:

-    if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat42);
-      Matrix.multiply(Transform._tempMat42, value, this._localMatrix);
-    } else {
+    if (parent) {
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat42);
+      if (inv) {
+        Matrix.multiply(inv, value, this._localMatrix);
+      } else {
+        // Parent not invertible; treat provided world as local.
+        this._localMatrix.copyFrom(value);
+      }
+    } else {
       this._localMatrix.copyFrom(value);
     }
-    this.localMatrix = this._localMatrix;
-    this._onWorldMatrixChange();
+    this.localMatrix = this._localMatrix;
+    this._onWorldMatrixChange();
♻️ Duplicate comments (1)
packages/core/src/Transform.ts (1)

637-654: Also guard inverse in worldPosition write path (repeat)

Same singular-parent risk when mapping world→local in _onWorldPositionChanged. Guard the invert and fall back to copying world→local.

Apply:

-    if (parent) {
-      Matrix.invert(parent.worldMatrix, Transform._tempMat41);
-      Vector3.transformCoordinate(worldPosition, Transform._tempMat41, this._position);
-    } else {
+    if (parent) {
+      const inv = Matrix.invert(parent.worldMatrix, Transform._tempMat41);
+      if (inv) {
+        Vector3.transformCoordinate(worldPosition, inv, this._position);
+      } else {
+        // Parent not invertible; treat as root.
+        this._position.copyFrom(worldPosition);
+      }
+    } else {
       this._position.copyFrom(worldPosition);
     }
🧹 Nitpick comments (1)
packages/core/src/Transform.ts (1)

611-615: Avoid dispatching zero dirty-flags

Skip dispatch when dispatchFlags === 0 to prevent redundant notifications.

Apply:

-    const dispatchFlags = type & ~this._dirtyFlag;
-    this._dirtyFlag |= type;
-    this._entity._updateFlagManager.dispatch(dispatchFlags);
+    const dispatchFlags = type & ~this._dirtyFlag;
+    this._dirtyFlag |= type;
+    if (dispatchFlags !== 0) {
+      this._entity._updateFlagManager.dispatch(dispatchFlags);
+    }
📜 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 8988b34 and 24225be.

📒 Files selected for processing (2)
  • packages/core/src/Transform.ts (5 hunks)
  • packages/ui/src/component/UITransform.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.
📚 Learning: 2025-08-26T08:22:31.028Z
Learnt from: cptbtptpbcptdtptp
PR: galacean/engine#2760
File: packages/ui/src/component/UITransform.ts:0-0
Timestamp: 2025-08-26T08:22:31.028Z
Learning: In UITransform, when there is a relative layout (alignment-based positioning), it has higher priority than absolute layout (direct position setting). This means that setting position directly while alignment is active should be overridden by the alignment calculations, which is the intended behavior.

Applied to files:

  • packages/ui/src/component/UITransform.ts
  • packages/core/src/Transform.ts
🧬 Code graph analysis (2)
packages/ui/src/component/UITransform.ts (3)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
packages/core/src/index.ts (2)
  • Entity (11-11)
  • TransformModifyFlags (17-17)
packages/core/src/Entity.ts (2)
  • Entity (21-830)
  • children (212-214)
packages/core/src/Transform.ts (5)
packages/physics-lite/src/LiteTransform.ts (6)
  • position (36-38)
  • position (40-46)
  • rotationQuaternion (52-63)
  • rotationQuaternion (65-72)
  • scale (110-112)
  • scale (114-120)
packages/math/src/Matrix.ts (4)
  • rotationQuaternion (206-242)
  • scale (670-694)
  • scale (1166-1169)
  • Matrix (11-1288)
packages/math/src/Vector3.ts (3)
  • scale (207-212)
  • scale (483-489)
  • Vector3 (11-612)
packages/core/src/Entity.ts (3)
  • parent (201-203)
  • parent (205-207)
  • transform (155-157)
packages/core/src/clone/CloneManager.ts (1)
  • ignoreClone (9-11)
⏰ 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). (1)
  • GitHub Check: e2e (22.x, 4/4)
🔇 Additional comments (14)
packages/ui/src/component/UITransform.ts (10)

1-12: Well-organized imports support new alignment features.

The imports cleanly bring in the necessary core components (TransformModifyFlags, Rect, MathUtil) and the new alignment enums, providing the foundation for the relative layout functionality.


22-32: Good state organization for alignment system.

The new private fields provide a comprehensive alignment state management system:

  • _rect for local rectangle calculations
  • Inset values for each alignment direction
  • Alignment mode tracking for both axes

60-83: Excellent alignment setter design with proper cascading.

The horizontal alignment setter correctly triggers position recalculation for all alignment modes and additionally triggers size recalculation for stretch mode (LeftAndRight), ensuring the layout system remains consistent.


85-204: Well-designed inset properties with smart invalidation.

The alignment inset properties (alignLeft, alignRight, etc.) have proper validation, change detection, and conditional updates based on the current alignment mode. The bitwise checks ensure updates only occur when the specific alignment is active.


209-225: Proper lifecycle integration.

The constructor properly binds the new event handlers and the _parentChange() method correctly integrates with the core Transform's parent management system, ensuring alignment-based layouts respond to hierarchy changes.


235-295: Smart override strategy preserves core functionality.

The overridden protected methods use conditional logic to only modify behavior when alignment is active, preserving the base Transform behavior for non-aligned elements. The position recalculation logic in _updatePositionByAlignment() correctly handles all alignment modes.


297-320: Robust size calculation with safety measures.

The _updateSizeByAlignment() method properly handles stretch modes and includes Math.max(..., 0) to prevent negative sizes, which addresses a critical edge case for constrained layouts.


322-356: Excellent change propagation system.

The size and pivot change handlers properly cascade updates through the alignment system and world flag propagation, ensuring that rect changes correctly trigger downstream layout updates in child elements.


358-386: Sophisticated hierarchical update logic.

The _updateWorldFlagWithParentRectChange() method implements a well-thought-out cascading system that:

  • Handles both self-initiated and parent-initiated changes
  • Correctly manages stretch layout recalculations
  • Properly propagates flags through the hierarchy
  • Avoids redundant updates through dirty flag checks

393-396: Clean flag extension.

The UITransformModifyFlags enum appropriately extends the core transform flags with UI-specific Size and Pivot flags, maintaining the established pattern from the core Transform system.

packages/core/src/Transform.ts (4)

53-53: Good: expose parent-dirty for subclasses

Making _isParentDirty protected enables UI components (e.g., UITransform) to invalidate cached parents safely.


589-593: Nice: local-matrix change hook + explicit world-matrix clear

Lightweight, targeted hooks; good extension points for higher-level layout systems.


617-635: Parent-transform cache looks solid

Lookup + cache invalidation via _isParentDirty is straightforward and safe.


878-878: Doc tweak acknowledged

The clarified comment for TransformModifyFlags reads well.

Comment on lines +265 to +269
value.decompose(position, rotationQuaternion, scale);
this._onLocalMatrixChanging?.();
this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler);
this._setDirtyFlagFalse(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle Matrix.decompose() failure to avoid corrupt local state

Matrix.decompose(...) returns boolean; ignoring a false result can leave position/rotation/scale inconsistent (NaNs/garbage). Add a guard and a safe fallback.

Apply:

-    value.decompose(position, rotationQuaternion, scale);
-    this._onLocalMatrixChanging?.();
-    this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler);
-    this._setDirtyFlagFalse(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat);
+    const ok = value.decompose(position, rotationQuaternion, scale);
+    this._onLocalMatrixChanging?.();
+    if (!ok) {
+      // Fallback: extract translation; reset rotation to identity and scale to 1s.
+      value.getTranslation(position);
+      rotationQuaternion.set(0, 0, 0, 1);
+      scale.set(1, 1, 1);
+    }
+    this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler);
+    this._setDirtyFlagFalse(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat);
📝 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
value.decompose(position, rotationQuaternion, scale);
this._onLocalMatrixChanging?.();
this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler);
this._setDirtyFlagFalse(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat);
const ok = value.decompose(position, rotationQuaternion, scale);
this._onLocalMatrixChanging?.();
if (!ok) {
// Fallback: extract translation; reset rotation to identity and scale to defaults.
value.getTranslation(position);
rotationQuaternion.set(0, 0, 0, 1);
scale.set(1, 1, 1);
}
this._setDirtyFlagTrue(TransformModifyFlags.LocalEuler);
this._setDirtyFlagFalse(TransformModifyFlags.LocalMatrix | TransformModifyFlags.LocalQuat);
🤖 Prompt for AI Agents
In packages/core/src/Transform.ts around lines 265-269, Matrix.decompose(...)
return value is ignored which can leave position/rotation/scale with NaNs on
failure; change the code to check the boolean result and only apply the
dirty-flag logic when decompose succeeds. On failure, restore previous
position/rotationQuaternion/scale (capture them before calling decompose) or set
safe defaults (position = zero, rotationQuaternion = identity, scale =
Vector3.One), and avoid marking LocalEuler as dirty while clearing
LocalMatrix|LocalQuat as appropriate; optionally log or warn about the decompose
failure for debugging.

@GuoLei1990 GuoLei1990 merged commit bf7da35 into galacean:dev/1.6 Sep 5, 2025
8 of 10 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in 2D Sep 5, 2025
@GuoLei1990 GuoLei1990 added the enhancement New feature or request label Sep 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2D enhancement New feature or request GUI

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

RFC: Galacean UITransform 的相对布局设计 UIWidget

2 participants