Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotate a set of object coordinates

I have a bunch of objects (ActiveRecord) that have x and y coordinates relative to a HTML5 canvas element. I need to rotate sets of them by different number of degrees (in 90 degrees increments) and then store them again for a one time data migration.

These objects are all relative to a canvas element so the coordinates start at (0,0) and I would like them to stay in that quadrant (bottom right), if possible. I assume that I need to do a rotation around (0,0) and then a translation down and right.

Is there a way I can write a block that will do this? Any help is appreciated.

Update: did some edits to make it clearer after some questions.

Thanks.

like image 584
Chris Butler Avatar asked Aug 18 '11 01:08

Chris Butler


1 Answers

  1. To rotate a point P an amount of R degrees around the origin:
    P2.x = P.x * cos(R) - P.y * sin(R)
    P2.y = P.x * sin(R) + P.y * cos(R)
    [citation]

  2. You probably want to rotate about an arbitrary point in the center of the quadrant you are keeping the objects in. If your quadrant is 200x100 units wide, you'd want to rotate around the point <100,50>.

    To rotate a point P around a location C other than the origin, you want to first translate the location to the origin, then rotate around the origin, and then translate back to C. In other words,
    P2 = P - C
    P3 = rotate(P2)
    P4 = P3 + C

    You can see this in action at http://phrogz.net/SVG/rotations.xhtml - click to set the rotation center, or change the rotation amount, and a transform is set on the group of points that translates to the origin, rotates, and then translates back again.

Put it all together, and to rotate a point with x and y attributes around an arbitrary point in Ruby, you'd use code like this:

Point = Struct.new(:x,:y) do
  def self.to_proc
    lambda{ |x| self.new *x }
  end

  def rotate( degrees, origin=Point.new(0,0) )
    radians = degrees * Math::PI/180
    x2 = x-origin.x; y2 = y-origin.y
    cos = Math.cos(radians); sin = Math.sin(radians)
    self.class.new(
      x2*cos - y2*sin + origin.x, 
      x2*sin + y2*cos + origin.y 
    )
  end

  def inspect
    "<%.1f,%.1f>" % [x,y]
  end
end

points = [ [0,0], [1,2], [3,4], [5,6] ].map(&Point)
p points
#=> [<0.0,0.0>, <1.0,2.0>, <3.0,4.0>, <5.0,6.0>]

p points.map{ |p| p.rotate(90) }
#=> [<0.0,0.0>, <-2.0,1.0>, <-4.0,3.0>, <-6.0,5.0>]

p points.map{ |p| p.rotate(90,Point.new(3,4)) }
#=> [<7.0,1.0>, <5.0,2.0>, <3.0,4.0>, <1.0,6.0>]
like image 141
Phrogz Avatar answered Oct 27 '22 17:10

Phrogz