Releases: tomasf/Cadova
Cadova 0.6.0
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
Trianglethat caused incorrect angle calculations and vertex placement for non-isosceles triangles. If you useTriangleas 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
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
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
wrappedAroundCirclecalculating incorrect radius when inferring from geometry bounds, causing geometry to only wrap halfway around the circle
Cadova 0.4.3
New Features
- 2D split and trimmed operations for cutting 2D geometry
offset()method onParametricCurvefilled()method onParametricCurvewhileAlignedoperation
Bug Fixes
- Fixed division by zero bugs in repeat operations
API Changes
Geometry2D.filled()has been renamed tofillingHoles()to avoid confusion with the newParametricCurve.filled(). The old name remains available but is deprecated.
Cadova 0.4.2
- 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 aLayerTransition - Added
inverted(reflects about center, swapping ease-in ↔ ease-out) andmirrored(geometric reflection, inverse function) properties
Cadova 0.4.1
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 spacingStroke 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.3mfCadova 0.4.0
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
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
-
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
cyclicallyparameter torepeated(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
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 onLoftshows 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.sinefor 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
lineSpacingenvironment setting for controlling spacing in multi-line text. -
Environment Scale Properties: Added
EnvironmentValues.scaleand 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