### SwiftUI Quick Start with MetalViewportView Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/README.md Integrate the MetalViewportView into your SwiftUI application. This example shows how to initialize the ViewportController and provide an initial set of bodies. ```swift import SwiftUI import OCCTSwiftViewport struct ContentView: View { @StateObject private var controller = ViewportController() @State private var bodies: [ViewportBody] = [ .box(size: 1, color: .gray) ] var body: some View { MetalViewportView(controller: controller, bodies: $bodies) } } ``` -------------------------------- ### Example: Load Environment Map from URL Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Shows how to load an environment map from a .hdr file found in the application's bundle. ```swift if let url = Bundle.main.url(forResource: "studio", withExtension: "hdr") { try renderer.loadEnvironmentMap(url: url) } ``` -------------------------------- ### Custom PBRMaterial Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Example of creating a custom PBRMaterial instance with specific base color, metallic, and roughness values to represent anodised aluminium. All parameters have defaults, so only non-default values need to be specified. ```swift // Custom anodised aluminium let material = PBRMaterial( baseColor: SIMD3(0.30, 0.35, 0.40), metallic: 1, roughness: 0.20 ) ``` -------------------------------- ### ClipPlane Example: X = 2 Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example demonstrating how to create a clip plane to section at X = 2, keeping the region where X > 2. ```swift let plane = ClipPlane(normal: SIMD3(1, 0, 0), distance: -2) ``` -------------------------------- ### Customize ViewportConfiguration Preset Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Demonstrates how to start with a preset configuration and then modify specific properties for fine-tuned adjustments. ```swift var config = ViewportConfiguration.cad config.showScaleBar = true config.scaleBarUnitLabel = "mm" ``` -------------------------------- ### OffscreenRenderer Initializer Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Demonstrates how to initialize an OffscreenRenderer. Returns nil if Metal is not available on the system. ```swift guard let renderer = await MainActor.run(body: { OffscreenRenderer() }) else { fatalError("Metal not available") } ``` -------------------------------- ### Example LightSettings Configurations Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Display-Lighting.md Demonstrates creating different light settings, including a warm key light, a disabled fill light, and a point light with a specified position and radius. ```swift // Warm key light from upper-right-front let key = LightSettings( direction: simd_normalize(SIMD3(-0.5, -0.6, -0.6)), intensity: 1.0, color: SIMD3(1.0, 0.96, 0.90) ) // Disabled fill slot let fill = LightSettings( direction: SIMD3(1, 0, 0), intensity: 0.0, isEnabled: false ) // Point light at a fixed world position let point = LightSettings( direction: .zero, // ignored for point lights intensity: 0.8, lightType: .point(radius: 5.0), position: SIMD3(0, 2, 4) ) ``` -------------------------------- ### Manual Ray-Triangle Intersection Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Picking.md Demonstrates how to manually test for ray-triangle intersection and calculate the hit point. ```swift let ray = Ray( origin: SIMD3(0, 0, 5), direction: SIMD3(0, 0, -1) ) let v0 = SIMD3(-1, -1, 0) let v1 = SIMD3( 1, -1, 0) let v2 = SIMD3( 0, 1, 0) if let t = ray.intersectsTriangle(v0: v0, v1: v1, v2: v2) { let hitPoint = ray.origin + ray.direction * t // (0, -0.333, 0) approx print("Hit at distance \(t)") } ``` -------------------------------- ### ClipPlane Example: Y = 5 Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example demonstrating how to create a clip plane to section at Y = 5, keeping the region where Y > 5. ```swift // dot((0,1,0), P) + (-5) = 0 → plane at Y=5 let plane = ClipPlane(normal: SIMD3(0, 1, 0), distance: -5) ``` -------------------------------- ### Basic Viewport Setup with a Box Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/loading-geometry.md Demonstrates how to initialize a MetalViewportView and populate it with a simple box geometry. The scene is updated by modifying the 'bodies' array. ```swift struct ContentView: View { @StateObject private var controller = ViewportController() @State private var bodies: [ViewportBody] = [] var body: some View { MetalViewportView(controller: controller, bodies: $bodies) .onAppear { bodies = [.box(id: "b1", color: SIMD4(0.6, 0.8, 1.0, 1.0))] } } } ``` -------------------------------- ### Example: Perform GPU Pick Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Demonstrates how to convert tap points to drawable pixels and perform a pick operation, then print the hit body and primitive ID. ```swift // In a tap handler, convert the tap point to drawable pixels first. let scale = renderer.lastDrawableSize.width / viewSize.width let drawablePx = SIMD2(Int(tapPt.x * scale), Int(tapPt.y * scale)) renderer.performPick(at: drawablePx) { result in if let result { print("Hit body \(result.bodyID), primitive \(result.primitiveID)") } } ``` -------------------------------- ### Script Harness Usage Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/CLAUDE.md Commands to build and run the OCCTSwift script harness. The initial build takes longer than subsequent incremental runs. ```bash cd /Users/elb/Projects/OCCTSwiftScripts swift build # First time ~30s # Edit Sources/Script/main.swift, then: swift run Script # ~1-2s incremental ``` -------------------------------- ### Render Bodies to CGImage Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Provides an example function to render a list of ViewportBody objects to a CGImage using OffscreenRenderer. It configures rendering options such as dimensions, camera state, and display mode. ```swift @MainActor func snapshot(bodies: [ViewportBody], camera: CameraState) -> CGImage? { guard let renderer = OffscreenRenderer() else { return nil } let opts = OffscreenRenderOptions( width: 1920, height: 1080, cameraState: camera, displayMode: .shadedWithEdges ) return renderer.render(bodies: bodies, options: opts) ``` -------------------------------- ### Navigating to a Standard View Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewCube.md Example of using the standardView property to navigate the controller to a specific view. ```swift if let sv = region.standardView { controller.goToStandardView(sv, duration: 0.3) } ``` -------------------------------- ### ViewportBody Effective Material Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Demonstrates accessing the effectiveMaterial, showing how it synthesizes from color-only bodies and updates when a named PBR preset is assigned. ```swift var body = ViewportBody.box(id: "b", color: SIMD4(0.8, 0.8, 0.8, 1)) // Color-only body — effectiveMaterial synthesizes from color/roughness/metallic. print(body.effectiveMaterial.roughness) // 0.5 // Switch to a named PBR preset: body.material = .steel print(body.effectiveMaterial.metallic) // 1.0 ``` -------------------------------- ### Example: Casting a Ray Through a Screen Tap Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Picking.md Demonstrates how to cast a ray from a screen point into the scene using the SceneRaycast.cast function. This example converts screen coordinates to normalized device coordinates (NDC) and then to a world-space ray. ```swift @MainActor func handleTap( at screenPoint: CGPoint, controller: ViewportController, bodies: [ViewportBody], viewportSize: CGSize ) { let ndc = SIMD2( Float(screenPoint.x / viewportSize.width) * 2 - 1, 1 - Float(screenPoint.y / viewportSize.height) * 2 ) let aspectRatio = Float(viewportSize.width / viewportSize.height) let ray = Ray.fromCamera( ndc: ndc, cameraState: controller.cameraController.cameraState, aspectRatio: aspectRatio ) if let hit = SceneRaycast.cast( ray: ray, bodies: bodies, boundingBoxCache: controller.boundingBoxCache ) { print("Hit '\(hit.bodyID)' at \(hit.point), distance \(hit.distance)") } } ``` -------------------------------- ### RadiusMeasurement ShowDiameter Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Example of how to toggle the display of a RadiusMeasurement between diameter and radius. This affects how the measurement value is interpreted. ```swift var m = RadiusMeasurement(center: .zero, edgePoint: SIMD3(10, 0, 0)) m.showDiameter = true // display as "Diameter: 20" ``` -------------------------------- ### ClipPlane Example: Quadrant Section Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example demonstrating how to combine two clip planes to create a quadrant section view, keeping geometry where X >= 0 and Z >= 0. ```swift controller.clipPlanes = [ ClipPlane(normal: SIMD3(1, 0, 0), distance: 0), // keep X ≥ 0 ClipPlane(normal: SIMD3(0, 0, 1), distance: 0), // keep Z ≥ 0 ] ``` -------------------------------- ### Clearcoat Material Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/lighting-and-materials.md Applies a material with clearcoat properties, suitable for surfaces like car paint or lacquered wood. This example uses a red base color with a clearcoat layer. ```swift body.material = PBRMaterial( baseColor: SIMD3(0.7, 0.05, 0.05), // red metallic: 0, roughness: 0.65, ior: 1.5, clearcoat: 1.0, clearcoatRoughness: 0.04 ) // Equivalent to PBRMaterial.paintedAutomotive (with a different baseColor). ``` -------------------------------- ### Create a Point Cloud Body Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Example showing how to create a ViewportBody to render as a point cloud. This ignores vertexData, indices, and edges, and applies vertexColors and pointRadius. ```swift let pts: [SIMD3] = scanPoints let body = ViewportBody( id: "scan", vertexData: [], indices: [], edges: [], vertices: pts, color: SIMD4(0.9, 0.7, 0.2, 1), pointRadius: 0.02, primitiveKind: .point ) ``` -------------------------------- ### Quick Start Offscreen Rendering Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/offscreen-rendering.md This snippet demonstrates the basic steps to create an OffscreenRenderer, define geometry, frame the camera, and render an image. Ensure a Metal device is available before creating the renderer. ```swift import OCCTSwiftViewport // 1. Create the renderer (failable — returns nil if no Metal device). guard let renderer = await MainActor.run(body: { OffscreenRenderer() }) else { fatalError("No Metal device") } // 2. Geometry. let body = ViewportBody.box(size: SIMD3(2, 1, 3), color: SIMD4(0.4, 0.6, 0.9, 1)) // 3. Frame the camera to fit the geometry. let aspectRatio: Float = 1024.0 / 768.0 let camera = CameraState.isometric.fit(to: [body], aspectRatio: aspectRatio) ?? CameraState.isometric // 4. Render. let options = OffscreenRenderOptions( width: 1024, height: 768, cameraState: camera ) if let image = renderer.render(bodies: [body], options: options) { // use CGImage … } ``` -------------------------------- ### Using Scene-Adaptive Clip Planes and Projection Matrix Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Camera.md Example of how to use scene-adaptive clip planes to calculate the projection matrix for a given aspect ratio. ```swift let (near, far) = state.clipPlanes(sceneBounds: sceneBB) let proj = state.projectionMatrix(aspectRatio: aspect, near: near, far: far) ``` -------------------------------- ### Modify a Preset Material Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Demonstrates starting with a preset material and then customizing its properties, such as roughness. ```swift // Start from a preset and tweak var custom = PBRMaterial.steel custom.roughness = 0.10 // more polished ``` -------------------------------- ### Enable Picking Configuration Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example of enabling picking when wiring up a selection handler by creating a PickingConfiguration with isEnabled set to true. ```swift var config = ViewportConfiguration.cad config.pickingConfiguration = PickingConfiguration(isEnabled: true) ``` -------------------------------- ### Render Bodies to PNG Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Shows how to render ViewportBody objects and save the output as a PNG file to a specified URL. It handles potential errors during the rendering and file writing process. ```swift @MainActor func savePNG(bodies: [ViewportBody], to url: URL) throws { guard let renderer = OffscreenRenderer() else { return } let size = try renderer.renderToPNG(bodies: bodies, url: url) print("Wrote \(size) bytes to \(url.lastPathComponent)") ``` -------------------------------- ### Example: Render 200x200mm Region with OrthoBounds Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Renders a specific 200x200mm region centered at the origin using explicit orthographic bounds. ```swift let bounds = OrthoBounds(left: -100, right: 100, bottom: -100, top: 100) var opts = OffscreenRenderOptions(width: 1024, height: 1024) opts.explicitOrthoBounds = bounds ``` -------------------------------- ### Configure Gesture Inversion Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Input.md Example of creating a gesture configuration and setting the `invertOrbitHorizontal` property to true for a "grab-and-drag" feel. ```swift // Orbit-invert for users who prefer "grab-and-drag" feel var config = ViewportConfiguration() config.gestureConfiguration = GestureConfiguration(invertOrbitHorizontal: true) ``` -------------------------------- ### Using a PBRMaterial Preset Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Example of assigning a predefined material preset, `.paintedAutomotive`, to a ViewportBody. The base color is overridden by the preset. ```swift var body = ViewportBody.box(id: "housing", color: .zero /* overridden */) body.material = .paintedAutomotive ``` -------------------------------- ### Upgrade Rendering Quality Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example of how to conditionally upgrade the rendering quality and tessellation settings on a device known to support enhanced features. ```swift if config.renderingQuality == .standard { config.renderingQuality = .enhanced config.tessellationMaxFactor = 32 config.adaptiveTessellation = true } ``` -------------------------------- ### Example: Compound SelectionFilter Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Picking.md Demonstrates combining multiple filters using OR and AND operations to accept edges and vertices, while excluding a specific body. ```swift // Accept edges and vertices, but never on the "datum-plane" body controller.selectionFilter = .edges .or(.vertices) .and(.excludingBodyIDs(["datum-plane"]))) ``` -------------------------------- ### RadiusMeasurement Example Usage Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Demonstrates creating a RadiusMeasurement and accessing its computed radius and diameter properties. This is useful for displaying measurement values. ```swift let m = RadiusMeasurement(center: .zero, edgePoint: SIMD3(5, 0, 0)) print(m.radius) // 5.0 print(m.diameter) // 10.0 ``` -------------------------------- ### Swift Package Manager Installation Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/README.md Add OCCTSwiftViewport to your project's Package.swift file. You can choose to include either the viewport-only product or the full tools product. ```swift // Package.swift dependencies: [ .package(path: "../OCCTSwiftViewport"), // or: .package(url: "https://github.com/gsdali/OCCTSwiftViewport.git", from: "1.0.0") ], targets: [ .target( name: "YourApp", dependencies: [ // Pick what you need: .product(name: "OCCTSwiftViewport", package: "OCCTSwiftViewport"), // viewport only .product(name: "OCCTSwiftTools", package: "OCCTSwiftViewport"), // + OCCTSwift bridge ] ) ] ``` -------------------------------- ### Creating a Box Crop View Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/clip-planes.md This example demonstrates how to use two clip planes to create a box crop view, showing only geometry within a specified slab along the Y-axis. ```swift // Box crop: only show geometry inside a slab -1 ≤ Y ≤ 1 controller.clipPlanes = [ ClipPlane(normal: SIMD3(0, 1, 0), distance: -1), // bottom face of slab ClipPlane(normal: SIMD3(0, -1, 0), distance: -1), // top face of slab (flipped normal) ] ``` -------------------------------- ### Creating a Quarter Section View Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/clip-planes.md This example shows how to use two clip planes to create a quarter section view, exposing the front-right quadrant by hiding negative X and Z geometry. ```swift // Quarter section: expose the front-right quadrant (positive X and positive Z) controller.clipPlanes = [ ClipPlane(normal: SIMD3( 1, 0, 0), distance: 0), // hide negative-X ClipPlane(normal: SIMD3( 0, 0, 1), distance: 0), // hide negative-Z ] ``` -------------------------------- ### Nice-Number Rounding Examples Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/hud-overlays.md Demonstrates the 1 / 2 / 5 × 10ⁿ rule for snapping numbers to a clean value using ScaleBarMetrics.niceNumber. ```swift ScaleBarMetrics.niceNumber(3.7) // → 5.0 ScaleBarMetrics.niceNumber(14.0) // → 10.0 ScaleBarMetrics.niceNumber(0.006) // → 0.005 ``` -------------------------------- ### Computed Midpoint Property Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Get the world-space midpoint between the start and end points. Useful for positioning labels in the viewport. ```swift public var midpoint: SIMD3 { get } ``` ```swift let m = DistanceMeasurement(start: SIMD3(0, 0, 0), end: SIMD3(10, 0, 0)) print(m.midpoint) // (5, 0, 0) ``` -------------------------------- ### Example: Render Diagnostic Unlit Pass at 2K Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Rendering.md Configures options for a 2K resolution diagnostic unlit rendering pass with a dark background and visible axes. ```swift var opts = OffscreenRenderOptions( width: 2048, height: 2048, displayMode: .unlit, backgroundColor: SIMD4(0.1, 0.1, 0.1, 1.0), showAxes: true ) ``` -------------------------------- ### Tap-to-Measure World Point Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Illustrates using worldHitPoint to find the world-space tap location on a body's surface, typically for adding measurement points in a viewport. ```swift // result from ViewportController.pickResult, ray from tap NDC coords if result.kind == .face, let point = body.worldHitPoint(ray: tapRay, triangleIndex: result.triangleIndex) { viewportController.addMeasurementPoint(point) } ``` -------------------------------- ### Setting up OCCTSwiftScripts Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/README.md Clone the OCCTSwiftScripts repository and build the project. The initial build may take longer as it downloads OCCTSwift. ```bash git clone https://github.com/gsdali/OCCTSwiftScripts.git cd OCCTSwiftScripts swift build # First build ~30s (pulls OCCTSwift) ``` -------------------------------- ### Computed Distance Property Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Get the computed Euclidean distance between the start and end points. This value is always non-negative. ```swift public var distance: Float { get } ``` ```swift let m = DistanceMeasurement(start: .zero, end: SIMD3(3, 4, 0)) print(m.distance) // 5.0 ``` -------------------------------- ### HDRLoader Load from URL Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Loads an HDR environment map from a file URL and uploads it to a Metal texture. Ensure the Metal device and texture descriptor are correctly set up. ```swift guard let url = Bundle.main.url(forResource: "studio", withExtension: "hdr") else { return } do { let (width, height, pixels) = try HDRLoader.loadFromURL(url) // Upload to MTLTexture let descriptor = MTLTextureDescriptor.texture2DDescriptor( pixelFormat: .rgba32Float, width: width, height: height, mipmapped: false ) if let texture = device.makeTexture(descriptor: descriptor) { texture.replace( region: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0, withBytes: pixels, bytesPerRow: width * 4 * MemoryLayout.size ) } } catch { print("HDR load failed: \(error)") } ``` -------------------------------- ### Run the Demo App via Swift Package Manager Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/CLAUDE.md Executes the demo application. Navigate to the 'Examples/MetalDemo' directory before running this command. ```bash cd Examples/MetalDemo && swift run OCCTSwiftMetalDemo ``` -------------------------------- ### LightSettings Initialization Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Display-Lighting.md Demonstrates how to initialize `LightSettings` for different lighting scenarios, including key lights, disabled fill lights, and point lights. ```APIDOC ## LightSettings `public struct LightSettings: Sendable` Settings for a single light source in a `LightingConfiguration`. Three `LightSettings` values (key, fill, back) make up every configuration. ### Stored Properties | Property | Type | Default | Description | |---|---|---|---| | `direction` | `SIMD3` | (required) | Normalised direction the light points toward. Ignored for `.point` lights. | | `intensity` | `Float` | `1.0` | Light intensity. Typical range 0–2; values above 1 are valid for HDR. | | `color` | `SIMD3` | `(1,1,1)` | RGB light colour in linear 0–1 space. | | `isEnabled` | `Bool` | `true` | Whether this light contributes to shading. | | `lightType` | `LightType` | `.directional` | Whether this is a directional or point light. | | `position` | `SIMD3` | `.zero` | World-space position. Used only for `.point` lights. | ### Initializer ```swift public init( direction: SIMD3, intensity: Float = 1.0, color: SIMD3 = SIMD3(1, 1, 1), isEnabled: Bool = true, lightType: LightType = .directional, position: SIMD3 = .zero ) ``` ```swift // Warm key light from upper-right-front let key = LightSettings( direction: simd_normalize(SIMD3(-0.5, -0.6, -0.6)), intensity: 1.0, color: SIMD3(1.0, 0.96, 0.90) ) // Disabled fill slot let fill = LightSettings( direction: SIMD3(1, 0, 0), intensity: 0.0, isEnabled: false ) // Point light at a fixed world position let point = LightSettings( direction: .zero, // ignored for point lights intensity: 0.8, lightType: .point(radius: 5.0), position: SIMD3(0, 2, 4) ) ``` ``` -------------------------------- ### Example: Get Midpoint of an Arc Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Demonstrates how to use the `point(at:)` method to find the midpoint of an existing `ViewportArc` instance by passing `0.5` as the parameter. ```swift let mid = arc.point(at: 0.5) // midpoint of the arc ``` -------------------------------- ### ContentView Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Views.md Example of how to use MetalViewportView within a SwiftUI ContentView. It initializes a ViewportController and a state variable for ViewportBody. ```swift struct ContentView: View { @StateObject private var controller = ViewportController(configuration: .cad) @State private var bodies: [ViewportBody] = [ .box(id: "box", color: SIMD4(0.5, 0.7, 1.0, 1.0)) ] var body: some View { MetalViewportView(controller: controller, bodies: $bodies) } } ``` -------------------------------- ### PivotStrategy.init() Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Camera.md Initializes a new PivotStrategy helper. ```APIDOC ## init() ### Description Initializes a new PivotStrategy. ### Method Signature ```swift public init() ``` ``` -------------------------------- ### DistanceMeasurement Start Property Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Access or modify the start point in world coordinates. This defines the beginning of the measured distance. ```swift public var start: SIMD3 ``` ```swift let m = DistanceMeasurement(start: SIMD3(1, 2, 3), end: SIMD3(4, 5, 6)) ``` -------------------------------- ### Run OCCTSwiftMetalDemo Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/Examples/MetalDemo/README.md Execute the interactive Metal demo from the repository root. This command launches the demo application. ```bash swift run --package-path Examples/MetalDemo OCCTSwiftMetalDemo ``` -------------------------------- ### Full Tap-to-World-Point Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/picking-and-selection.md Combines screen-to-ray conversion and scene raycasting to find the world-space intersection point of a user's tap. Logs the hit body ID, point, and distance. ```swift func onTap(at screenPoint: CGPoint, viewportSize: CGSize, controller: ViewportController, bodies: [ViewportBody]) { let aspectRatio = Float(viewportSize.width / viewportSize.height) let ndcX = Float(screenPoint.x / viewportSize.width) * 2.0 - 1.0 let ndcY = 1.0 - Float(screenPoint.y / viewportSize.height) * 2.0 let ray = Ray.fromCamera( ndc: SIMD2(ndcX, ndcY), cameraState: controller.cameraState, aspectRatio: aspectRatio ) var bbCache: [String: BoundingBox] = [: ] for body in bodies { bbCache[body.id] = body.boundingBox } if let hit = SceneRaycast.cast(ray: ray, bodies: bodies, boundingBoxCache: bbCache) { print("hit \(hit.bodyID) at \(hit.point), distance \(hit.distance)") } } ``` -------------------------------- ### Build OCCTSwiftViewport with Swift Package Manager Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/CLAUDE.md Use this command to build the library using Swift Package Manager. Ensure you are in the root directory of the repository. ```bash swift build ``` -------------------------------- ### ViewportArc Point Method Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Calculates and returns the 3D position on the arc corresponding to a given parameter `t`. The parameter `t` maps linearly from the start angle to the end angle, where `t=0` is the start and `t=1` is the end. ```swift public func point(at t: Float) -> SIMD3 ``` -------------------------------- ### SwiftUI Viewport with Selection Handling Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/getting-started.md This example shows how to set up a MetalViewportView in SwiftUI, display initial geometry, and react to user selections. It demonstrates observing selected body IDs and using an `onPick` callback for programmatic selection events. ```swift struct SelectionDemo: View { @StateObject private var controller = ViewportController(configuration: .cad) @State private var bodies: [ViewportBody] = [ .sphere(id: "s1", color: SIMD4(0.9, 0.3, 0.3, 1.0)), .box(id: "b1", color: SIMD4(0.3, 0.6, 0.9, 1.0)), ] var body: some View { VStack { MetalViewportView(controller: controller, bodies: $bodies) .ignoresSafeArea() // Show the selected body ID under the viewport. if let id = controller.selectedBodyIDs.first { Text("Selected: \(id)") .padding() } } // Or react programmatically via the onPick callback: .onAppear { controller.onPick = { result in guard let result else { return } print("Picked body \(result.bodyID), face \(result.triangleIndex)") } } } } ``` -------------------------------- ### Run Demo App on macOS Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/README.md Execute this command in the terminal to build and run the OCCTSwiftMetalDemo application on macOS. ```bash swift run OCCTSwiftMetalDemo ``` -------------------------------- ### Get Projected Axes for OrientationGnomon Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/hud-overlays.md Access the nonisolated static function projectedAxes(rotation:) from OrientationGnomon to get the screen-space projection of world axes. This is useful for custom HUDs or unit tests. The output is sorted by depth. ```swift let axes = OrientationGnomon.projectedAxes(rotation: controller.cameraState.rotation) // axes is [ProjectedAxis] sorted back-to-front // Each axis has: label ("X"/"Y"/"Z"), direction (CGSize, screen-space), // color (.red/.green/.blue), depth (Float) ``` -------------------------------- ### Custom Renderer Example for Arc Sampling Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewportBody.md Demonstrates how to use the `segmentCount` function in a custom renderer to determine the number of vertices needed for an arc. The example iterates from 0 to `n` (inclusive) to emit the correct number of points. ```swift let n = ArcSampling.segmentCount( arc: arc, mvp: uniforms.modelViewProjectionMatrix, viewportSize: SIMD2(Float(drawableSize.width), Float(drawableSize.height)) ) // Emit n+1 vertices from arc.point(at:) calls for i in 0...n { let p = arc.point(at: Float(i) / Float(n)) // ... add to line buffer } ``` -------------------------------- ### BoundingBox.init(min:max:) Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Math.md Creates a bounding box from minimum and maximum corners. ```APIDOC ## `BoundingBox.init(min:max:)` ### Description Creates a bounding box from minimum and maximum corners. ### Parameters - **min** (SIMD3) - lower corner - **max** (SIMD3) - upper corner ### Example ```swift let box = BoundingBox( min: SIMD3(0, 0, 0), max: SIMD3(10, 10, 10) ) ``` ``` -------------------------------- ### Integrating NavigationCubeView in SwiftUI Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewCube.md Example of placing the NavigationCubeView in the top-trailing corner of a MetalViewportView. ```swift // Place in the top-trailing corner of the viewport ZStack(alignment: .topTrailing) { MetalViewportView(controller: controller, bodies: $bodies) NavigationCubeView(controller: controller) .frame(width: 96, height: 96) .padding(12) } ``` -------------------------------- ### Configure Lighting Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Sets up lighting presets and per-light properties for the viewport. ```swift public var lightingConfiguration: LightingConfiguration // default: .threePoint ``` -------------------------------- ### Move ViewCube to Top-Right Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example of how to move the ViewCube to the top-right corner of the viewport by modifying ViewportConfiguration. ```swift var config = ViewportConfiguration.cad config.viewCubePosition = .topTrailing ``` -------------------------------- ### Initialize ViewportController Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Views.md Creates a new ViewportController instance. Use the default configuration or provide a custom one. ```swift public init(configuration: ViewportConfiguration = .cad) ``` ```swift let controller = ViewportController(configuration: .cadHighQuality) ``` -------------------------------- ### Clone OCCTSwift and OCCTSwiftViewport Repositories Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/README.md Clone both OCCTSwift and OCCTSwiftViewport repositories as siblings in the same directory. This is necessary because OCCTSwift is a local path dependency. ```bash git clone https://github.com/gsdali/OCCTSwift.git ``` ```bash git clone https://github.com/gsdali/OCCTSwiftViewport.git # They should be at the same directory level ``` -------------------------------- ### RadiusMeasurement Diameter Calculation Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Illustrates how to access the computed diameter of a RadiusMeasurement. The diameter is derived from the radius. ```swift let m = RadiusMeasurement(center: .zero, edgePoint: SIMD3(5, 0, 0)) print(m.diameter) // 10.0 ``` -------------------------------- ### PBRMaterial Initialization Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Initializes a PBRMaterial with optional parameters for physically based rendering. All parameters have defaults, allowing for a zero-argument call to create a default material. ```APIDOC ## PBRMaterial Initialization ### Description Initializes a PBRMaterial with optional parameters for physically based rendering. All parameters have defaults, allowing for a zero-argument call to create a default material. ### Method `init(baseColor:metallic:roughness:ior:clearcoat:clearcoatRoughness:emissive:emissiveStrength:opacity:)` ### Parameters - **baseColor** (SIMD3) - Optional - Albedo in linear RGB. Default `(0.8, 0.8, 0.8)`. - **metallic** (Float) - Optional - 0 = dielectric, 1 = full metal. Default `0`. - **roughness** (Float) - Optional - Perceptual roughness. 0 = mirror, 1 = fully diffuse. Default `0.5`. - **ior** (Float) - Optional - Index of refraction for dielectrics. Default `1.5`. - **clearcoat** (Float) - Optional - Clearcoat layer weight. 0 = no coat, 1 = full coat. Default `0`. - **clearcoatRoughness** (Float) - Optional - Roughness of the clearcoat layer. Default `0.03`. - **emissive** (SIMD3) - Optional - Linear RGB self-emission color. Default `(0, 0, 0)`. - **emissiveStrength** (Float) - Optional - Emissive intensity multiplier. Default `1`. - **opacity** (Float) - Optional - Surface opacity. `1` = fully opaque. Default `1`. ### Request Example ```swift // Custom anodised aluminium let material = PBRMaterial( baseColor: SIMD3(0.30, 0.35, 0.40), metallic: 1, roughness: 0.20 ) ``` ``` -------------------------------- ### Setting Camera State to a ViewCubeRegion Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/ViewCube.md Example of manually positioning the camera to a specific corner using cameraState. ```swift // Manually position the camera to look at the top-front-right isometric corner let state = ViewCubeRegion.topFrontRight.cameraState( pivot: sceneBounds.center, distance: 25.0 ) controller.setCameraState(state, animated: true) ``` -------------------------------- ### Get BoundingBox Size Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Math.md Retrieves the size of the bounding box along each axis. Computed as max - min. ```swift public var size: SIMD3 { get } ``` ```swift let dimensions = box.size // SIMD3(10, 10, 10) ``` -------------------------------- ### Look up a Preset Material by Key Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Demonstrates how to retrieve a PBRMaterial from the presets dictionary using its string key. ```swift // Look up by key if let m = PBRMaterial.presets["brass"] { body.material = m } ``` -------------------------------- ### Get BoundingBox Center Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Math.md Retrieves the center point of the bounding box. Computed as (min + max) * 0.5. ```swift public var center: SIMD3 { get } ``` ```swift let midpoint = box.center // SIMD3(5, 5, 5) ``` -------------------------------- ### DistanceMeasurement Properties Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Access and modify properties of a DistanceMeasurement, including its ID, start and end points, and an optional label. ```APIDOC ## Properties ### id Unique identifier for this measurement. - **Type:** string - **Example:** ```swift let m = DistanceMeasurement(id: "dist-1", start: .zero, end: SIMD3(5, 0, 0)) ``` ### start Start point in world coordinates. - **Type:** SIMD3 - **Example:** ```swift let m = DistanceMeasurement(start: SIMD3(1, 2, 3), end: SIMD3(4, 5, 6)) ``` ### end End point in world coordinates. - **Type:** SIMD3 - **Example:** ```swift var m = DistanceMeasurement(start: .zero, end: SIMD3(10, 0, 0)) m.end = SIMD3(5, 0, 0) ``` ### label Optional label override for custom text display. - **Type:** string? - **Description:** If `nil`, the overlay displays the computed `distance` value formatted as a string. - **Example:** ```swift var m = DistanceMeasurement(start: .zero, end: SIMD3(50, 0, 0)) m.label = "Gap: 50 mm" ``` ``` -------------------------------- ### Get BoundingBox Diagonal Length Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Math.md Calculates the length of the space diagonal of the bounding box. Computed as simd_length(size). ```swift public var diagonalLength: Float { get } ``` ```swift let radius = box.diagonalLength * 0.5 // half-diagonal for sphere fit ``` -------------------------------- ### Frustum.init(viewProjection:) Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Math.md Initializes a Frustum by extracting its six planes from a given view-projection matrix. The planes are normalized and their normals point inward for easy inside/outside testing. ```APIDOC ## `Frustum.init(viewProjection:)` ### Description Extracts the six frustum planes from a view-projection matrix. Decomposes the matrix into six half-space planes (left, right, bottom, top, near, far) and normalizes each normal vector. The normals point inward so that a world point `p` is inside the plane when `dot(normal, p) + d >= 0`. ### Parameters - **m** (simd_float4x4) - A column-major 4x4 view-projection matrix (typically `projectionMatrix * viewMatrix` from a camera). ### Example ```swift let camera = Camera() camera.eye = SIMD3(0, -100, 50) camera.center = SIMD3(0, 0, 0) let viewProj = camera.projectionMatrix * camera.viewMatrix let frustum = Frustum(viewProjection: viewProj) ``` ``` -------------------------------- ### Build View-Picker Toolbar Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/camera-and-navigation.md Creates a menu with buttons that allow users to quickly navigate to standard camera views. Each button triggers a call to `goToStandardView`. ```swift Menu("Views") { Button("Top") { viewport.goToStandardView(.top) } Button("Front") { viewport.goToStandardView(.front) } Button("Right") { viewport.goToStandardView(.right) } Button("Isometric") { viewport.goToStandardView(.isometricFrontRight) } } ``` -------------------------------- ### Build Doc Figures Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/guides/cookbook/index.md Rebuilds documentation figures for the viewport. Assumes you are in the Examples/DocFigures directory. ```swift cd Examples/DocFigures && swift run DocFigures ../../docs/guides/cookbook/images ``` -------------------------------- ### ClipPlane Uniform Buffer Assignment Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example showing how to assign the packed clip plane data to a uniform buffer in Metal. ```swift uniforms.clipPlane = plane.asFloat4 ``` -------------------------------- ### RadiusMeasurement Label Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Demonstrates setting a custom label for a RadiusMeasurement. This overrides the default display of the computed radius or diameter. ```swift var m = RadiusMeasurement(center: .zero, edgePoint: SIMD3(25, 0, 0)) m.label = "R25" ``` -------------------------------- ### Initialize MaterialLibrary Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Initializes a MaterialLibrary. Use the default initializer for persistence to Application Support, or pass nil for storageURL to disable disk I/O, which is useful for previews and tests. ```swift // Default (persists to Application Support) let library = await MaterialLibrary() // In-memory only (no disk I/O) let previewLibrary = await MaterialLibrary(storageURL: nil) ``` -------------------------------- ### Set RenderLayer for Overlay Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example of setting a ViewportBody's render layer to .overlay to ensure it is always visible, regardless of occlusion. ```swift var gizmo = ViewportBody(/* … */) gizmo.renderLayer = .overlay // always visible, never occluded ``` -------------------------------- ### Mutating LightingConfiguration Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Display-Lighting.md Demonstrates how to create a default lighting configuration and then mutate specific properties before assigning it to a ViewportController. This is useful for applying presets with minor adjustments. ```swift var config = LightingConfiguration.threePoint config.shadowsEnabled = false config.exposure = 1.3 controller.lightingConfiguration = config ``` -------------------------------- ### PBRMaterial Initializer Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Materials.md Initializes a PBRMaterial with default values for all parameters, allowing for a zero-argument call to create a standard mid-grey dielectric material. Supports optional KHR_materials_clearcoat and KHR_materials_emissive_strength extensions. ```swift public init( baseColor: SIMD3 = SIMD3(0.8, 0.8, 0.8), metallic: Float = 0, roughness: Float = 0.5, ior: Float = 1.5, clearcoat: Float = 0, clearcoatRoughness: Float = 0.03, emissive: SIMD3 = SIMD3(0, 0, 0), emissiveStrength: Float = 1, opacity: Float = 1 ) ``` -------------------------------- ### Disable Frustum Culling for Debugging Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Configuration.md Example demonstrating how to disable frustum culling for debugging purposes by setting the `enableFrustumCulling` property to false. ```swift var config = ViewportConfiguration.cad config.enableFrustumCulling = false ``` -------------------------------- ### Configure Gestures with Presets Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/README.md Apply predefined gesture configurations like '.blender', '.fusion360', or the '.default' preset. ```swift let config = ViewportConfiguration( gestureConfiguration: .blender // or .fusion360, .default ) ``` -------------------------------- ### Run All Tests for OCCTSwiftViewport Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/CLAUDE.md Execute all test suites using the Swift Testing framework. This command runs all 9 suites defined in the project. ```bash swift test ``` -------------------------------- ### Initialize Gesture Configuration Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Input.md Creates a gesture configuration with customizable sensitivity and gesture mappings. All parameters are optional, allowing for partial configuration with sensible defaults. ```swift public init( orbitSensitivity: Float = 0.005, panSensitivity: Float = 0.005, zoomSensitivity: Float = 1.0, scrollZoomSensitivity: Float = 0.25, minPanSpeed: Float = 0.001, invertOrbitHorizontal: Bool = false, invertOrbitVertical: Bool = false, enableInertia: Bool = true, dampingFactor: Float = 0.1, singleFingerDrag: GestureAction = .orbit, twoFingerDrag: GestureAction = .pan, pinchGesture: GestureAction = .zoom, doubleTap: GestureAction = .focusOnPoint, mouseDrag: GestureAction = .orbit, shiftDrag: GestureAction = .pan, optionDrag: GestureAction = .zoom, commandDrag: GestureAction = .select, scrollWheel: GestureAction = .zoom, trackpadPinch: GestureAction = .zoom, doubleClick: GestureAction = .focusOnPoint ) ``` -------------------------------- ### RadiusMeasurement Radius Calculation Example Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Measurements.md Shows how to retrieve the computed radius of a RadiusMeasurement. The radius is calculated based on the center and edge point. ```swift let m = RadiusMeasurement(center: .zero, edgePoint: SIMD3(3, 4, 0)) print(m.radius) // 5.0 ``` -------------------------------- ### LightingConfiguration Initializer Source: https://github.com/secondmouseau/occtswiftviewport/blob/main/docs/reference/Display-Lighting.md Initializes a LightingConfiguration with various lighting and rendering properties. All parameters except the three primary lights have default values, allowing for easy customization. ```swift public init( keyLight: LightSettings, fillLight: LightSettings, backLight: LightSettings, ambientIntensity: Float = 0.3, ambientColor: SIMD3 = SIMD3(1, 1, 1), shadowsEnabled: Bool = true, shadowSoftness: Float = 0.3, shadowMapSize: Int = 2048, shadowIntensity: Float = 0.4, shadowBias: Float = 0.005, specularPower: Float = 64.0, specularIntensity: Float = 0.5, fresnelPower: Float = 3.0, fresnelIntensity: Float = 0.3, matcapBlend: Float = 0.0, ambientSkyColor: SIMD3 = SIMD3(0.9, 0.95, 1.0), ambientGroundColor: SIMD3 = SIMD3(0.3, 0.25, 0.2), enableSSAO: Bool = true, ssaoRadius: Float = 0.5, ssaoIntensity: Float = 0.6, exposure: Float = 1.1, whitePoint: Float = 1.0, shadowLightSize: Float = 0.02, shadowSearchRadius: Float = 0.01, environmentMapData: Data? = nil, environmentMapURL: URL? = nil, environmentIntensity: Float = 1.0, environmentRotationY: Float = 0, backgroundExposure: Float = 1.0, drawBackground: Bool = false ) ```