Rotate a set of object coordinates

2019-05-12 05:53发布

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.

1条回答
劫难
2楼-- · 2019-05-12 06:42
  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>]
查看更多
登录 后发表回答