Skip to content

Releases: tomasf/Cadova

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.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)

Cadova 0.3.3

27 Dec 00:12

Choose a tag to compare

New Features

  • extending() - lengthens geometry by a given amount at a specified plane or axis position. The cross-section at that point is extruded, effectively stretching the geometry while preserving its profile on both sides. Useful for making shapes taller or longer, like extending a bottle's neck.
  • projected(onto:) - project 3D geometry onto arbitrary planes (not just XY)

Improvements

  • New optimizations in Loft for faster polygon alignment

Bug Fixes

  • Fixed model file detection for Finder selection - files with spaces in their names were always being selected as "new" after export

Testing

  • Added comprehensive test coverage for many operations

Cadova 0.3.2

19 Dec 10:08

Choose a tag to compare

  • Package-Relative Project Output: New Project(packageRelative:) initializer automatically derives the package root from the source file path, making it easy to output models alongside your source code:

    await Project(packageRelative: "Models") {
        await Model("example") { Box(10) }
    }
  • Cyclic Repetition: Added cyclically parameter to repeated(along:in:minimumSpacing:). When true, spacing is added after the last instance as well, so the gap between the last and first instance matches the others. Useful when wrapping the result around a circle.

I changed some git commit hashes recently, which can cause problems with Swift Package Manager. If you're having problems resolving Cadova packages, fix it with rm -rf ~/Library/org.swift.swiftpm.

Cadova 0.3.1

18 Dec 09:15

Choose a tag to compare

New Features

  • STL Import: You can now import STL files in addition to 3MF.

  • Convex Hull Loft Transitions: Convex hull is back as an option for loft transitions, but the interface has been simplified. This creates the smallest convex shape containing both adjacent layers, useful for faceted or tapered connections between convex shapes:

    Loft {
        layer(z: 0) { Circle(diameter: 20) }
        layer(z: 10, interpolation: .convexHull) { Rectangle([10, 10]).aligned(at: .center) }
        layer(z: 20) { Circle(diameter: 15) }
    }
  • Loft Layer Visualization: New visualized() method on Loft shows each layer as a thin colored slab at its Z position, making it easy to debug layer configurations without running the full loft.

  • Sine Shaping Function: Added ShapingFunction.sine for smooth sinusoidal interpolation in lofts and other contexts.

  • Partial Bounds Visualization: visualizingBounds() now accepts optional range parameters to show only a portion of the bounding box: geometry.visualizingBounds(z: 0...) // Show bounds from z=0 upward

  • Text Line Spacing: Added lineSpacing environment setting for controlling spacing in multi-line text.

  • Environment Scale Properties: Added EnvironmentValues.scale and related properties for accessing cumulative transform information.

Improvements

  • Loft optimization: Identical adjacent layers now skip intermediate layer generation, significantly improving performance for lofts with straight sections
  • Memory optimization: Pre-allocated memory for mesh data improves performance
  • Documentation: Extensive documentation improvements across the codebase

Internal Changes

  • Various mistakenly public APIs, including MeshData, have been made internal
  • Test suite refactored to use descriptive backtick-style names