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 / .NET Framework / New Users / May 2007

Tip: Looking for answers? Try searching our database.

Bug in System.Drawing.Graphics.DrawLines

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
mfr - 23 May 2007 16:05 GMT
Hi,

I think I've found a bug in the System.Drawing.Graphic.DrawLines method.

The code below fills a list with a number of points and then draws these
points using DrawLines and then using DrawLine within a loop.

The DrawLines() method inserts a superious line that that is not drawn using
the DrawLine() function.

This bug was initially found in a ASP.NET application but has been
replicated in a Windows Form application.

Regards,
Dave.

private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;

List<System.Drawing.PointF> pointsToDraw =    new List<System.Drawing.PointF>();

Pen penCyan = new Pen(Brushes.Cyan, 5);
Pen penRed = new Pen(Brushes.Red, 5);

pointsToDraw.Clear();
pointsToDraw.Add(new PointF((float)513.4, (float)311.3));
pointsToDraw.Add(new PointF((float)517.6, (float)314));
pointsToDraw.Add(new PointF((float)518.5, (float)314.6));
pointsToDraw.Add(new PointF((float)526.4, (float)319.8));
pointsToDraw.Add(new PointF((float)529.1, (float)321.5));
pointsToDraw.Add(new PointF((float)529.25, (float)321.6));
pointsToDraw.Add(new PointF((float)529.25, (float)321.85));
pointsToDraw.Add(new PointF((float)531.6, (float)329.7));
pointsToDraw.Add(new PointF((float)534, (float)337.6));
pointsToDraw.Add(new PointF((float)533.95, (float)337.85));
pointsToDraw.Add(new PointF((float)533.85, (float)338));
pointsToDraw.Add(new PointF((float)533.6, (float)338.4));
pointsToDraw.Add(new PointF((float)529.9, (float)343.75));
pointsToDraw.Add(new PointF((float)526.2, (float)349.15));
pointsToDraw.Add(new PointF((float)524.9, (float)352.55));
pointsToDraw.Add(new PointF((float)524.8, (float)353));
pointsToDraw.Add(new PointF((float)524.85, (float)353.6));
pointsToDraw.Add(new PointF((float)525, (float)355.25));
pointsToDraw.Add(new PointF((float)525.1, (float)355.95));
pointsToDraw.Add(new PointF((float)525.55, (float)364));
pointsToDraw.Add(new PointF((float)525.55, (float)364.05));
pointsToDraw.Add(new PointF((float)525.95, (float)371.55));
pointsToDraw.Add(new PointF((float)526, (float)371.8));
pointsToDraw.Add(new PointF((float)526.05, (float)372.1));
pointsToDraw.Add(new PointF((float)526.05, (float)372.25));
pointsToDraw.Add(new PointF((float)526.1, (float)372.4));
pointsToDraw.Add(new PointF((float)526.45, (float)373.2));
pointsToDraw.Add(new PointF((float)526.45, (float)373.3));
pointsToDraw.Add(new PointF((float)526.5, (float)373.45));
pointsToDraw.Add(new PointF((float)526.45, (float)373.55));
pointsToDraw.Add(new PointF((float)526.4, (float)373.6));
pointsToDraw.Add(new PointF((float)519.4, (float)381.5));
pointsToDraw.Add(new PointF((float)519.6, (float)381.3));  // <- this point
causes the problem
pointsToDraw.Add(new PointF((float)514.75, (float)387.2));
pointsToDraw.Add(new PointF((float)514.5, (float)387.55));
pointsToDraw.Add(new PointF((float)514.45, (float)387.8));
pointsToDraw.Add(new PointF((float)514.4, (float)388));
pointsToDraw.Add(new PointF((float)514.3, (float)388.25));
pointsToDraw.Add(new PointF((float)514.2, (float)388.45));
pointsToDraw.Add(new PointF((float)514.25, (float)388.6));
pointsToDraw.Add(new PointF((float)514.3, (float)388.8));
pointsToDraw.Add(new PointF((float)516.3, (float)391.35));
pointsToDraw.Add(new PointF((float)518.5, (float)394.15));
pointsToDraw.Add(new PointF((float)524.45, (float)401.8));
pointsToDraw.Add(new PointF((float)530.45, (float)409.5));
pointsToDraw.Add(new PointF((float)534.15, (float)415.45));
pointsToDraw.Add(new PointF((float)537.85, (float)421.4));
pointsToDraw.Add(new PointF((float)538.05, (float)421.8));
pointsToDraw.Add(new PointF((float)541.9, (float)431.15));
pointsToDraw.Add(new PointF((float)541.95, (float)431.6));
pointsToDraw.Add(new PointF((float)542, (float)431.9));
pointsToDraw.Add(new PointF((float)542, (float)432.3));
pointsToDraw.Add(new PointF((float)542, (float)432.65));

g.DrawLines(penCyan, pointsToDraw.ToArray());

for (int loop = 1; loop < pointsToDraw.Count; loop++)
{
    g.DrawLine(penRed, pointsToDraw[loop - 1], pointsToDraw[loop]);
}
}
Peter Duniho - 23 May 2007 21:42 GMT
> I think I've found a bug in the System.Drawing.Graphic.DrawLines method.

I'm not so sure.

> The code below fills a list with a number of points and then draws these
> points using DrawLines and then using DrawLine within a loop.
>
> The DrawLines() method inserts a superious line that that is not drawn  
> using the DrawLine() function.

As near as I tell, it's not so much a "spurious" line as it a consequence  
of trying to fill in the pen in between the endpoints you gave DrawLines().

I took your code and added some UI to allow me to modify how much of the  
lines are drawn and in what order, as well as scale and move the drawing  
within the form, so that I could see things better (on my computer  
monitor, the absolute positions you've provided are fairly small and well  
to the right and bottom of the application window).  (New code available  
on request...I wrote it mostly as an exercise in learning, and have no  
idea if anyone else would want it).

I noticed a couple of things.  The first is that the problem only happens  
when you provide DrawLines() with more than two points.  If it only draws  
a single line between two endpoints, it works just fine.  The second is  
that the point that is "problematic" doesn't follow the sequence of the  
lines.  That is, within the overall line, it doubles back.

I'm no expert in the math that DrawLines() uses to deal with fitting a  
curve onto the sequence of points you've provided it, but given that it  
does have to do something along those lines to implement its output, it's  
not surprising to me that when there's a sharp discontinuity in the input  
data, you get some anomalous results.  I would not use DrawLines() with  
data that I do not know ahead of time to represent a reasonably smooth,  
continuous function.

For what it's worth, if you move the "problematic" point by swapping it  
with the previous point in the array, the line draws just fine both ways.  
Doing that rectifies the region of discontinuity in your input data, and  
allows DrawLines() to produce smoother results.

Pete
mfr - 24 May 2007 08:17 GMT
Hi Peter, Thanks for looking at this.

According to the documentationon MSDN (quoted below) there is no mention of
curve fitting, and indeed there shouldn't be as I want to plot the exact
points and not a smoothed curve. The data points are actually extracted from
a route that is being overlayed onto a map.

Quote from MSDN....

"This method draws a series of lines connecting an array of ending points.
The first two points in the array specify the first line. Each additional
point specifies the end of a line segment whose starting point is the ending
point of the previous line segment."

Even if a line segment doubles back on itself, DrawLines() should handle
this correctly.

Regards,
Dave.
Peter Duniho - 24 May 2007 09:31 GMT
> According to the documentationon MSDN (quoted below) there is no mention  
> of curve fitting, and indeed there shouldn't be as I want to plot the  
> exact
> points and not a smoothed curve.

You can tell just by looking at the output that *some* sort of curve  
fitting must take place.  You'll note that when you draw simply a sequence  
of line segments, there are gaps in the drawing graphics.  This is because  
Windows just takes the pen of the width you specify, and extends a line  
using that pen from the start point to the finish point.  If you then draw  
another line in a different direction, the new line segment heads off with  
the end not matching up exactly with the first line segment.

Contrast that to the output of DrawLines(), which makes sure that every  
joint between each line segment is smooth and filled in.  A very simple  
way to accomplish this is to put circles at every joint, but then you get  
rounded corners everywhere, which is not necessarily what is desired when  
drawing lines.  Instead, Windows fills in with straight connections  
between the gaps.

As I said, I don't know the specifics of the math that's going into  
calculating what pixels need to get set, but it's clear to me that it is a  
non-trivial calculation and given that, it's not too surprising that when  
you provide data that has tight, reversing corners, the output gets a  
little odd.

> The data points are actually extracted from
> a route that is being overlayed onto a map.

Why does the route have a sharp reversal in it then?

> [...]
> Even if a line segment doubles back on itself, DrawLines() should handle
> this correctly.

Sometimes, you have to accept that the built-in functionality is there to  
provide basic needs, and if your needs are more complex than that, you  
have to "roll your own".  This seems to me to be such a situation.  I have  
seen situations in which an actual bug has been reported and Microsoft has  
acknowledged it as such, but I would be surprised if they would consider  
this to be an actual bug.  I suspect that they would respond by saying  
that the algorithm in DrawLines() is working as designed, and that your  
scenario is a known limitation, and not one that they feel warrants  
complicating the code further to solve.

For what it's worth, you've got at least a couple of reasonable  
workarounds, IMHO:

    * Draw the lines yourself.  It's not hard...you can implement your own  
DrawLines() easily as long as you don't mind rounded corners.

    * Do some data smoothing before you pass your points off to  
DrawLines().  Throw out any point that involves a course reversal of more  
than some threshold amount (135 degrees, for example).  That sort of  
pre-processing should eliminate the kinds of discontinuities that give  
DrawLines() problems, and will allow you to continue using DrawLines() and  
take advantage of its other positive benefits.

Pete
Linda Liu [MSFT] - 24 May 2007 09:04 GMT
Hi Dave,

I performed a test based on your sample code and did see the problem on my
side.

If I change the size of the 'penCyan' in your sample code to 1, the problem
doesn't exist.

I think the key point that causes the problem is that the "problematic"
point doesn't follow the sequence of the line, just as Peter mentioned.

It seems that there's an error when there's such a "problematic" point in
the point array and this error is enlarged when the size of the pen used to
draw the lines becomes bigger.

A workaround to this issue is to diminish the disorder in the point array.
To do this, you may simply swap the "problematic" point with its previous
piont the list.

If you have any concern, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
mfr - 24 May 2007 10:12 GMT
Linda,

I can't go swapping the data points round! This is drawing a pipeline route
onto a map.  Perhaps the pipelines should be relaid to match the microsoft
drawing function!

Since it does appear that this a bug in the DrawLines() code - will it be
raised as a bug report and will it get fixed?

Regards,
Dave.

> Hi Dave,
>
[quoted text clipped - 40 lines]
>  
> This posting is provided "AS IS" with no warranties, and confers no rights.
Peter Duniho - 24 May 2007 10:47 GMT
> I can't go swapping the data points round! This is drawing a pipeline  
> route
> onto a map.  Perhaps the pipelines should be relaid to match the  
> microsoft
> drawing function!

What kind of pipeline makes two immediate 180-degree turns like that?  
Every bend in a pipeline is drag, limiting flow.  It's really not a good  
idea to design a pipeline like that.  So yes, maybe the pipelines *should*  
be relaid.

;)

> Since it does appear that this a bug in the DrawLines() code - will it be
> raised as a bug report and will it get fixed?

So far, you are the only person saying it's a bug.  Personally, I'd call  
it a "limitation".  You're feeding DrawLines() data that it's just not  
designed to deal with.

I can't speak for Microsoft, but I doubt that a bug report will get filed  
unless you do it, and even if you do it, I doubt anything will be done to  
change the behavior of DrawLines().

But good luck with that.  :)

Pete
Linda Liu [MSFT] - 25 May 2007 05:30 GMT
Hi Dave,

Thank you for your prompt response.

I do more research on this issue and get more information on the
Graphics.DrawLines method.

The reason of this issue should not be the calculation error, but the
drawing style in the internal implementation of the DrawLines method.

When connecting two segments of lines, the DrawLines method gets the
intersection of the two lines at the conjunction. The more sharp the angle
formed by the two lines and more big the size of the pen used to draw the
lines, the more large the intersection part.

I will illuminate this with a sample. The following is the Paint event
handler of a form.

void Form1_Paint(object sender, PaintEventArgs e)
       {
           Point[] points = new Point[3];
           points[0]= new Point(30,40);
           points[1] = new Point(50,30);
           points[2] = new Point(20,60);        
           // draw the lines with a pen of size 10
           using (Pen p = new Pen(Color.Black,10))
           {
               e.Graphics.DrawLines(p,points);
           }
           // draw the lines with a pen of size 1
           using (Pen p = new Pen(Color.White, 1))
           {
               e.Graphics.DrawLines(p, points);
           }
       }

Run the application and you should see the conjunction of the two lines
drawn by the black color is larger than that drawn by the white color.

Due to this characteristic of the DrawLines method, I suggest that you use
DrawLine method instead.

Hope I make some clarifications.

If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
mfr - 30 May 2007 09:45 GMT
Linda,

Thank you for your reply. However a collegue of mine has found the solution.

Setting the pen's lineJoin property as below fixes the problem.

penCyan.LineJoin = System.Drawing.Drawing2D.LineJoin.MiterClipped;

Regards,
Dave.

> Hi Dave,
>
[quoted text clipped - 45 lines]
> Linda Liu
> Microsoft Online Community Support
Linda Liu [MSFT] - 30 May 2007 10:26 GMT
Hi Dave,

Thank you for your feedback on how you successfully solved the problem!

Sorry that I wasn't aware of the LineJoin property of the Pen class.

I perform a test setting the LineJoin property of the penCyan to
MiterClipped and see it works. I also find it works when I set this
property to Bevel or Round.

If you have any other questions in the future, please don't hesitate to
contact us.

Sincerely,
Linda Liu
Microsoft Online Community Support
mfr - 30 May 2007 10:46 GMT
As a point to note - the HELP for the line join property is incorrect.

https://msdn2.microsoft.com/en-us/library/system.drawing.drawing2d.linejoin.aspx

The description for the Miter incorrectly says

"Specifies a mitered join. This produces a sharp corner or a clipped corner,
depending on whether the length of the miter exceeds the miter limit."

whereas it should read, something like.....

"Specifies a mitered join. This produces a sharp corner."

Regards,
Dave.

> Hi Dave,
>
[quoted text clipped - 12 lines]
> Linda Liu
> Microsoft Online Community Support
Linda Liu [MSFT] - 31 May 2007 10:42 GMT
Hi Dave,

IMHO, I don't think the description for the Miter enumeration value in MSDN
document is incorrect.

The document says that this produces a sharp corner or a clipped corner,
DEPENDING on whether the lengh of the miter exceeds the miter limit.

Nevertheless, it is always safe to use the Bevel enumeration value, which
always reproduces a beveled join.

Sincerely,
Linda Liu
Microsoft Online Community Support

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.