Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ classDiagram
class HelixParameters {
double LayerHeight
bool DirectionCCW
int FramesPerLayer
double FrameSpacingMm
bool SpiralSlice
bool OuterWallBracing
int BracingContactPoints
Expand Down
26 changes: 17 additions & 9 deletions CCL_Clay3DP/Core/BaseBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,10 @@ public static class BaseBuilder
/// pitch as the part body.</param>
/// <param name="beadDiameter">Material bead diameter in mm.
/// Drives infill line spacing.</param>
/// <param name="framesPerLayer">Frame count per closed loop
/// (skirt and per-layer contour). Mirrors HelixParameters.FramesPerLayer.</param>
/// <param name="frameSpacingMm">Target arc-length spacing between
/// frames on the skirt and on each base layer contour, in mm.
/// Mirrors HelixParameters.FrameSpacingMm so the base prints at
/// the same bead density as the part body (Issue #22).</param>
/// <returns>BaseResult with frames and bake geometry. Returns
/// an empty result (LayerCount=0, empty lists) if the input
/// contour is unusable — caller treats that as "no base".</returns>
Expand All @@ -109,7 +111,7 @@ public static BaseResult Build(
BaseSettings settings,
double layerHeight,
double beadDiameter,
int framesPerLayer)
double frameSpacingMm)
{
var result = new BaseResult { LayerHeight = layerHeight };

Expand Down Expand Up @@ -142,7 +144,7 @@ public static BaseResult Build(
if (result.SkirtCurve != null)
{
result.SkirtFrames = SkirtBuilder.SampleSkirtFrames(
result.SkirtCurve, framesPerLayer);
result.SkirtCurve, frameSpacingMm);
}

for (int i = 0; i < n; i++)
Expand All @@ -153,7 +155,7 @@ public static BaseResult Build(
var contour = footprint.DuplicateCurve();
contour.Translate(0.0, 0.0, z);
result.ContourCurves.Add(contour);
result.Frames.AddRange(SampleContourFrames(contour, framesPerLayer));
result.Frames.AddRange(SampleContourFrames(contour, frameSpacingMm));

// Infill: alternating ±45 per layer. Even layers (i=0,2,…)
// at +45°; odd layers at -45°. Stacked, this gives a
Expand Down Expand Up @@ -181,14 +183,20 @@ public static BaseResult Build(
/// <summary>
/// Sample a closed planar contour into frames suitable for the
/// robot. Mirrors SkirtBuilder.SampleSkirtFrames: uniform arc
/// length, last sample coincides with first to close the loop,
/// frame YAxis = +Z so the build plate stays flat regardless
/// length at frameSpacingMm (sample count derived from perimeter
/// per Issue #22), last sample coincides with first to close the
/// loop, frame YAxis = +Z so the build plate stays flat regardless
/// of the SpiralFollowsCurveNormal setting.
/// </summary>
private static List<Plane> SampleContourFrames(Curve contour, int sampleCount)
private static List<Plane> SampleContourFrames(Curve contour, double frameSpacingMm)
{
var frames = new List<Plane>();
if (contour == null || sampleCount < 4) return frames;
if (contour == null || frameSpacingMm <= 0.0) return frames;

double perimeter = contour.GetLength();
if (perimeter <= 0.0) return frames;

int sampleCount = Math.Max(4, (int)Math.Ceiling(perimeter / frameSpacingMm));

double[] ts = contour.DivideByCount(sampleCount, true);
if (ts == null || ts.Length == 0) return frames;
Expand Down
21 changes: 15 additions & 6 deletions CCL_Clay3DP/Core/SkirtBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using Rhino.Geometry;
Expand Down Expand Up @@ -92,19 +93,27 @@ private static Curve SafeOffset(Curve curve, Plane plane,

/// <summary>
/// Sample the skirt curve into a list of frames the robot can
/// follow. Sampling is by uniform arc-length so the per-frame
/// spacing is consistent regardless of where the curve's
/// internal seam lies. The last frame duplicates the first so
/// the loop closes back on itself when RoboDK traces the curve.
/// follow. Sampling is by uniform arc-length at frameSpacingMm
/// (Issue #22): the sample count is derived from the skirt's
/// perimeter, so the bead density matches the spiral toolpath
/// regardless of part size. The last frame duplicates the first
/// so the loop closes back on itself when RoboDK traces the curve.
///
/// Frame normals are ALWAYS +Z world (build plate up). The
/// SpiralFollowsCurveNormal toggle does not apply to the skirt
/// — it sits flat on the plate by construction.
/// </summary>
public static List<Plane> SampleSkirtFrames(Curve skirt, int sampleCount)
public static List<Plane> SampleSkirtFrames(Curve skirt, double frameSpacingMm)
{
var frames = new List<Plane>();
if (skirt == null || sampleCount < 4) return frames;
if (skirt == null || frameSpacingMm <= 0.0) return frames;

double perimeter = skirt.GetLength();
if (perimeter <= 0.0) return frames;

// Sample count = perimeter / spacing, but never below 4 — a
// closed loop needs at least four corners to be meaningful.
int sampleCount = Math.Max(4, (int)Math.Ceiling(perimeter / frameSpacingMm));

// DivideByCount(N, true) returns N+1 parameters, with the last
// one at curve.Domain.T1 — for a closed curve that coincides
Expand Down
Loading
Loading