SvgShapes.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 GCTEXT = GrapeCity.Documents.Text;
  16. using GCDRAW = GrapeCity.Documents.Drawing;
  17. using DsImagingWeb.Demos.Common;
  18.  
  19. namespace DsImagingWeb.Demos
  20. {
  21. // This sample draws some geometric shapes using GcSvgDocument and GcGraphics.DrawSvg().
  22. // The code and the results are similar to the Shapes sample.
  23. public class SvgShapes
  24. {
  25. // Helper method to draw a polygon and a caption beneath it.
  26. // Can also be used to just calculate the points without actual drawing.
  27. // startAngle is for the first point, clockwise from (1,0).
  28. // Points are relative to 'center'.
  29. private SvgPoint[] DrawPolygon(GcGraphics g, PointF center, float r, int n, float startAngle, SvgPaint stroke, string caption = null)
  30. {
  31. const float Q = 1.4f;
  32. SvgPolygonElement plgn = new SvgPolygonElement();
  33. plgn.Points = new List<SvgPoint>(n);
  34. for (int i = 0; i < n; ++i)
  35. plgn.Points.Add(new SvgPoint(
  36. new SvgLength((float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)) * Q, SvgLengthUnits.Percentage),
  37. new SvgLength((float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)) * Q, SvgLengthUnits.Percentage)));
  38.  
  39. if (stroke != null)
  40. {
  41. plgn.Stroke = stroke;
  42. plgn.Fill = new SvgPaint(SvgColor.Transparent);
  43. var svgDoc = new GcSvgDocument();
  44. var svg = svgDoc.RootSvg;
  45. svg.Children.Add(plgn);
  46. g.DrawSvg(svgDoc, new RectangleF(center.X, center.Y, r * 2, r * 2));
  47. }
  48.  
  49. if (!string.IsNullOrEmpty(caption))
  50. DrawCaption(g, center, r, caption);
  51.  
  52. return plgn.Points.ToArray();
  53. }
  54. // Helper method to draw a caption beneath a shape:
  55. private void DrawCaption(GcGraphics g, PointF center, float r, string caption)
  56. {
  57. g.DrawString(caption,
  58. new TextFormat() { FontSize = 10, },
  59. new RectangleF(center.X - r, center.Y + r + 24, r * 2, 24),
  60. TextAlignment.Center, ParagraphAlignment.Center, false);
  61. }
  62. // Main entry point.
  63. public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
  64. {
  65. var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi);
  66. using var g = bmp.CreateGraphics(Color.White);
  67. // Document header:
  68. g.DrawString("Shapes (SVG)",
  69. new TextFormat() { FontSize = 14, Underline = true, },
  70. new RectangleF(PointF.Empty, new SizeF(pixelSize.Width, 44)),
  71. TextAlignment.Center, ParagraphAlignment.Far);
  72. // Pen used to draw shapes:
  73.  
  74. // Set up the helper layout grid:
  75. var grid = new
  76. {
  77. Cols = 3,
  78. Rows = 5,
  79. MarginX = dpi,
  80. MarginY = dpi / 2,
  81. Radius = dpi / 2,
  82. StepX = (pixelSize.Width - dpi * 2) / 3,
  83. StepY = (pixelSize.Height - dpi) / 5,
  84. };
  85.  
  86. // Insertion point of the next figure's center:
  87. PointF startIp = new PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10);
  88. PointF ip = startIp;
  89. // Debug code to show the layout grid:
  90. /*
  91. var ipp = ip;
  92. for (int i = 0; i < grid.Cols; ++i)
  93. {
  94. ipp.Y = ip.Y;
  95. for (int j = 0; j < grid.Rows; ++j)
  96. {
  97. g.DrawRectangle(new RectangleF(ipp.X - grid.Radius, ipp.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), Color.LightGreen, 0.5f);
  98. ipp.Y += grid.StepY;
  99. }
  100. ipp.X += grid.StepX;
  101. }
  102. */
  103.  
  104. // Reusable SVG primitives:
  105. var len0 = new SvgLength(0, SvgLengthUnits.Percentage);
  106. var len10 = new SvgLength(10, SvgLengthUnits.Percentage);
  107. var len20 = new SvgLength(20, SvgLengthUnits.Percentage);
  108. var len30 = new SvgLength(30, SvgLengthUnits.Percentage);
  109. var len40 = new SvgLength(40, SvgLengthUnits.Percentage);
  110. var len50 = new SvgLength(50, SvgLengthUnits.Percentage);
  111. var len60 = new SvgLength(60, SvgLengthUnits.Percentage);
  112. var len70 = new SvgLength(70, SvgLengthUnits.Percentage);
  113. var len80 = new SvgLength(80, SvgLengthUnits.Percentage);
  114. var len90 = new SvgLength(90, SvgLengthUnits.Percentage);
  115. var len100 = new SvgLength(100, SvgLengthUnits.Percentage);
  116. var paintTrans = new SvgPaint(SvgColor.Transparent);
  117. var pntStroke0 = new SvgPaint(new SvgColor(Color.Green));
  118. var pntStroke0inv = new SvgPaint(new SvgColor(Color.FromArgb(100, Color.Green)));
  119. var pntFill0 = new SvgPaint(new SvgColor(Color.FromArgb(100, Color.Orange)));
  120. var pt0_0 = new SvgPoint(len0, len0);
  121. var pt100_100 = new SvgPoint(len100, len100);
  122.  
  123. // Circle:
  124. var svgDoc = new GcSvgDocument();
  125. var svg = svgDoc.RootSvg;
  126. svg.Children.Add(new SvgEllipseElement()
  127. {
  128. CenterX = len50,
  129. CenterY = len50,
  130. RadiusX = len50,
  131. RadiusY = len50,
  132. Fill = paintTrans,
  133. Stroke = pntStroke0,
  134. });
  135. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  136. DrawCaption(g, ip, grid.Radius, "Circle");
  137. ip.X += grid.StepX;
  138.  
  139. // Ellipse:
  140. svg.Children.Clear();
  141. svg.Children.Add(new SvgEllipseElement()
  142. {
  143. CenterX = len50,
  144. CenterY = len50,
  145. RadiusX = len50,
  146. RadiusY = len50,
  147. Fill = paintTrans,
  148. Stroke = pntStroke0,
  149. });
  150. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius * 1.4f, ip.Y - grid.Radius / 2, grid.Radius * 2 * 1.4f, grid.Radius));
  151. DrawCaption(g, ip, grid.Radius, "Ellipse");
  152. ip.X += grid.StepX;
  153.  
  154. // Cylinder:
  155. svg.Children.Clear();
  156. float radX = grid.Radius / 1.4f;
  157. float radY = grid.Radius / 6;
  158. float height = grid.Radius * 1.8f;
  159. svg.Children.Add(new SvgEllipseElement()
  160. {
  161. CenterX = len50,
  162. CenterY = len10,
  163. RadiusX = len50,
  164. RadiusY = len10,
  165. Fill = paintTrans,
  166. Stroke = pntStroke0,
  167. });
  168. svg.Children.Add(new SvgEllipseElement()
  169. {
  170. CenterX = len50,
  171. CenterY = len90,
  172. RadiusX = len50,
  173. RadiusY = len10,
  174. Fill = pntFill0,
  175. Stroke = pntStroke0,
  176. });
  177. svg.Children.Add(new SvgLineElement()
  178. {
  179. X1 = len0,
  180. Y1 = len10,
  181. X2 = len0,
  182. Y2 = len90,
  183. Stroke = pntStroke0,
  184. });
  185. svg.Children.Add(new SvgLineElement()
  186. {
  187. X1 = len100,
  188. Y1 = len10,
  189. X2 = len100,
  190. Y2 = len90,
  191. Stroke = pntStroke0,
  192. });
  193. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  194. DrawCaption(g, ip, grid.Radius, "Cylinder");
  195. ip.X = startIp.X;
  196. ip.Y += grid.StepY;
  197.  
  198. // Square:
  199. DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, pntStroke0, "Square");
  200. ip.X += grid.StepX;
  201.  
  202. // Rectangle:
  203. svg.Children.Clear();
  204. svg.Children.Add(new SvgRectElement()
  205. {
  206. X = new SvgLength(-20, SvgLengthUnits.Percentage),
  207. Y = len20,
  208. Width = new SvgLength(140, SvgLengthUnits.Percentage),
  209. Height = len60,
  210. Stroke = pntStroke0,
  211. Fill = paintTrans,
  212. });
  213. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  214. DrawCaption(g, ip, grid.Radius, "Rectangle");
  215. ip.X += grid.StepX;
  216.  
  217. // Cube:
  218. svg.Children.Clear();
  219. svg.Children.Add(new SvgRectElement()
  220. {
  221. X = len10,
  222. Y = len10,
  223. Width = len60,
  224. Height = len60,
  225. Stroke = pntStroke0,
  226. Fill = paintTrans,
  227. });
  228. svg.Children.Add(new SvgRectElement()
  229. {
  230. X = len30,
  231. Y = len30,
  232. Width = len60,
  233. Height = len60,
  234. Stroke = pntStroke0,
  235. Fill = paintTrans,
  236. });
  237. svg.Children.Add(new SvgLineElement()
  238. {
  239. X1 = len10,
  240. Y1 = len10,
  241. X2 = len30,
  242. Y2 = len30,
  243. Stroke = pntStroke0,
  244. });
  245. svg.Children.Add(new SvgLineElement()
  246. {
  247. X1 = len10,
  248. Y1 = len70,
  249. X2 = len30,
  250. Y2 = len90,
  251. Stroke = pntStroke0,
  252. });
  253. svg.Children.Add(new SvgLineElement()
  254. {
  255. X1 = len70,
  256. Y1 = len10,
  257. X2 = len90,
  258. Y2 = len30,
  259. Stroke = pntStroke0,
  260. });
  261. svg.Children.Add(new SvgLineElement()
  262. {
  263. X1 = len70,
  264. Y1 = len70,
  265. X2 = len90,
  266. Y2 = len90,
  267. Stroke = pntStroke0,
  268. });
  269. g.DrawSvg(svgDoc, new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2));
  270. DrawCaption(g, ip, grid.Radius, "Cube");
  271. ip.X = startIp.X;
  272. ip.Y += grid.StepY;
  273.  
  274. // Pentagon:
  275. DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pntStroke0, "Pentagon");
  276. ip.X += grid.StepX;
  277.  
  278. // Hexagon:
  279. // For sample sake, we apply a transform to make the hexagon wider and shorter:
  280. g.Transform = Matrix3x2.CreateScale(1.4f, 0.8f) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
  281. DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pntStroke0, null);
  282. g.Transform = Matrix3x2.Identity;
  283. DrawCaption(g, ip, grid.Radius, "Hexagon");
  284. ip.X += grid.StepX;
  285.  
  286. // Octagon:
  287. DrawPolygon(g, ip, grid.Radius, 8, (float)-Math.PI / 8, pntStroke0, "Octagon");
  288. ip.X = startIp.X;
  289. ip.Y += grid.StepY;
  290.  
  291. // Triangle:
  292. DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, pntStroke0, "Triangle");
  293. ip.X += grid.StepX;
  294.  
  295. // Filled pentagram:
  296. var svgPts = DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pntStroke0, "Pentagram");
  297. svg.Children.Clear();
  298. svg.Children.Add(new SvgPolygonElement()
  299. {
  300. Points = new List<SvgPoint>()
  301. {
  302. svgPts[0], svgPts[2], svgPts[4], svgPts[1], svgPts[3],
  303. },
  304. Stroke = pntStroke0,
  305. Fill = pntFill0,
  306. FillRule = SvgFillRule.EvenOdd,
  307. });
  308. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  309. ip.X += grid.StepX;
  310.  
  311. // Set up a simple kind of oblique projection to draw a pyramid:
  312. var angle = Math.PI / 6;
  313. float s = (float)Math.Sin(angle);
  314. float c = (float)Math.Cos(angle);
  315. Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
  316. Func<Vector3, PointF> p3d = v_ => project(v_.X, v_.Y, v_.Z);
  317. float hedge = grid.Radius; // 1/2 edge
  318. // Debug - draw the 3 axis:
  319. /*
  320. g.DrawLine(project(0, 0, 0), project(100, 0, 0), Color.Red);
  321. g.DrawLine(project(0, 0, 0), project(0, 100, 0), Color.Green);
  322. g.DrawLine(project(0, 0, 0), project(0, 0, 100), Color.Blue);
  323. */
  324. // 3d points forming a square pyramid:
  325. var pts3d = new Vector3[]
  326. {
  327. new Vector3(-hedge, -hedge, 0),
  328. new Vector3(hedge, -hedge, 0),
  329. new Vector3(hedge, hedge, 0),
  330. new Vector3(-hedge, hedge, 0),
  331. new Vector3(0, 0, hedge * 2),
  332. };
  333. // Project the points to draw the pyramid:
  334. var pts = pts3d.Select(v_ => p3d(v_));
  335. svgPts = pts.Select(p_ => new SvgPoint(new SvgLength(p_.X, SvgLengthUnits.Percentage), new SvgLength(p_.Y, SvgLengthUnits.Percentage))).ToArray();
  336. g.Transform = Matrix3x2.CreateTranslation(0, hedge * 0.7f);
  337. svg.Children.Clear();
  338. // Visible edges:
  339. svg.Children.Add(new SvgPolygonElement()
  340. {
  341. Points = new List<SvgPoint>()
  342. {
  343. svgPts[4],
  344. svgPts[1],
  345. svgPts[2],
  346. svgPts[3],
  347. svgPts[4],
  348. svgPts[2],
  349. },
  350. Stroke = pntStroke0,
  351. Fill = paintTrans,
  352. });
  353. // Bottom:
  354. svg.Children.Add(new SvgPolygonElement()
  355. {
  356. Points = svgPts.Take(4).ToList(),
  357. Fill = pntFill0,
  358. });
  359. // Invisible edges:
  360. svg.Children.Add(new SvgLineElement()
  361. {
  362. X1 = svgPts[0].X,
  363. Y1 = svgPts[0].Y,
  364. X2 = svgPts[4].X,
  365. Y2 = svgPts[4].Y,
  366. Stroke = pntStroke0inv,
  367. });
  368. svg.Children.Add(new SvgLineElement()
  369. {
  370. X1 = svgPts[0].X,
  371. Y1 = svgPts[0].Y,
  372. X2 = svgPts[1].X,
  373. Y2 = svgPts[1].Y,
  374. Stroke = pntStroke0inv,
  375. });
  376. svg.Children.Add(new SvgLineElement()
  377. {
  378. X1 = svgPts[0].X,
  379. Y1 = svgPts[0].Y,
  380. X2 = svgPts[3].X,
  381. Y2 = svgPts[3].Y,
  382. Stroke = pntStroke0inv,
  383. });
  384. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  385. g.Transform = Matrix3x2.Identity;
  386. DrawCaption(g, ip, grid.Radius, "Pyramid");
  387. ip.X = startIp.X;
  388. ip.Y += grid.StepY;
  389.  
  390. // Cone:
  391. float baseh = grid.Radius * 0.3f;
  392. svgPts = DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, null, "Cone");
  393. svg.Children.Clear();
  394. svg.Children.Add(new SvgPolylineElement()
  395. {
  396. Points = new List<SvgPoint>()
  397. {
  398. svgPts[2], svgPts[0], svgPts[1],
  399. },
  400. Stroke = pntStroke0,
  401. Fill = paintTrans,
  402. });
  403. svg.Children.Add(new SvgEllipseElement()
  404. {
  405. CenterX = new SvgLength(svgPts[0].X.Value, SvgLengthUnits.Percentage),
  406. CenterY = svgPts[2].Y,
  407. RadiusX = new SvgLength((svgPts[1].X.Value - svgPts[2].X.Value) / 2, SvgLengthUnits.Percentage),
  408. RadiusY = len10,
  409. Fill = pntFill0,
  410. Stroke = pntStroke0,
  411. });
  412. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  413. ip.X += grid.StepX;
  414.  
  415. // Parallelogram (use graphics.Transform on a rectangle):
  416. svg.Children.Clear();
  417. var svgParal = new SvgRectElement()
  418. {
  419. X = new SvgLength(-20, SvgLengthUnits.Percentage),
  420. Y = len20,
  421. Width = new SvgLength(140, SvgLengthUnits.Percentage),
  422. Height = len60,
  423. Stroke = pntStroke0,
  424. Fill = paintTrans,
  425. };
  426. svg.Children.Add(svgParal);
  427. g.Transform = Matrix3x2.CreateSkew((float)Math.PI / 6, 0) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
  428. g.DrawSvg(svgDoc, new RectangleF(-grid.Radius, -grid.Radius, grid.Radius * 2, grid.Radius * 2));
  429. g.Transform = Matrix3x2.Identity;
  430. DrawCaption(g, ip, grid.Radius, "Parallelogram");
  431. ip.X += grid.StepX;
  432.  
  433. // Trapezoid (use DrawPolygon to just get the points of the square):
  434. svg.Children.Clear();
  435. float dx = 10;
  436. svgPts = DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, null, "Trapezoid");
  437. svg.Children.Add(new SvgPolygonElement()
  438. {
  439. Points = new List<SvgPoint>()
  440. {
  441. new SvgPoint(new SvgLength(svgPts[0].X.Value - dx, SvgLengthUnits.Percentage), svgPts[0].Y),
  442. new SvgPoint(new SvgLength(svgPts[1].X.Value + dx, SvgLengthUnits.Percentage), svgPts[1].Y),
  443. new SvgPoint(new SvgLength(svgPts[2].X.Value - dx, SvgLengthUnits.Percentage), svgPts[2].Y),
  444. new SvgPoint(new SvgLength(svgPts[3].X.Value + dx, SvgLengthUnits.Percentage), svgPts[3].Y),
  445. },
  446. Stroke = pntStroke0,
  447. Fill = paintTrans,
  448. });
  449. g.DrawSvg(svgDoc, new RectangleF(ip.X, ip.Y, grid.Radius * 2, grid.Radius * 2));
  450.  
  451. // Done:
  452. return bmp;
  453. }
  454. }
  455. }
  456.