SvgShapes.cs
- //
- // This code is part of Document Solutions for Imaging demos.
- // Copyright (c) MESCIUS inc. All rights reserved.
- //
- using System;
- using System.IO;
- using System.Drawing;
- using System.Collections.Generic;
- using System.Linq;
- using System.Numerics;
- using GrapeCity.Documents.Drawing;
- using GrapeCity.Documents.Text;
- using GrapeCity.Documents.Imaging;
- using GrapeCity.Documents.Svg;
- using GCTEXT = GrapeCity.Documents.Text;
- using GCDRAW = GrapeCity.Documents.Drawing;
- using DsImagingWeb.Demos.Common;
-
- namespace DsImagingWeb.Demos
- {
- // This sample draws some geometric shapes using GcSvgDocument and GcGraphics.DrawSvg().
- // The code and the results are similar to the Shapes sample.
- public class SvgShapes
- {
- // Helper method to draw a polygon and a caption beneath it.
- // Can also be used to just calculate the points without actual drawing.
- // startAngle is for the first point, clockwise from (1,0).
- // Points are relative to 'center'.
- private SvgPoint[] DrawPolygon(GcGraphics g, PointF center, float r, int n, float startAngle, SvgPaint stroke, string caption = null)
- {
- const float Q = 1.4f;
- SvgPolygonElement plgn = new SvgPolygonElement();
- plgn.Points = new List<SvgPoint>(n);
- for (int i = 0; i < n; ++i)
- plgn.Points.Add(new SvgPoint(
- new SvgLength((float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)) * Q, SvgLengthUnits.Percentage),
- new SvgLength((float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)) * Q, SvgLengthUnits.Percentage)));
-
- if (stroke != null)
- {
- plgn.Stroke = stroke;
- plgn.Fill = new SvgPaint(SvgColor.Transparent);
- var svgDoc = new GcSvgDocument();
- var svg = svgDoc.RootSvg;
- svg.Children.Add(plgn);
- g.DrawSvg(svgDoc, new RectangleF(center.X, center.Y, r * 2, r * 2));
- }
-
- if (!string.IsNullOrEmpty(caption))
- DrawCaption(g, center, r, caption);
-
- return plgn.Points.ToArray();
- }
- // Helper method to draw a caption beneath a shape:
- private void DrawCaption(GcGraphics g, PointF center, float r, string caption)
- {
- g.DrawString(caption,
- new TextFormat() { FontSize = 10, },
- new RectangleF(center.X - r, center.Y + r + 24, r * 2, 24),
- TextAlignment.Center, ParagraphAlignment.Center, false);
- }
- // Main entry point.
- public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
- {
- var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
- using var g = bmp.CreateGraphics(Color.White);
- // Document header:
- g.DrawString("Shapes (SVG)",
- new TextFormat() { FontSize = 14, Underline = true, },
- new RectangleF(PointF.Empty, new SizeF(pixelSize.Width, 44)),
- TextAlignment.Center, ParagraphAlignment.Far);
- // Pen used to draw shapes:
-
- // Set up the helper layout grid:
- var grid = new
- {
- Cols = 3,
- Rows = 5,
- MarginX = dpi,
- MarginY = dpi / 2,
- Radius = dpi / 2,
- StepX = (pixelSize.Width - dpi * 2) / 3,
- StepY = (pixelSize.Height - dpi) / 5,
- };
-
- // Insertion point of the next figure's center:
- PointF startIp = new PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10);
- PointF ip = startIp;
- // Debug code to show the layout grid:
- /*
- var ipp = ip;
- for (int i = 0; i < grid.Cols; ++i)
- {
- ipp.Y = ip.Y;
- for (int j = 0; j < grid.Rows; ++j)
- {
- g.DrawRectangle(new RectangleF(ipp.X - grid.Radius, ipp.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), Color.LightGreen, 0.5f);
- ipp.Y += grid.StepY;
- }
- ipp.X += grid.StepX;
- }
- */
-
- // Reusable SVG primitives:
- var len0 = new SvgLength(0, SvgLengthUnits.Percentage);
- var len10 = new SvgLength(10, SvgLengthUnits.Percentage);
- var len20 = new SvgLength(20, SvgLengthUnits.Percentage);
- var len30 = new SvgLength(30, SvgLengthUnits.Percentage);
- var len40 = new SvgLength(40, SvgLengthUnits.Percentage);
- var len50 = new SvgLength(50, SvgLengthUnits.Percentage);
- var len60 = new SvgLength(60, SvgLengthUnits.Percentage);
- var len70 = new SvgLength(70, SvgLengthUnits.Percentage);
- var len80 = new SvgLength(80, SvgLengthUnits.Percentage);
- var len90 = new SvgLength(90, SvgLengthUnits.Percentage);
- var len100 = new SvgLength(100, SvgLengthUnits.Percentage);
- var paintTrans = new SvgPaint(SvgColor.Transparent);
- var pntStroke0 = new SvgPaint(new SvgColor(Color.Green));
- var pntStroke0inv = new SvgPaint(new SvgColor(Color.FromArgb(100, Color.Green)));
- var pntFill0 = new SvgPaint(new SvgColor(Color.FromArgb(100, Color.Orange)));
- var pt0_0 = new SvgPoint(len0, len0);
- var pt100_100 = new SvgPoint(len100, len100);
-
- // Circle:
- var svgDoc = new GcSvgDocument();
- var svg = svgDoc.RootSvg;
- svg.Children.Add(new SvgEllipseElement()
- {
- CenterX = len50,
- CenterY = len50,
- RadiusX = len50,
- RadiusY = len50,
- Fill = paintTrans,
- Stroke = pntStroke0,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
- DrawCaption(g, ip, grid.Radius, "Circle");
- ip.X += grid.StepX;
-
- // Ellipse:
- svg.Children.Clear();
- svg.Children.Add(new SvgEllipseElement()
- {
- CenterX = len50,
- CenterY = len50,
- RadiusX = len50,
- RadiusY = len50,
- Fill = paintTrans,
- Stroke = pntStroke0,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius * 1.4f, ip.Y - grid.Radius / 2, grid.Radius * 2 * 1.4f, grid.Radius));
- DrawCaption(g, ip, grid.Radius, "Ellipse");
- ip.X += grid.StepX;
-
- // Cylinder:
- svg.Children.Clear();
- float radX = grid.Radius / 1.4f;
- float radY = grid.Radius / 6;
- float height = grid.Radius * 1.8f;
- svg.Children.Add(new SvgEllipseElement()
- {
- CenterX = len50,
- CenterY = len10,
- RadiusX = len50,
- RadiusY = len10,
- Fill = paintTrans,
- Stroke = pntStroke0,
- });
- svg.Children.Add(new SvgEllipseElement()
- {
- CenterX = len50,
- CenterY = len90,
- RadiusX = len50,
- RadiusY = len10,
- Fill = pntFill0,
- Stroke = pntStroke0,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = len0,
- Y1 = len10,
- X2 = len0,
- Y2 = len90,
- Stroke = pntStroke0,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = len100,
- Y1 = len10,
- X2 = len100,
- Y2 = len90,
- Stroke = pntStroke0,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
- DrawCaption(g, ip, grid.Radius, "Cylinder");
- ip.X = startIp.X;
- ip.Y += grid.StepY;
-
- // Square:
- DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, pntStroke0, "Square");
- ip.X += grid.StepX;
-
- // Rectangle:
- svg.Children.Clear();
- svg.Children.Add(new SvgRectElement()
- {
- X = new SvgLength(-20, SvgLengthUnits.Percentage),
- Y = len20,
- Width = new SvgLength(140, SvgLengthUnits.Percentage),
- Height = len60,
- Stroke = pntStroke0,
- Fill = paintTrans,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
- DrawCaption(g, ip, grid.Radius, "Rectangle");
- ip.X += grid.StepX;
-
- // Cube:
- svg.Children.Clear();
- svg.Children.Add(new SvgRectElement()
- {
- X = len10,
- Y = len10,
- Width = len60,
- Height = len60,
- Stroke = pntStroke0,
- Fill = paintTrans,
- });
- svg.Children.Add(new SvgRectElement()
- {
- X = len30,
- Y = len30,
- Width = len60,
- Height = len60,
- Stroke = pntStroke0,
- Fill = paintTrans,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = len10,
- Y1 = len10,
- X2 = len30,
- Y2 = len30,
- Stroke = pntStroke0,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = len10,
- Y1 = len70,
- X2 = len30,
- Y2 = len90,
- Stroke = pntStroke0,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = len70,
- Y1 = len10,
- X2 = len90,
- Y2 = len30,
- Stroke = pntStroke0,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = len70,
- Y1 = len70,
- X2 = len90,
- Y2 = len90,
- Stroke = pntStroke0,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
- DrawCaption(g, ip, grid.Radius, "Cube");
- ip.X = startIp.X;
- ip.Y += grid.StepY;
-
- // Pentagon:
- DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pntStroke0, "Pentagon");
- ip.X += grid.StepX;
-
- // Hexagon:
- // For sample sake, we apply a transform to make the hexagon wider and shorter:
- g.Transform = Matrix3x2.CreateScale(1.4f, 0.8f) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
- DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pntStroke0, null);
- g.Transform = Matrix3x2.Identity;
- DrawCaption(g, ip, grid.Radius, "Hexagon");
- ip.X += grid.StepX;
-
- // Octagon:
- DrawPolygon(g, ip, grid.Radius, 8, (float)-Math.PI / 8, pntStroke0, "Octagon");
- ip.X = startIp.X;
- ip.Y += grid.StepY;
-
- // Triangle:
- DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, pntStroke0, "Triangle");
- ip.X += grid.StepX;
-
- // Filled pentagram:
- var svgPts = DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pntStroke0, "Pentagram");
- svg.Children.Clear();
- svg.Children.Add(new SvgPolygonElement()
- {
- Points = new List<SvgPoint>()
- {
- svgPts[0], svgPts[2], svgPts[4], svgPts[1], svgPts[3],
- },
- Stroke = pntStroke0,
- Fill = pntFill0,
- FillRule = SvgFillRule.EvenOdd,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
- ip.X += grid.StepX;
-
- // Set up a simple kind of oblique projection to draw a pyramid:
- var angle = Math.PI / 6;
- float s = (float)Math.Sin(angle);
- float c = (float)Math.Cos(angle);
- Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
- Func<Vector3, PointF> p3d = v_ => project(v_.X, v_.Y, v_.Z);
- float hedge = grid.Radius; // 1/2 edge
- // Debug - draw the 3 axis:
- /*
- g.DrawLine(project(0, 0, 0), project(100, 0, 0), Color.Red);
- g.DrawLine(project(0, 0, 0), project(0, 100, 0), Color.Green);
- g.DrawLine(project(0, 0, 0), project(0, 0, 100), Color.Blue);
- */
- // 3d points forming a square pyramid:
- var pts3d = new Vector3[]
- {
- new Vector3(-hedge, -hedge, 0),
- new Vector3(hedge, -hedge, 0),
- new Vector3(hedge, hedge, 0),
- new Vector3(-hedge, hedge, 0),
- new Vector3(0, 0, hedge * 2),
- };
- // Project the points to draw the pyramid:
- var pts = pts3d.Select(v_ => p3d(v_));
- svgPts = pts.Select(p_ => new SvgPoint(new SvgLength(p_.X, SvgLengthUnits.Percentage), new SvgLength(p_.Y, SvgLengthUnits.Percentage))).ToArray();
- g.Transform = Matrix3x2.CreateTranslation(0, hedge * 0.7f);
- svg.Children.Clear();
- // Visible edges:
- svg.Children.Add(new SvgPolygonElement()
- {
- Points = new List<SvgPoint>()
- {
- svgPts[4],
- svgPts[1],
- svgPts[2],
- svgPts[3],
- svgPts[4],
- svgPts[2],
- },
- Stroke = pntStroke0,
- Fill = paintTrans,
- });
- // Bottom:
- svg.Children.Add(new SvgPolygonElement()
- {
- Points = svgPts.Take(4).ToList(),
- Fill = pntFill0,
- });
- // Invisible edges:
- svg.Children.Add(new SvgLineElement()
- {
- X1 = svgPts[0].X,
- Y1 = svgPts[0].Y,
- X2 = svgPts[4].X,
- Y2 = svgPts[4].Y,
- Stroke = pntStroke0inv,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = svgPts[0].X,
- Y1 = svgPts[0].Y,
- X2 = svgPts[1].X,
- Y2 = svgPts[1].Y,
- Stroke = pntStroke0inv,
- });
- svg.Children.Add(new SvgLineElement()
- {
- X1 = svgPts[0].X,
- Y1 = svgPts[0].Y,
- X2 = svgPts[3].X,
- Y2 = svgPts[3].Y,
- Stroke = pntStroke0inv,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
- g.Transform = Matrix3x2.Identity;
- DrawCaption(g, ip, grid.Radius, "Pyramid");
- ip.X = startIp.X;
- ip.Y += grid.StepY;
-
- // Cone:
- float baseh = grid.Radius * 0.3f;
- svgPts = DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, null, "Cone");
- svg.Children.Clear();
- svg.Children.Add(new SvgPolylineElement()
- {
- Points = new List<SvgPoint>()
- {
- svgPts[2], svgPts[0], svgPts[1],
- },
- Stroke = pntStroke0,
- Fill = paintTrans,
- });
- svg.Children.Add(new SvgEllipseElement()
- {
- CenterX = new SvgLength(svgPts[0].X.Value, SvgLengthUnits.Percentage),
- CenterY = svgPts[2].Y,
- RadiusX = new SvgLength((svgPts[1].X.Value - svgPts[2].X.Value) / 2, SvgLengthUnits.Percentage),
- RadiusY = len10,
- Fill = pntFill0,
- Stroke = pntStroke0,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
- ip.X += grid.StepX;
-
- // Parallelogram (use graphics.Transform on a rectangle):
- svg.Children.Clear();
- var svgParal = new SvgRectElement()
- {
- X = new SvgLength(-20, SvgLengthUnits.Percentage),
- Y = len20,
- Width = new SvgLength(140, SvgLengthUnits.Percentage),
- Height = len60,
- Stroke = pntStroke0,
- Fill = paintTrans,
- };
- svg.Children.Add(svgParal);
- g.Transform = Matrix3x2.CreateSkew((float)Math.PI / 6, 0) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
- g.DrawSvg(svgDoc, new RectangleF(-grid.Radius, -grid.Radius, grid.Radius * 2, grid.Radius * 2));
- g.Transform = Matrix3x2.Identity;
- DrawCaption(g, ip, grid.Radius, "Parallelogram");
- ip.X += grid.StepX;
-
- // Trapezoid (use DrawPolygon to just get the points of the square):
- svg.Children.Clear();
- float dx = 10;
- svgPts = DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, null, "Trapezoid");
- svg.Children.Add(new SvgPolygonElement()
- {
- Points = new List<SvgPoint>()
- {
- new SvgPoint(new SvgLength(svgPts[0].X.Value - dx, SvgLengthUnits.Percentage), svgPts[0].Y),
- new SvgPoint(new SvgLength(svgPts[1].X.Value + dx, SvgLengthUnits.Percentage), svgPts[1].Y),
- new SvgPoint(new SvgLength(svgPts[2].X.Value - dx, SvgLengthUnits.Percentage), svgPts[2].Y),
- new SvgPoint(new SvgLength(svgPts[3].X.Value + dx, SvgLengthUnits.Percentage), svgPts[3].Y),
- },
- Stroke = pntStroke0,
- Fill = paintTrans,
- });
- g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
-
- // Done:
- return bmp;
- }
- }
- }
-