26 using System.Collections.Generic;
28 using System.Xml.Linq;
30 using System.Drawing.Drawing2D;
31 using System.Drawing.Imaging;
33 namespace Satsuma.Drawing
42 void Draw(Graphics graphics, Pen pen, Brush brush);
45 PointF GetBoundary(
double angle);
60 public PointF Size {
get;
private set; }
62 private readonly RectangleF rect;
63 private readonly PointF[] points;
70 rect =
new RectangleF(-size.X * 0.5f, -size.Y * 0.5f, size.X, size.Y);
73 case NodeShapeKind.Diamond: points =
new PointF[] { P(rect, 0, 0.5f), P(rect, 0.5f, 1),
74 P(rect, 1, 0.5f), P(rect, 0.5f, 0) };
break;
75 case NodeShapeKind.Rectangle: points =
new PointF[] { rect.Location,
new PointF(rect.Left, rect.Bottom),
76 new PointF(rect.Right, rect.Bottom),
new PointF(rect.Right, rect.Top) };
break;
77 case NodeShapeKind.Triangle: points =
new PointF[] { P(rect, 0.5f, 0), P(rect, 0, 1),
78 P(rect, 1, 1) };
break;
79 case NodeShapeKind.UpsideDownTriangle: points =
new PointF[] { P(rect, 0.5f, 1), P(rect, 0, 0),
80 P(rect, 1, 0) };
break;
84 private static PointF P(RectangleF rect,
float x,
float y)
86 return new PointF(rect.Left + rect.Width * x, rect.Top + rect.Height * y);
89 public void Draw(Graphics graphics, Pen pen, Brush brush)
94 graphics.FillEllipse(brush, rect);
95 graphics.DrawEllipse(pen, rect);
99 graphics.FillPolygon(brush, points);
100 graphics.DrawPolygon(pen, points);
105 public PointF GetBoundary(
double angle)
107 double cos = Math.Cos(angle), sin = Math.Sin(angle);
111 return new PointF((
float)(Size.X * 0.5f * cos), (
float)(Size.Y * 0.5f * sin));
115 for (
int i = 0; i < points.Length; i++)
117 int i2 = (i + 1) % points.Length;
118 float t = (
float)((points[i].Y * cos - points[i].X * sin) /
119 ((points[i2].X - points[i].X) * sin - (points[i2].Y - points[i].Y) * cos));
120 if (t >= 0 && t <= 1)
122 var result =
new PointF(points[i].X + t * (points[i2].X - points[i].X),
123 points[i].Y + t * (points[i2].Y - points[i].Y));
124 if (result.X * cos + result.Y * sin > 0)
return result;
127 return new PointF(0, 0);
137 public Pen Pen {
get;
set; }
140 public Brush Brush {
get;
set; }
146 public Font TextFont {
get;
set; }
149 public Brush TextBrush {
get;
set; }
157 Brush = Brushes.White;
158 Shape = DefaultShape;
159 TextFont = SystemFonts.DefaultFont;
160 TextBrush = Brushes.Black;
163 internal void DrawNode(Graphics graphics,
float x,
float y,
string text)
165 var state = graphics.Save();
166 graphics.TranslateTransform(x, y);
167 Shape.Draw(graphics, Pen, Brush);
169 graphics.DrawString(text, TextFont, TextBrush, 0, 0,
170 new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
171 graphics.Restore(state);
200 public Func<Node, PointF> NodePosition {
get;
set; }
203 public Func<Node, string> NodeCaption {
get;
set; }
212 public Func<Arc, Pen> ArcPen {
get;
set; }
216 public Pen DirectedPen {
get;
set; }
220 public Pen UndirectedPen {
get;
set; }
224 NodeCaption = (node =>
"");
227 ArcPen = (arc => Graph.IsEdge(arc) ? UndirectedPen : DirectedPen);
228 DirectedPen =
new Pen(Color.Black) { CustomEndCap =
new AdjustableArrowCap(3, 5) };
229 UndirectedPen = Pens.Black;
236 public void Draw(Graphics graphics, Matrix matrix = null)
239 PointF[] arcPos =
new PointF[2];
240 PointF[] boundary =
new PointF[2];
241 foreach (var arc
in Graph.Arcs())
243 Node u = Graph.U(arc);
244 arcPos[0] = NodePosition(u);
245 Node v = Graph.V(arc);
246 arcPos[1] = NodePosition(v);
247 if (matrix != null) matrix.TransformPoints(arcPos);
250 double angle = Math.Atan2(arcPos[1].Y - arcPos[0].Y, arcPos[1].X - arcPos[0].X);
254 graphics.DrawLine(ArcPen(arc), arcPos[0].X + boundary[0].X, arcPos[0].Y + boundary[0].Y,
255 arcPos[1].X + boundary[1].X, arcPos[1].Y + boundary[1].Y);
259 PointF[] nodePos =
new PointF[1];
260 foreach (var node
in Graph.Nodes())
262 nodePos[0] = NodePosition(node);
263 if (matrix != null) matrix.TransformPoints(nodePos);
264 NodeStyle(node).DrawNode(graphics, nodePos[0].X, nodePos[0].Y, NodeCaption(node));
270 public void Draw(Graphics graphics, RectangleF box)
272 if (!Graph.Nodes().Any())
return;
274 float maxShapeWidth = 0, maxShapeHeight = 0;
275 float xmin =
float.PositiveInfinity, ymin =
float.PositiveInfinity;
276 float xmax =
float.NegativeInfinity, ymax =
float.NegativeInfinity;
277 foreach (var node
in Graph.Nodes())
280 maxShapeWidth = Math.Max(maxShapeWidth, size.X);
281 maxShapeHeight = Math.Max(maxShapeHeight, size.Y);
283 PointF pos = NodePosition(node);
284 xmin = Math.Min(xmin, pos.X);
285 xmax = Math.Max(xmax, pos.X);
286 ymin = Math.Min(ymin, pos.Y);
287 ymax = Math.Max(ymax, pos.Y);
290 float xspan = xmax - xmin;
291 if (xspan == 0) xspan = 1;
292 float yspan = ymax - ymin;
293 if (yspan == 0) yspan = 1;
295 Matrix matrix =
new Matrix();
296 matrix.Translate(maxShapeWidth*0.6f, maxShapeHeight*0.6f);
297 matrix.Scale((box.Width - maxShapeWidth * 1.2f) / xspan, (box.Height - maxShapeHeight * 1.2f) / yspan);
298 matrix.Translate(-xmin, -ymin);
299 Draw(graphics, matrix);
308 public Bitmap Draw(
int width,
int height, Color backColor,
309 bool antialias =
true, PixelFormat pixelFormat = PixelFormat.Format32bppArgb)
311 Bitmap bm =
new Bitmap(width, height, pixelFormat);
312 using (var g = Graphics.FromImage(bm))
314 g.SmoothingMode = antialias ? SmoothingMode.AntiAlias : SmoothingMode.None;
316 Draw(g,
new RectangleF(0, 0, width, height));