Skip to content

Releases: tomasf/Cadova

Cadova 0.7.0

29 May 14:51

Choose a tag to compare

Anchors and Tags

  • Anchor APIs generalized to 2D, with bridging between 2D and 3D anchors.
  • New Anchor.readingTransforms for reading every transform recorded for an anchor.
  • Transforms chained directly onto a tag reference now move the reference in its local frame.
  • New APIs for removing anchor and tag definitions.

Additions

  • New readingSurfaces for querying where a line or line segment crosses a geometry's surface, with new LineSegment and SurfaceCrossing value types.
  • New range-based split methods, plus a 2D split variant that takes a mask.
  • New modifyingBodyAndParts to apply an operation to a geometry's body and its parts in one call.
  • New readSamples(at:) for arc-length resampling of curves, plus a new transform property on CurveSample.

Improvements

  • hidden() now preserves geometry dimensionality and result elements.
  • Items in exported 3MF files are sorted by their part names.
  • Several named color constants whose values didn't match their names were changed to match common perception: .lavender, .azure, .violet, .brown, .chocolate, .khaki, .crimson, .gold, .lime, .plum, .sandyBrown.

Fixes

  • Edge-profile slicing and plane-local projection now correctly inherit the surrounding transform and environment, so profiles produced inside transformed scopes behave the same as profiles produced at the top level.
  • Natural up direction no longer produces incorrect results when defined inside a transformed scope.
  • naturalUpDirectionXYAngle is now guarded against floating-point noise.

Cadova 0.6.1

23 Apr 16:26

Choose a tag to compare

New features

  • Model filtering: a Project build can now be restricted to a subset of its models from the command line. Passing one or more --model NAME (or --model=NAME) flags causes a Project to build only the named models; with no flags, every model is built as before. The flag may be repeated to include several (--model A --model B).

Names are matched against each model's full output-relative path, joined with /. For example, given:

Project {
    Group("Brackets") {
        Model("Left") { ... }
        Model("Right") { ... }
    }
    Model("Base") { ... }
}

use --model Brackets/Left to build just the left bracket, or --model Base for the top-level model. This means identically named models in different groups can be targeted independently.

  • Tag introspection: Tag.readingMembers(_:) and Tag.map(_:) give access to individual tagged geometries instead of their union, with each member's captured world-space transform preserved.
  • 2D chamfering: chamfered(insideDepth:outsideDepth:) and chamfered(depth:) on Geometry2D, similar to the existing rounded operations.

Improvements

  • Validation: vector components and color components (0.0–1.0) are now validated at the call site instead of propagating silently.
  • Log formatting: error and warning log output now has emoji prefixes.
  • separated() dimensionality: the closure can now return geometry of a different dimensionality than the input.

Bug fixes

  • Nested tag references: tag references defined deep in the tree are now merged correctly with same-named tags higher up, instead of the upstream definition silently overwriting the deeper one.
  • Antipodal rotation reflection: Transform3D.rotation(from:to:) no longer produces a reflection when the two directions are antipodal.
  • Edge profile orientation: an asymmetric 2D shape passed to cuttingEdgeProfile(_:on:using:) / formingEdgeProfile(_:on:using:) now lands with consistent orientation across all six box sides.
  • Exponential shaping function: was inverted; now produces the correct curve.
  • Loft islands: loft now properly validates that all layers have the same number of islands, instead of producing broken geometry.

Cadova 0.6.0

06 Mar 14:13

Choose a tag to compare

3D Smoothing

New smoothed(strength:) operation softens hard edges and corners. Lower values produce subtle softening; higher values give visibly rounder shapes.

Loft Improvements

Relative layers: Layers can now be placed relative to the previous layer using layer(zOffset:), in addition to absolute positions.

Better polygon lofting: Lofting between shapes with sharp corners (triangles, rectangles, etc.) now preserves corner vertices exactly during resampling. Previously this caused visible anomalies, as reported in #21.

Bug Fixes

  • Fixed two bugs in Triangle that caused incorrect angle calculations and vertex placement for non-isosceles triangles. If you use Triangle as geometry in your models, retest them.
  • Fixed SVG import silently ignoring open paths.

Dependencies

Updated to Manifold-Swift 1.0, which includes Manifold 3.4.0.

Cadova 0.5.2

14 Feb 16:28

Choose a tag to compare

New Features

  • Added .easeInCubic and .easeOutCubic shaping functions

Bug Fixes

  • Fixed twist extrusion producing one fewer segment than requested
  • Fixed helix sweep segments not aligning evenly across revolutions

Cadova 0.5.1

02 Feb 15:02

Choose a tag to compare

ModelFileGenerator

Cadova 0.5.1 adds ModelFileGenerator, a new API for building models programmatically. While Model is designed for executable Swift packages for development of models, ModelFileGenerator is intended for embedding Cadova into other codebases such as GUI apps, server code, or any context where you need more control over how and where files are generated.

Use ModelFileGenerator.build(named:options:content:) to render geometry into a ModelFile. From there, you can access the file contents as Data via .data(), get a suggested filename via .suggestedFileName, or write directly to disk with .write(to:). If you're generating multiple files, you can reuse a ModelFileGenerator instance to benefit from caching.

let modelFile = try await ModelFileGenerator.build(named: "my-model") {
    Box(x: 10, y: 10, z: 5)
}

let fileData = try await modelFile.data()

Thanks to @iKenndac for contributing this feature.

3MF colors

Geometry without explicit colors no longer defaults to white in 3MF files. Bambu Studio now supports mapping 3MF colors to filaments, but prompts for this even when the entire model is a single color. With this change, uncolored geometry stays uncolored, which is more correct and avoids unnecessary prompts in Bambu Studio.

Cadova 0.5.0

28 Jan 14:08

Choose a tag to compare

SVG Import

Cadova 0.5 adds support for importing SVG as 2D geometry via Pelagos. Import is now generic over Dimensionality. Importing 3D models works as before, but new initializers for SVG have been added. These take the path or URL to an SVG file and two options:

scale:

  • .physical: The default. SVGs are imported according to standard SVG conversions. Physical units are preserved, so "1mm" in SVG becomes an actual millimeter in your model. Pixel units are scaled correspondingly: 1 pixel becomes ~0.265 mm (96 pixels per inch).
  • .pixels: Pixels are converted to millimeters and physical units are scaled correspondingly. This can be more convenient when dealing with graphics designed for screens.

Regardless of the scale option, you can scale the result however you'd like after import.

origin:

  • .flipped: The default. The SVG is flipped along the Y axis so it appears the same way it normally would. This is needed because SVG starts Y at the top and Cadova starts it at the bottom.
  • .native: Preserves SVG coordinates as-is, which can look incorrect but can be easier to deal with in some cases.

The easiest way to embed an SVG into your Swift package is to create a resource directory and reference it in your target in Package.swift:

resources: [.embedInCode("resources")],

You will get generated code in a struct called PackageResources. Pass these data arrays to Import:

Import(svg: PackageResources.snake_svg)

A corresponding DataProtocol initializer has been added for Import<D3> so you can use the same approach for embedded 3D models as well.

Bug Fixes

  • Fixed wrappedAroundCircle calculating incorrect radius when inferring from geometry bounds, causing geometry to only wrap halfway around the circle

Cadova 0.4.3

19 Jan 14:11

Choose a tag to compare

New Features

  • 2D split and trimmed operations for cutting 2D geometry
  • offset() method on ParametricCurve
  • filled() method on ParametricCurve
  • whileAligned operation

Bug Fixes

  • Fixed division by zero bugs in repeat operations

API Changes

  • Geometry2D.filled() has been renamed to fillingHoles() to avoid confusion with the new ParametricCurve.filled(). The old name remains available but is deprecated.

Cadova 0.4.2

10 Jan 14:49

Choose a tag to compare

  • Convert parametric curves into stroked 2D geometry with curve.stroked(width:alignment:style:). Supports left/right/centered alignment, all line join styles (miter, bevel, square, round), and line cap styles (butt, round, square)
  • New .withLineCapStyle() environment modifier for controlling stroke end caps
  • New lofted() overload that accepts a LayerTransition
  • Added inverted (reflects about center, swapping ease-in ↔ ease-out) and mirrored (geometric reflection, inverse function) properties

Cadova 0.4.1

02 Jan 01:27

Choose a tag to compare

New Features

Text Rendering Enhancements

Text rendering now uses Apus, bringing variable font support and improved typography controls.

Variable Fonts - Control font axes for precise typographic styling. Requires a variable font that supports the specified axes (e.g., Inter, Roboto Flex, SF Pro):

// Common axis modifiers
Text("Bold Condensed")
    .withFontVariations(weight: 700, width: 75)

// Full control with FontVariation array
Text("Custom Style")
    .withFontVariations([.weight(600), .width(85), .slant(-6)])

// Custom axes
Text("Graded")
    .withFontVariations([FontVariation(tag: "GRAD", value: 50)])

Tracking - Adjust uniform spacing between characters:

Text("SPACED OUT")
    .withTracking(1)  // Add 1mm between characters

Text("TIGHT")
    .withTracking(-0.5)  // Reduce spacing

Stroke Modifier

Convert 2D shapes to outlined strokes:

// Basic stroke
Circle(radius: 10)
    .stroked(width: 2)

// Control alignment: .inside, .centered, or .outside
Rectangle([20, 10])
    .stroked(width: 1, alignment: .inside)

Debug Isolation

Temporarily isolate geometry during debugging:

Union {
    complexShape1
    complexShape2.only()  // Only this shape will render
    complexShape3
}

Project Organization

Groups let you organize related models within a Project into a hierarchy. When exporting, groups create subdirectories to keep your output organized:

let project = Project {
    Group("Enclosure") {
        Model("Top") { topGeometry }
        Model("Bottom") { bottomGeometry }
    }
    Group("Hardware") {
        Model("Bracket") { bracketGeometry }
    }
}

// Exports to:
// output/Enclosure/Top.3mf
// output/Enclosure/Bottom.3mf
// output/Hardware/Bracket.3mf

Cadova 0.4.0

28 Dec 23:20

Choose a tag to compare

New Part API

Breaking change: Parts are now created as reusable instances instead of using string names:

// Before
Box(10).inPart(named: "bracket", type: .solid)

// After
let bracket = Part("Bracket")
Box(10).inPart(bracket)

The same Part instance used multiple times collects geometry into a single part. Different instances with the same name are treated as separate parts.

Parts can specify their semantic role and a default material applied to geometry that doesn't specify its own:

let bracket = Part("Bracket", semantic: .solid, color: .gray)
let insert = Part("Insert", color: .silver, metallicness: 0.9, roughness: 0.3)

Ruler Visualization

New Ruler type for measuring distances:

Ruler(length: 100, interval: 10)