Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 17 additions & 19 deletions packages/examples/src/examples/clipping/ExampleClipping.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,27 +140,19 @@ class PlayScreen extends Stage {
4,
);

// RIGHT — wrapper centered on its pos, with a clipping
// container offset back to the top-left. The wrapper's
// `currentTransform` gets a sinusoidal scale each frame, so
// the inner clip pulses around the wrapper's center.
const RIGHT_CENTER_X = 370;
const RIGHT_CENTER_Y = 160;
// RIGHT — wrapper at top-left with a clipping container as a
// direct child (no negative offset). The wrapper's
// `currentTransform` gets a sinusoidal scale each frame,
// composed as translate→scale→translate-back so the pulse
// breathes around the wrapper's visual center while keeping
// `pos` and bounds aligned with what's actually drawn.
const RIGHT_X = 280;
const RIGHT_Y = 80;
const RIGHT_W = 180;
const RIGHT_H = 160;
const wrapper = new Container(
RIGHT_CENTER_X,
RIGHT_CENTER_Y,
RIGHT_W,
RIGHT_H,
);
const wrapper = new Container(RIGHT_X, RIGHT_Y, RIGHT_W, RIGHT_H);
wrapper.clipping = false;
const innerClip = new Container(
-RIGHT_W / 2,
-RIGHT_H / 2,
RIGHT_W,
RIGHT_H,
);
const innerClip = new Container(0, 0, RIGHT_W, RIGHT_H);
innerClip.clipping = true;
innerClip.addChild(new OverflowingRect("#e74c3c"));
wrapper.addChild(innerClip);
Expand All @@ -170,11 +162,17 @@ class PlayScreen extends Stage {
wrapper.alwaysUpdate = true;
let t = 0;
const baseUpdate = wrapper.update.bind(wrapper);
const cx = RIGHT_W / 2;
const cy = RIGHT_H / 2;
wrapper.update = function (dt: number) {
t += dt;
this.currentTransform.identity();
const s = 1 + 0.25 * Math.sin(t * 0.0025);
// scale around (cx, cy) without moving the wrapper's pos:
// translate to center → scale → translate back.
this.currentTransform.identity();
this.currentTransform.translate(cx, cy);
this.currentTransform.scale(s, s);
this.currentTransform.translate(-cx, -cy);
return baseUpdate(dt) || true;
};
game.world.addChild(wrapper, 2);
Expand Down
11 changes: 6 additions & 5 deletions packages/melonjs/src/physics/bounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,12 @@ export class Bounds {
y1: number,
m?: Matrix2d | Matrix3d,
) {
if (m === undefined) {
// no transform: fold the 4 corners' min/max directly into
// the AABB without any Point allocation. Mirrors `addPoint`
// for each corner — using Math.min/max so the caller may
// pass swapped corners (x1 < x0 etc.) without breaking.
if (m === undefined || m.isIdentity()) {
// no transform (or a no-op transform): fold the 4 corners'
// min/max directly into the AABB without any Point or
// `m.apply` work. Mirrors `addPoint` for each corner —
// using Math.min/max so the caller may pass swapped corners
// (x1 < x0 etc.) without breaking.
const minX = Math.min(x0, x1);
const maxX = Math.max(x0, x1);
const minY = Math.min(y0, y1);
Expand Down
23 changes: 12 additions & 11 deletions packages/melonjs/src/video/webgl/webgl_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -903,12 +903,12 @@ export default class WebGLRenderer extends Renderer {
enableScissor(x, y, width, height) {
const gl = this.gl;
const canvas = this.getCanvas();
// Walk the 4 corners through `currentTransform` to derive the
// screen-space AABB, matching the convention `clipRect` and
// `restore` use. `currentScissor` stores screen-space coords
// directly so save/restore can re-apply without re-running
// transform math (and so a scaled/rotated parent transform is
// honored, not just translation). Issue #1349.
// Derive the screen-space AABB via `Bounds.addFrame`, which
// short-circuits the 4-corner walk when `currentTransform` is
// identity. `currentScissor` stores screen-space coords directly
// so save/restore can re-apply without re-running transform math
// and so a scaled or rotated parent transform is honored — not
// just translation. Issue #1349.
const aabb = this._clipAABB;
aabb.clear();
aabb.addFrame(x, y, x + width, y + height, this.currentTransform);
Expand Down Expand Up @@ -2237,11 +2237,12 @@ export default class WebGLRenderer extends Renderer {
}

// derive the screen-space AABB by feeding the rect's 4 corners
// through `currentTransform` via `Bounds.addFrame`. `gl.scissor`
// is not transform-aware, so any rotation collapses to the
// rotated-rect AABB on screen — Canvas's `context.clip()` would
// produce a true polygonal clip, but downstream rendering only
// observes the AABB anyway. Issue #1349.
// through `currentTransform` via `Bounds.addFrame` (which short-
// circuits the corner walk when the transform is identity).
// `gl.scissor` is not transform-aware, so any rotation collapses
// to the rotated-rect AABB on screen — Canvas's `context.clip()`
// would produce a true polygonal clip, but downstream rendering
// only observes the AABB anyway. Issue #1349.
const aabb = this._clipAABB;
aabb.clear();
aabb.addFrame(x, y, x + width, y + height, m);
Expand Down
17 changes: 17 additions & 0 deletions packages/melonjs/tests/bounds.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,23 @@ describe("Physics : Bounds", () => {
expect(b.width).toBeCloseTo(100, 5);
expect(b.height).toBeCloseTo(200, 5);
});
it("addFrame with an identity matrix takes the no-corner-walk fast path", () => {
// Sentinel: spy on `m.apply` and confirm the identity
// short-circuit in addFrame avoids calling it. Guards
// against a future refactor that drops the identity check.
const m = new Matrix3d();
let applyCalls = 0;
const orig = m.apply.bind(m);
m.apply = (v) => {
applyCalls += 1;
return orig(v);
};
const b = new Bounds();
b.addFrame(10, 50, 110, 250, m);
expect(applyCalls).toBe(0);
expect(b.x).toBeCloseTo(10, 5);
expect(b.width).toBeCloseTo(100, 5);
});
it("addFrame with Matrix3d translation shifts the AABB", () => {
const m = new Matrix3d();
m.translate(40, 30);
Expand Down
Loading