SvgGraphicsShapes.cs
  1. //
  2. // This code is part of Document Solutions for Imaging demos.
  3. // Copyright (c) MESCIUS inc. All rights reserved.
  4. //
  5. using System;
  6. using System.IO;
  7. using System.Drawing;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Numerics;
  11. using GrapeCity.Documents.Drawing;
  12. using GrapeCity.Documents.Text;
  13. using GrapeCity.Documents.Imaging;
  14. using GrapeCity.Documents.Svg;
  15. using DsImagingWeb.Demos.Common;
  16. using GCTEXT = GrapeCity.Documents.Text;
  17. using GCDRAW = GrapeCity.Documents.Drawing;
  18.  
  19. namespace DsImagingWeb.Demos
  20. {
  21. // This example shows how to use GcSvgGraphics for common graphics
  22. // operations. It draws some geometric shapes on GcSvgGraphics,
  23. // then converts that graphics to GcSvgDocument, which is saved
  24. // to an SVG stream that is shown by this sample.
  25. // Note that the code drawing shapes on GcSvgGraphics is identical
  26. // to the code drawing shapes on GcBitmapGraphics in the Shapes sample.
  27. // Compare this to the SvgShapes sample which creates SVG elements
  28. // representing shapes, and draws them on the resulting raster GcBitmap.
  29. public class SvgGraphicsShapes
  30. {
  31. public string DefaultMime { get => Common.Util.MimeTypes.SVG; }
  32.  
  33. // Helper method to draw a polygon and a caption beneath it.
  34. // Can also be used to just calculate the points without actual drawing.
  35. // startAngle is for the first point, clockwise from (1,0).
  36. private PointF[] DrawPolygon(GcGraphics g, PointF center, float r, int n, float startAngle, GCDRAW.Pen pen, string caption = null)
  37. {
  38. PointF[] pts = new PointF[n];
  39. for (int i = 0; i < n; ++i)
  40. pts[i] = new PointF(center.X + (float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + (float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)));
  41. if (pen != null)
  42. g.DrawPolygon(pts, pen);
  43. if (!string.IsNullOrEmpty(caption))
  44. DrawCaption(g, center, r, caption);
  45. return pts;
  46. }
  47.  
  48. // Helper method to draw a caption beneath a shape:
  49. private void DrawCaption(GcGraphics g, PointF center, float r, string caption)
  50. {
  51. g.DrawString(caption,
  52. new TextFormat()
  53. {
  54. Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "times.ttf")),
  55. FontSize = 12,
  56. },
  57. new RectangleF(center.X - r, center.Y + r, r * 2, 24),
  58. TextAlignment.Center, ParagraphAlignment.Center, false);
  59. }
  60.  
  61. // Main entry point.
  62. public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
  63. {
  64. if (targetMime != Util.MimeTypes.SVG)
  65. throw new Exception("This sample only supports SVG output format.");
  66.  
  67. var Inch = dpi;
  68. var ms = new MemoryStream();
  69. using var g = new GcSvgGraphics(pixelSize.Width, pixelSize.Height);
  70. if (opaque)
  71. g.FillRectangle(new RectangleF(0, 0, g.Width, g.Height), Color.White);
  72.  
  73. // Document header:
  74. g.DrawString("Shapes",
  75. new TextFormat()
  76. {
  77. Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "timesbd.ttf")),
  78. FontBold = true,
  79. FontSize = 14,
  80. Underline = true,
  81. },
  82. new RectangleF(PointF.Empty, new SizeF(g.Width, 44)),
  83. TextAlignment.Center, ParagraphAlignment.Far);
  84. // Pen used to draw shapes:
  85. var pen = new GCDRAW.Pen(Color.Orange, 1);
  86. pen.LineJoin = PenLineJoin.Round;
  87. int fill = 100; // Surfaces fill alpha
  88.  
  89. // Set up the helper layout grid:
  90. var grid = new
  91. {
  92. Cols = 3,
  93. Rows = 5,
  94. MarginX = Inch / 4,
  95. MarginY = Inch / 3,
  96. Radius = Inch * 0.7f,
  97. StepX = (g.Width - Inch / 2) / 3,
  98. StepY = (g.Height - Inch / 4) / 5.5f,
  99. };
  100.  
  101. // Insertion point of the next figure's center:
  102. PointF startIp = new PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10);
  103. PointF ip = startIp;
  104. /* // Debug code to show the layout grid:
  105. var ipp = ip;
  106. for (int i = 0; i < grid.Cols; ++i)
  107. {
  108. ipp.Y = ip.Y;
  109. for (int j = 0; j < grid.Rows; ++j)
  110. {
  111. g.DrawRectangle(new RectangleF(ipp.X - grid.Radius, ipp.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), Color.LightGreen, 0.5f);
  112. ipp.Y += grid.StepY;
  113. }
  114. ipp.X += grid.StepX;
  115. }
  116. */
  117.  
  118. // Circle:
  119. g.DrawEllipse(new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), pen);
  120. DrawCaption(g, ip, grid.Radius, "Circle");
  121. ip.X += grid.StepX;
  122.  
  123. // Ellipse:
  124. g.DrawEllipse(new RectangleF(ip.X - grid.Radius * 1.4f, ip.Y - grid.Radius / 2, grid.Radius * 2 * 1.4f, grid.Radius), pen);
  125. DrawCaption(g, ip, grid.Radius, "Ellipse");
  126. ip.X += grid.StepX;
  127.  
  128. // Cylinder:
  129. float radX = grid.Radius / 1.4f;
  130. float radY = grid.Radius / 6;
  131. float height = grid.Radius * 1.8f;
  132. g.DrawEllipse(new RectangleF(ip.X - radX, ip.Y - height / 2, radX * 2, radY * 2), pen);
  133. g.FillEllipse(new RectangleF(ip.X - radX, ip.Y + height / 2 - radY * 2, radX * 2, radY * 2), Color.FromArgb(fill, pen.Color));
  134. g.DrawEllipse(new RectangleF(ip.X - radX, ip.Y + height / 2 - radY * 2, radX * 2, radY * 2), pen);
  135. g.DrawLine(new PointF(ip.X - radX, ip.Y - height / 2 + radY), new PointF(ip.X - radX, ip.Y + height / 2 - radY), pen);
  136. g.DrawLine(new PointF(ip.X + radX, ip.Y - height / 2 + radY), new PointF(ip.X + radX, ip.Y + height / 2 - radY), pen);
  137. DrawCaption(g, ip, grid.Radius, "Cylinder");
  138. ip.X = startIp.X;
  139. ip.Y += grid.StepY;
  140. pen.Color = Color.Indigo;
  141.  
  142. // Square:
  143. DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, pen, "Square");
  144. ip.X += grid.StepX;
  145.  
  146. // Rectangle:
  147. float rectQx = 1.4f;
  148. float rectQy = 0.6f;
  149. var rect = new RectangleF(ip.X - grid.Radius * rectQx, ip.Y - grid.Radius * rectQy, grid.Radius * 2 * rectQx, grid.Radius * 2 * rectQy);
  150. g.DrawRectangle(rect, pen);
  151. DrawCaption(g, ip, grid.Radius, "Rectangle");
  152. ip.X += grid.StepX;
  153.  
  154. // Cube:
  155. float cubex = 6;
  156. var cubePtsFar = DrawPolygon(g, new PointF(ip.X - cubex, ip.Y - cubex), grid.Radius, 4, (float)-Math.PI / 4, pen);
  157. var cubePtsNear = DrawPolygon(g, new PointF(ip.X + cubex, ip.Y + cubex), grid.Radius, 4, (float)-Math.PI / 4, pen);
  158. g.DrawLine(cubePtsFar[0], cubePtsNear[0], pen);
  159. g.DrawLine(cubePtsFar[1], cubePtsNear[1], pen);
  160. g.DrawLine(cubePtsFar[2], cubePtsNear[2], pen);
  161. g.DrawLine(cubePtsFar[3], cubePtsNear[3], pen);
  162. g.FillPolygon(new PointF[] { cubePtsFar[1], cubePtsFar[2], cubePtsNear[2], cubePtsNear[1], }, Color.FromArgb(fill, pen.Color));
  163. DrawCaption(g, ip, grid.Radius, "Cube");
  164. ip.X = startIp.X;
  165. ip.Y += grid.StepY;
  166. pen.Color = Color.DarkGreen;
  167.  
  168. // Pentagon:
  169. DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pen, "Pentagon");
  170. ip.X += grid.StepX;
  171.  
  172. // Hexagon:
  173. // For sample sake, we apply a transform to make the hexagon wider and shorter:
  174. g.Transform = Matrix3x2.CreateScale(1.4f, 0.8f) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
  175. DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pen, null);
  176. g.Transform = Matrix3x2.Identity;
  177. DrawCaption(g, ip, grid.Radius, "Hexagon");
  178. ip.X += grid.StepX;
  179.  
  180. // Octagon:
  181. DrawPolygon(g, ip, grid.Radius, 8, (float)-Math.PI / 8, pen, "Octagon");
  182. ip.X = startIp.X;
  183. ip.Y += grid.StepY;
  184. pen.Color = Color.DarkRed;
  185.  
  186. // Triangle:
  187. DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, pen, "Triangle");
  188. ip.X += grid.StepX;
  189.  
  190. // Filled pentagram:
  191. var pts = DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pen, "Pentagram");
  192. pts = new PointF[] { pts[0], pts[2], pts[4], pts[1], pts[3], };
  193. g.FillPolygon(pts, Color.FromArgb(fill, pen.Color));
  194. g.DrawPolygon(pts, pen);
  195. ip.X += grid.StepX;
  196.  
  197. // Set up a simple kind of oblique projection to draw a pyramid:
  198. var angle = Math.PI / 6;
  199. float s = (float)Math.Sin(angle);
  200. float c = (float)Math.Cos(angle);
  201. Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
  202. Func<Vector3, PointF> p3d = v_ => project(v_.X, v_.Y, v_.Z);
  203. float hedge = grid.Radius; // 1/2 edge
  204. // Debug - draw the 3 axis:
  205. // g.DrawLine(project(0, 0, 0), project(100, 0, 0), Color.Red);
  206. // g.DrawLine(project(0, 0, 0), project(0, 100, 0), Color.Green);
  207. // g.DrawLine(project(0, 0, 0), project(0, 0, 100), Color.Blue);
  208. // 3d points forming a square pyramid:
  209. var pts3d = new Vector3[]
  210. {
  211. new Vector3(-hedge, -hedge, 0),
  212. new Vector3(hedge, -hedge, 0),
  213. new Vector3(hedge, hedge, 0),
  214. new Vector3(-hedge, hedge, 0),
  215. new Vector3(0, 0, hedge * 2),
  216. };
  217. // project the points to draw the pyramid:
  218. pts = pts3d.Select(v_ => p3d(v_)).ToArray();
  219. g.Transform = Matrix3x2.CreateTranslation(ip.X, ip.Y + hedge * 0.7f);
  220. // Visible edges:
  221. g.DrawPolygon(new PointF[] { pts[4], pts[1], pts[2], pts[3], pts[4], pts[2] }, pen);
  222. // Invisible edges:
  223. pen.Width /= 2;
  224. pen.Color = Color.FromArgb(fill, pen.Color);
  225. g.DrawLine(pts[0], pts[4], pen);
  226. g.DrawLine(pts[0], pts[1], pen);
  227. g.DrawLine(pts[0], pts[3], pen);
  228. g.FillPolygon(pts.Take(4).ToArray(), pen.Color);
  229. //
  230. g.Transform = Matrix3x2.Identity;
  231. DrawCaption(g, ip, grid.Radius, "Pyramid");
  232. ip.X = startIp.X;
  233. ip.Y += grid.StepY;
  234. pen.Width *= 2;
  235. pen.Color = Color.Green;
  236.  
  237. // Cone:
  238. float baseh = grid.Radius * 0.3f;
  239. pts = DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, null, "Cone");
  240. g.DrawLines(new PointF[] { pts[2], pts[0], pts[1] }, pen);
  241. rect = new RectangleF(pts[2].X, pts[2].Y - baseh / 2, pts[1].X - pts[2].X, baseh);
  242. g.FillEllipse(rect, Color.FromArgb(fill, pen.Color));
  243. g.DrawEllipse(rect, pen);
  244. ip.X += grid.StepX;
  245.  
  246. // Parallelogram (use graphics.Transform on a rectangle):
  247. rect = new RectangleF(-grid.Radius * rectQx, -grid.Radius * rectQy, grid.Radius * 2 * rectQx, grid.Radius * 2 * rectQy);
  248. g.Transform = Matrix3x2.CreateSkew((float)Math.PI / 6, 0) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
  249. g.DrawRectangle(rect, pen);
  250. g.Transform = Matrix3x2.Identity;
  251. DrawCaption(g, ip, grid.Radius, "Parallelogram");
  252. ip.X += grid.StepX;
  253.  
  254. // Trapezoid (use DrawPolygon to just get the points of the square):
  255. float dx = 10;
  256. pts = DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, null, "Trapezoid");
  257. pts[0].X -= dx;
  258. pts[1].X += dx;
  259. pts[2].X -= dx;
  260. pts[3].X += dx;
  261. g.DrawPolygon(pts, pen);
  262.  
  263. // Draw border around the whole image:
  264. // g.DrawRectangle(new RectangleF(0, 0, bmp.Width, bmp.Height), Color.DarkSlateBlue, 4);
  265.  
  266. // Done:
  267. var svg = g.ToSvgDocument();
  268. svg.Save(ms);
  269. ms.Seek(0, SeekOrigin.Begin);
  270. return ms;
  271. }
  272. }
  273. }
  274.