diff --git a/Package.resolved b/Package.resolved index eee1671..c4b5a89 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "0e11f021a4d366bad494e92af92839bdf3a2394466e3876a9e1c2182012bd394", + "originHash" : "193f12a2c125ba349244b6ef2c622ab89425ebd63522973119c7c5869efbaa80", "pins" : [ { "identity" : "occtswift", "kind" : "remoteSourceControl", "location" : "https://github.com/gsdali/OCCTSwift.git", "state" : { - "revision" : "6e3f99603ed14790816cb4857a6df20848bd11ca", - "version" : "1.7.1" + "revision" : "94eea64c25c265f268ecd6da6fef36b05a60ce9b", + "version" : "1.8.0" } }, { diff --git a/Package.swift b/Package.swift index b0e25c7..8b7b7dd 100644 --- a/Package.swift +++ b/Package.swift @@ -42,7 +42,9 @@ let package = Package( // setters no-op but getters return the live derived value. The cookbook // ergonomics relied on since v1.3.1 (circularPatternCut #169, sweep // orientation #170, concave/convex/edges(where:) #171) are unchanged. - .package(url: "https://github.com/gsdali/OCCTSwift.git", from: "1.7.1"), + // 1.8.0 adds Exporter.writeBREP(allowInvalid:) for the load-brep / + // import `--allow-invalid` flags (OCCTMCP #41). + .package(url: "https://github.com/gsdali/OCCTSwift.git", from: "1.8.0"), // RenderPreview rasterizes through Viewport's OffscreenRenderer. // Floored at v1.0.4: v1.0.3 fixes an uncatchable quantize() crash on // body load (Viewport #30) and v1.0.4 makes the published Viewport diff --git a/Sources/ScriptHarness/GraphIO.swift b/Sources/ScriptHarness/GraphIO.swift index ec1a387..ad85569 100644 --- a/Sources/ScriptHarness/GraphIO.swift +++ b/Sources/ScriptHarness/GraphIO.swift @@ -32,10 +32,13 @@ public enum GraphIO { } } - public static func writeBREP(_ shape: Shape, to path: String) throws { + /// - Parameter allowInvalid: skip the `shape.isValid` write gate so an + /// in-progress / loose-face reconstruction can be persisted as-is + /// (OCCTSwift ≥ 1.8.0; loadBREP doesn't gate on read). + public static func writeBREP(_ shape: Shape, to path: String, allowInvalid: Bool = false) throws { let url = URL(fileURLWithPath: path) do { - try Exporter.writeBREP(shape: shape, to: url) + try Exporter.writeBREP(shape: shape, to: url, allowInvalid: allowInvalid) } catch { throw ScriptError.message("Failed to write BREP at \(path): \(error.localizedDescription)") } diff --git a/Sources/occtkit/Commands/Import.swift b/Sources/occtkit/Commands/Import.swift index 5309f58..0f41a39 100644 --- a/Sources/occtkit/Commands/Import.swift +++ b/Sources/occtkit/Commands/Import.swift @@ -43,7 +43,7 @@ enum ImportCommand: Subcommand { Usage: import --emit-manifest [--format auto|step|iges|stl|obj] - [--id-prefix

] [--preserve-assembly] [--heal-on-import] + [--id-prefix

] [--preserve-assembly] [--heal-on-import] [--allow-invalid] import (JSON request from file) import (JSON request from stdin) """ @@ -55,6 +55,7 @@ enum ImportCommand: Subcommand { var idPrefix: String var preserveAssembly: Bool var healOnImport: Bool + var allowInvalid: Bool } private enum Format: String, Decodable { @@ -68,6 +69,7 @@ enum ImportCommand: Subcommand { let idPrefix: String? let preserveAssembly: Bool? let healOnImport: Bool? + let allowInvalid: Bool? } struct Response: Encodable { @@ -107,7 +109,8 @@ enum ImportCommand: Subcommand { let document = try loadDocumentSTEP(path: req.inputPath) assembly = try walkAssembly( document: document, emitDir: emitDir, - idPrefix: req.idPrefix, bodies: &bodies, addedIds: &addedIds + idPrefix: req.idPrefix, allowInvalid: req.allowInvalid, + bodies: &bodies, addedIds: &addedIds ) } else { if req.preserveAssembly && format != .step { @@ -116,7 +119,7 @@ enum ImportCommand: Subcommand { let shape = try loadSingleShape(format: format, path: req.inputPath) let id = "\(req.idPrefix)_0" let bodyURL = emitDir.appendingPathComponent("\(id).brep") - try GraphIO.writeBREP(shape, to: bodyURL.path) + try GraphIO.writeBREP(shape, to: bodyURL.path, allowInvalid: req.allowInvalid) bodies.append(BodyDescriptor(id: id, file: "\(id).brep")) addedIds.append(id) } @@ -185,6 +188,7 @@ enum ImportCommand: Subcommand { document: Document, emitDir: URL, idPrefix: String, + allowInvalid: Bool, bodies: inout [BodyDescriptor], addedIds: inout [String] ) throws -> Response.Assembly { @@ -195,7 +199,8 @@ enum ImportCommand: Subcommand { for root in roots { let component = try walkNode( node: root, idPrefix: idPrefix, parentPathSegment: nil, - emitDir: emitDir, bodies: &bodies, addedIds: &addedIds, counter: &counter + emitDir: emitDir, allowInvalid: allowInvalid, + bodies: &bodies, addedIds: &addedIds, counter: &counter ) components.append(component) } @@ -209,6 +214,7 @@ enum ImportCommand: Subcommand { idPrefix: String, parentPathSegment: String?, emitDir: URL, + allowInvalid: Bool, bodies: inout [BodyDescriptor], addedIds: inout [String], counter: inout Int @@ -219,7 +225,7 @@ enum ImportCommand: Subcommand { // Write geometry if this node has any (pure-assembly nodes have no shape). if let shape = node.shape { let bodyURL = emitDir.appendingPathComponent("\(id).brep") - try GraphIO.writeBREP(shape, to: bodyURL.path) + try GraphIO.writeBREP(shape, to: bodyURL.path, allowInvalid: allowInvalid) bodies.append(BodyDescriptor( id: id, file: "\(id).brep", @@ -238,7 +244,8 @@ enum ImportCommand: Subcommand { for child in node.children { children.append(try walkNode( node: child, idPrefix: idPrefix, parentPathSegment: id, - emitDir: emitDir, bodies: &bodies, addedIds: &addedIds, counter: &counter + emitDir: emitDir, allowInvalid: allowInvalid, + bodies: &bodies, addedIds: &addedIds, counter: &counter )) } @@ -272,6 +279,7 @@ enum ImportCommand: Subcommand { var idPrefix: String = "imported" var preserveAssembly = false var healOnImport = false + var allowInvalid = false var i = 1 while i < args.count { switch args[i] { @@ -290,6 +298,8 @@ enum ImportCommand: Subcommand { preserveAssembly = true case "--heal-on-import": healOnImport = true + case "--allow-invalid": + allowInvalid = true default: throw ScriptError.message("Unknown flag: \(args[i])") } @@ -298,7 +308,7 @@ enum ImportCommand: Subcommand { guard let emitManifest else { throw ScriptError.message("--emit-manifest is required") } return Request(inputPath: inputPath, emitManifest: emitManifest, format: format, idPrefix: idPrefix, preserveAssembly: preserveAssembly, - healOnImport: healOnImport) + healOnImport: healOnImport, allowInvalid: allowInvalid) } private static func valueOrThrow(args: [String], i: Int, flag: String) throws -> String { @@ -326,7 +336,8 @@ enum ImportCommand: Subcommand { format: raw.format ?? .auto, idPrefix: raw.idPrefix ?? "imported", preserveAssembly: raw.preserveAssembly ?? false, - healOnImport: raw.healOnImport ?? false + healOnImport: raw.healOnImport ?? false, + allowInvalid: raw.allowInvalid ?? false ) } } diff --git a/Sources/occtkit/Commands/LoadBrep.swift b/Sources/occtkit/Commands/LoadBrep.swift index 1d860e9..2193c47 100644 --- a/Sources/occtkit/Commands/LoadBrep.swift +++ b/Sources/occtkit/Commands/LoadBrep.swift @@ -29,7 +29,7 @@ enum LoadBrepCommand: Subcommand { static let summary = "Load a BREP and emit a manifest entry for OCCTSwiftViewport" static let usage = """ Usage: - load-brep --emit-manifest

[--id ] [--color ] + load-brep --emit-manifest [--id ] [--color ] [--allow-invalid] load-brep (JSON request from file) load-brep (JSON request from stdin) """ @@ -39,6 +39,7 @@ enum LoadBrepCommand: Subcommand { var emitManifest: String var id: String? var color: String? + var allowInvalid: Bool } private struct JSONRequest: Decodable { @@ -46,6 +47,7 @@ enum LoadBrepCommand: Subcommand { let emitManifest: String let id: String? let color: String? + let allowInvalid: Bool? } struct Response: Encodable { @@ -71,7 +73,7 @@ enum LoadBrepCommand: Subcommand { let emitDir = URL(fileURLWithPath: req.emitManifest) try? FileManager.default.createDirectory(at: emitDir, withIntermediateDirectories: true) let bodyURL = emitDir.appendingPathComponent("\(bodyId).brep") - try GraphIO.writeBREP(shape, to: bodyURL.path) + try GraphIO.writeBREP(shape, to: bodyURL.path, allowInvalid: req.allowInvalid) let manifest = ScriptManifest( description: "Imported via load-brep", @@ -104,6 +106,7 @@ enum LoadBrepCommand: Subcommand { var emitManifest: String? var id: String? var color: String? + var allowInvalid = false var i = 1 while i < args.count { switch args[i] { @@ -119,13 +122,16 @@ enum LoadBrepCommand: Subcommand { i += 1 guard i < args.count else { throw ScriptError.message("--color expects a value") } color = args[i] + case "--allow-invalid": + allowInvalid = true default: throw ScriptError.message("Unknown flag: \(args[i])") } i += 1 } guard let emitManifest else { throw ScriptError.message("--emit-manifest is required") } - return Request(inputBrep: inputBrep, emitManifest: emitManifest, id: id, color: color) + return Request(inputBrep: inputBrep, emitManifest: emitManifest, id: id, color: color, + allowInvalid: allowInvalid) } private static func readFile(_ path: String) throws -> Data { @@ -143,7 +149,7 @@ enum LoadBrepCommand: Subcommand { throw ScriptError.message("Invalid JSON: \(error.localizedDescription)") } return Request(inputBrep: raw.inputBrep, emitManifest: raw.emitManifest, - id: raw.id, color: raw.color) + id: raw.id, color: raw.color, allowInvalid: raw.allowInvalid ?? false) } // MARK: - Helpers (shared with Import)