Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

Optimize trigonometric calculations?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Andreas - 12 Mar 2008 10:16 GMT
Hi,

I was debating if I should be posting this in the drawing newsgroup but
finally decided that it wasn't drawing related (even if that's what I'm
ultimatly doing) but instead optimization / math. In a sense it's not
c-sharp specific either but I hope someone can share some suggestions. So
what I'm doing can be viewed as graphis a graph (Vertex and Edges).

I have a series of points drawn on a bitmap and some of them should be
connected with a line. That itself is no problem, it would be a basic
DrawLine call. However what I need to do is to have the line not start/end
in the center of each point but instead at a certain distance from it, i.e
having a sort of safe radius around each point

So instead of this (sorry for the poor ascii)

*----*

I want this

*   ----    *

Now please note that this would not be a problem is all points where lined
up on the horizontal and vertical axis but they are not, meaning that I will
be drawing diagonale lines. So what I came up with was some basic
trigonomety to calculate where the line coordinates are. My problem is
performance and the need to optimize. I have about 5000 points and they are
all connected in a web. I have a need to redraw this when ever scrolling
occures (yeah I only redraw the things that are visible in my viewing pane)
and performance is a bit slow

So is there any math-tricks / optimizations anyone could recommend to do the
same? Below is the code I use to calculate and draw the line. For the sake
of the argument, assume I cannot cache the calculation after the first time
it's  been drawn and have a need to run it each time

int safeRadius = 7;

Point p1 = new Point(x1, y1);
Point p2 = new Point(x2, y2);

double angleX = Math.Abs(p2.X - p1.X);
if (angleX == 0)
   angleX = 1;

double angleY = Math.Abs(p2.Y - p1.Y);
if (angleY == 0)
   angleY = 1;

double angle1 = Math.Atan(angleY / angleX);

int xoff = (int)Math.Cos(angle1) * safeRadius;
if (p1.X > p2.X)
   xoff = -xoff;

double yoff = Math.Sin(angle1) * safeRadius;
if (p1.Y > p2.Y)
   yoff = -yoff;

gfx.DrawLine(p, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y - yoff);
Marc Gravell - 12 Mar 2008 10:25 GMT
Do you even need trig here? I haven't run through all the logic, but I'm
wondering if you can't simply multiply by the y/x quotient? I'd need a scrap
of paper to check it, admittedly...

For the cos/sin - how accurate does it need to be? Would the trick of an
array of pre-calculated values work? (picking your scale accordingly) - for
instance, with degrees (which is very rare in reality) you might choose a
double[360] and round to the nearest degree... obviously with radians you
would choose some suitable factor, multiple and round etc...

Marc
Andreas - 12 Mar 2008 11:02 GMT
A resonable approximation should  be ok.. I'm trying out some stuff atm.
Will report back if I get something worked out. I could cheat by just going

endPoint1 = new Point(p1.X+safeRadius, p1.Y+safeRadius)
endPoitn2 = ...

it wont give me the EXACT position as it would do with trig but I think it's
an ok trade-off in this case

> Do you even need trig here? I haven't run through all the logic, but I'm
> wondering if you can't simply multiply by the y/x quotient? I'd need a
[quoted text clipped - 7 lines]
>
> Marc
Marc Gravell - 12 Mar 2008 10:48 GMT
Just checking my math... the atan is the nuicance - perhaps needed after
all. Unfortunately this is likely to be the most expensive operation...

But you can reduce some of the math - since (Y,X = full offsets, y,x =
scaled)

tan(angle) = Y/X= y/x
=> y=xY/X
So you can remove of of the sin/cos, and pehaps use the lookup for the other
(so only one lookup table - suggest "sin" is easier as easy to scale for
0-90 [or 0-Pi/4]

That just leaves the atan...

Marc
tony lock - 12 Mar 2008 11:40 GMT
> Hi,
>
[quoted text clipped - 56 lines]
>
> gfx.DrawLine(p, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y - yoff);

Wil not Pythagoras give you what you want

int safeRadius = 7;

Point p1 = new Point(x1, y1);
Point p2 = new Point(x2, y2);

double angleX = Math.Abs(p2.X - p1.X);
double angleY = Math.Abs(p2.Y - p1.Y);

double hypot = Math.Sqr(angleX*angleX + angleY*angleY)

int xoff = (int)safeRadius * angleX / hypot
int yoff = (int)safeRadius * angleY / hypot

gfx.DrawLine(p, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y - yoff);

Tony
tony lock - 12 Mar 2008 11:53 GMT
Sorry should have been

int xoff = (int)(safeRadius * angleX / hypot)
int yoff = (int)(safeRadius * angleY / hypot)

Tony
tony lock - 12 Mar 2008 11:58 GMT
Also should be

double angleX = p2.X - p1.X
double angleY = p2.Y - p1.Y

 Tony
Andreas - 12 Mar 2008 12:00 GMT
You know... this is what I wanted to do but I ended up taking the long
route.. =/
Thank you :)

> Sorry should have been
>
> int xoff = (int)(safeRadius * angleX / hypot)
> int yoff = (int)(safeRadius * angleY / hypot)
>
> Tony
Fred Mellender - 12 Mar 2008 12:31 GMT
The slope of the line: m = dy/dx = (p2.y-p1.y)/(p2.x-p1.x).
If p1 and p2 have the same x coord, then the line is vertical which you
handle as an easy special case.

then:
newPt.x = p1.x + safeRadius,  and newPt.y = p1.y + safeRadius*m.

The actual distance between the newPt and p1 is not safeRadius, but only
approximately so (=safeR*sqrt(1+m^2)).  However, the newPt is on the line.

> Hi,
>
[quoted text clipped - 56 lines]
>
> gfx.DrawLine(p, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y - yoff);
Fred Mellender - 12 Mar 2008 13:01 GMT
More exactly:

The slope of the line: m = dy/dx = (p2.y-p1.y)/(p2.x-p1.x).
If p1 and p2 have the same x coord, then the line is vertical which you
handle as an easy special case.

then:
safeRad^2 = ndy^2 + ndx^2, so that ndx = safeRad/sqrt(m^2+1) and
ndy = m*safeRadius/sqrt(1+m^2);

Thus newPt = (x1+ndx, y1+ndy).

>> Hi,
>>
[quoted text clipped - 56 lines]
>>
>> gfx.DrawLine(p, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y - yoff);
Ben Voigt [C++ MVP] - 12 Mar 2008 14:34 GMT
> Hi,
>
[quoted text clipped - 9 lines]
> start/end in the center of each point but instead at a certain
> distance from it, i.e having a sort of safe radius around each point

And now for something completely different (Using ratios instead of calling
the trig functions is already a very good optimization):

You could always draw the lines, endpoint to endpoint, then draw white
circles over each point.  This may or may not be faster than adjusting the
lines.
Andreas - 12 Mar 2008 15:47 GMT
Actually I did that first but then the drawing evolved to have underlaying
graphics which got erased when I drew the circles, so I needed a way of
getting the same effect. Tony Lock helped me come to my senses, I was
overlooking the most basic mathmatical solution :)

>> Hi,
>>
[quoted text clipped - 16 lines]
> circles over each point.  This may or may not be faster than adjusting the
> lines.
Martin Bonner - 12 Mar 2008 15:09 GMT
> Hi,
>
[quoted text clipped - 44 lines]
> if (angleY == 0)
>     angleY = 1;

deltaX, deltaY would be better names here.

> double angle1 = Math.Atan(angleY / angleX);

You almost NEVER want to use Math.Atan.  a) It only gives you an angle
in the range -90..90 degrees; b) it won't like deltaX == 0 (yes, I
know you kludge round that above).

Instead use Math.Atan2 which a) gets you an angle in the full circle;
b) only has problems if /both/ it's arguments are zero.

However, read on:

> int xoff = (int)Math.Cos(angle1) * safeRadius;

You rarely want to use Cos(angle).  Instead remember that the dot
product of two vectors is the product of their lengths and the cosine
of the angle.

Just calculate the unit vector in the direction p1->p2, and then
calculate the dot product of that with a unit vector along the X-axis.

> if (p1.X > p2.X)
>     xoff = -xoff;
>
> double yoff = Math.Sin(angle1) * safeRadius;

Sin(angle) can be done in two ways:  It is either the length of the
cross product vector, OR you can just calculate the cosine of the
complementary angle (90-angle).  In this case, the latter is easier;
just calculate the dot product with a unit vector along the Y-axis.

> if (p1.Y > p2.Y)
>     yoff = -yoff;
>
> gfx.DrawLine(p, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y - yoff);

So.  Code looks like:

void DrawShortenedLine( Pen pen, Point p1, Point p2, int safeRadius )
{
   // Calculate the delta's etc in double.
   // It makes handling rounding *so* much simpler.
   double deltaX = p2.X - p1.X;  // Do this in double
   double deltaY = p2.Y - p2.Y;
   double hypot = Math.Sqrt( deltaX*deltaX + deltaY*deltaY );
   if (hypot <= safeRadius*2)
   {
      return;  // Nothing to do
   }

   deltaX /= hypot;
   deltaY /= hypot;

   // deltaX/Y now represents a unit vector from p1 to p2.

   int xoff = (int)Math.Round( deltaX*safeRadius );
   int yoff = (int)Math.Round( deltaY*safeRadius );

   this.gfx.DrawLine(pen, p1.X + xoff, p1.Y + yoff, p2.X - xoff, p2.Y
- yoff);
}

(Note.  At some loss of clarity, one can use integer deltaX/deltaY/
hypot and write

  xOff = (deltaX*safeRadius + Math.Sign(deltaX)*(hypot/2)) / hypot;

One would have to profile to see if this was faster or not.)

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.