From c6f56f1b9408e8c51217c1634e3708b50ca0acb4 Mon Sep 17 00:00:00 2001
From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com>
Date: Tue, 10 Dec 2024 04:16:06 +0100
Subject: [PATCH] Made `Arrow` immutable (#1170)
---
.../Editable/EditEngines/ArrowedEditEngine.cs | 68 ++++++++++---------
Pinta.Tools/Editable/Shapes/Arrow.cs | 50 +++++++-------
.../Editable/Shapes/LineCurveSeriesEngine.cs | 62 ++++++++++-------
3 files changed, 97 insertions(+), 83 deletions(-)
diff --git a/Pinta.Tools/Editable/EditEngines/ArrowedEditEngine.cs b/Pinta.Tools/Editable/EditEngines/ArrowedEditEngine.cs
index ae4f56ccad..38858e0a0c 100644
--- a/Pinta.Tools/Editable/EditEngines/ArrowedEditEngine.cs
+++ b/Pinta.Tools/Editable/EditEngines/ArrowedEditEngine.cs
@@ -46,8 +46,8 @@ public abstract class ArrowedEditEngine : BaseEditEngine
private Gtk.SpinButton? arrow_length_offset;
private Gtk.Label? arrow_length_offset_label;
- private readonly Arrow previous_settings_1 = new ();
- private readonly Arrow previous_settings_2 = new ();
+ private Arrow previous_settings_1 = new ();
+ private Arrow previous_settings_2 = new ();
// NRT - These are all set by HandleBuildToolBar
private ISettingsService settings = null!;
@@ -117,9 +117,9 @@ private void ArrowEnabledToggled (bool arrow1)
return;
if (arrow1)
- activeEngine.Arrow1.Show = ArrowOneEnabled;
+ activeEngine.Arrow1 = activeEngine.Arrow1 with { Show = ArrowOneEnabled };
else
- activeEngine.Arrow2.Show = ArrowTwoEnabled;
+ activeEngine.Arrow2 = activeEngine.Arrow2 with { Show = ArrowTwoEnabled };
DrawActiveShape (false, false, true, false, false);
@@ -158,16 +158,21 @@ protected void setNewArrowSettings (LineCurveSeriesEngine newEngine)
if (show_arrow_one_box == null)
return;
- newEngine.Arrow1.Show = ArrowOneEnabled;
- newEngine.Arrow2.Show = ArrowTwoEnabled;
-
- newEngine.Arrow1.ArrowSize = ArrowSize.Value;
- newEngine.Arrow1.AngleOffset = ArrowAngleOffset.Value;
- newEngine.Arrow1.LengthOffset = ArrowLengthOffset.Value;
-
- newEngine.Arrow2.ArrowSize = newEngine.Arrow1.ArrowSize;
- newEngine.Arrow2.AngleOffset = newEngine.Arrow1.AngleOffset;
- newEngine.Arrow2.LengthOffset = newEngine.Arrow1.LengthOffset;
+ double newArrowSize = ArrowSize.Value;
+ double newAngleOffset = ArrowAngleOffset.Value;
+ double newLengthOffset = ArrowLengthOffset.Value;
+
+ newEngine.Arrow1 = new (
+ Show: ArrowOneEnabled,
+ ArrowSize: newArrowSize,
+ AngleOffset: newAngleOffset,
+ LengthOffset: newLengthOffset);
+
+ newEngine.Arrow2 = new (
+ Show: ArrowTwoEnabled,
+ ArrowSize: newArrowSize,
+ AngleOffset: newAngleOffset,
+ LengthOffset: newLengthOffset);
}
@@ -211,12 +216,14 @@ protected override void RecallPreviousSettings ()
protected override void StorePreviousSettings ()
{
if (show_arrow_one_box != null) {
- previous_settings_1.Show = ArrowOneEnabled;
- previous_settings_2.Show = ArrowTwoEnabled;
- previous_settings_1.ArrowSize = ArrowSize.Value;
- previous_settings_1.AngleOffset = ArrowAngleOffset.Value;
- previous_settings_1.LengthOffset = ArrowLengthOffset.Value;
+ previous_settings_1 = new Arrow (
+ Show: ArrowOneEnabled,
+ ArrowSize: ArrowSize.Value,
+ AngleOffset: ArrowAngleOffset.Value,
+ LengthOffset: ArrowLengthOffset.Value);
+
+ previous_settings_2 = previous_settings_2 with { Show = ArrowTwoEnabled };
//Other Arrow2 settings are unnecessary since they are the same as Arrow1's.
}
@@ -293,11 +300,10 @@ private Gtk.SpinButton CreateArrowSize ()
Gtk.SpinButton result = GtkExtensions.CreateToolBarSpinButton (1, 100, 1, settings.GetSetting (ARROW_SIZE_SETTING (tool_prefix), 10));
result.OnValueChanged += (o, e) => {
var activeEngine = (LineCurveSeriesEngine?) ActiveShapeEngine;
- if (activeEngine == null)
- return;
+ if (activeEngine == null) return;
var size = result.Value;
- activeEngine.Arrow1.ArrowSize = size;
- activeEngine.Arrow2.ArrowSize = size;
+ activeEngine.Arrow1 = activeEngine.Arrow1 with { ArrowSize = size };
+ activeEngine.Arrow2 = activeEngine.Arrow2 with { ArrowSize = size };
DrawActiveShape (false, false, true, false, false);
StorePreviousSettings ();
};
@@ -315,11 +321,10 @@ private Gtk.SpinButton CreateArrowAngleOffset ()
Gtk.SpinButton result = GtkExtensions.CreateToolBarSpinButton (-89, 89, 1, settings.GetSetting (ARROW_ANGLE_SETTING (tool_prefix), 15));
result.OnValueChanged += (o, e) => {
var activeEngine = (LineCurveSeriesEngine?) ActiveShapeEngine;
- if (activeEngine == null)
- return;
+ if (activeEngine == null) return;
var angle = result.Value;
- activeEngine.Arrow1.AngleOffset = angle;
- activeEngine.Arrow2.AngleOffset = angle;
+ activeEngine.Arrow1 = activeEngine.Arrow1 with { AngleOffset = angle };
+ activeEngine.Arrow2 = activeEngine.Arrow2 with { AngleOffset = angle };
DrawActiveShape (false, false, true, false, false);
StorePreviousSettings ();
};
@@ -337,13 +342,10 @@ private Gtk.SpinButton CreateArrowLengthOffset ()
Gtk.SpinButton result = GtkExtensions.CreateToolBarSpinButton (-100, 100, 1, settings.GetSetting (ARROW_LENGTH_SETTING (tool_prefix), 10));
result.OnValueChanged += (o, e) => {
var activeEngine = (LineCurveSeriesEngine?) ActiveShapeEngine;
-
- if (activeEngine == null)
- return;
-
+ if (activeEngine == null) return;
var length = result.Value;
- activeEngine.Arrow1.LengthOffset = length;
- activeEngine.Arrow2.LengthOffset = length;
+ activeEngine.Arrow1 = activeEngine.Arrow1 with { LengthOffset = length };
+ activeEngine.Arrow2 = activeEngine.Arrow2 with { LengthOffset = length };
DrawActiveShape (false, false, true, false, false);
StorePreviousSettings ();
};
diff --git a/Pinta.Tools/Editable/Shapes/Arrow.cs b/Pinta.Tools/Editable/Shapes/Arrow.cs
index 7e0043c009..8311a9deb3 100644
--- a/Pinta.Tools/Editable/Shapes/Arrow.cs
+++ b/Pinta.Tools/Editable/Shapes/Arrow.cs
@@ -30,35 +30,34 @@
namespace Pinta.Tools;
-public sealed class Arrow
+public readonly record struct Arrow (
+ bool Show,
+ double ArrowSize,
+ double AngleOffset,
+ double LengthOffset)
{
- public bool Show { get; internal set; } = false;
- public double ArrowSize { get; internal set; } = 10d;
- public double AngleOffset { get; internal set; } = 15d;
- public double LengthOffset { get; internal set; } = 10d;
+ public Arrow ()
+ : this (false, 10d, 15d, 10d)
+ { }
+}
+public static class ArrowExtensions
+{
private const double RadiansToDegrees = Math.PI / 180d;
private const double InvRadiansToDegrees = 180d / Math.PI;
- ///
- /// Returns a clone of the Arrow.
- ///
- /// A clone of the Arrow.
- public Arrow Clone ()
- => new () {
- Show = Show,
- ArrowSize = ArrowSize,
- AngleOffset = AngleOffset,
- LengthOffset = LengthOffset,
- };
-
///
/// Draws the arrow.
///
/// The drawing context.
/// The end point of a shape.
/// The point right before the end point.
- public RectangleD Draw (Context g, Color outlineColor, PointD endPoint, PointD almostEndPoint)
+ public static RectangleD Draw (
+ this in Arrow arrow,
+ Context g,
+ Color outlineColor,
+ PointD endPoint,
+ PointD almostEndPoint)
{
//First, calculate the ending angle.
double endingAngle = Math.Atan (Math.Abs (endPoint.Y - almostEndPoint.Y) / Math.Abs (endPoint.X - almostEndPoint.X)) * InvRadiansToDegrees;
@@ -82,16 +81,16 @@ public RectangleD Draw (Context g, Color outlineColor, PointD endPoint, PointD a
endPoint,
new PointD (
- endPoint.X + Math.Cos ((endingAngle + 270 + AngleOffset) * RadiansToDegrees) * ArrowSize,
- endPoint.Y + Math.Sin ((endingAngle + 270 + AngleOffset) * RadiansToDegrees) * ArrowSize * -1d),
+ endPoint.X + Math.Cos ((endingAngle + 270 + arrow.AngleOffset) * RadiansToDegrees) * arrow.ArrowSize,
+ endPoint.Y + Math.Sin ((endingAngle + 270 + arrow.AngleOffset) * RadiansToDegrees) * arrow.ArrowSize * -1d),
new PointD (
- endPoint.X + Math.Cos ((endingAngle + 180) * RadiansToDegrees) * (ArrowSize + LengthOffset),
- endPoint.Y + Math.Sin ((endingAngle + 180) * RadiansToDegrees) * (ArrowSize + LengthOffset) * -1d),
+ endPoint.X + Math.Cos ((endingAngle + 180) * RadiansToDegrees) * (arrow.ArrowSize + arrow.LengthOffset),
+ endPoint.Y + Math.Sin ((endingAngle + 180) * RadiansToDegrees) * (arrow.ArrowSize + arrow.LengthOffset) * -1d),
new PointD (
- endPoint.X + Math.Cos ((endingAngle + 90 - AngleOffset) * RadiansToDegrees) * ArrowSize,
- endPoint.Y + Math.Sin ((endingAngle + 90 - AngleOffset) * RadiansToDegrees) * ArrowSize * -1d),
+ endPoint.X + Math.Cos ((endingAngle + 90 - arrow.AngleOffset) * RadiansToDegrees) * arrow.ArrowSize,
+ endPoint.Y + Math.Sin ((endingAngle + 90 - arrow.AngleOffset) * RadiansToDegrees) * arrow.ArrowSize * -1d),
};
//Draw the arrow.
@@ -102,8 +101,7 @@ public RectangleD Draw (Context g, Color outlineColor, PointD endPoint, PointD a
PointD min = new (
X: Math.Min (Math.Min (arrowPoints[1].X, arrowPoints[2].X), arrowPoints[3].X),
- Y: Math.Min (Math.Min (arrowPoints[1].Y, arrowPoints[2].Y), arrowPoints[3].Y)
- );
+ Y: Math.Min (Math.Min (arrowPoints[1].Y, arrowPoints[2].Y), arrowPoints[3].Y));
return new RectangleD (
min.X,
diff --git a/Pinta.Tools/Editable/Shapes/LineCurveSeriesEngine.cs b/Pinta.Tools/Editable/Shapes/LineCurveSeriesEngine.cs
index ca2b11ffe0..72c7ead20c 100644
--- a/Pinta.Tools/Editable/Shapes/LineCurveSeriesEngine.cs
+++ b/Pinta.Tools/Editable/Shapes/LineCurveSeriesEngine.cs
@@ -32,8 +32,8 @@ namespace Pinta.Tools;
public sealed class LineCurveSeriesEngine : ShapeEngine
{
- public Arrow Arrow1 { get; }
- public Arrow Arrow2 { get; }
+ public Arrow Arrow1 { get; internal set; }
+ public Arrow Arrow2 { get; internal set; }
///
/// Create a new LineCurveSeriesEngine.
@@ -47,24 +47,41 @@ public sealed class LineCurveSeriesEngine : ShapeEngine
/// The fill color for the shape.
/// The width of the outline of the shape.
/// Defines the edge of the line drawn.
- public LineCurveSeriesEngine (UserLayer parentLayer, ReEditableLayer? drawingLayer, BaseEditEngine.ShapeTypes shapeType,
- bool antialiasing, bool closed, Color outlineColor, Color fillColor, int brushWidth, LineCap lineCap) : base (parentLayer,
- drawingLayer, shapeType, antialiasing, closed, outlineColor, fillColor, brushWidth, lineCap)
+ public LineCurveSeriesEngine (
+ UserLayer parentLayer,
+ ReEditableLayer? drawingLayer,
+ BaseEditEngine.ShapeTypes shapeType,
+ bool antialiasing,
+ bool closed,
+ Color outlineColor,
+ Color fillColor,
+ int brushWidth,
+ LineCap lineCap
+ ) : base (
+ parentLayer,
+ drawingLayer,
+ shapeType,
+ antialiasing,
+ closed,
+ outlineColor,
+ fillColor,
+ brushWidth,
+ lineCap)
{
Arrow1 = new ();
Arrow2 = new ();
}
private LineCurveSeriesEngine (LineCurveSeriesEngine src)
- : base (src)
+ : base (src)
{
- Arrow1 = src.Arrow1.Clone ();
- Arrow2 = src.Arrow2.Clone ();
+ Arrow1 = src.Arrow1;
+ Arrow2 = src.Arrow2;
}
- public override ShapeEngine Clone ()
+ public override LineCurveSeriesEngine Clone ()
{
- return new LineCurveSeriesEngine (this);
+ return new (this);
}
///
@@ -79,7 +96,7 @@ public override void GeneratePoints (int brush_width)
return;
}
- List generatedPoints = new List ();
+ List generatedPoints = new ();
//Generate tangents for each of the smaller cubic Bezier curves that make up each segment of the resulting curve.
@@ -87,11 +104,10 @@ public override void GeneratePoints (int brush_width)
//control point's tension and the following control point's tension.
//Stores all of the tangent values.
- List bezierTangents = new List ();
+ List bezierTangents = new ();
int pointCount = ControlPoints.Count - 1;
double pointCountDouble = pointCount;
- double tensionForPoint;
//Calculate the first tangent.
if (Closed) {
@@ -106,8 +122,7 @@ public override void GeneratePoints (int brush_width)
//Calculate all of the middle tangents.
for (int i = 1; i < pointCount; ++i) {
- tensionForPoint = ControlPoints[i].Tension * i / pointCountDouble;
-
+ double tensionForPoint = ControlPoints[i].Tension * i / pointCountDouble;
bezierTangents.Add (new PointD (
tensionForPoint *
(ControlPoints[i + 1].Position.X - ControlPoints[i - 1].Position.X),
@@ -130,13 +145,11 @@ public override void GeneratePoints (int brush_width)
(ControlPoints[pointCount].Position.Y - ControlPoints[pointCount - 1].Position.Y)));
}
-
- int iMinusOne;
-
//Generate the resulting curve's points with consecutive cubic Bezier curves that
//use the given points as end points and the calculated tangents as control points.
for (int i = 1; i < ControlPoints.Count; ++i) {
- iMinusOne = i - 1;
+
+ int iMinusOne = i - 1;
generatedPoints.AddRange (GenerateCubicBezierCurvePoints (
ControlPoints[iMinusOne].Position,
@@ -153,7 +166,7 @@ public override void GeneratePoints (int brush_width)
if (Closed) {
// Close the shape.
- iMinusOne = ControlPoints.Count - 1;
+ int iMinusOne = ControlPoints.Count - 1;
generatedPoints.AddRange (GenerateCubicBezierCurvePoints (
ControlPoints[iMinusOne].Position,
@@ -182,7 +195,7 @@ public override void GeneratePoints (int brush_width)
private static IEnumerable GenerateCubicBezierCurvePoints (PointD p0, PointD p1, PointD p2, PointD p3, int cPIndex)
{
//Note: this must be low enough for mouse clicks to be properly considered on/off the curve at any given point.
- double tInterval = .025d;
+ const double tInterval = .025d;
//t will go from 0d to 1d at the interval of tInterval.
for (double t = 0d; t < 1d + tInterval; t += tInterval) {
@@ -211,9 +224,10 @@ private static IEnumerable GenerateCubicBezierCurvePoints (Point
//This is done for both the X and Y given a value t going from 0d to 1d at a very small interval
//and given 4 points p0, p1, p2, and p3, where p0 and p3 are end points and p1 and p2 are control points.
- yield return new GeneratedPoint (new PointD (
- oneMinusTCubed * p0.X + oneMinusTSquaredTimesTTimesThree * p1.X + oneMinusTTimesTSquaredTimesThree * p2.X + tCubed * p3.X,
- oneMinusTCubed * p0.Y + oneMinusTSquaredTimesTTimesThree * p1.Y + oneMinusTTimesTSquaredTimesThree * p2.Y + tCubed * p3.Y),
+ yield return new (
+ new PointD (
+ oneMinusTCubed * p0.X + oneMinusTSquaredTimesTTimesThree * p1.X + oneMinusTTimesTSquaredTimesThree * p2.X + tCubed * p3.X,
+ oneMinusTCubed * p0.Y + oneMinusTSquaredTimesTTimesThree * p1.Y + oneMinusTTimesTSquaredTimesThree * p2.Y + tCubed * p3.Y),
cPIndex);
}
}