two.js
two.js copied to clipboard
ZUI Rotation
I'm trying to add a rotation method to the ZUI. The two things I'm stuck on are: 1. getting an accurate rotation point/origin. 2. maintaining zoom Not sure why it's zooming, and I think the pivot point is off because it's not account for the zui's object orientation? I'm a little lost, thanks for any help!
// zui.js
rotateBy(amount, surfaceX, surfaceY) {
this.surfaceMatrix.translate(-surfaceX, -surfaceY);
this.surfaceMatrix.rotate(amount);
this.rotation += amount;
this.updateSurface();
return this;
}
updateSurface() {
const e = this.surfaceMatrix.elements;
for (let i = 0; i < this.surfaces.length; i++) {
this.surfaces[i].apply(e[2], e[5], e[0], this.rotation);
}
return this;
}
// Surface#apply
apply(px, py, s, rotation) {
this.object.translation.set(px, py);
this.object.scale = s;
this.object.rotation = rotation;
return this;
}
Here's the scene setup (minimal setup based on my actual code). ZuiManager is an event handler/wrapper for the zui. ZuiManager.instance is the two.js ZUI
// .... more class def / code here.
this.$el = document.getElementById('map');
this.two = new Two({ fullscreen: true, autostart: true });
this.two.add(this.layer);
this.two.appendTo(this.$el);
this.layer = new Two.Group();
this.layer.position.set(this.two.width / 2, this.two.height / 2);
this.two.add(this.layer);
this.zuiManager = new ZuiManager(this.layer, this.two);
this.zui = this.zuiManager.instance;
// where the action happens for the demo
this.zuiManager.on('dblclick', ({ surface }) => {
this.zuiManager.instance.rotateBy(5, surface.x, surface.y);
});
// Add some shapes for visual reference.
this.container = new Two.Group();
this.layer.add(this.container);
const r1 = new Two.Rectangle(0, 0, 200, 100);
const r2 = new Two.Rectangle(200, -100, 100, 200);
const r3 = new Two.Rectangle(400, 0, 200, 100);
this.container.add(r1);
this.container.add(r2);
this.container.add(r3);
// The red rectangle. just a visual guide to see original state
setTimeout(() => {
const elem = this.two.scene._renderer.elem;
const rect = elem.getBoundingClientRect();
const div = document.createElement('div');
div.style.position = 'fixed';
div.style.top = `${rect.top}px`;
div.style.left = `${rect.left}px`;
div.style.width = `${rect.width}px`;
div.style.height = `${rect.height}px`;
div.style.border = '1px solid red';
div.style.zIndex = 1000;
this.$el.appendChild(div);
}, 100);
// ... rest of the real code here...
Screenshots
Initial state
after one dbl click. The click was just left/down of the red square. But it's not zoomed around that point, and it's also zoomed way out now. it is rotated though.
Environment (please select one):
- [x] Packaged software (e.g: ES6 imports, react, angular, vue.js)
Hmm, Two.js objects by default (so all Two.Group, Two.Shape, Two.Path, and Two.Text instances) have a managed matrix where the origin is assumed to be the center of the object. To do rotations as what I think you want to do. You'll need to overwrite this entire system and manually manage the matrix yourself. Two.ZUI relies on this same orientation, so it won't be too helpful as is written.
To overwrite the matrix and manually manage the matrix positioning yourself you can do:
const scene = two.scene;
scene.matrix.manual = true;
// Do whatever matrix operations you want
Another idea:
Have you tried putting the ZUI on a group. And then putting that group in another group which has the rotation applied?
You'll need to project the mouseX, mouseY to the rotated group. But, you should be able to do that with these calls (assuming that group is outer most group):
const mouse = new Two.Vector(e.clientX, e.clientY);
const projection = group.matrix.multiply(mouse.x, mouse.y, 1);