mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-13 08:01:26 -05:00
Geometry, nesting and import code now call PortableMath instead of System.Math for Sin, Cos, Tan, Atan, Atan2, Asin and Acos (Core, Engine, IO, Data; 269 calls in 64 files). A test fails if those projects call the runtime's trig again. With the runtime's trig, Windows and Linux disagreed in the last bit at non-quarter angles and the same job could nest differently: the learned-angle nests (a cross fixture) placed 23 parts in Strip on Windows and 28 on Linux. Now every Engine.Tests pin gives the same layout on both (3600 passing on Linux and on DesktopPC), Linux layouts are unchanged, and the learned-angle rows are re-pinned to the shared layouts, which pass the overlap and work-area checks, and run on every platform again. Fixes Project Memory 518f3b58.
155 lines
4.7 KiB
C#
155 lines
4.7 KiB
C#
using System.Collections.Generic;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.Math;
|
|
|
|
namespace OpenNest;
|
|
|
|
/// <summary>
|
|
/// Generates interlocking spike/V-groove pairs along the split edge.
|
|
/// Spikes protrude from the positive side into the negative side.
|
|
/// V-grooves on the negative side receive the spikes for self-alignment during welding.
|
|
/// The weld gap (grooveDepth - spikeDepth) is the clearance at the tip when assembled.
|
|
/// </summary>
|
|
public class SpikeGrooveSplit : ISplitFeature
|
|
{
|
|
public string Name => "Spike / V-Groove";
|
|
|
|
public SplitFeatureResult GenerateFeatures(
|
|
SplitLine line,
|
|
double extentStart,
|
|
double extentEnd,
|
|
SplitParameters parameters
|
|
)
|
|
{
|
|
var extent = extentEnd - extentStart;
|
|
var pairCount = parameters.SpikePairCount;
|
|
var spikeDepth = parameters.SpikeDepth;
|
|
var grooveDepth = parameters.GrooveDepth;
|
|
var angleRad = OpenNest.Math.Angle.ToRadians(parameters.SpikeAngle / 2);
|
|
var spikeHalfWidth = spikeDepth * PortableMath.Tan(angleRad);
|
|
var grooveHalfWidth = grooveDepth * PortableMath.Tan(angleRad);
|
|
|
|
var isVertical = line.Axis == CutOffAxis.Vertical;
|
|
var pos = line.Position;
|
|
|
|
// Use custom positions if provided, otherwise place evenly with margin
|
|
var pairPositions = new List<double>();
|
|
if (line.FeaturePositions.Count > 0)
|
|
{
|
|
pairPositions.AddRange(line.FeaturePositions);
|
|
}
|
|
else if (pairCount == 1)
|
|
{
|
|
pairPositions.Add(extentStart + extent / 2);
|
|
}
|
|
else
|
|
{
|
|
var margin = extent * 0.15;
|
|
var usable = extent - 2 * margin;
|
|
for (var i = 0; i < pairCount; i++)
|
|
pairPositions.Add(extentStart + margin + usable * i / (pairCount - 1));
|
|
}
|
|
|
|
var negEntities = BuildGrooveSide(
|
|
pairPositions,
|
|
grooveHalfWidth,
|
|
grooveDepth,
|
|
extentStart,
|
|
extentEnd,
|
|
pos,
|
|
isVertical
|
|
);
|
|
var posEntities = BuildSpikeSide(
|
|
pairPositions,
|
|
spikeHalfWidth,
|
|
spikeDepth,
|
|
extentStart,
|
|
extentEnd,
|
|
pos,
|
|
isVertical
|
|
);
|
|
|
|
return new SplitFeatureResult(negEntities, posEntities);
|
|
}
|
|
|
|
private static List<Entity> BuildGrooveSide(
|
|
List<double> pairPositions,
|
|
double halfWidth,
|
|
double depth,
|
|
double extentStart,
|
|
double extentEnd,
|
|
double pos,
|
|
bool isVertical
|
|
)
|
|
{
|
|
var entities = new List<Entity>();
|
|
var cursor = extentStart;
|
|
|
|
foreach (var center in pairPositions)
|
|
{
|
|
var grooveStart = center - halfWidth;
|
|
var grooveEnd = center + halfWidth;
|
|
|
|
if (grooveStart > cursor + OpenNest.Math.Tolerance.Epsilon)
|
|
entities.Add(MakeLine(pos, cursor, pos, grooveStart, isVertical));
|
|
|
|
entities.Add(MakeLine(pos, grooveStart, pos - depth, center, isVertical));
|
|
entities.Add(MakeLine(pos - depth, center, pos, grooveEnd, isVertical));
|
|
|
|
cursor = grooveEnd;
|
|
}
|
|
|
|
if (extentEnd > cursor + OpenNest.Math.Tolerance.Epsilon)
|
|
entities.Add(MakeLine(pos, cursor, pos, extentEnd, isVertical));
|
|
|
|
return entities;
|
|
}
|
|
|
|
private static List<Entity> BuildSpikeSide(
|
|
List<double> pairPositions,
|
|
double halfWidth,
|
|
double depth,
|
|
double extentStart,
|
|
double extentEnd,
|
|
double pos,
|
|
bool isVertical
|
|
)
|
|
{
|
|
var entities = new List<Entity>();
|
|
var cursor = extentEnd;
|
|
|
|
for (var i = pairPositions.Count - 1; i >= 0; i--)
|
|
{
|
|
var center = pairPositions[i];
|
|
var spikeEnd = center + halfWidth;
|
|
var spikeStart = center - halfWidth;
|
|
|
|
if (cursor > spikeEnd + OpenNest.Math.Tolerance.Epsilon)
|
|
entities.Add(MakeLine(pos, cursor, pos, spikeEnd, isVertical));
|
|
|
|
entities.Add(MakeLine(pos, spikeEnd, pos - depth, center, isVertical));
|
|
entities.Add(MakeLine(pos - depth, center, pos, spikeStart, isVertical));
|
|
|
|
cursor = spikeStart;
|
|
}
|
|
|
|
if (cursor > extentStart + OpenNest.Math.Tolerance.Epsilon)
|
|
entities.Add(MakeLine(pos, cursor, pos, extentStart, isVertical));
|
|
|
|
return entities;
|
|
}
|
|
|
|
private static Line MakeLine(
|
|
double splitAxis1,
|
|
double along1,
|
|
double splitAxis2,
|
|
double along2,
|
|
bool isVertical
|
|
)
|
|
{
|
|
return isVertical
|
|
? new Line(new Vector(splitAxis1, along1), new Vector(splitAxis2, along2))
|
|
: new Line(new Vector(along1, splitAxis1), new Vector(along2, splitAxis2));
|
|
}
|
|
}
|