mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-15 00:55:34 -05:00
Dragging a Shape Library leader on a bolt circle, hole or corner arc now moves its arrow round the curve so the line stays radial. The flange's bolt circle is drawn dashed in teal instead of dotted orange, which read as a solid dimension line. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
200 lines
9.2 KiB
C#
200 lines
9.2 KiB
C#
using OpenNest.Geometry;
|
|
using OpenNest.Math;
|
|
|
|
namespace OpenNest.Shapes
|
|
{
|
|
public enum ShapeDimensionKind
|
|
{
|
|
/// <summary>A dimension line outside the shape, past its bounds on the <see cref="ShapeDimension.Side"/> side.</summary>
|
|
Linear,
|
|
|
|
/// <summary>A dimension line drawn directly between the two points, such as across a hole.</summary>
|
|
Inline,
|
|
|
|
/// <summary>A leader from <see cref="ShapeDimension.Start"/> out along <see cref="ShapeDimension.Side"/>.</summary>
|
|
Leader,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Where the Shape Library draws one parameter's dimension on the shape's preview, in the
|
|
/// shape's own coordinates (those of the entities <see cref="ShapeDefinition.GetDrawing"/>
|
|
/// builds). Annotation only: it never affects the drawing.
|
|
/// </summary>
|
|
public sealed class ShapeDimension
|
|
{
|
|
private ShapeDimension(string property, ShapeDimensionKind kind, Vector start, Vector end, Vector side, string prefix)
|
|
{
|
|
Property = property;
|
|
Kind = kind;
|
|
Start = start;
|
|
End = end;
|
|
Side = side;
|
|
Prefix = prefix ?? "";
|
|
}
|
|
|
|
/// <summary>The name of the shape property the dimension shows and edits.</summary>
|
|
public string Property { get; }
|
|
|
|
public ShapeDimensionKind Kind { get; }
|
|
|
|
public Vector Start { get; }
|
|
|
|
/// <summary>The second measured point; equal to <see cref="Start"/> for a leader.</summary>
|
|
public Vector End { get; }
|
|
|
|
/// <summary>Unit vector: the side a linear dimension sits on, or a leader's direction.</summary>
|
|
public Vector Side { get; }
|
|
|
|
/// <summary>Text before the value, such as "Ø" or "R".</summary>
|
|
public string Prefix { get; }
|
|
|
|
/// <summary>
|
|
/// An integer property shown after the value as "x {count} {noun}", such as a
|
|
/// flange's hole count beside its hole diameter; null when there is none.
|
|
/// </summary>
|
|
public string CountProperty { get; private init; }
|
|
|
|
/// <summary>The plural noun after the count, such as "holes".</summary>
|
|
public string CountNoun { get; private init; }
|
|
|
|
/// <summary>
|
|
/// The center of the hole a diameter leader calls out; null for other dimensions.
|
|
/// The leader's line crosses the hole through it, wherever the label is dragged.
|
|
/// </summary>
|
|
public Vector? HoleCenter { get; private init; }
|
|
|
|
/// <summary>The radius of the hole at <see cref="HoleCenter"/>.</summary>
|
|
public double HoleRadius { get; private init; }
|
|
|
|
/// <summary>
|
|
/// The center of a reference circle (such as a flange's bolt circle) that the preview
|
|
/// draws dashed; null when there is none. A leader on it points at this center
|
|
/// wherever the label is dragged.
|
|
/// </summary>
|
|
public Vector? CircleCenter { get; private init; }
|
|
|
|
/// <summary>The radius of the reference circle at <see cref="CircleCenter"/>.</summary>
|
|
public double CircleRadius { get; private init; }
|
|
|
|
/// <summary>True when the preview draws a dashed reference circle for this dimension.</summary>
|
|
public bool DrawsCircle => CircleCenter != null;
|
|
|
|
/// <summary>
|
|
/// The center of the arc a radius leader calls out; null for other dimensions. The
|
|
/// leader points at it, its arrow sliding along the arc as the label is dragged.
|
|
/// </summary>
|
|
public Vector? ArcCenter { get; private init; }
|
|
|
|
/// <summary>The radius of the arc at <see cref="ArcCenter"/>.</summary>
|
|
public double ArcRadius { get; private init; }
|
|
|
|
/// <summary>The angle the arc at <see cref="ArcCenter"/> starts at, in radians counterclockwise.</summary>
|
|
public double ArcStartAngle { get; private init; }
|
|
|
|
/// <summary>The angle the arc at <see cref="ArcCenter"/> ends at; never less than <see cref="ArcStartAngle"/>.</summary>
|
|
public double ArcEndAngle { get; private init; }
|
|
|
|
/// <summary>This dimension with "x {count} {noun}" after its value.</summary>
|
|
public ShapeDimension WithCount(string countProperty, string noun) =>
|
|
new(Property, Kind, Start, End, Side, Prefix)
|
|
{
|
|
CountProperty = countProperty,
|
|
CountNoun = noun,
|
|
HoleCenter = HoleCenter,
|
|
HoleRadius = HoleRadius,
|
|
CircleCenter = CircleCenter,
|
|
CircleRadius = CircleRadius,
|
|
ArcCenter = ArcCenter,
|
|
ArcRadius = ArcRadius,
|
|
ArcStartAngle = ArcStartAngle,
|
|
ArcEndAngle = ArcEndAngle,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Measures from <paramref name="start"/> to <paramref name="end"/> across
|
|
/// <paramref name="side"/>, which must be one of the four axis directions.
|
|
/// </summary>
|
|
public static ShapeDimension Linear(string property, Vector start, Vector end, Vector side, string prefix = "") =>
|
|
new(property, ShapeDimensionKind.Linear, start, end, side, prefix);
|
|
|
|
public static ShapeDimension Inline(string property, Vector start, Vector end, string prefix = "") =>
|
|
new(property, ShapeDimensionKind.Inline, start, end, new Vector(0, 1), prefix);
|
|
|
|
public static ShapeDimension Leader(string property, Vector point, Vector direction, string prefix = "")
|
|
{
|
|
var length = System.Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y);
|
|
return new(property, ShapeDimensionKind.Leader, point, point,
|
|
new Vector(direction.X / length, direction.Y / length), prefix);
|
|
}
|
|
|
|
/// <summary>
|
|
/// A diameter leader on the hole at <paramref name="center"/>: it starts on the hole's
|
|
/// edge facing <paramref name="direction"/>, and its line runs across the hole through the center.
|
|
/// </summary>
|
|
public static ShapeDimension HoleLeader(string property, Vector center, double radius, Vector direction, string prefix = "")
|
|
{
|
|
var length = System.Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y);
|
|
var unit = new Vector(direction.X / length, direction.Y / length);
|
|
var edge = new Vector(center.X + unit.X * radius, center.Y + unit.Y * radius);
|
|
return new(property, ShapeDimensionKind.Leader, edge, edge, unit, prefix)
|
|
{
|
|
HoleCenter = center,
|
|
HoleRadius = radius,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// A diameter leader on a dashed reference circle (such as a flange's bolt circle) at
|
|
/// <paramref name="center"/>: it starts on the circle facing <paramref name="direction"/>
|
|
/// and runs out from there, its arrow pointing in onto the circle; no line crosses it.
|
|
/// Dragged, the arrow moves around the circle so the leader keeps pointing at the center.
|
|
/// </summary>
|
|
public static ShapeDimension CircleLeader(string property, Vector center, double radius, Vector direction, string prefix = "")
|
|
{
|
|
var length = System.Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y);
|
|
var unit = new Vector(direction.X / length, direction.Y / length);
|
|
var edge = new Vector(center.X + unit.X * radius, center.Y + unit.Y * radius);
|
|
return new(property, ShapeDimensionKind.Leader, edge, edge, unit, prefix)
|
|
{
|
|
CircleCenter = center,
|
|
CircleRadius = radius,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// A radius leader on the arc at <paramref name="center"/> from <paramref name="startAngle"/>
|
|
/// counterclockwise to <paramref name="endAngle"/> (radians): it starts at the arc's middle
|
|
/// and runs out along that radius, in line with the center.
|
|
/// </summary>
|
|
public static ShapeDimension ArcLeader(string property, Vector center, double radius, double startAngle, double endAngle, string prefix = "")
|
|
{
|
|
var middle = (startAngle + endAngle) / 2;
|
|
var unit = new Vector(PortableMath.Cos(middle), PortableMath.Sin(middle));
|
|
var edge = new Vector(center.X + unit.X * radius, center.Y + unit.Y * radius);
|
|
return new(property, ShapeDimensionKind.Leader, edge, edge, unit, prefix)
|
|
{
|
|
ArcCenter = center,
|
|
ArcRadius = radius,
|
|
ArcStartAngle = startAngle,
|
|
ArcEndAngle = endAngle,
|
|
};
|
|
}
|
|
|
|
/// <summary>The distance the dimension shows between its points (zero for a leader).</summary>
|
|
public double Measured =>
|
|
Kind switch
|
|
{
|
|
ShapeDimensionKind.Linear => System.Math.Abs(Side.X) > System.Math.Abs(Side.Y)
|
|
? System.Math.Abs(End.Y - Start.Y)
|
|
: System.Math.Abs(End.X - Start.X),
|
|
ShapeDimensionKind.Inline => Start.DistanceTo(End),
|
|
_ => 0,
|
|
};
|
|
|
|
internal static readonly Vector Up = new(0, 1);
|
|
internal static readonly Vector Down = new(0, -1);
|
|
internal static readonly Vector Left = new(-1, 0);
|
|
internal static readonly Vector Right = new(1, 0);
|
|
}
|
|
}
|