Why this Top Left of rectangle function won't work?

I do not understand what is wrong with this function? I am trying to rotate a point around an origin. The point is based on the edge of a rotated rectangle. I have already been given the maths for this in the past. I was given a top left function in the past and a function for rotating around an origin but I don’t seem to be able to get the method to work. I have tried passing in both radians and degrees for the angle.

Function TopLeft(obj As VectorObject) As Point
  dim hw as double = 0
  
  dim hh As Double = 0
  
  dim cos As Double = Xojo.Math.Cos( obj.Angle.Radians )
  
  dim sin As Double = Xojo.Math.Sin( obj.Angle.Radians )
  
//code from original method which doesn't seem to support rotation around an origin
  'dim x1 As double = obj.Left + (obj.AnchorPoint.X * obj.Width) + (hw * cos - hh * sin)
  '
  'dim y1 As Double = obj.Top + (obj.AnchorPoint.Y * obj.Height) + (hw * sin + hh * cos)
  
  dim a as double = obj.angle.Radians
  dim origin as point = new point(obj.AnchorPoint.X * obj.Width, obj.AnchorPoint.Y * obj.Height)
  x1 = (origin.x + ((obj.Left - origin.x) * cos - (obj.Top - origin.x) * sin))
  y1 = (origin.y + ((obj.Left - origin.x) * sin + (obj.Top - origin.y) * cos)))
  
  return new Point(x1, y1)
End Function

The VectorObject class is similar to the Rectangle class and the Point class is similar to REALbasic.Point.

Thanks

On the line for x1, replace the second origin.x with origin.y

Thanks for pointing that out. It still doesn’t work. Do you think it has something to do with the coordinate system being incorrect? Thanks

Bump

If obj is a VectorObject, which is like a Rectangle, then what is AnchorPoint? To me an AnchorPoint is what it should be rotating about so the origin is the AnchorPoint.

dim origin As point = obj.AnchorPoint

That’s just a wild guess though.

I’m confused by the ‘Vector’ being a ‘Rectangle’. A vector is just a point, or it can be a base point and a direction point (really a ray), so is TopLeft supposed to be one of those points? before or after rotating? Maybe more of a description or even picture will help us understand.

When my math isn’t working I’ll draw a simple example of the geometries on paper and manually work out what values should be, then stepping through code you can find where values go awry.

[quote=214913:@Will Shank]If obj is a VectorObject, which is like a Rectangle, then what is AnchorPoint? To me an AnchorPoint is what it should be rotating about so the origin is the AnchorPoint.

dim origin As point = obj.AnchorPoint

That’s just a wild guess though.

I’m confused by the ‘Vector’ being a ‘Rectangle’. A vector is just a point, or it can be a base point and a direction point (really a ray), so is TopLeft supposed to be one of those points? before or after rotating? Maybe more of a description or even picture will help us understand.

When my math isn’t working I’ll draw a simple example of the geometries on paper and manually work out what values should be, then stepping through code you can find where values go awry.[/quote]
For now the AnchorPoint, is exclusively the point to rotate around. It does not determine the position of the rectangle but just the rotation point. The TopLeft is suppose to give me the rotated rectangle. The VectorObject class is a rotatable rectangle.

Thanks

OK, then the position of the rectangle seems to be information that’s missing. I take it obj.Width and obj.Height are the size of the rectangle, obj.Angle.Radians is the rectangles current rotation amount. But where is the rectangles offset? And is the rectangles initial orientation centered on <0,0> or is the top left corner at <0,0> or something else? Is AnchorPoint expressed relative to the rectangle or is it an absolute coordinate? Is Scale part of the rectangles transform?

I guess I’m trying to say I still don’t have a clear picture how this rectangle is constructed. There’s many ways to represent these geometries and the way you’re doing it is not my usual habit so I need it spelled out.

[quote=215230:@Will Shank]OK, then the position of the rectangle seems to be information that’s missing. I take it obj.Width and obj.Height are the size of the rectangle, obj.Angle.Radians is the rectangles current rotation amount. But where is the rectangles offset? And is the rectangles initial orientation centered on <0,0> or is the top left corner at <0,0> or something else? Is AnchorPoint expressed relative to the rectangle or is it an absolute coordinate? Is Scale part of the rectangles transform?

I guess I’m trying to say I still don’t have a clear picture how this rectangle is constructed. There’s many ways to represent these geometries and the way you’re doing it is not my usual habit so I need it spelled out.[/quote]
Anchor point is relative to width and height. So 0, 0 would rotate from top left, 0.5, 0.5 would rotate from centre and 1, 1 would rotate from bottom right. I have a class that bases around working with the value for an angle. obj.left and obj.top refers to the position of the rectangle. Thanks

Oh, now your calculation of origin makes sense. So a rectangle 200 wide, 100 tall, top-left at 0, and 0 rotation looks like this…

AnchorPoint is <0.4, 0.3> and marked in purple with a line to the top-left corner. Then changing the top-left from <0,0> to <70,-120> looks like this…

Now changing the rotation angle to 1 (in radians) looks like this, where the blue dot is the point you want to calculate and the grey dot is the unrotated top-left point…

I worked out these 2 methods that calculate the blue point

[code]Function topLeft(obj As VectorObject) As Point
dim ax, ay, radius, angle, x, y As double

ax = obj.width * obj.AnchorPoint.x
ay = obj.height * obj.AnchorPoint.y

radius = Sqrt(ax * ax + ay * ay)
angle = ATan2(ay, ax) + obj.angleRadians

x = obj.left + ax - radius * cos(angle)
y = obj.top + ay - radius * sin(angle)

return new Point(x, y)

End Function

Function topLeft(obj As VectorObject) As Point
dim ax, ay, sina, cosa, x, y As double

ax = obj.width * obj.AnchorPoint.x
ay = obj.height * obj.AnchorPoint.y

cosa = cos(obj.angleRadians)
sina = sin(obj.angleRadians)

x = -ax * cosa - -ay * sina
y = -ax * sina + -ay * cosa

return new Point(obj.left+ax+x, obj.top+ay+y)

End Function[/code]

This last one is very similar to yours and I think changing your x1, y1 lines to this would be equivalent… (the negatives are left to be factored out)

x1 = obj.Left + origin.x + ( -origin.x * cos - -origin.y * sin ) y1 = obj.Top + origin.y + ( -origin.x * sin + -origin.y * cos )

Also, this is working for positive Y going down orientation (positive rotation is clockwise). I think it works for Y-up orientation too but not sure.

[quote=215506:@Will Shank]Oh, now your calculation of origin makes sense. So a rectangle 200 wide, 100 tall, top-left at 0, and 0 rotation looks like this…

AnchorPoint is <0.4, 0.3> and marked in purple with a line to the top-left corner. Then changing the top-left from <0,0> to <70,-120> looks like this…

Now changing the rotation angle to 1 (in radians) looks like this, where the blue dot is the point you want to calculate and the grey dot is the unrotated top-left point…

I worked out these 2 methods that calculate the blue point

[code]Function topLeft(obj As VectorObject) As Point
dim ax, ay, radius, angle, x, y As double

ax = obj.width * obj.AnchorPoint.x
ay = obj.height * obj.AnchorPoint.y

radius = Sqrt(ax * ax + ay * ay)
angle = ATan2(ay, ax) + obj.angleRadians

x = obj.left + ax - radius * cos(angle)
y = obj.top + ay - radius * sin(angle)

return new Point(x, y)

End Function

Function topLeft(obj As VectorObject) As Point
dim ax, ay, sina, cosa, x, y As double

ax = obj.width * obj.AnchorPoint.x
ay = obj.height * obj.AnchorPoint.y

cosa = cos(obj.angleRadians)
sina = sin(obj.angleRadians)

x = -ax * cosa - -ay * sina
y = -ax * sina + -ay * cosa

return new Point(obj.left+ax+x, obj.top+ay+y)

End Function[/code]

This last one is very similar to yours and I think changing your x1, y1 lines to this would be equivalent… (the negatives are left to be factored out)

x1 = obj.Left + origin.x + ( -origin.x * cos - -origin.y * sin ) y1 = obj.Top + origin.y + ( -origin.x * sin + -origin.y * cos )

Also, this is working for positive Y going down orientation (positive rotation is clockwise). I think it works for Y-up orientation too but not sure.[/quote]
Thankyou very much. You have been very helpful. I have attempted to study into the maths behind this and I have used that to alter it so that it supports TopRight, BottomRight, etc.

One small issue I have ran into is the points seems slightly off. I was wondering if this looks like it has anything to do with the rendering system I have used or if this is a small flaw in my modified version of the method. I also do not understand why with ATan2, the coordinates are swapped. I would expected x, y not y, x.

I am fine with this now but just wanted to get better understanding or see if the algorithm has any significant flaws.

Function TopLeft(obj As VectorObject, addX as double = 0, addY as double = 0) As Point
  dim ax, ay, aax, aay, radius, angle, x, y as double
  
  //set point to anchor around
  ax = obj.AnchorAbsoluteX
  ay = obj.AnchorAbsoluteY
  
  //calculate the anchor position with added relative position
  aax = ax - addX
  aay = ay - addY
  
  //calculate length of line from 0, 0 to absolute anchor position with added relative position
  radius = Sqrt((aax * aax) + (aay * aay))
  //calculate angle based on were the line just meets the anchor point and add the applied rotation from anchor with added relative position
  angle = ATan2(aay, aax) + obj.Angle.Radians
  
  x = (obj.left + ax) - (radius * cos(angle))
  y = (obj.top + ay) - (radius * sin(angle))
  
  return new Point(x, y)
  
End Function

By slightly off do you mean by just a pixels worth? If something were wrong with the math I’d expect if to be significantly off.

You’ll notice in my rotated rect pic that the blue dot is just off center of the corner. This is because I used Graphics.FillOval to draw and that only takes integer coordinates, so the calculated floating point value gets floored. The fix was to Round the values being passed to FillOval. Object2Ds take floating point values but looks like they get floored when being drawn. Actually, floored and subtract 1, I can’t be sure, I don’t use Object2D very often.

Hmm, actually OvalShape seems to be 1 pixel off but CurveShape lines up correctly. This code should draw a circle centered on the lines endpoint but it’s 1 pixel up and left. Adding 0.5 to the ovals X Y makes it centered on the endpoint so maybe it’s coordinate is treated as inter-pixel position; but the line doesn’t do that. Very confusing. This might be happening just on Mac.

What rendering system are you using?

[code] dim o As new OvalShape
o.Fill = 0
o.Border = 100
o.Width = 9
o.Height = 9
o.X = 100
o.Y = 100

dim l As new CurveShape
l.X = 100
l.Y = 100
l.X2 = 0
l.Y2 = 200

g.DrawObject o
g.DrawObject l[/code]

I’m not sure either but that’s the specification :). ATan means arc-tangent which means inverse-tangent. Regular tangent takes an angle and returns the slope, where slope is ‘rise over run’, y/x. So the inverse of that takes y/x and returns the angle. ATan(y/x) does that but a common complication is when x = 0. ATan2 handles that complication for you and I guess because y is written first in y/x the powers that be decided to use y x parameter order.

[quote=216220:@Will Shank]By slightly off do you mean by just a pixels worth? If something were wrong with the math I’d expect if to be significantly off.

You’ll notice in my rotated rect pic that the blue dot is just off center of the corner. This is because I used Graphics.FillOval to draw and that only takes integer coordinates, so the calculated floating point value gets floored. The fix was to Round the values being passed to FillOval. Object2Ds take floating point values but looks like they get floored when being drawn. Actually, floored and subtract 1, I can’t be sure, I don’t use Object2D very often.

Hmm, actually OvalShape seems to be 1 pixel off but CurveShape lines up correctly. This code should draw a circle centered on the lines endpoint but it’s 1 pixel up and left. Adding 0.5 to the ovals X Y makes it centered on the endpoint so maybe it’s coordinate is treated as inter-pixel position; but the line doesn’t do that. Very confusing. This might be happening just on Mac.

What rendering system are you using?

[code] dim o As new OvalShape
o.Fill = 0
o.Border = 100
o.Width = 9
o.Height = 9
o.X = 100
o.Y = 100

dim l As new CurveShape
l.X = 100
l.Y = 100
l.X2 = 0
l.Y2 = 200

g.DrawObject o
g.DrawObject l[/code]

I’m not sure either but that’s the specification :). ATan means arc-tangent which means inverse-tangent. Regular tangent takes an angle and returns the slope, where slope is ‘rise over run’, y/x. So the inverse of that takes y/x and returns the angle. ATan(y/x) does that but a common complication is when x = 0. ATan2 handles that complication for you and I guess because y is written first in y/x the powers that be decided to use y x parameter order.[/quote]
I am using PixmapShape so Object2D for rendering system on Windows. For now I don’t think it matters. The app can be improved in the future. Thanks for the help.