two.js icon indicating copy to clipboard operation
two.js copied to clipboard

ZUI Rotation

Open rttll opened this issue 1 year ago • 2 comments

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 Screen Shot 2024-08-14 at 2 59 24 PM

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.

Screen Shot 2024-08-14 at 2 59 34 PM

Environment (please select one):

  • [x] Packaged software (e.g: ES6 imports, react, angular, vue.js)

rttll avatar Aug 14 '24 22:08 rttll

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

jonobr1 avatar Aug 16 '24 19:08 jonobr1

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);

jonobr1 avatar Sep 04 '24 04:09 jonobr1