Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collide rotated rects #46

Open
blocksploit opened this issue May 2, 2021 · 5 comments
Open

Collide rotated rects #46

blocksploit opened this issue May 2, 2021 · 5 comments

Comments

@blocksploit
Copy link

blocksploit commented May 2, 2021

p5.collide2d.js lib can't collide rotated rects.
I'm suggesting to add this feature, bcs I lost much time to write it myself.

/*
 * collide rotated rects  
 */

// get corners of rects
// x - center x of rect
// y - center y of rect
// w - width of rect
// h - height of rect
// a - angle
p5.prototype.getCorners = function getCorners(x, y, w, h, a) {
  return {
    get topLeft() {
      var cx = x - ((w / 2) * cos(a)) + ((h / 2) * sin(a));
      var cy = y - ((w / 2) * sin(a)) - ((h / 2) * cos(a));
      return {
        x: cx,
        y: cy
      };
    },
    get topRight() {
      var cx = x + ((w / 2) * cos(a)) + ((h / 2) * sin(a));
      var cy = y + ((w / 2) * sin(a)) - ((h / 2) * cos(a));
      return {
        x: cx,
        y: cy
      };
    },
    get bottomLeft() {
      var cx = x - ((w / 2) * cos(a)) - ((h / 2) * sin(a));
      var cy = y - ((w / 2) * sin(a)) + ((h / 2) * cos(a));
      return {
        x: cx,
        y: cy
      };
    },
    get bottomRight() {
      var cx = x + ((w / 2) * cos(a)) - ((h / 2) * sin(a));
      var cy = y + ((w / 2) * sin(a)) + ((h / 2) * cos(a));
      return {
        x: cx,
        y: cy
      };
    },
    toArray: function() {
      return [
        this.topLeft,
        this.topRight,
        this.bottomRight,
        this.bottomLeft
      ];
    }
  }
}

// collide 2 rects by corners as a polies

p5.prototype.collideRects = function collideRects(x1, y1, w1, h1, a1, x2, y2, w2, h2, a2) {
  var c1 = p5.prototype.getCorners(x1, y1, w1, h1, a1).toArray();
  var c2 = p5.prototype.getCorners(x2, y2, w2, h2, a2).toArray();
  return p5.prototype.collidePolyPoly(c1, c2, true);
}

// vector version of colliderects

p5.prototype.collideRectsVector = function collideRectsVector(p1, s1, a1, p2, s2, a2) {
  return p5.prototype.collideRects(p1.x, p1.y, s1.x, s1.y, a1, p2.x, p2.y, s2.x, s2.y, a2);
}

my version of code

@bmoren
Copy link
Owner

bmoren commented May 2, 2021

I'll have a more in depth look soon. Thank you for looking at this, it's absolutely something that's been on my radar, but... I'll offer some context for why this hasn't been included yet :)

First is that the functionality is as you pointed out, achievable with collidePolyPoly. Although I'll be the first to admit it's rather complex for a beginner / without some extra work, which is why I'm considering it!

Second is that it's maybe faster (maybe you could run some speed test comparisons on it?) to use the vector rotation to do the calculations. See some links to implementation ideas: #23. This potentially opens up a whole can of worms regarding refactoring everything, maybe to vectors for the basis of the library (inverting the logic that's happening now), which isn't bad, but is something to consider as it would be a considerable amount of work to add the feature into the core that way.

Third is that to make this idea 'complete', Collide Rect Rotation is not the only feature that needs to be added before rotation overarchingly would be implemented. We'll need to add the ability for rotation in every piece of geometry which can rotate, so this means adding rotation capability for for anything with an ellipse, line, rectangle, or polygon. This is a big task and it would likely be more effective to implement something that looks at the internal rotate(), push(), pop(), translate() functions and acts accordingly.

Ok, so that's all tough stuff, so lastly I want to say THANK YOU for writing this! It's something that many people have asked for and as you can see there are many considerations above that I just haven't had the time to work on yet. I'd love to hear your thoughts on the above points (I try to run these repos with as much community feedback before implementation or changing anything as possible – see closed issues)

One thing that I think we should do immediately while working out an overarching plan for rotation would be to include this as an example in the live examples section of the readme / docs : #23

Do you think you'd be up for adapting your code above into an example instead of an implementation, at least in the short term?

edit/addition: we now have a p5js editor account to house these examples and I'm happy to add examples to that account if you reply to this issue with one!

@blocksploit
Copy link
Author

Yes you are right. It will take a lot of time to make it possible to collide any rotated object. Maybe we should try bitmap collision? Like make second invisible canvas where it will draw objects inside push() pop() and use bitmap collision to collide them? Sorry if i suggested dumb idea, im not mega pro in p5.js )

@bmoren
Copy link
Owner

bmoren commented May 11, 2021

that's certainly one option! I do wonder how its speed would be?..

I agree that it probably would be likely be the most seamless approach to make the library generally aware of the push() pop() translate() rotate() situation in a given project and just 'work' for those situations.

I don't really even know where to begin with that! But having behavior which worked inside of that convention (which is very standard practice / used widely) would seem like a good place to put some effort. The bonus of this approach (I think?!) is also that it should be easily applied to the other collision circumstances with minimal code modification for each.

@blocksploit
Copy link
Author

blocksploit commented May 11, 2021

I think for the start will be good to learn math and make code to work with rotations. You need to calculate object transformation when its rotated and then just collide like normal non rotated object. For example you have ellipse 200 200 30 50 and its rotated on PI / 3 in radians, you can use sin cos idk to calculate new x y dx and dy, and then just collide like normal ellipses. I think thats will be good choice from many variants.

@blocksploit
Copy link
Author

blocksploit commented Jun 22, 2021

I think i found easy solution of 2 problems.

  1. Collide rotated objects
  2. Collide objects when rectMode is CENTER or RADIUS

I upgraded code that i wrote before:

/* Convert simple figures to polygons and collide them */


// get X rotation
p5.prototype.rotX = function rotX(x, y, a) {
  return x * cos(a) - y * sin(a);
}

// get Y rotation
p5.prototype.rotY = function rotY(x, y, a) {
  return x * sin(a) + y * cos(a);
}

// a - angle in radians

// convert rectangle to polygon, works with rectModes. You can use that to prevent rectMode(CENTER) or another mode issuse
p5.prototype.rectToPoly = function rectToPoly(x, y, w, h, a = 0) {
  // Rect Mode: CENTER

  if (this._renderer._rectMode == CENTER) {
    return [
      { x: this.rotX(x - w / 2, y - h / 2, a), y: this.rotY(x - w / 2, y - h / 2, a) },
      { x: this.rotX(x - w / 2, y + h / 2, a), y: this.rotY(x - w / 2, y + h / 2, a) },
      { x: this.rotX(x + w / 2, y + h / 2, a), y: this.rotY(x + w / 2, y + h / 2, a) },
      { x: this.rotX(x + w / 2, y - h / 2, a), y: this.rotY(x + w / 2, y - h / 2, a) },
      { x: this.rotX(x - w / 2, y - h / 2, a), y: this.rotY(x - w / 2, y - h / 2, a) },
    ];
  }
  
  // Rect Mode: RADIUS
  
  if (this._renderer._rectMode == RADIUS) {
    return [
      { x: this.rotX(x - w, y - h, a), y: this.rotY(x - w, y - h, a) },
      { x: this.rotX(x - w, y + h, a), y: this.rotY(x - w, y + h, a) },
      { x: this.rotX(x + w, y + h, a), y: this.rotY(x + w, y + h, a) },
      { x: this.rotX(x + w, y - h, a), y: this.rotY(x + w, y - h, a) },
      { x: this.rotX(x - w, y - h, a), y: this.rotY(x - w, y - h, a) },
    ];
  }

  // Rect Mode: CORNER

  return [
    { x: this.rotX(x, y, a), y: this.rotY(x, y, a) },
    { x: this.rotX(x, y + h, a), y: this.rotY(x, y + h, a) },
    { x: this.rotX(x + w, y + h, a), y: this.rotY(x + w, y + h, a) },
    { x: this.rotX(x + w, y, a), y: this.rotY(x + w, y, a) },
    { x: this.rotX(x, y, a), y: this.rotY(x, y, a) },
  ];
};

// convert ellipse to polygon, i will add ellipseMode support later. vc - how many vertexes polygon will has, 50 is default
p5.prototype.ellipseToPoly = function ellipseToPoly(x, y, xr, yr, a = 0, vc = 50) {
  var ra = new Array(vc).fill();
  for (var i = 0; i < vc; ++i) {
    const rx = cos((TAU / vc) * i) * xr + x;
    const ry = sin((TAU / vc) * i) * yr + y;
    ra[i] = { x: this.rotX(rx, ry, a), y: this.rotY(rx, ry, a) };
  }
  ra[vc] = ra[0];
  return ra;
}

// convert triangle to polygon
p5.prototype.triangleToPoly = function triangleToPoly(x1, y1, x2, y2, x3, y3, a = 0) {
  return [
    { x: this.rotX(x1, y1, a), y: this.rotY(x1, y1, a) },
    { x: this.rotX(x2, y2, a), y: this.rotY(x2, y2, a) },
    { x: this.rotX(x3, y3, a), y: this.rotY(x3, y3, a) },
    { x: this.rotX(x1, y1, a), y: this.rotY(x1, y1, a) },
  ];
}

Maybe this will help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants