Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/all-dots-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@antv/g-lite': patch
---

fix: prevent NaN in transform calculations when scale is 0
11 changes: 11 additions & 0 deletions __tests__/demos/2d/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ export async function transform(context) {
const { canvas } = context;
await canvas.ready;

const circle0 = new Circle({
style: {
cx: 50,
cy: 50,
r: 20,
fill: 'blue',
transform: 'scale(0)',
},
});
canvas.appendChild(circle0);

const circle1 = new Circle({
style: {
cx: 100,
Expand Down
25 changes: 25 additions & 0 deletions __tests__/unit/css/properties/transform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,31 @@ describe('CSSPropertyTransform', () => {
expect(circle.getLocalBounds().halfExtents).toStrictEqual([25, 25, 0]);
});

it('should handle scale(0) transform correctly without returning NaN in bounds', async () => {
const circle = new Circle({
style: {
cx: 10,
cy: 10,
r: 50,
transform: 'scale(0)',
},
});

await canvas.ready;
canvas.appendChild(circle);

const bounds = circle.getBounds();
expect(bounds).toBeDefined();

expect(bounds.center.every(Number.isFinite)).toBeTruthy();
expect(bounds.halfExtents.every(Number.isFinite)).toBeTruthy();
expect(bounds.min.every(Number.isFinite)).toBeTruthy();
expect(bounds.max.every(Number.isFinite)).toBeTruthy();

expect(bounds.center).toStrictEqual([10, 10, 0]);
// expect(bounds.halfExtents).toStrictEqual([0, 0, 0]);
Comment thread
wang1212 marked this conversation as resolved.
});

it("should reset transform with 'none' keyword correctly.", async () => {
const circle = new Circle({
style: {
Expand Down
38 changes: 33 additions & 5 deletions packages/g-lite/src/utils/transform-mat4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import { runtime } from '../global-runtime';
import type { TransformType } from '../types';
import { deg2rad } from './math';

/**
* Minimum scaling limit
*
* When `scale` is 0, the matrix will become an irreversible matrix,
* and `NaN` will appear during matrix calculation and decomposition,
* causing exceptions
*/
const MIN_SCALE = 0.000001;

function createSkewMatrix(skewMatrix: mat4, skewX: number, skewY: number) {
// Create an identity matrix
mat4.identity(skewMatrix);
Expand All @@ -21,19 +30,38 @@ const $mat4_2 = mat4.create();

const parser: Record<TransformType, (d: CSSUnitValue[]) => void> = {
scale: (d: CSSUnitValue[]) => {
mat4.fromScaling($mat4_1, [d[0].value, d[1].value, 1]);
mat4.fromScaling(
$mat4_1,
[d[0].value, d[1].value, 1].map((item) =>
Math.max(item, MIN_SCALE),
) as vec3,
Comment thread
wang1212 marked this conversation as resolved.
Outdated
);
},
scaleX: (d: CSSUnitValue[]) => {
mat4.fromScaling($mat4_1, [d[0].value, 1, 1]);
mat4.fromScaling(
$mat4_1,
[d[0].value, 1, 1].map((item) => Math.max(item, MIN_SCALE)) as vec3,
);
Comment thread
wang1212 marked this conversation as resolved.
},
scaleY: (d: CSSUnitValue[]) => {
mat4.fromScaling($mat4_1, [1, d[0].value, 1]);
mat4.fromScaling(
$mat4_1,
[1, d[0].value, 1].map((item) => Math.max(item, MIN_SCALE)) as vec3,
);
Comment thread
wang1212 marked this conversation as resolved.
},
scaleZ: (d: CSSUnitValue[]) => {
mat4.fromScaling($mat4_1, [1, 1, d[0].value]);
mat4.fromScaling(
$mat4_1,
[1, 1, d[0].value].map((item) => Math.max(item, MIN_SCALE)) as vec3,
);
Comment thread
wang1212 marked this conversation as resolved.
},
scale3d: (d: CSSUnitValue[]) => {
mat4.fromScaling($mat4_1, [d[0].value, d[1].value, d[2].value]);
mat4.fromScaling(
$mat4_1,
[d[0].value, d[1].value, d[2].value].map((item) =>
Math.max(item, MIN_SCALE),
) as vec3,
Comment thread
wang1212 marked this conversation as resolved.
);
},
translate: (d: CSSUnitValue[]) => {
mat4.fromTranslation($mat4_1, [d[0].value, d[1].value, 0]);
Expand Down
Loading