/// file: BezierPencil.cs /// Author: Maxime Rohmer /// Brief: A unique tool that draws Bezier curves /// Version: 0.1.0 /// Date: 17/06/2022 using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Paint_2 { internal class BezierPencil : PaintTool { private List> _drawings; private List> _drawingsRedo; private bool _needsFullRefresh; private bool _isCtrlPressed; private bool _isShiftPressed; private PaintToolUtils Utils; private List _colors; private List _colorsRedo; private List _widths; private List _widthsRedo; private Color _color; private string _name; private int _width; public List> Drawings { get => _drawings; set => _drawings = value; } public List> DrawingsRedo { get => _drawingsRedo; set => _drawingsRedo = value; } public bool NeedsFullRefresh { get => _needsFullRefresh; set => _needsFullRefresh = value; } public List Colors { get => _colors; set => _colors = value; } public List ColorsRedo { get => _colorsRedo; set => _colorsRedo = value; } public List Widths { get => _widths; set => _widths = value; } public List WidthsRedo { get => _widthsRedo; set => _widthsRedo = value; } public bool IsShiftPressed { get => _isShiftPressed; set => _isShiftPressed = value; } public bool IsCtrlPressed { get => _isCtrlPressed; set => _isCtrlPressed = value; } public Color Color { get => _color; set => _color = value; } public string Name { get => _name; set => _name = value; } public int Width { get => _width; set => _width = value; } public BezierPencil(string name) { NeedsFullRefresh = true; Drawings = new List>(); DrawingsRedo = new List>(); Colors = new List(); ColorsRedo = new List(); Widths = new List(); WidthsRedo = new List(); Name = name; Utils = new PaintToolUtils(this); } public void Add(Point point) { if (Drawings[Drawings.Count - 1].Count == 0) { Drawings[Drawings.Count - 1].Add(point); } } public void Clear() { Utils.StandartClear(); } public List GetLastColors(int colorNumber) { return Utils.SandartGetLastColors(colorNumber); } public void Paint(Bitmap canvas) { Graphics gr = Graphics.FromImage(canvas); Size pointSize; List points = new List(); foreach (List drawing in Drawings) { if (drawing.Count > 0) { points.Add(drawing[0]); } } if (points.Count > 0) { for (int i = 0; i < points.Count; i++) { pointSize = new Size(Widths[0] / 2, Widths[0] / 2); //ContinuousBezierGenerator(gr, points, Colors[Colors.Count-1], Widths[Widths.Count -1]); AdjustedBezierGenerator(gr, points, Colors[Colors.Count - 1], Widths[Widths.Count - 1]); } } } private void AdjustedBezierGenerator(Graphics gr, List points, Color color, int width) { foreach (Point p in points) { gr.FillEllipse(new SolidBrush(color), new Rectangle(new Point(p.X - width / 2, p.Y - width / 2), new Size(width,width))); } if (points.Count >= 3) { float precision = 0.01f; for (float t = 0; t <= 1; t += precision) { List DumpList = new List(); List WorkingList = new List(); foreach (Point p in points) { WorkingList.Add(new Point(p.X, p.Y)); } while (WorkingList.Count > 1) { for (int i = 0; i < WorkingList.Count - 1; i++) { Point p1 = WorkingList[i]; Point p2 = WorkingList[i + 1]; Point tmp = Utils.Lerp(p1, p2, t); DumpList.Add(tmp); } WorkingList.Clear(); foreach (Point p in DumpList) { WorkingList.Add(new Point(p.X, p.Y)); } DumpList.Clear(); } gr.FillEllipse(new SolidBrush(color), new Rectangle(WorkingList[0].X - Widths[0] / 2, WorkingList[0].Y - Widths[0] / 2, width, width)); } } } private void ContinuousBezierGenerator(Graphics gr, List points, Color color, int width) { if (points.Count >= 4 && points.Count % 2 == 0) { float precision = 0.01f; for (float t = 0; t <= 1; t += precision) { List DumpList = new List(); List WorkingList = new List(points); while (WorkingList.Count != 1) { if (WorkingList.Count > 1) { for (int pos = 0; pos < WorkingList.Count - 1; pos++) { DumpList.Add(Utils.Lerp(WorkingList[pos], WorkingList[pos + 1], t)); } } else { DumpList.Add(Utils.Lerp(WorkingList[0], WorkingList[1], precision)); } WorkingList = new List(DumpList); DumpList = new List(); } gr.FillEllipse(new SolidBrush(color), new Rectangle(WorkingList[0].X - Widths[0] / 2, WorkingList[0].Y - Widths[0] / 2, width, width)); } } } public string PaintSVG() { string result = ""; string newLine = Environment.NewLine; Size pointSize; List points = new List(); foreach (List drawing in Drawings) { if (drawing.Count > 0) { points.Add(drawing[0]); } } if (points.Count > 0) { for (int i = 0; i < points.Count; i++) { pointSize = new Size(Widths[0] / 2, Widths[0] / 2); //Not really usefull in the SVG world because only the last layer will be displayed //result += ""; //result += newLine; result += ContinuousBezierGeneratorSVG(points, Colors[Colors.Count - 1], Widths[Widths.Count - 1]); } } return result; } private string ContinuousBezierGeneratorSVG(List points, Color color, int width) { string result = ""; string newLine = Environment.NewLine; if (points.Count >= 3) { float precision = 0.01f; for (float t = 0; t <= 1; t += precision) { List DumpList = new List(); List WorkingList = new List(); foreach (Point p in points) { WorkingList.Add(new Point(p.X, p.Y)); } while (WorkingList.Count > 1) { for (int i = 0; i < WorkingList.Count - 1; i++) { Point p1 = WorkingList[i]; Point p2 = WorkingList[i + 1]; Point tmp = Utils.Lerp(p1, p2, t); DumpList.Add(tmp); } WorkingList.Clear(); foreach (Point p in DumpList) { WorkingList.Add(new Point(p.X, p.Y)); } DumpList.Clear(); } result += ""; result += newLine; } } return result; } private void BezierGenerator(Graphics gr, Point p1, Point p2, Point p3, Point p4, int i) { gr.DrawLine(new Pen(Colors[i - 4], Widths[i - 4]), p1, p2); gr.DrawLine(new Pen(Colors[i - 3], Widths[i - 3]), p2, p3); gr.DrawLine(new Pen(Colors[i - 2], Widths[i - 2]), p3, p4); //float t = 0.5f; float precision = 0.01f; for (float t = 0; t <= 1; t += precision) { Point p1_2 = Utils.Lerp(p1, p2, t); Point p2_2 = Utils.Lerp(p2, p3, t); Point p3_2 = Utils.Lerp(p3, p4, t); //Drawing the second step of the curve Point p1_3 = Utils.Lerp(p1_2, p2_2, t); Point p2_3 = Utils.Lerp(p2_2, p3_2, t); //Drawing the Bezier Point Point p1_4 = Utils.Lerp(p1_3, p2_3, t); gr.FillEllipse(new SolidBrush(GetRandomColor()), new Rectangle(p1_4.X - Widths[0] / 2, p1_4.Y - Widths[0] / 2, Widths[0], Widths[0])); } } private Color GetRandomColor() { Random rnd = new Random(); return Color.FromArgb(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256)); } public void Start(Color color, int width) { Utils.StandartStart(color, width); } public void Stop() { //Empty } public override string ToString() { return Name; } public void Undo() { Utils.StandartUndo(); } public void Redo() { Utils.StandartRedo(); } public object Clone() { BezierPencil result = new BezierPencil(Name); result.Width = Width; result.Color = Color; return (Object)(result); } } }