LineTool.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Windows.Media;
  4. using PixiEditor.Models.DataHolders;
  5. using PixiEditor.Models.Enums;
  6. using PixiEditor.Models.Layers;
  7. using PixiEditor.Models.Position;
  8. using PixiEditor.Models.Tools.ToolSettings.Settings;
  9. using PixiEditor.Models.Tools.ToolSettings.Toolbars;
  10. namespace PixiEditor.Models.Tools.Tools
  11. {
  12. public class LineTool : ShapeTool
  13. {
  14. public LineTool()
  15. {
  16. Tooltip = "Draws line on canvas (L). Hold Shift to draw even line.";
  17. Toolbar = new BasicToolbar();
  18. }
  19. public override ToolType ToolType => ToolType.Line;
  20. public override LayerChange[] Use(Layer layer, Coordinates[] coordinates, Color color)
  21. {
  22. BitmapPixelChanges pixels =
  23. BitmapPixelChanges.FromSingleColoredArray(
  24. CreateLine(
  25. coordinates,
  26. Toolbar.GetSetting<SizeSetting>("ToolSize").Value,
  27. CapType.Square,
  28. CapType.Square), color);
  29. return Only(pixels, layer);
  30. }
  31. public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness)
  32. {
  33. return CreateLine(new[] { end, start }, thickness, CapType.Square, CapType.Square);
  34. }
  35. public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
  36. {
  37. return CreateLine(new[] { end, start }, thickness, startCap, endCap);
  38. }
  39. private IEnumerable<Coordinates> CreateLine(Coordinates[] coordinates, int thickness, CapType startCap, CapType endCap)
  40. {
  41. Coordinates startingCoordinates = coordinates[^1];
  42. Coordinates latestCoordinates = coordinates[0];
  43. if (thickness == 1)
  44. {
  45. return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X, latestCoordinates.Y);
  46. }
  47. return GetLinePoints(startingCoordinates, latestCoordinates, thickness, startCap, endCap);
  48. }
  49. private IEnumerable<Coordinates> GetLinePoints(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
  50. {
  51. IEnumerable<Coordinates> startingCap = GetCapCoordinates(startCap, start, thickness);
  52. if (start == end)
  53. {
  54. return startingCap;
  55. }
  56. IEnumerable<Coordinates> line = BresenhamLine(start.X, start.Y, end.X, end.Y);
  57. List<Coordinates> output = new List<Coordinates>(startingCap);
  58. output.AddRange(GetCapCoordinates(endCap, end, thickness));
  59. if (line.Count() > 2)
  60. {
  61. output.AddRange(GetThickShape(line.Except(new[] { start, end }).ToArray(), thickness));
  62. }
  63. return output.Distinct();
  64. }
  65. private IEnumerable<Coordinates> GetCapCoordinates(CapType cap, Coordinates position, int thickness)
  66. {
  67. switch (cap)
  68. {
  69. case CapType.Round:
  70. {
  71. return GetRoundCap(position, thickness); // Round cap is not working very well, circle tool must be improved
  72. }
  73. default:
  74. return GetThickShape(new[] { position }, thickness);
  75. }
  76. }
  77. /// <summary>
  78. /// Gets points for rounded cap on specified position and thickness.
  79. /// </summary>
  80. /// <param name="position">Starting position of cap.</param>
  81. /// <param name="thickness">Thickness of cap.</param>
  82. private IEnumerable<Coordinates> GetRoundCap(Coordinates position, int thickness)
  83. {
  84. CircleTool circle = new CircleTool();
  85. Coordinates[] rectangleCords = CoordinatesCalculator.RectangleToCoordinates(
  86. CoordinatesCalculator.CalculateThicknessCenter(position, thickness));
  87. return circle.CreateEllipse(rectangleCords[0], rectangleCords[^1], 1, true);
  88. }
  89. private IEnumerable<Coordinates> BresenhamLine(int x1, int y1, int x2, int y2)
  90. {
  91. List<Coordinates> coordinates = new List<Coordinates>();
  92. if (x1 == x2 && y1 == y2)
  93. {
  94. return new[] { new Coordinates(x1, y1) };
  95. }
  96. int d, dx, dy, ai, bi, xi, yi;
  97. int x = x1, y = y1;
  98. if (x1 < x2)
  99. {
  100. xi = 1;
  101. dx = x2 - x1;
  102. }
  103. else
  104. {
  105. xi = -1;
  106. dx = x1 - x2;
  107. }
  108. if (y1 < y2)
  109. {
  110. yi = 1;
  111. dy = y2 - y1;
  112. }
  113. else
  114. {
  115. yi = -1;
  116. dy = y1 - y2;
  117. }
  118. coordinates.Add(new Coordinates(x, y));
  119. if (dx > dy)
  120. {
  121. ai = (dy - dx) * 2;
  122. bi = dy * 2;
  123. d = bi - dx;
  124. while (x != x2)
  125. {
  126. if (d >= 0)
  127. {
  128. x += xi;
  129. y += yi;
  130. d += ai;
  131. }
  132. else
  133. {
  134. d += bi;
  135. x += xi;
  136. }
  137. coordinates.Add(new Coordinates(x, y));
  138. }
  139. }
  140. else
  141. {
  142. ai = (dx - dy) * 2;
  143. bi = dx * 2;
  144. d = bi - dy;
  145. while (y != y2)
  146. {
  147. if (d >= 0)
  148. {
  149. x += xi;
  150. y += yi;
  151. d += ai;
  152. }
  153. else
  154. {
  155. d += bi;
  156. y += yi;
  157. }
  158. coordinates.Add(new Coordinates(x, y));
  159. }
  160. }
  161. return coordinates;
  162. }
  163. }
  164. }