Skip to content

Commit

Permalink
feat: Add some new functions to get global tint / alpha / transform (p…
Browse files Browse the repository at this point in the history
…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
GoodBoyDigital and Zyie authored Nov 21, 2024
1 parent 7236953 commit 0fd7d03
Show file tree
Hide file tree
Showing 8 changed files with 595 additions and 30 deletions.
2 changes: 2 additions & 0 deletions src/scene/SceneMixins.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { CacheAsTextureMixin, CacheAsTextureMixinConstructor } from './cont
import type { ChildrenHelperMixin } from './container/container-mixins/childrenHelperMixin';
import type { EffectsMixin, EffectsMixinConstructor } from './container/container-mixins/effectsMixin';
import type { FindMixin, FindMixinConstructor } from './container/container-mixins/findMixin';
import type { GetGlobalMixin } from './container/container-mixins/getGlobalMixin';
import type { MeasureMixin, MeasureMixinConstructor } from './container/container-mixins/measureMixin';
import type { OnRenderMixin, OnRenderMixinConstructor } from './container/container-mixins/onRenderMixin';
import type { SortMixin, SortMixinConstructor } from './container/container-mixins/sortMixin';
Expand All @@ -21,6 +22,7 @@ declare global
EffectsMixin,
FindMixin,
SortMixin,
GetGlobalMixin,
CacheAsTextureMixin {}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand Down
8 changes: 3 additions & 5 deletions src/scene/container/Container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { cacheAsTextureMixin } from './container-mixins/cacheAsTextureMixin';
import { childrenHelperMixin } from './container-mixins/childrenHelperMixin';
import { effectsMixin } from './container-mixins/effectsMixin';
import { findMixin } from './container-mixins/findMixin';
import { bgr2rgb, getGlobalMixin } from './container-mixins/getGlobalMixin';
import { measureMixin } from './container-mixins/measureMixin';
import { onRenderMixin } from './container-mixins/onRenderMixin';
import { sortMixin } from './container-mixins/sortMixin';
Expand Down Expand Up @@ -852,8 +853,6 @@ export class Container<C extends ContainerChild = ContainerChild> extends EventE
return this._worldTransform;
}

// / ////// transform related stuff

/**
* The position of the container on the x axis relative to the local coordinates of the parent.
* An alias to position.x
Expand Down Expand Up @@ -1213,10 +1212,8 @@ export class Container<C extends ContainerChild = ContainerChild> extends EventE
*/
get tint(): number
{
const bgr = this.localColor;
// convert bgr to rgb..

return ((bgr & 0xFF) << 16) + (bgr & 0xFF00) + ((bgr >> 16) & 0xFF);
return bgr2rgb(this.localColor);
}

// / //////////////// blend related stuff
Expand Down Expand Up @@ -1392,3 +1389,4 @@ Container.mixin(findMixin);
Container.mixin(sortMixin);
Container.mixin(cullingMixin);
Container.mixin(cacheAsTextureMixin);
Container.mixin(getGlobalMixin);
147 changes: 147 additions & 0 deletions src/scene/container/container-mixins/getGlobalMixin.ts
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;
33 changes: 11 additions & 22 deletions src/scene/container/container-mixins/toLocalGlobalMixin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Matrix } from '../../../maths/matrix/Matrix';
import { Point } from '../../../maths/point/Point';
import { updateTransformBackwards } from '../bounds/getGlobalBounds';
import { matrixPool } from '../bounds/utils/matrixAndBoundsPool';

import type { PointData } from '../../../maths/point/PointData';
import type { Container } from '../Container';
Expand Down Expand Up @@ -46,19 +45,14 @@ export const toLocalGlobalMixin: Partial<Container> = {
*/
toGlobal<P extends PointData = Point>(position: PointData, point?: P, skipUpdate = false): P
{
if (!skipUpdate)
{
this.updateLocalTransform();

const globalMatrix = updateTransformBackwards(this, new Matrix());
const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate);

globalMatrix.append(this.localTransform);
// simply apply the matrix..
point = globalMatrix.apply(position, point);

return globalMatrix.apply<P>(position, point);
}
matrixPool.return(globalMatrix);

// simply apply the matrix..
return this.worldTransform.apply<P>(position, point);
return point;
},

/**
Expand All @@ -78,18 +72,13 @@ export const toLocalGlobalMixin: Partial<Container> = {
position = from.toGlobal(position, point, skipUpdate);
}

if (!skipUpdate)
{
this.updateLocalTransform();

const globalMatrix = updateTransformBackwards(this, new Matrix());
const globalMatrix = this.getGlobalTransform(matrixPool.get(), skipUpdate);

globalMatrix.append(this.localTransform);
// simply apply the matrix..
point = globalMatrix.applyInverse(position, point);

return globalMatrix.applyInverse<P>(position, point);
}
matrixPool.return(globalMatrix);

// simply apply the matrix..
return this.worldTransform.applyInverse<P>(position, point);
return point;
}
} as Container;
5 changes: 2 additions & 3 deletions src/scene/particle-container/shared/Particle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Color } from '../../../color/Color';
import { Texture } from '../../../rendering/renderers/shared/texture/Texture';
import { bgr2rgb } from '../../container/container-mixins/getGlobalMixin';
import { assignWithIgnore } from '../../container/utils/assignWithIgnore';

import type { ColorSource } from '../../../color/Color';
Expand Down Expand Up @@ -140,9 +141,7 @@ export class Particle implements IParticle
/** Gets or sets the tint color of the particle. */
get tint(): number
{
const bgr = this._tint;

return ((bgr & 0xFF) << 16) + (bgr & 0xFF00) + ((bgr >> 16) & 0xFF);
return bgr2rgb(this._tint);
}

set tint(value: ColorSource)
Expand Down
118 changes: 118 additions & 0 deletions tests/scene/getGlobalAlpha.tests.ts
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);
});
});
});
Loading

0 comments on commit 0fd7d03

Please sign in to comment.