Back to school - Office art geometry
Let's start off with the following observation- the following two shapes have the same Left and Top coordinates:
What's different is obviously the rotation. What Office saves for a shape is the Left and Top coordinates together with a Rotation. So the Top coordinate, for instance, is not necessarily the uppermost part of a shape.
So let's solve the following problem: to which shape is the following arrow pointing?
Input data:
Arrow: Left = 98.74763 Top = 67.08575 Width = 162 Height = 60 Rotation = 45
Upper shape: Left = 264 Top = 36 Width = 168 Height = 72 Rotation = 0
Lower shape: Left = 264 Top = 114 Width = 168 Height = 72 Rotation = 0
The code for computing the "From" and "To" points:
public static void GetFromAndTo(RectangleF bounds, double rot,
ref PointF from, ref PointF to)
{
double La, Ta, Wa, Ha, alpha;
La = bounds.Left;
Ta = bounds.Top;
Wa = bounds.Width;
Ha = bounds.Height;
alpha = (rot * Math.PI) / 180;
from.X = (float)(La + Wa * (1 - Math.Cos(alpha)) / 2);
from.Y = (float)(Ta + (Ha / 2) - (Wa * Math.Sin(alpha)) / 2);
to.X = (float)(La + Wa * (1 + Math.Cos(alpha)) / 2);
to.Y = (float)(Ta + (Ha / 2) + (Wa * Math.Sin(alpha)) / 2);
}
The "From" point has coordinates: (122.512 ; 39.8101) and the "To" point has coordinates:(237.0633 ; 154.3614)
What needs to be determined now is the distance between the "To" point and each rectangle. For that, we'll need to determine where the segment between "To" and the center of the rectangle intersects the bounds of the rectangle.
The line between "To" and the center intersects the bounds in found points at most. So we'll need to determine which one of these points is inside the bounds and between "To" and the center.
We start off by computing the line equation for the line that contains "To" and the center (Y = m * X + b). Then we find out the intersection by substituting the points on the bounds (e.g. Y = Top or X = Left + Width).
//Line equation between point and center
double m = (cen.Y - src.Y) / (cen.X - src.X);
double b = cen.Y - (cen.X * m);
PointF[] pis = new PointF[4];
pis[0] = new PointF((float)((bounds.Top - b) / m), bounds.Top);
pis[1] = new PointF((float)(bounds.Left + bounds.Width), (float)((bounds.Left + bounds.Width) * m + b));
pis[2] = new PointF((float)((bounds.Top + bounds.Height - b) / m), (float)(bounds.Top + bounds.Height));
pis[3] = new PointF(bounds.Left, (float)(bounds.Left * m + b));
sqdist = double.NaN;
//Select the intersection with the bounds which is inside the bounds and between the two points
for (int i = 0; i < 4; ++i)
{
if (IsInsideAndBetwen(pis[i], src, cen, bounds))
{
sqdist = Math.Pow(src.X - pis[i].X, 2) + Math.Pow(src.Y - pis[i].Y, 2);
return;
}
}
The method IsInsideAndBetween(pi, src,cen, bounds) returns true if pi is between cen and src and if pi is inside the bounds.
private bool IsInsideAndBetwen(PointF pi, PointF src, PointF cen, RectangleF bounds)
{
if (pi.X < bounds.Left ||
pi.X > bounds.Left + bounds.Width ||
pi.Y < bounds.Top ||
pi.Y > bounds.Top + bounds.Height)
return false; //It's outside bounds
if (cen.X == pi.X)
{
if ((src.Y - cen.Y) / (pi.Y - cen.Y) < 1)
return false;
}
else
if (cen.Y == pi.Y)
{
if ((src.X - cen.X) / (pi.X - cen.X) < 1)
return false;
}
else
if ((src.X - cen.X) / (pi.X - cen.X) < 1 ||
(src.Y - cen.Y) / (pi.Y - cen.Y) < 1)
return false; //It's not between the two points.
return true;
}
Disclaimer: Sample Code is provided for the purpose of illustration only and is not intended to be
used in a production environment. THIS SAMPLE CODE AND ANY RELATED INFORMATION
ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. We grant You a
nonexclusive, royalty-free right to use and modify the Sample Code and to
reproduce and distribute the object code form of the Sample Code, provided
that. You agree: (i) to not use Our name, logo, or trademarks to market Your
software product in which the Sample Code is embedded; (ii) to include a valid
copyright notice on Your software product in which the Sample Code is embedded;
and (iii) to indemnify, hold harmless, and defend Us and Our suppliers from and
against any claims or lawsuits, including attorneys’ fees, that arise or result
from the use or distribution of the Sample Code