forked from pixijs/pixijs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add some new functions to get global tint / alpha / transform (p…
…ixijs#11057) * add some new functions to get global tint / alpha / transform * fix import * move to mixin --------- Co-authored-by: Zyie <[email protected]>
- Loading branch information
1 parent
7236953
commit 0fd7d03
Showing
8 changed files
with
595 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { updateTransformBackwards } from '../bounds/getGlobalBounds'; | ||
import { matrixPool } from '../bounds/utils/matrixAndBoundsPool'; | ||
import { multiplyColors } from '../utils/multiplyColors'; | ||
|
||
import type { Matrix } from '../../../maths/matrix/Matrix'; | ||
import type { Container } from '../Container'; | ||
|
||
export function bgr2rgb(color: number): number | ||
{ | ||
return ((color & 0xFF) << 16) + (color & 0xFF00) + ((color >> 16) & 0xFF); | ||
} | ||
|
||
export interface GetGlobalMixin | ||
{ | ||
getGlobalAlpha(skipUpdate: boolean): number; | ||
getGlobalTransform(matrix: Matrix, skipUpdate: boolean): Matrix; | ||
getGlobalTint(skipUpdate?: boolean): number; | ||
} | ||
|
||
export const getGlobalMixin: Partial<Container> = { | ||
/** | ||
* Returns the global (compound) alpha of the container within the scene. | ||
* @param skipUpdate - Performance optimization flag: | ||
* - If false (default): Recalculates the entire alpha chain through parents for accuracy | ||
* - If true: Uses cached worldAlpha from the last render pass for better performance | ||
* @returns The resulting alpha value (between 0 and 1) | ||
* @example | ||
* // Accurate but slower - recalculates entire alpha chain | ||
* const preciseAlpha = container.getGlobalAlpha(); | ||
* | ||
* // Faster but may be outdated - uses cached alpha | ||
* const cachedAlpha = container.getGlobalAlpha(true); | ||
*/ | ||
getGlobalAlpha(skipUpdate: boolean): number | ||
{ | ||
if (skipUpdate) | ||
{ | ||
if (this.renderGroup) | ||
{ | ||
return this.renderGroup.worldAlpha; | ||
} | ||
|
||
if (this.parentRenderGroup) | ||
{ | ||
return this.parentRenderGroup.worldAlpha * this.alpha; | ||
} | ||
|
||
return this.alpha; | ||
} | ||
|
||
let alpha = this.alpha; | ||
let current = this.parent; | ||
|
||
while (current) | ||
{ | ||
alpha *= current.alpha; | ||
current = current.parent; | ||
} | ||
|
||
return alpha; | ||
}, | ||
|
||
/** | ||
* Returns the global transform matrix of the container within the scene. | ||
* @param matrix - Optional matrix to store the result. If not provided, a new Matrix will be created. | ||
* @param skipUpdate - Performance optimization flag: | ||
* - If false (default): Recalculates the entire transform chain for accuracy | ||
* - If true: Uses cached worldTransform from the last render pass for better performance | ||
* @returns The resulting transformation matrix (either the input matrix or a new one) | ||
* @example | ||
* // Accurate but slower - recalculates entire transform chain | ||
* const preciseTransform = container.getGlobalTransform(); | ||
* | ||
* // Faster but may be outdated - uses cached transform | ||
* const cachedTransform = container.getGlobalTransform(undefined, true); | ||
* | ||
* // Reuse existing matrix | ||
* const existingMatrix = new Matrix(); | ||
* container.getGlobalTransform(existingMatrix); | ||
*/ | ||
getGlobalTransform(matrix: Matrix, skipUpdate: boolean): Matrix | ||
{ | ||
if (skipUpdate) | ||
{ | ||
return matrix.copyFrom(this.worldTransform); | ||
} | ||
|
||
this.updateLocalTransform(); | ||
|
||
if (!this.parent) | ||
{ | ||
return matrix.copyFrom(this.localTransform); | ||
} | ||
|
||
const parentTransform = updateTransformBackwards(this, matrixPool.get().identity()); | ||
|
||
matrix.appendFrom(parentTransform, this.localTransform); | ||
matrixPool.return(parentTransform); | ||
|
||
return matrix; | ||
}, | ||
|
||
/** | ||
* Returns the global (compound) tint color of the container within the scene. | ||
* @param skipUpdate - Performance optimization flag: | ||
* - If false (default): Recalculates the entire tint chain through parents for accuracy | ||
* - If true: Uses cached worldColor from the last render pass for better performance | ||
* @returns The resulting tint color as a 24-bit RGB number (0xRRGGBB) | ||
* @example | ||
* // Accurate but slower - recalculates entire tint chain | ||
* const preciseTint = container.getGlobalTint(); | ||
* | ||
* // Faster but may be outdated - uses cached tint | ||
* const cachedTint = container.getGlobalTint(true); | ||
*/ | ||
getGlobalTint(skipUpdate?: boolean): number | ||
{ | ||
if (skipUpdate) | ||
{ | ||
if (this.renderGroup) | ||
{ | ||
return bgr2rgb(this.renderGroup.worldColor); | ||
} | ||
|
||
if (this.parentRenderGroup) | ||
{ | ||
return bgr2rgb( | ||
multiplyColors(this.localColor, this.parentRenderGroup.worldColor) | ||
); | ||
} | ||
|
||
return this.tint; | ||
} | ||
|
||
let color = this.localColor; | ||
let parent = this.parent; | ||
|
||
while (parent) | ||
{ | ||
color = multiplyColors(color, parent.localColor); | ||
parent = parent.parent; | ||
} | ||
|
||
return bgr2rgb(color); | ||
} | ||
|
||
} as Container; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { Container } from '../../src/scene/container/Container'; | ||
|
||
describe('getGlobalAlpha', () => | ||
{ | ||
describe('with skipUpdateTransform = false', () => | ||
{ | ||
it('should return container alpha when no parent exists', () => | ||
{ | ||
const container = new Container(); | ||
|
||
container.alpha = 0.5; | ||
|
||
expect(container.getGlobalAlpha(false)).toBe(0.5); | ||
}); | ||
|
||
it('should multiply alpha with single parent', () => | ||
{ | ||
const parent = new Container(); | ||
const container = new Container(); | ||
|
||
parent.alpha = 0.5; | ||
|
||
container.alpha = 0.5; | ||
container.parent = parent; | ||
|
||
expect(container.getGlobalAlpha(false)).toBe(0.25); // 0.5 * 0.5 | ||
}); | ||
|
||
it('should multiply alpha through multiple parents', () => | ||
{ | ||
const container = new Container(); | ||
const grandParent = new Container(); | ||
const parent = new Container(); | ||
|
||
grandParent.alpha = 0.5; | ||
parent.alpha = 0.5; | ||
container.alpha = 0.5; | ||
|
||
parent.parent = grandParent; | ||
container.parent = parent; | ||
|
||
expect(container.getGlobalAlpha(false)).toBe(0.125); // 0.5 * 0.5 * 0.5 | ||
}); | ||
|
||
it('should return renderGroup worldAlpha when container has renderGroup', () => | ||
{ | ||
const container = new Container({ | ||
alpha: 0.75, | ||
isRenderGroup: true | ||
}); | ||
|
||
expect(container.getGlobalAlpha(false)).toBe(0.75); | ||
}); | ||
}); | ||
|
||
describe('with skipUpdateTransform = true', () => | ||
{ | ||
it('should multiply parentRenderGroup worldAlpha with container alpha', () => | ||
{ | ||
const parent = new Container(); | ||
|
||
const container = new Container(); | ||
|
||
container.alpha = 0.5; | ||
|
||
parent.addChild(container); | ||
|
||
parent.alpha = 0.5; | ||
|
||
expect(container.getGlobalAlpha(true)).toBe(0.5); // 0.8 * 0.5 | ||
}); | ||
}); | ||
|
||
describe('edge cases', () => | ||
{ | ||
it('should handle alpha value of 0', () => | ||
{ | ||
const container = new Container(); | ||
|
||
container.alpha = 0; | ||
|
||
expect(container.getGlobalAlpha(false)).toBe(0); | ||
}); | ||
|
||
it('should handle alpha value of 1', () => | ||
{ | ||
const container = new Container(); | ||
|
||
container.alpha = 1; | ||
|
||
expect(container.getGlobalAlpha(false)).toBe(1); | ||
}); | ||
|
||
it('should handle deeply nested containers', () => | ||
{ | ||
const container = new Container(); | ||
let current = new Container(); | ||
|
||
// Create a chain of 10 containers | ||
for (let i = 0; i < 10; i++) | ||
{ | ||
const parent = new Container(); | ||
|
||
parent.alpha = 0.9; | ||
current.addChild(parent); | ||
current = parent; | ||
} | ||
|
||
container.alpha = 0.9; | ||
|
||
current.addChild(container); | ||
|
||
const expectedAlpha = Math.pow(0.9, 11); | ||
|
||
expect(container.getGlobalAlpha(false)).toBeCloseTo(expectedAlpha, 3); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.