Dryad/Calypso/Tools/drawingSurface.cs

757 lines
30 KiB
C#

/*
Copyright (c) Microsoft Corporation
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License
at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
namespace Microsoft.Research.Calypso.Tools
{
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
/// <summary>
/// A 2D drawing surface with FP coordinates.
/// </summary>
public class DrawingSurface2D
{
/// <summary>
/// Panel where the drawing is done.
/// </summary>
Panel drawingpanel;
/// <summary>
/// Graphics context used for drawing.
/// </summary>
Graphics grph;
/// <summary>
/// Screen graphics context (used for some transient pictures).
/// </summary>
Graphics screenGrph;
/// <summary>
/// Margins around the axes of the drawing area, in pixels.
/// </summary>
public int rightMargin, leftMargin; // public because other areas need to know about them
/// <summary>
/// Drawing area border coordinates, must be set before drawing anything.
/// </summary>
Rectangle2D boundingBox;
/// <summary>
/// Double-buffering support; by setting this to null in the constructor you can disable double-buffering.
/// </summary>
Bitmap buffer;
/// <summary>
/// Old size of canvas, before resizing.
/// </summary>
Size oldsize;
/// <summary>
/// Width of drawing area on screen in pixels, excluding margins.
/// </summary>
private int drawingAreaWidth;
/// <summary>
/// Height of drawing area on screen in pixels, excluding margins.
/// </summary>
private int drawingAreaHeight;
/// <summary>
/// Scaling coefficient on x (from drawing to screen coordinates).
/// </summary>
private double xScale;
/// <summary>
/// Scaling coefficient on y (from drawing to screen coordinates).
/// </summary>
private double yScale;
/// <summary>
/// Width of the drawing area.
/// </summary>
public double Width { get; protected set; }
/// <summary>
/// Height of the drawing area.
/// </summary>
public double Height { get; protected set; }
/// <summary>
/// Instantiate a 2D drawing surface.
/// </summary>
/// <param name="panel">Panel where the image ends up being drawn.</param>
public DrawingSurface2D(Panel panel)
{
// Comment-out the assignment to buffer to disable double-buffering.
buffer = new Bitmap(panel.Width, panel.Height);
this.drawingpanel = panel;
screenGrph = this.drawingpanel.CreateGraphics();
if (buffer != null)
grph = Graphics.FromImage(buffer);
else
grph = screenGrph;
this.oldsize = panel.Size;
grph.SmoothingMode = SmoothingMode.AntiAlias;
this.RecomputeCachedValues();
}
/// <summary>
/// Things may have changed, recompute some cached values.
/// </summary>
private void RecomputeCachedValues()
{
this.drawingAreaWidth = this.drawingpanel.Size.Width - this.leftMargin - this.rightMargin;
this.drawingAreaHeight = this.drawingpanel.Size.Height - this.TopMargin - this.BottomMargin;
if (this.boundingBox != null)
{
this.Width = this.xDrawMax - this.xDrawMin;
this.Height = this.yDrawMax - this.yDrawMin;
this.yScale = this.drawingAreaHeight / this.Height;
this.xScale = this.drawingAreaWidth / this.Width;
}
}
/// <summary>
/// Set the margins in pixels around the area which is mapped to.
/// </summary>
/// <param name="top">Distance in pixels from top of panel to top of drawing area.</param>
/// <param name="bottom">Distance in pixels from bottom of panel to bottom of drawing area.</param>
/// <param name="right">Distance in pixels from right of panel to right of drawing area.</param>
/// <param name="left">Distance in pixels from left of panel to left of drawing area.</param>
public void SetMargins(int top, int bottom, int right, int left)
{
this.TopMargin = top;
this.BottomMargin = bottom;
this.rightMargin = right;
this.leftMargin = left;
this.RecomputeCachedValues();
}
private bool fastDrawing;
/// <summary>
/// If true try to draw faster.
/// </summary>
public bool FastDrawing
{
get
{
return this.fastDrawing;
}
set
{
this.fastDrawing = value;
if (!value)
this.grph.SmoothingMode = SmoothingMode.AntiAlias;
else
this.grph.SmoothingMode = SmoothingMode.HighSpeed;
}
}
/// <summary>
/// How big is the rectangle containing a string in this window?
/// </summary>
/// <param name="text">String to measure.</param>
/// <param name="font">Font to draw the text in.</param>
/// <returns>The size of a rectangle in pixels.</returns>
public Size MeasureString(string text, Font font)
{
return this.grph.MeasureString(text, font).ToSize();
}
/// <summary>
/// The bounding box of the drawing area.
/// </summary>
public Rectangle2D BoundingBox { get { return this.boundingBox; } }
/// <summary>
/// The size of the drawing area in pixels.
/// </summary>
/// <returns>Size of the drawing area for plots (excluding margins).</returns>
public Size DrawingAreaSize()
{
return new Size(this.drawingAreaWidth, this.drawingAreaHeight);
}
/// <summary>
/// Maximum X coordinate of drawing area.
/// </summary>
public double xDrawMax { get { return boundingBox.Corner2.X; } }
/// <summary>
/// Maximum Y coordinate of drawing area.
/// </summary>
public double yDrawMax { get { return boundingBox.Corner2.Y; } }
/// <summary>
/// Minimum X coordinate of drawing area.
/// </summary>
public double xDrawMin { get { return boundingBox.Corner1.X; } }
/// <summary>
/// Minimum Y coordinate of drawing area.
/// </summary>
public double yDrawMin { get { return boundingBox.Corner1.Y; } }
/// <summary>
/// Number of pixels above graphics plot.
/// </summary>
public int TopMargin { get; protected set; }
/// <summary>
/// Number of pixels below graphics plot.
/// </summary>
public int BottomMargin { get; protected set; }
/// <summary>
/// Size in pixels of the drawing panel including all margins.
/// </summary>
public Size SurfaceSize { get { return this.drawingpanel.Size; } }
/// <summary>
/// Before drawing anything one has to set the drawing area size.
/// This maps the four specified coordinates to the corners of the drawing panel.
/// </summary>
/// <param name="bbox">Perimeter of area.</param>
public void SetDrawingArea(Rectangle2D bbox)
{
if (bbox.Degenerate())
throw new ArgumentException("Cannot draw in a degenerate bounding box");
this.boundingBox = bbox;
this.RecomputeCachedValues();
}
/// <summary>
/// Scale one point position and compute actual panel pixel coordinates.
/// </summary>
/// <param name="x">X coordinate to scale relative to the drawing area.</param>
/// <param name="y">Y coordinate to scale relative to the drawing area.</param>
/// <param name="xo">Resulting X coordinate after scaling (in panel coordinates).</param>
/// <param name="yo">Resulting Y coordinate after scaling (in panel coordinates).</param>
public void Scale(double x, double y, out int xo, out int yo)
{
double ynew = (y - this.yDrawMin) * this.yScale;
double xnew = (x - this.xDrawMin) * this.xScale;
xo = this.leftMargin + (int)xnew;
yo = this.TopMargin + this.drawingAreaHeight - (int)ynew;
}
/// <summary>
/// Scale one point position and compute actual panel pixel coordinates.
/// </summary>
/// <param name="point">Point to scale.</param>
/// <param name="xo">Resulting X coordinate after scaling (in panel coordinates).</param>
/// <param name="yo">Resulting Y coordinate after scaling (in panel coordinates).</param>
public void Scale(Point2D point, out int xo, out int yo)
{
this.Scale(point.X, point.Y, out xo, out yo);
}
/// <summary>
/// Scale one point position and compute actual panel pixel coordinates.
/// </summary>
/// <param name="point">Point to scale.</param>
/// <returns>Point containing pixel coordinates.</returns>
public Point Scale(Point2D point)
{
int xo, yo;
this.Scale(point.X, point.Y, out xo, out yo);
return new Point(xo, yo);
}
/// <summary>
/// Convert a line length on the screen to user coordinates.
/// </summary>
/// <param name="line">Line length.</param>
/// <returns>The plotting coordinates.</returns>
public Point2D LengthScreenToUser(Point line)
{
Point2D res = new Point2D(line.X / this.xScale, line.Y / this.yScale);
return res;
}
/// <summary>
/// Invert the scaling operation: convert from screen coordinates to plotting coordinates.
/// </summary>
/// <param name="x">X coordinate in pixels in window.</param>
/// <param name="y">Y coordinate in pixels in window.</param>
/// <param name="xo">Original x coordinate, in drawing area.</param>
/// <param name="yo">Original y coordinate, in drawing area.</param>
public void ScreenToUser(int x, int y, out double xo, out double yo)
{
// remove margins and reflect
x -= this.leftMargin;
y = this.TopMargin + this.drawingAreaHeight - y;
yo = y / this.yScale + this.yDrawMin;
xo = x / this.xScale + this.xDrawMin;
}
/// <summary>
/// Convert a dimension in screen coordinates to a dimension in plotting coordinates.
/// </summary>
/// <param name="size">Size to convert.</param>
/// <returns>The result is really a size, encoded as a Point2D.</returns>
public Point2D Unscale(Point2D size)
{
return new Point2D(size.X / this.xScale, size.Y / this.yScale);
}
/// <summary>
/// The graphics context used for drawing.
/// </summary>
/// <param name="onscreen">If true returns the screen graphics context, else the backing buffer.</param>
/// <returns>The graphics context used for drawing.</returns>
internal Graphics GetGraphics(bool onscreen)
{
return onscreen ? this.screenGrph : this.grph;
}
/// <summary>
/// Draw a line in screen coordinates.
/// </summary>
/// <param name="c">Line color.</param>
/// <param name="left">Left endpoint.</param>
/// <param name="right">Right endpoint.</param>
/// <param name="lineWidth">Line width.</param>
/// <param name="arrow">Cap line with arrow?</param>
/// <param name="onscreen">Draw the line on the screen?</param>
public void DrawLine(Color c, Point left, Point right, int lineWidth, bool arrow, bool onscreen)
{
System.Drawing.Pen linePen = new System.Drawing.Pen(c);
linePen.Width = lineWidth;
if (arrow)
{
linePen.EndCap = LineCap.ArrowAnchor;
}
this.DrawLine(linePen, left, right, onscreen);
}
/// <summary>
/// Draw a line in screen coordinates.
/// </summary>
/// <param name="pen">Pen used to draw the line.</param>
/// <param name="left">Left endpoint.</param>
/// <param name="right">Right endpoint.</param>
/// <param name="onscreen">Draw the line on the screen?</param>
public void DrawLine(Pen pen, Point left, Point right, bool onscreen)
{
Graphics g = this.GetGraphics(onscreen);
g.DrawLine(pen, left, right);
}
/// <summary>
/// Draw a line in draw coordinates between two given endpoints.
/// </summary>
/// <param name="pen">Pen to use.</param>
/// <param name="left">Left endpoint, relative to the DrawingArea.</param>
/// <param name="right">Right endpoint, relative to the DrawingArea.</param>
/// <param name="onscreen">Should the drawing be made just on screen (transient),
/// or on the backing storage (persistent)?</param>
/// <returns>True if line is visible.</returns>
public void DrawLine(Pen pen, Point2D left, Point2D right, bool onscreen)
{
if (!Visible(left, right))
return;
Point l = Scale(left);
Point r = Scale(right);
this.DrawLine(pen, l, r, onscreen);
return;
}
/// <summary>
/// Draw a rectangle in screen coordinates.
/// <param name="left">Left corner.</param>
/// <param name="right">Right corner.</param>
/// <param name="pen">Pen to draw.</param>
/// </summary>
public void DrawRectangle(Point left, Point right, Pen pen)
{
this.grph.DrawRectangle(pen, left.X, left.Y, right.X - left.X, right.Y - left.Y);
}
/// <summary>
/// Check if a point is visible.
/// </summary>
/// <param name="x">Point x coordinate (unscaled).</param>
/// <param name="y">Point y coordinate (unscaled).</param>
/// <returns>True if the point is visible.</returns>
public bool Visible(double x, double y)
{
if (x < this.xDrawMin || x > this.xDrawMax)
return false;
if (y < this.yDrawMin || y > this.yDrawMax)
return false;
return true;
}
/// <summary>
/// True if a point is visible on canvas.
/// </summary>
/// <param name="p">Point to test.</param>
/// <returns>True if point is visible.</returns>
public bool Visible(Point2D p)
{
return this.Visible(p.X, p.Y);
}
/// <summary>
/// Is any of these two points visible?
/// </summary>
/// <param name="left">Left endpoint.</param>
/// <param name="right">Right endpoint.</param>
/// <returns>True if any endpoint is on the screen.</returns>
private bool Visible(Point2D left, Point2D right)
{
return this.Visible(left) || this.Visible(right);
}
/// <summary>
/// Draw a filled rectangle with the given color in the drawing area.
/// </summary>
/// <param name="b">Brush to use for filling.</param>
/// <param name="rect">Rectangle, relative to the DrawingArea.</param>
public void FillRectangle(Brush b, Rectangle2D rect)
{
Rectangle scaledRect = this.Scale(rect);
this.grph.FillRectangle(b, scaledRect);
}
/// <summary>
/// Draw a rectangle with the given color in the drawing area.
/// </summary>
/// <param name="rect">Rectangle, relative to the DrawingArea.</param>
/// <param name="pen">Pen used to draw rectangle.</param>
public void DrawRectangle(Rectangle2D rect, Pen pen)
{
Rectangle scaledRect = this.Scale(rect);
this.grph.DrawRectangle(pen, scaledRect);
}
/// <summary>
/// Draw a rectangle filled with a specific brush.
/// </summary>
/// <param name="b">Brush to use to fill.</param>
/// <param name="rect">Rectangle to draw.</param>
public void DrawRectangle(Brush b, Rectangle2D rect)
{
Rectangle scaledRect = this.Scale(rect);
this.grph.FillRectangle(b, scaledRect);
}
/// <summary>
/// Draw a rectangle with the given color in the drawing area.
/// </summary>
/// <param name="c">Color to use.</param>
/// <param name="vertices">Polygon vertices relative to the DrawingArea.</param>
/// <param name="filled">Fill the polygon with the given color.</param>
/// <param name="pen">Pen used to draw polygon.</param>
public void DrawPolygon(Color c, Point2D [] vertices, Pen pen, bool filled)
{
Point[] points = new Point[vertices.Length];
for (int i = 0; i < vertices.Length; i++)
{
points[i] = this.Scale(vertices[i]);
}
if (filled)
{
Brush b = new System.Drawing.SolidBrush(c);
this.grph.FillPolygon(b, points);
}
else
{
this.grph.DrawPolygon(pen, points);
}
}
/// <summary>
/// Write some text in the drawing area, in absolute coordinates.
/// </summary>
/// <param name="c">Text color.</param>
/// <param name="text">Text string.</param>
/// <param name="font">Font used to draw text.</param>
/// <param name="x">Upper-left corner x coordinate, in pixels.</param>
/// <param name="y">Upper-left corner y coordinate, in pixels.</param>
public void DrawTextAbsoluteCoordinates(Color c, Font font, string text, int x, int y)
{
this.grph.DrawString(text, font, new System.Drawing.SolidBrush(c), x, y);
}
/// <summary>
/// Draw text rotated with 90 degrees.
/// </summary>
/// <param name="c">Text color.</param>
/// <param name="text">Text to draw.</param>
/// <param name="font">Font to use for text.</param>
/// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param>
public void DrawRotatedTextAbsoluteCoordinates(Color c, string text, Font font, int x, int y)
{
// bring the origin in the center
this.grph.TranslateTransform(x, y);
this.grph.RotateTransform(-90);
this.grph.TranslateTransform(-x, -y);
this.grph.DrawString(text, font, new System.Drawing.SolidBrush(c), x, y);
this.grph.ResetTransform();
}
/// <summary>
/// Draw text rotated with 90 degrees, right-justified.
/// </summary>
/// <param name="text">Text to draw.</param>
/// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param>
/// <param name="brush">Brush to use for text.</param>
/// <param name="font">Font to use for text.</param>
/// <param name="angleDegrees">Angle of rotation in degrees.</param>
public void DrawRotatedRightJustifiedTextAbsoluteCoordinates(string text, Font font, Brush brush, int x, int y, int angleDegrees)
{
// bring the origin in the center
this.grph.TranslateTransform(x, y);
this.grph.RotateTransform(angleDegrees);
this.grph.TranslateTransform(-x, -y);
Size textsize = this.MeasureString(text, font);
Rectangle box = new Rectangle(x - textsize.Width, y, textsize.Width + 2, textsize.Height);
this.grph.DrawString(text, font, brush, box, new StringFormat());
this.grph.ResetTransform();
}
/// <summary>
/// Write some text in the drawing area, in absolute coordinates.
/// </summary>
/// <param name="text">Text to write.</param>
/// <param name="brush">Brush to use for text.</param>
/// <param name="font">Font to use for text.</param>
/// <param name="x">Upper-right corner x coordinate, in pixels.</param>
/// <param name="y">y coordinate of center, in pixels.</param>
public void DrawRightJustifiedTextAbsoluteCoordinates(string text, Font font, Brush brush, int x, int y)
{
Size textsize = this.MeasureString(text, font);
Rectangle box = new Rectangle(x - textsize.Width, y - textsize.Height / 2, textsize.Width + 2, textsize.Height);
this.grph.DrawString(text, font, brush, box, new StringFormat());
}
/// <summary>
/// Chose an appropriate font to fit the text in a given box.
/// </summary>
/// <param name="graph">Graphics context where the string will be drawn.</param>
/// <param name="baseFont">Font to enlarge/shrink.</param>
/// <param name="spaceAvailable">How much space is available for the string, in pixels.</param>
/// <param name="text">String to write.</param>
/// <returns>A suitably-sized font.</returns>
public static Font ChooseFont(Graphics graph, Font baseFont, Size spaceAvailable, string text)
{
SizeF size = graph.MeasureString(text, baseFont);
double scaling = Math.Min(spaceAvailable.Width / size.Width, spaceAvailable.Height / size.Height);
double newSize = baseFont.Size * scaling;
if (newSize < 2)
// too small
return null;
Font retval = new Font(baseFont.FontFamily, (float)newSize, baseFont.Style);
return retval;
}
/// <summary>
/// Draw a one-line string to fit in the specified rectangle.
/// </summary>
/// <param name="text">Text to draw.</param>
/// <param name="baseFont">Start from this font, but resize it.</param>
/// <param name="brush">Brush to use for drawing.</param>
/// <param name="place">Where to draw it.</param>
public void DrawTextInRectangle(string text, Brush brush, Font baseFont, Rectangle2D place)
{
Rectangle dest = this.Scale(place);
Font font = DrawingSurface2D.ChooseFont(this.grph, baseFont, dest.Size, text);
if (font == null || font.Size < 3)
// too small anyway
return;
SizeF actualSize = this.grph.MeasureString(text, font);
int adjustX = (int)(dest.Width - actualSize.Width) / 2;
int adjustY = dest.Height / 2;
this.DrawRightJustifiedTextAbsoluteCoordinates(text, font, brush, dest.Right - adjustX, dest.Top + adjustY);
}
/// <summary>
/// Write some text in the drawing area. Font is not scaled.
/// </summary>
/// <param name="text">Text to write.</param>
/// <param name="brush">Brush to use for text.</param>
/// <param name="font">Font to use for text.</param>
/// <param name="x">Upper-right corner x coordinate.</param>
/// <param name="y">Upper-right corner y coordinate.</param>
public void DrawRightJustifiedText(string text, Font font, Brush brush, double x, double y)
{
int xo, yo;
this.Scale(x, y, out xo, out yo);
Size textsize = this.MeasureString(text, font);
Rectangle box = new Rectangle(xo - textsize.Width, yo, textsize.Width + 2, textsize.Height);
this.grph.DrawString(text, font, brush, box, new StringFormat());
}
/// <summary>
/// Draw a point in screen pixel coordinates (really a small circle).
/// </summary>
/// <param name="brush">Brush used to draw the point.</param>
/// <param name="x">Point x coordinate.</param>
/// <param name="y">Point y coordinate.</param>
/// <param name="dotSize">Size of dot to draw.</param>
/// <param name="onscreen">If true draw the point directly on the screen, else on the backing buffer.</param>
public void DrawPointAbsoluteCoordinates(Brush brush, int x, int y, int dotSize, bool onscreen)
{
Graphics g = this.GetGraphics(onscreen);
g.FillEllipse(brush, x - dotSize / 2, y - dotSize / 2, dotSize, dotSize);
}
/// <summary>
/// Draw a "fat" point in the drawing area.
/// </summary>
/// <param name="brush">Brush used to draw point.</param>
/// <param name="x">Point x coordinate.</param>
/// <param name="y">Point y coordinate.</param>
/// <param name="dotSize">Size of dot to draw.</param>
/// <param name="onscreen">If true draw the point directly on screen, otherwise to the backing buffer.</param>
/// <returns>True if point is visible.</returns>
public bool DrawPoint(Brush brush, double x, double y, int dotSize, bool onscreen)
{
if (!this.Visible(x, y))
return false;
int x1, y1;
this.Scale(x, y, out x1, out y1);
this.DrawPointAbsoluteCoordinates(brush, x1, y1, dotSize, onscreen);
return true;
}
/// <summary>
/// Clear the drawing area.
/// </summary>
public void Clear()
{
this.grph.Clear(drawingpanel.BackColor);
}
/// <summary>
/// Invoked when the panel has been resized.
/// <returns>True if the size has changed and is not zero, false otherwise.</returns>
/// </summary>
public bool Resize()
{
if (this.oldsize == this.drawingpanel.Size)
return false;
if (this.drawingpanel.Size.Width == 0 &&
this.drawingpanel.Size.Height == 0)
// window has been minimized; do nothing
return false;
this.oldsize = this.drawingpanel.Size;
this.RecomputeCachedValues();
if (this.screenGrph != null)
this.screenGrph.Dispose();
this.screenGrph = this.drawingpanel.CreateGraphics();
if (this.buffer != null)
{
if (this.grph != null)
this.grph.Dispose();
if (this.drawingpanel.Width != 0 &&
this.drawingpanel.Height != 0)
{
// when minimizing the window the size of the panel can be 0
this.buffer.Dispose();
this.buffer = new Bitmap(this.drawingpanel.Width, this.drawingpanel.Height);
this.grph = Graphics.FromImage(buffer);
}
}
else
this.grph = this.screenGrph;
this.grph.SmoothingMode = SmoothingMode.AntiAlias;
return true;
}
/// <summary>
/// Paint the graphics area from the bitmap.
/// </summary>
public void Repaint(PaintEventArgs e)
{
e.Graphics.DrawImageUnscaled(buffer, 0, 0);
}
/// <summary>
/// Scale and normalize a rectangle.
/// </summary>
/// <param name="rectangle">Rectangle to normalize.</param>
/// <returns>Corresponding rectangle in pixels.</returns>
private Rectangle Scale(Rectangle2D rectangle)
{
int xl1, yl1, xr1, yr1;
this.Scale(rectangle.Corner1, out xl1, out yl1);
this.Scale(rectangle.Corner2, out xr1, out yr1);
int deltax = xr1 - xl1;
int deltay = yr1 - yl1;
if (deltax < 0)
{
xl1 += deltax;
deltax = -deltax;
}
if (deltax == 0)
{
// zero is not shown, make it at least 1.
deltax = 1;
}
if (deltay < 0)
{
yl1 += deltay;
deltay = -deltay;
}
if (deltay == 0)
{
// zero is not shown, make it at least 1.
deltay = 1;
}
Rectangle retval = new Rectangle(new Point(xl1, yl1), new Size(deltax, deltay));
return retval;
}
/// <summary>
/// Draw an ellipse.
/// </summary>
/// <param name="color">Fill color.</param>
/// <param name="rect">Rectangle enclosing ellipse.</param>
/// <param name="pen">Pen to use.</param>
/// <param name="filled">If true, fill the ellipse.</param>
internal void DrawEllipse(Color color, Rectangle2D rect, Pen pen, bool filled)
{
Rectangle scaledRect = this.Scale(rect);
if (filled)
{
Brush b = new System.Drawing.SolidBrush(color);
this.grph.FillEllipse(b, scaledRect);
}
else
{
this.grph.DrawEllipse(pen, scaledRect);
}
}
/// <summary>
/// The image plotted.
/// </summary>
/// <returns>A bitmap.</returns>
public Bitmap PlottedImage()
{
return this.buffer;
}
}
}