### Camera Control Examples Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Control camera rotation, navigate to standard views, focus on geometry, and reset the camera. ```swift // Rotation styles controller.cameraController.rotationStyle = .turntable // Z-up locked (CAD default) controller.cameraController.rotationStyle = .arcball // Free rotation // Standard views controller.goToStandardView(.top) controller.goToStandardView(.front) controller.goToStandardView(.isometricFrontRight) // Focus on geometry controller.focusOnBounds() // Fit all bodies in view controller.focusOn(point: SIMD3(0, 0, 0), distance: 10) // Reset controller.reset() ``` -------------------------------- ### Lighting Configuration Presets Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Apply lighting presets to the viewport, such as a three-point lighting setup or studio lighting. ```swift // Lighting presets let config = ViewportConfiguration( lightingConfiguration: .threePoint // Key, fill, and back lights ) // Also: .studio, .architectural, .flat ``` -------------------------------- ### CADView App-Level Usage Example Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Demonstrates how to use ViewportView and ViewportController in an application. It loads geometry from a Shape, extracts edge polylines, builds interleaved vertex data, and sets up face indices for selection mapping. ```swift import OCCTSwift import OCCTSwiftViewport struct CADView: View { @StateObject private var controller = ViewportController( configuration: .cad ) @State private var bodies: [ViewportBody] = [] let shape: Shape var body: some View { ViewportView(controller: controller, bodies: bodies) .onAppear { loadGeometry() } } func loadGeometry() { let mesh = shape.mesh(linearDeflection: 0.1) // Extract edge polylines for wireframe let edges: [[SIMD3]] = shape.edges().map { edge.points(count: 64) } // Build interleaved vertex data [px, py, pz, nx, ny, nz, ...] let verts = mesh.vertexData // [px, py, pz, ...] let norms = mesh.normalData // [nx, ny, nz, ...] var interleaved = [Float]() interleaved.reserveCapacity(mesh.vertexCount * 6) for i in 0..]() let pivot = cameraState.pivot let cx = (pivot.x / spacing).rounded() * spacing let cz = (pivot.z / spacing).rounded() * spacing for ix in -15...15 { for iz in -15...15 { instances.append(SIMD2( cx + Float(ix) * spacing, cz + Float(iz) * spacing )) } } instanceBuffer = device.makeBuffer( bytes: instances, length: instances.count * MemoryLayout>.stride, options: .storageModeShared )! } func draw(encoder: MTLRenderCommandEncoder) { encoder.setVertexBuffer(dotMesh, offset: 0, index: 0) encoder.setVertexBuffer(instanceBuffer, offset: 0, index: 2) encoder.drawIndexedPrimitives( type: .triangle, indexCount: 6, // two triangles for quad indexType: .uint16, indexBuffer: quadIndices, indexBufferOffset: 0, instanceCount: 961 ) } } ``` -------------------------------- ### Regenerate Xcode Project Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Run 'xcodegen' to regenerate the Xcode project file from the 'project.yml' configuration. ```bash xcodegen # Regenerate Xcode project from project.yml ``` -------------------------------- ### Generate Xcode Project for iOS Demo Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Use xcodegen to generate the Xcode project for running the demo app on iOS. ```bash xcodegen open OCCTSwiftViewport.xcodeproj ``` -------------------------------- ### Grid Instance Structure and Vertex Shader Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Defines the structure for grid instances and the Metal vertex shader for instanced grid rendering. Uses a unit quad mesh and instance offsets to render multiple grid dots efficiently. ```metal struct GridInstance { float2 offset; }; vertex float4 grid_vertex( float3 position [[attribute(0)]], // unit quad constant Uniforms& u [[buffer(1)]], constant GridInstance* instances [[buffer(2)]], uint instanceID [[instance_id]] ) { float3 worldPos = position; worldPos.xz += instances[instanceID].offset; return u.projectionMatrix * u.viewMatrix * float4(worldPos, 1.0); } ``` -------------------------------- ### ViewportView Initialization Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Initializes the ViewportView with a ViewportController and an array of ViewportBody objects. This replaces the RealityKit 'entities' parameter with plain value types. ```swift public struct ViewportView: View { public init( controller: ViewportController, bodies: [ViewportBody] ) } ``` -------------------------------- ### Performance Preset Configuration Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Use the performance preset to disable expensive per-frame rendering passes for smoother performance in large scenes. ```swift // 1. Use the performance preset: disables the expensive per-frame passes // (directional shadow map, SSAO, MSAA, silhouettes). let controller = ViewportController(configuration: .performance) // or toggle the individual levers on any configuration: // lightingConfiguration.shadowsEnabled = false // lightingConfiguration.enableSSAO = false // msaaSampleCount = 1 // enableSilhouettes = false ``` -------------------------------- ### Loading and Converting CAD Files with OCCTSwiftTools Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Use OCCTSwiftTools to load various CAD file formats (STEP, STL, OBJ, BREP) and convert OCCTSwift geometry types like Shape, Wire, Curve, and Surface into ViewportBody objects. ```swift import OCCTSwiftTools // Load a CAD file (STEP, STL, OBJ, BREP) let result = try await CADFileLoader.load(from: stepFileURL, format: .step) // result.bodies: [ViewportBody], result.shapes: [Shape], result.metadata: [String: CADBodyMetadata] // Convert a Shape directly let shape = Shape.box(width: 10, height: 5, depth: 3)! let (body, metadata) = CADFileLoader.shapeToBodyAndMetadata(shape, id: "box", color: SIMD4(0.7, 0.7, 0.75, 1)) // Convert wires, curves, surfaces let wireBody = WireConverter.wireToBody(wire, id: "sketch", color: SIMD4(1, 1, 0, 1)) let curveBody = CurveConverter.curve3DToBody(helix, id: "helix", color: SIMD4(0, 0.8, 1, 1)) let curve2DBody = CurveConverter.curve2DToBody(circle, id: "circle", color: SIMD4(1, 0, 0, 1)) let gridBodies = SurfaceConverter.surfaceToGridBodies(surface, idPrefix: "surf", uColor: SIMD4(0.8, 0.3, 0.3, 1), vColor: SIMD4(0.3, 0.3, 0.8, 1)) // Utility: position markers, offset bodies let marker = BodyUtilities.makeMarkerSphere(at: SIMD3(5, 0, 0), radius: 0.3, id: "pt", color: .one) let shifted = BodyUtilities.offsetBody(body!, dx: 10) // Export shapes try await ExportManager.export(shapes: [shape], format: .step, to: outputURL) ``` -------------------------------- ### Writing a Script with OCCTSwift Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Create geometry using the OCCTSwift API within a script. The ScriptHarness context allows adding and emitting shapes with metadata, which are automatically loaded into the viewport. ```swift // Sources/Script/main.swift import OCCTSwift import ScriptHarness let ctx = ScriptContext(metadata: ManifestMetadata( name: "Bracket Assembly", revision: "3", source: "Customer drawing D-1234" )) let C = ScriptContext.Colors.self // Build geometry using the full OCCTSwift API let base = Shape.box(width: 50, height: 10, depth: 30)! let hole = Shape.cylinder(radius: 5, height: 12)! .translated(by: SIMD3(20, -1, 15)) let bracket = base.subtracting(hole)! .filleted(radius: 1.5)! try ctx.add(bracket, id: "bracket", color: C.steel, name: "Main bracket") try ctx.emit(description: "Bracket with mounting hole") ``` -------------------------------- ### ViewportController focusOn Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Adjusts the camera to frame a given bounding box, with an option to animate the transition. ```APIDOC ## ViewportController focusOn ### Description Focus the camera to frame the given bounding box. ### Method `func focusOn(min: SIMD3, max: SIMD3, animated: Bool = true)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response None ### Response Example None ``` -------------------------------- ### Run a Specific Test Case Source: https://github.com/gsdali/occtswiftviewport/blob/main/CLAUDE.md Isolate and run a single test case within a specified test suite. ```bash swift test --filter "CameraStateTests/Default initialization" ``` -------------------------------- ### Clean Build Artifacts and Rebuild Source: https://github.com/gsdali/occtswiftviewport/blob/main/CLAUDE.md Cleans the build directory and rebuilds the project. Useful for resolving stale PCH errors after code renames. ```bash swift package clean && swift build ``` -------------------------------- ### Package.swift for OCCTSwiftViewport Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Add OCCTSwiftViewport or OCCTSwiftTools to your Swift Package Manager dependencies. Choose the product based on your needs: viewport-only or viewport with OCCTSwift bridge. ```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 ] ) ] ``` -------------------------------- ### Promoting Scripts to Libraries Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Extract validated geometry-building code from scripts into shared Swift libraries. This allows for code reuse across multiple scripts and applications. ```swift // Sources/BracketLib/Bracket.swift public struct BracketResult { ... } public enum Bracket { public static func build(holeRadius: Double = 5) -> BracketResult { ... } } // Sources/Script/main.swift — now a thin wrapper let result = Bracket.build(holeRadius: 6) try ctx.add(result.shape, id: "bracket", color: C.steel) try ctx.emit(description: result.metadata.name) // YourApp/ContentView.swift — same build() function let result = Bracket.build(holeRadius: 6) let body = convertToViewportBody(result.shape) ``` -------------------------------- ### Manual ViewportBody Construction Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Construct a ViewportBody directly when using OCCTSwiftViewport without the OCCTSwift dependency. Provide vertex data, indices, edges, and color. ```swift import OCCTSwiftViewport let body = ViewportBody( id: "my-part", vertexData: vertexData, // Interleaved [px, py, pz, nx, ny, nz, ...] indices: indices, // Triangle indices edges: edges, // Wireframe polylines [[SIMD3]] color: SIMD4(0.7, 0.7, 0.75, 1.0) ) ``` -------------------------------- ### Custom Gesture Configuration Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Customize individual gesture behaviors such as single-finger drag, two-finger drag, pinch gestures, and inertia. ```swift // Or customize let config = ViewportConfiguration( gestureConfiguration: GestureConfiguration( singleFingerDrag: .orbit, twoFingerDrag: .pan, pinchGesture: .zoom, enableInertia: true, dampingFactor: 0.1 ) ) let controller = ViewportController(configuration: config) ``` -------------------------------- ### Selection Highlighting in Swift Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Handles the app's response to a hit test result by highlighting the tapped face. It updates the `faceColors` property of a `ViewportBody` to apply a visual indicator to the selected face. ```swift func onTap(at point: CGPoint) { let hits = controller.hitTest(at: point) guard let hit = hits.first else { return } // Highlight the tapped face bodies[bodyIndex(for: hit.bodyID)].faceColors = [ hit.faceIndex: SIMD4(0.2, 0.5, 1.0, 1.0) // blue highlight ] } ``` -------------------------------- ### Display Mode Settings Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Set the display mode for the viewport, including shaded, wireframe, and shaded with edges. ```swift // Display modes controller.displayMode = .shaded controller.displayMode = .wireframe controller.displayMode = .shadedWithEdges ``` -------------------------------- ### ScriptContext API for Adding Shapes Source: https://github.com/gsdali/occtswiftviewport/blob/main/CLAUDE.md Demonstrates how to use the ScriptContext API to add various geometric entities like solids, wires, edges, and compounds to the script output. The emit function should be called last. ```swift let ctx = ScriptContext() // also writes output.step on emit let ctx = ScriptContext(exportSTEP: false) // BREP only, faster let C = ScriptContext.Colors.self // .red .blue .steel .brass etc. // Solids try ctx.add(shape, id: "part", color: C.steel, name: "Bracket") // Wires / sketches (displayed as wireframe edges) try ctx.add(wire, id: "sketch", color: C.yellow) // Edges try ctx.add(edge, id: "axis", color: C.red) // Compounds (assemblies) try ctx.addCompound([part1, part2], id: "asm", color: C.gray) // Emit — writes output.step + manifest.json (call LAST) try ctx.emit(description: "My parametric design") ``` -------------------------------- ### CPU Ray Casting Implementation in Swift Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Implements CPU ray casting for hit testing against triangle meshes. It iterates through bodies, their vertices, and indices to find intersections using the Moller-Trumbore algorithm. Requires a Ray struct and returns a sorted array of HitResult. ```swift struct Ray { let origin: SIMD3 let direction: SIMD3 } func hitTest(ray: Ray, bodies: [ViewportBody]) -> [HitResult] { var hits: [HitResult] = [] for body in bodies where body.isSelectable && body.isVisible { let vertices = body.vertexData // stride 6 let indices = body.indices for tri in 0..<(indices.count / 3) { let i0 = Int(indices[tri * 3]) let i1 = Int(indices[tri * 3 + 1]) let i2 = Int(indices[tri * 3 + 2]) let v0 = SIMD3(vertices[i0*6], vertices[i0*6+1], vertices[i0*6+2]) let v1 = SIMD3(vertices[i1*6], vertices[i1*6+1], vertices[i1*6+2]) let v2 = SIMD3(vertices[i2*6], vertices[i2*6+1], vertices[i2*6+2]) if let t = rayTriangleIntersection(ray: ray, v0: v0, v1: v1, v2: v2) { let position = ray.origin + ray.direction * t let faceIndex = tri < body.faceIndices.count ? body.faceIndices[tri] : -1 hits.append(HitResult( bodyID: body.id, position: position, triangleIndex: tri, faceIndex: faceIndex, distance: t )) } } } return hits.sorted { $0.distance < $1.distance } } ``` -------------------------------- ### Swift and Metal Uniform Struct Synchronization Source: https://github.com/gsdali/occtswiftviewport/blob/main/CLAUDE.md Ensure identical field order, matching types, and 16-byte alignment for SIMD types when defining uniform structs in both Swift and Metal shaders. This is crucial for maintaining data integrity between the CPU and GPU. ```swift struct Uniforms { var modelViewProjectionMatrix: matrix_float4x4 var normalMatrix: matrix_float3x3 var lightDirection: SIMD3 var cameraPosition: SIMD3 var ambientLightColor: SIMD3 var rimLightColor: SIMD3 var time: Float } struct BodyUniforms { var objectColor: SIMD4 var pickID: UInt32 } ``` ```metal struct Uniforms { float4x4 modelViewProjectionMatrix; float3x3 normalMatrix; float3 lightDirection; float3 cameraPosition; float3 ambientLightColor; float3 rimLightColor; float time; }; struct BodyUniforms { float4 objectColor; uint pickID; }; ``` -------------------------------- ### ViewportController hitTest Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Performs a hit test at a specified screen point and returns an array of HitResult objects, sorted by distance from the camera (nearest first). ```APIDOC ## ViewportController hitTest ### Description Perform a hit test at a screen point. Returns hits sorted by distance (nearest first). ### Method `@MainActor func hitTest(at point: CGPoint) -> [HitResult]` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response - **[HitResult]** - An array of HitResult objects, sorted by distance. ### Response Example ```json [ { "bodyID": "string", "position": [Float, Float, Float], "triangleIndex": "integer", "faceIndex": "integer", "distance": "number" } ] ``` ``` -------------------------------- ### Integrating and Using ViewCube Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md The ViewCube is integrated into MetalViewportView for intuitive camera control. It supports clicking faces for orthographic views and corners for isometric views. It can also be used standalone. ```swift // The ViewCube is built into MetalViewportView // Click faces → orthographic views (Top, Front, Right, etc.) // Click corners → isometric views // Click edges → intermediate views // Or use ViewCubeView standalone ViewCubeView(controller: controller) .frame(width: 100, height: 100) ``` -------------------------------- ### Uniforms for Shaded Rendering Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Defines uniform structures for camera, lighting, and object properties in Metal shaders. Includes model, view, projection, and normal matrices, camera position, light direction, and intensity. ```metal struct Uniforms { float4x4 modelMatrix; float4x4 viewMatrix; float4x4 projectionMatrix; float4x4 normalMatrix; // transpose(inverse(modelView)), for lighting float3 cameraPosition; float3 lightDirection; // single directional light float lightIntensity; float ambientIntensity; }; struct BodyUniforms { float4 color; float opacity; int faceIndexOffset; // offset into face-index buffer for this body }; ``` -------------------------------- ### Axes Rendering with Constant Screen Width Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Renders three colored cylinders representing axes. Supports scaling radius to maintain constant screen width based on camera distance. Uses three draw calls total. ```swift struct AxisRenderer { let cylinderMesh: MTLBuffer // shared cylinder geometry let cylinderIndices: MTLBuffer func draw( encoder: MTLRenderCommandEncoder, config: ViewportConfiguration, cameraState: CameraState ) { let radius: Float if config.axisStyle == .constantScreenWidth { let scale = cameraState.distance / config.initialCameraState.distance radius = config.axisRadius * scale } else { radius = config.axisRadius } // Draw X (red), Y (green), Z (blue) with per-axis model matrix for (color, modelMatrix) in axisTransforms(length: config.axisLength) { var bodyUniforms = BodyUniforms(color: color, opacity: 1.0) encoder.setVertexBytes(&bodyUniforms, length: ..., index: 2) // ... set model matrix, draw } } } ``` -------------------------------- ### Adding Clip Planes for Section Cuts Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Define and apply clip planes to create section cuts in the geometry. The normal vector and distance determine the orientation and position of the cut. ```swift // Add a section cut let clip = ClipPlane( normal: SIMD3(0, 1, 0), // Cut along Y axis distance: 0.0 ) controller.clipPlanes = [clip] ``` -------------------------------- ### Creating Distance Measurements Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Generate distance measurements between two points in 3D space. The MeasurementOverlay can automatically render leader lines and labels for clarity. ```swift // Distance between two points let measurement = DistanceMeasurement( from: SIMD3(0, 0, 0), to: SIMD3(10, 0, 0) ) controller.measurements = [.distance(measurement)] // Overlay renders leader lines and labels automatically MeasurementOverlay(controller: controller) ``` -------------------------------- ### Viewport Body Buffers Structure Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Defines the structure for Metal buffers associated with each ViewportBody, including vertex, index, and face index buffers. ```swift struct BodyBuffers { let vertexBuffer: MTLBuffer // interleaved positions + normals let indexBuffer: MTLBuffer // triangle indices let faceIndexBuffer: MTLBuffer? // per-triangle face index (for picking) let edgeVertexBuffer: MTLBuffer? // edge polyline vertices let edgeIndexBuffer: MTLBuffer? // line segment indices let triangleCount: Int let edgeVertexCount: Int } ``` -------------------------------- ### Update Uniforms with Camera State Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Updates the uniforms buffer with view and projection matrices, and camera position derived from the CameraState. ```swift // In ViewportRenderer, each frame: var uniforms = Uniforms() uniforms.viewMatrix = cameraState.viewMatrix // new computed property uniforms.projectionMatrix = cameraState.projectionMatrix( aspectRatio: Float(drawableSize.width / drawableSize.height) ) uniforms.cameraPosition = cameraState.position ``` -------------------------------- ### ViewportController Focus Extension Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Adds a focusOn method to ViewportController to frame a given bounding box. Supports animated focusing. ```swift /// Focus the camera to frame the given bounding box. public func focusOn( min: SIMD3, max: SIMD3, animated: Bool = true ) } ``` -------------------------------- ### ViewportController Hit Test Extension Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Adds a hitTest method to ViewportController to perform hit tests at a given screen point. Returns hits sorted by distance, nearest first. ```swift extension ViewportController { /// Perform a hit test at a screen point. /// Returns hits sorted by distance (nearest first). @MainActor public func hitTest(at point: CGPoint) -> [HitResult] ``` -------------------------------- ### Shaded Vertex Shader Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Metal vertex shader for shaded rendering. Transforms vertex positions to clip space and calculates world normal, position, and normal for fragment processing. ```metal vertex VertexOut shaded_vertex( Vertex in [[stage_in]], constant Uniforms& u [[buffer(1)]] ) { VertexOut out; float4 worldPos = u.modelMatrix * float4(in.position, 1.0); out.position = u.projectionMatrix * u.viewMatrix * worldPos; out.worldNormal = (u.normalMatrix * float4(in.normal, 0.0)).xyz; out.worldPosition = worldPos.xyz; return out; } ``` -------------------------------- ### ViewportBody Geometry Input Structure Source: https://github.com/gsdali/occtswiftviewport/blob/main/README.md Defines the structure for providing geometry data to the ViewportBody. It includes vertex and index data for rendering, along with optional edge and face index information. ```swift ViewportBody( id: String, // Unique identifier vertexData: [Float], // Interleaved [px,py,pz, nx,ny,nz, ...] indices: [UInt32], // Triangle indices edges: [[SIMD3]], // Wireframe polylines color: SIMD4, // RGBA color faceIndices: [Int32]? = nil // Optional: maps triangles → face IDs ) ``` -------------------------------- ### Update Viewport Buffers Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Updates the Metal buffers for ViewportBodies. It reuses existing buffers if the body has not changed, otherwise, it creates new buffers. ```swift func updateBuffers(bodies: [ViewportBody], device: MTLDevice) { var newBufferMap: [String: BodyBuffers] = [: ] for body in bodies { if let existing = bufferMap[body.id], !bodyChanged(body, existing) { newBufferMap[body.id] = existing // reuse } else { newBufferMap[body.id] = createBuffers(for: body, device: device) } } bufferMap = newBufferMap } ``` -------------------------------- ### ViewportBody Structure Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Defines a renderable body in the viewport. It holds geometry data like vertices, indices, and edges, along with display properties such as color, opacity, and visibility. ```swift /// A renderable body in the viewport. Geometry-source agnostic. public struct ViewportBody: Identifiable, Sendable { public let id: String /// Interleaved vertex data: [px, py, pz, nx, ny, nz, ...] /// Stride: 6 floats per vertex. public var vertexData: [Float] /// Triangle indices (3 per triangle). public var indices: [UInt32] /// Per-triangle source face index (parallel to triangle count). /// Used for selection mapping. Pass empty array if not needed. public var faceIndices: [Int32] /// Edge polylines for wireframe rendering. /// Each inner array is one polyline: [p0, p1, p2, ...] as SIMD3. public var edges: [[SIMD3]] /// Display color (RGBA, 0-1). public var color: SIMD4 /// Per-face colors, indexed by face index. Overrides body color /// for individual faces (e.g. selection highlighting). /// Pass empty dictionary for uniform color. public var faceColors: [Int32: SIMD4] /// Opacity (0-1). Values < 1 enable transparency. public var opacity: Float /// Whether this body is visible. public var isVisible: Bool /// Whether this body participates in hit testing. public var isSelectable: Bool } ``` -------------------------------- ### MetalViewRepresentable for iOS Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Defines a UIViewRepresentable for integrating an MTKView into SwiftUI on iOS. It configures the Metal device, renderer delegate, pixel formats, and clear color. ```swift #if os(iOS) struct MetalViewRepresentable: UIViewRepresentable { let renderer: ViewportRenderer func makeUIView(context: Context) -> MTKView { let view = MTKView() view.device = MTLCreateSystemDefaultDevice() view.delegate = renderer view.colorPixelFormat = .bgra8Unorm view.depthStencilPixelFormat = .depth32Float view.clearColor = MTLClearColor(...) view.preferredFramesPerSecond = 60 view.isPaused = false view.enableSetNeedsDisplay = false return view } func updateUIView(_ view: MTKView, context: Context) { // Trigger buffer updates when bodies change renderer.bodiesDidChange() } } #elseif os(macOS) struct MetalViewRepresentable: NSViewRepresentable { // Same pattern with NSView } #endif ``` -------------------------------- ### Shaded Fragment Shader Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Metal fragment shader for shaded rendering. Calculates diffuse and ambient lighting components and returns the final color with specified opacity. Suitable for CAD applications. ```metal fragment float4 shaded_fragment( VertexOut in [[stage_in]], constant Uniforms& u [[buffer(1)]], constant BodyUniforms& body [[buffer(2)]] ) { float3 N = normalize(in.worldNormal); float3 L = normalize(-u.lightDirection); float diffuse = max(dot(N, L), 0.0) * u.lightIntensity; float ambient = u.ambientIntensity; float3 color = body.color.rgb * (diffuse + ambient); return float4(color, body.opacity); } ``` -------------------------------- ### CameraState View Matrix Computed Property Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Provides the view matrix (world to camera space) based on the camera's position, target (pivot), and up vector. ```swift extension CameraState { /// View matrix (world -> camera space). public var viewMatrix: simd_float4x4 { let pos = position let target = pivot let up = upVector return simd_float4x4(lookAt: target, from: pos, up: up) } } ``` -------------------------------- ### Fragment Shader for Per-Face Coloring in Metal Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md A Metal fragment shader function that checks for per-face color overrides. It uses a sparse override table (`faceColors`) and a face index buffer to apply custom colors to specific faces during rendering. ```metal fragment float4 shaded_fragment( VertexOut in [[stage_in]], constant Uniforms& u [[buffer(1)]], constant BodyUniforms& body [[buffer(2)]], constant int32_t* faceIndices [[buffer(3)]], // per-triangle constant float4* faceColors [[buffer(4)]], // sparse override table constant int& faceColorCount [[buffer(5)]], uint primitiveID [[primitive_id]] ) { float4 baseColor = body.color; // Check for per-face color override int32_t faceIdx = faceIndices[primitiveID + body.faceIndexOffset]; for (int i = 0; i < faceColorCount; i++) { // faceColors is packed as [faceIndex (as float), r, g, b, a, ...] // (actual implementation would use a lookup buffer) } // ... lighting as before ... } ``` -------------------------------- ### Wireframe Vertex Shader Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Metal vertex shader for wireframe rendering. Transforms vertex positions to clip space and applies a depth bias to prevent z-fighting. Used for edges only or overlaid on shaded passes. ```metal vertex float4 wireframe_vertex( float3 position [[attribute(0)]], constant Uniforms& u [[buffer(1)]] ) { float4 worldPos = u.modelMatrix * float4(position, 1.0); float4 clipPos = u.projectionMatrix * u.viewMatrix * worldPos; // Depth bias: pull edges slightly toward camera to prevent z-fighting clipPos.z -= 0.0001 * clipPos.w; return clipPos; } ``` -------------------------------- ### Vertex Structure Definition Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Defines the vertex structure for Metal shaders, including position and normal attributes. Matches the interleaved layout of ViewportBody.vertexData with a stride of 24 bytes. ```metal struct Vertex { float3 position [[attribute(0)]]; float3 normal [[attribute(1)]]; }; ``` -------------------------------- ### CameraState Projection Matrix Computed Property Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Calculates the projection matrix based on whether the camera is orthographic or perspective, using the provided aspect ratio. ```swift /// Projection matrix for the given aspect ratio. public func projectionMatrix(aspectRatio: Float) -> simd_float4x4 { if isOrthographic { let halfWidth = orthographicScale * aspectRatio / 2 let halfHeight = orthographicScale / 2 return simd_float4x4( orthographic: -halfWidth...halfWidth, bottom: -halfHeight, top: halfHeight, near: 0.01, far: maxDistance * 2 ) } else { return simd_float4x4( perspectiveFOV: fieldOfView * .pi / 180, aspectRatio: aspectRatio, near: 0.01, far: maxDistance * 2 ) } } ``` -------------------------------- ### HitResult Structure Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Represents the result of a hit test against viewport geometry. It includes the ID of the hit body, the world-space position, triangle index, face index, and distance from the camera. ```swift /// Result of a hit test against viewport geometry. public struct HitResult: Sendable { /// ID of the body that was hit. public let bodyID: String /// World-space position of the hit. public let position: SIMD3 /// Index of the triangle that was hit. public let triangleIndex: Int /// Source face index from the body's faceIndices array. /// -1 if the body has no face index data. public let faceIndex: Int32 /// Distance from the camera to the hit point. public let distance: Float } ``` -------------------------------- ### X-Ray Fragment Shader Source: https://github.com/gsdali/occtswiftviewport/blob/main/docs/metal-architecture.md Metal fragment shader for x-ray rendering. Adds transparency and a Fresnel-like falloff for opacity, making internal edges and surfaces visible. Renders with disabled culling and depth writes. ```metal fragment float4 xray_fragment( VertexOut in [[stage_in]], constant Uniforms& u [[buffer(1)]], constant BodyUniforms& body [[buffer(2)]] ) { float3 N = normalize(in.worldNormal); float3 V = normalize(u.cameraPosition - in.worldPosition); // Fresnel-like falloff: more opaque at glancing angles float facing = abs(dot(N, V)); float alpha = mix(0.4, 0.1, facing); return float4(body.color.rgb * 0.5, alpha); } ```