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.
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]
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>]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With