diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 946bb059d..2a020d5e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -397,6 +397,17 @@ jobs: cd SDK/CPackExamples/Cpp/build cmake --build . ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + - name: Go Bindings + run: | + cd SDK/Examples/Go + go mod init lib3mfExamples + go get github.com/3MFConsortium/lib3mf.go/v2@v2.3.2 + go build -o create_cube create_cube.go + ./create_cube deploy-windows: runs-on: windows-2019 @@ -451,6 +462,18 @@ jobs: cd SDK/CPackExamples/Cpp/build cmake --build . --config Release ./Release/Example_ExtractInfo.exe ../../../Examples/Files/Helix.3mf + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + - name: Go Bindings + run: | + cd SDK/Examples/Go + go mod init lib3mfExamples + go get github.com/3MFConsortium/lib3mf.go/v2@v2.3.2 + go build -o create_cube.exe create_cube.go + ./create_cube.exe + deploy-macos: runs-on: macos-latest @@ -507,6 +530,17 @@ jobs: cd SDK/CPackExamples/Cpp/build cmake --build . ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + - name: Go Bindings + run: | + cd SDK/Examples/Go + go mod init lib3mfExamples + go get github.com/3MFConsortium/lib3mf.go/v2@v2.3.2 + go build -o create_cube create_cube.go + ./create_cube deploy-source-code-with-submodules: runs-on: ubuntu-20.04 diff --git a/SDK/Examples/Go/3mf_convert.go b/SDK/Examples/Go/3mf_convert.go new file mode 100644 index 000000000..93fa38bdc --- /dev/null +++ b/SDK/Examples/Go/3mf_convert.go @@ -0,0 +1,138 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: An example to convert between 3MF and STL + +Interface version: 2.3.2 ++++ +*/ + + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" + "os" + "strings" +) + +// findExtension returns the file extension from a given filename. +func findExtension(filename string) string { + idx := strings.LastIndex(filename, ".") + if idx != -1 { + return filename[idx:] + } + return "" +} + +// convert performs the conversion between 3MF and STL formats. +func convert(filename string) int { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatal("Error loading 3MF library:", err) + return -1 + } + + // Check the library version (optional, similar to get_version in Python) + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatal("Error fetching lib3mf version:", err) + return -1 + } + fmt.Printf("lib3mf version: %d.%d.%d\n", nMajor, nMinor, nMicro) + + extension := strings.ToLower(findExtension(filename)) + var readerName, writerName, newExtension string + + switch extension { + case ".stl": + readerName = "stl" + writerName = "3mf" + newExtension = ".3mf" + case ".3mf": + readerName = "3mf" + writerName = "stl" + newExtension = ".stl" + default: + fmt.Printf("Unknown input file extension: %s\n", extension) + return -1 + } + + outputFilename := filename[:len(filename)-len(extension)] + newExtension + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatal("Error creating 3MF model:", err) + return -1 + } + + // Read from the input file + reader, err := model.QueryReader(readerName) + if err != nil { + log.Fatal("Error querying reader for format:", readerName) + return -1 + } + fmt.Printf("Reading %s...\n", filename) + err = reader.ReadFromFile(filename) + if err != nil { + log.Fatal("Error reading from file:", err) + return -1 + } + + // Write to the output file + writer, err := model.QueryWriter(writerName) + if err != nil { + log.Fatal("Error querying writer for format:", writerName) + return -1 + } + fmt.Printf("Writing %s...\n", outputFilename) + err = writer.WriteToFile(outputFilename) + if err != nil { + log.Fatal("Error writing to file:", err) + return -1 + } + + fmt.Println("Done") + return 0 +} + +func main() { + if len(os.Args) != 2 { + fmt.Println("Usage:") + fmt.Println("Convert 3MF to STL: go run 3mf_convert.go model.3mf") + fmt.Println("Convert STL to 3MF: go run 3mf_convert.go model.stl") + os.Exit(1) + } else { + if err := convert(os.Args[1]); err != 0 { + os.Exit(err) + } + } +} diff --git a/SDK/Examples/Go/add_triangle.go b/SDK/Examples/Go/add_triangle.go new file mode 100644 index 000000000..29b993188 --- /dev/null +++ b/SDK/Examples/Go/add_triangle.go @@ -0,0 +1,110 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: Simplest 3mf example that just includes a single triangle + +Interface version: 2.3.2 ++++ +*/ + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" +) + +// createVertexAndReturnIndex creates a vertex on the mesh object and returns its index. +func createVertexAndReturnIndex(mesh lib3mf.MeshObject, x, y, z float32) uint32 { + position := lib3mf.Position{ + Coordinates: [3]float32{x, y, z}, + } + vertexIndex, err := mesh.AddVertex(position) + if err != nil { + log.Fatalf("Error adding vertex at (%f, %f, %f): %v", x, y, z, err) + } + return vertexIndex +} + +// addTriangle adds a triangle to the mesh object using three vertex indices. +func addTriangle(mesh lib3mf.MeshObject, p1, p2, p3 uint32) { + triangle := lib3mf.Triangle{ + Indices: [3]uint32{p1, p2, p3}, + } + _, err := mesh.AddTriangle(triangle) + if err != nil { + log.Fatalf("Error adding triangle with vertices %d, %d, %d: %v", p1, p2, p3, err) + } +} + +func main() { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatalf("Error loading 3MF library: %v", err) + } + + // Check the lib3mf version + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatalf("Error fetching lib3mf version: %v", err) + } + fmt.Printf("lib3mf version: %d.%d.%d\n", nMajor, nMinor, nMicro) + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatalf("Error creating 3MF model: %v", err) + } + + // Initialize a mesh object + meshObject, err := model.AddMeshObject() + if err != nil { + log.Fatalf("Error adding mesh object: %v", err) + } + + // Create 3 vertices + p1 := createVertexAndReturnIndex(meshObject, 0, 0, 0) + p2 := createVertexAndReturnIndex(meshObject, 0, 1, 0) + p3 := createVertexAndReturnIndex(meshObject, 0, 0, 1) + + // Create a triangle with 3 positions + addTriangle(meshObject, p1, p2, p3) + + // Get a 3MF writer and write the single triangle + writer, err := model.QueryWriter("3mf") + if err != nil { + log.Fatalf("Error querying writer for 3MF format: %v", err) + } + err = writer.WriteToFile("triangle.3mf") + if err != nil { + log.Fatalf("Error writing to file 'triangle.3mf': %v", err) + } + + fmt.Println("3MF file with a single triangle written successfully to 'triangle.3mf'") +} diff --git a/SDK/Examples/Go/beam_lattice.go b/SDK/Examples/Go/beam_lattice.go new file mode 100644 index 000000000..80eb09412 --- /dev/null +++ b/SDK/Examples/Go/beam_lattice.go @@ -0,0 +1,197 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: Beam Lattice example + +Interface version: 2.3.2 ++++ +*/ + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" +) + +// createVertex creates a vertex on the mesh object and returns it. +func createVertex(mesh lib3mf.MeshObject, x, y, z float32) lib3mf.Position { + position := lib3mf.Position{ + Coordinates: [3]float32{x, y, z}, + } + _, err := mesh.AddVertex(position) + if err != nil { + log.Fatalf("Error adding vertex at (%f, %f, %f): %v", x, y, z, err) + } + return position +} + +// convertBeamStringToEnum converts a string to a lib3mf.BeamLatticeCapMode enum. +func convertBeamStringToEnum(beamMode string) lib3mf.BeamLatticeCapMode { + switch beamMode { + case "Butt": + return lib3mf.BeamLatticeCapMode_Butt + case "Sphere": + return lib3mf.BeamLatticeCapMode_Sphere + case "HemiSphere": + return lib3mf.BeamLatticeCapMode_HemiSphere + default: + log.Fatalf("Invalid beam cap mode: %s", beamMode) + return lib3mf.BeamLatticeCapMode_Butt // Default value, won't be reached + } +} + +// createBeam creates a beam with given indices, radii, and cap modes. +func createBeam(v0, v1 uint32, r0, r1 float64, c0, c1 string) lib3mf.Beam { + beam := lib3mf.Beam{ + Indices: [2]uint32{v0, v1}, + Radii: [2]float64{r0, r1}, + CapModes: [2]lib3mf.BeamLatticeCapMode{ + convertBeamStringToEnum(c0), + convertBeamStringToEnum(c1), + }, + } + return beam +} + +func main() { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatalf("Error loading 3MF library: %v", err) + } + + // Check the lib3mf version + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatalf("Error fetching lib3mf version: %v", err) + } + fmt.Printf("lib3mf version: %d.%d.%d\n", nMajor, nMinor, nMicro) + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatalf("Error creating 3MF model: %v", err) + } + + // Initialize a mesh object and set its name + meshObject, err := model.AddMeshObject() + if err != nil { + log.Fatalf("Error adding mesh object: %v", err) + } + err = meshObject.SetName("Beamlattice") + if err != nil { + log.Fatalf("Error setting mesh object name: %v", err) + } + + // Define the size of the box + fSizeX, fSizeY, fSizeZ := float32(100.0), float32(200.0), float32(300.0) + + // Create vertices + vertices := []lib3mf.Position{ + createVertex(meshObject, 0.0, 0.0, 0.0), + createVertex(meshObject, fSizeX, 0.0, 0.0), + createVertex(meshObject, fSizeX, fSizeY, 0.0), + createVertex(meshObject, 0.0, fSizeY, 0.0), + createVertex(meshObject, 0.0, 0.0, fSizeZ), + createVertex(meshObject, fSizeX, 0.0, fSizeZ), + createVertex(meshObject, fSizeX, fSizeY, fSizeZ), + createVertex(meshObject, 0.0, fSizeY, fSizeZ), + } + + // Define beam variables + r0 := 1.0 + r1 := 1.5 + r2 := 2.0 + r3 := 2.5 + + // Create beams + beams := []lib3mf.Beam{ + createBeam(2, 1, r0, r0, "Butt", "Butt"), + createBeam(0, 3, r0, r1, "Sphere", "Butt"), + createBeam(4, 5, r0, r2, "Sphere", "Butt"), + createBeam(6, 7, r0, r3, "HemiSphere", "Butt"), + createBeam(0, 1, r1, r0, "HemiSphere", "Butt"), + createBeam(5, 4, r1, r1, "Sphere", "HemiSphere"), + createBeam(2, 3, r1, r2, "Sphere", "Sphere"), + createBeam(7, 6, r1, r3, "Butt", "Butt"), + createBeam(1, 2, r2, r2, "Butt", "Butt"), + createBeam(6, 5, r2, r3, "HemiSphere", "Butt"), + createBeam(3, 0, r3, r0, "Butt", "Sphere"), + createBeam(4, 7, r3, r1, "HemiSphere", "HemiSphere"), + } + + // Add vertices to mesh object using SetVertex + for i, vertex := range vertices { + err := meshObject.SetVertex(uint32(i), vertex) + if err != nil { + log.Fatalf("Error setting vertex at index %d: %v", i, err) + } + } + + // Set beam lattice + beamLattice, err := meshObject.BeamLattice() + if err != nil { + log.Fatalf("Error getting beam lattice: %v", err) + } + + // Add beams one by one + for i, beam := range beams { + _, err := beamLattice.AddBeam(beam) + if err != nil { + log.Fatalf("Error adding a beam lattice %d, %v", i, err) + } + } + + // Set beam min length + err = beamLattice.SetMinLength(0.005) + if err != nil { + log.Fatalf("Error setting minimum length in beam lattice: %v", err) + } + + // Get identity transform + transform, _ := wrapper.GetIdentityTransform() + + // Add mesh object to the model as a build item + _, err = model.AddBuildItem(meshObject.Object, transform) + if err != nil { + log.Fatalf("Error adding build item with identity transform: %v", err) + } + + // Write the model to a 3MF file + writer, err := model.QueryWriter("3mf") + if err != nil { + log.Fatalf("Error querying writer for 3MF format: %v", err) + } + err = writer.WriteToFile("beamlattice.3mf") + if err != nil { + log.Fatalf("Error writing to file 'beamlattice.3mf': %v", err) + } + + fmt.Println("3MF file with beam lattice written successfully to 'beamlattice.3mf'") +} diff --git a/SDK/Examples/Go/color_cube.go b/SDK/Examples/Go/color_cube.go new file mode 100644 index 000000000..6527f66b0 --- /dev/null +++ b/SDK/Examples/Go/color_cube.go @@ -0,0 +1,239 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: Color cube example + +Interface version: 2.3.2 ++++ +*/ + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" +) + +// createVertex creates a vertex on the mesh object and returns it. +func createVertex(mesh lib3mf.MeshObject, x, y, z float32) lib3mf.Position { + position := lib3mf.Position{ + Coordinates: [3]float32{x, y, z}, + } + _, err := mesh.AddVertex(position) + if err != nil { + log.Fatalf("Error adding vertex at (%f, %f, %f): %v", x, y, z, err) + } + return position +} + +// addTriangle adds a triangle to the mesh object using three vertex indices. +func addTriangle(mesh lib3mf.MeshObject, p1, p2, p3 uint32) lib3mf.Triangle { + triangle := lib3mf.Triangle{ + Indices: [3]uint32{p1, p2, p3}, + } + _, err := mesh.AddTriangle(triangle) + if err != nil { + log.Fatalf("Error adding triangle with vertices %d, %d, %d: %v", p1, p2, p3, err) + } + return triangle +} + +// createTriangleColor creates a triangle color property. +func createTriangleColor(colorGroup lib3mf.ColorGroup, colorID1, colorID2, colorID3 uint32) lib3mf.TriangleProperties { + colorResourceId, _ := colorGroup.GetResourceID() + triangleProperties := lib3mf.TriangleProperties{ + ResourceID: colorResourceId, + PropertyIDs: [3]uint32{ + colorID1, + colorID2, + colorID3, + }, + } + return triangleProperties +} + +// A function to create color +func createColor(wrapper lib3mf.Wrapper, red, green, blue, alpha uint8) lib3mf.Color { + color, err := wrapper.RGBAToColor(red, green, blue, alpha) + if err != nil { + log.Fatalf("Error creating color with RGBA(%d, %d, %d, %d): %v", red, green, blue, alpha, err) + } + return color +} + +func main() { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatalf("Error loading 3MF library: %v", err) + } + + // Check the lib3mf version + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatalf("Error fetching lib3mf version: %v", err) + } + fmt.Printf("lib3mf version: %d.%d.%d\n", nMajor, nMinor, nMicro) + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatalf("Error creating 3MF model: %v", err) + } + + // Initialize a mesh object and set its name + meshObject, err := model.AddMeshObject() + if err != nil { + log.Fatalf("Error adding mesh object: %v", err) + } + err = meshObject.SetName("Colored Box") + if err != nil { + log.Fatalf("Error setting mesh object name: %v", err) + } + + // Define the size of the cube + fSizeX, fSizeY, fSizeZ := float32(100.0), float32(200.0), float32(300.0) + + // Create vertices + vertices := []lib3mf.Position{ + createVertex(meshObject, 0.0, 0.0, 0.0), + createVertex(meshObject, fSizeX, 0.0, 0.0), + createVertex(meshObject, fSizeX, fSizeY, 0.0), + createVertex(meshObject, 0.0, fSizeY, 0.0), + createVertex(meshObject, 0.0, 0.0, fSizeZ), + createVertex(meshObject, fSizeX, 0.0, fSizeZ), + createVertex(meshObject, fSizeX, fSizeY, fSizeZ), + createVertex(meshObject, 0.0, fSizeY, fSizeZ), + } + + // Define triangles by vertices indices + triangleIndices := [][3]uint32{ + {2, 1, 0}, {0, 3, 2}, {4, 5, 6}, {6, 7, 4}, + {0, 1, 5}, {5, 4, 0}, {2, 3, 7}, {7, 6, 2}, + {1, 2, 6}, {6, 5, 1}, {3, 0, 4}, {4, 7, 3}, + } + + // Create triangles + triangles := make([]lib3mf.Triangle, 0, len(triangleIndices)) + for _, indices := range triangleIndices { + triangle := addTriangle(meshObject, indices[0], indices[1], indices[2]) + triangles = append(triangles, triangle) + } + + // Set geometry to the mesh object after creating vertices and triangles + err = meshObject.SetGeometry(vertices, triangles) + if err != nil { + log.Fatalf("Error setting geometry for mesh object: %v", err) + } + + // Define colors + colorGroup, err := model.AddColorGroup() + if err != nil { + log.Fatalf("Error adding color group: %v", err) + } + + // Create and add colors directly using the createColor function + idRed, err := colorGroup.AddColor(createColor(wrapper, 255, 0, 0, 255)) + if err != nil { + log.Fatalf("Error adding red color to color group: %v", err) + } + + idGreen, err := colorGroup.AddColor(createColor(wrapper, 0, 255, 0, 255)) + if err != nil { + log.Fatalf("Error adding green color to color group: %v", err) + } + + idBlue, err := colorGroup.AddColor(createColor(wrapper, 0, 0, 255, 255)) + if err != nil { + log.Fatalf("Error adding blue color to color group: %v", err) + } + + idOrange, err := colorGroup.AddColor(createColor(wrapper, 255, 128, 0, 255)) + if err != nil { + log.Fatalf("Error adding orange color to color group: %v", err) + } + + idYellow, err := colorGroup.AddColor(createColor(wrapper, 255, 255, 0, 255)) + if err != nil { + log.Fatalf("Error adding yellow color to color group: %v", err) + } + + // Set triangle colors + sTriangleColorRed := createTriangleColor(colorGroup, idRed, idRed, idRed) + sTriangleColorGreen := createTriangleColor(colorGroup, idGreen, idGreen, idGreen) + sTriangleColorBlue := createTriangleColor(colorGroup, idBlue, idBlue, idBlue) + sTriangleColor1 := createTriangleColor(colorGroup, idOrange, idRed, idYellow) + sTriangleColor2 := createTriangleColor(colorGroup, idYellow, idGreen, idOrange) + + // One-colored Triangles + for i := 0; i <= 5; i++ { + switch i { + case 0, 1: + meshObject.SetTriangleProperties(uint32(i), sTriangleColorRed) + case 2, 3: + meshObject.SetTriangleProperties(uint32(i), sTriangleColorGreen) + case 4, 5: + meshObject.SetTriangleProperties(uint32(i), sTriangleColorBlue) + } + } + + // Gradient-colored Triangles + for i := 6; i <= 11; i++ { + if i%2 == 0 { + meshObject.SetTriangleProperties(uint32(i), sTriangleColor1) + } else { + meshObject.SetTriangleProperties(uint32(i), sTriangleColor2) + } + } + + // Set object level property + err = meshObject.SetObjectLevelProperty(sTriangleColorRed.ResourceID, sTriangleColorRed.PropertyIDs[0]) + if err != nil { + log.Fatalf("Error setting object level property: %v", err) + } + + // Get identity transform + transform, _ := wrapper.GetIdentityTransform() + + // Add build item and write to file + _, err = model.AddBuildItem(meshObject.Object, transform) + if err != nil { + log.Fatalf("Error adding build item with identity transform: %v", err) + } + + writer, err := model.QueryWriter("3mf") + if err != nil { + log.Fatalf("Error querying writer for 3MF format: %v", err) + } + err = writer.WriteToFile("colorcube.3mf") + if err != nil { + log.Fatalf("Error writing to file 'colorcube.3mf': %v", err) + } + + fmt.Println("3MF file with a colored cube written successfully to 'colorcube.3mf'") +} diff --git a/SDK/Examples/Go/create_components.go b/SDK/Examples/Go/create_components.go new file mode 100644 index 000000000..4244b74ce --- /dev/null +++ b/SDK/Examples/Go/create_components.go @@ -0,0 +1,200 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: An example that creates multiple components using transformations + +Interface version: 2.3.2 ++++ +*/ + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" +) + +// createVertex creates a vertex on the mesh object and returns it. +func createVertex(mesh lib3mf.MeshObject, x, y, z float32) lib3mf.Position { + position := lib3mf.Position{ + Coordinates: [3]float32{x, y, z}, + } + _, err := mesh.AddVertex(position) + if err != nil { + log.Fatalf("Error adding vertex at (%f, %f, %f): %v", x, y, z, err) + } + return position +} + +// addTriangle adds a triangle to the mesh object using three vertex indices. +func addTriangle(mesh lib3mf.MeshObject, p1, p2, p3 uint32) lib3mf.Triangle { + triangle := lib3mf.Triangle{ + Indices: [3]uint32{p1, p2, p3}, + } + _, err := mesh.AddTriangle(triangle) + if err != nil { + log.Fatalf("Error adding triangle with vertices %d, %d, %d: %v", p1, p2, p3, err) + } + return triangle +} + +// createTranslationMatrix creates a translation matrix. +func createTranslationMatrix(x, y, z float32) lib3mf.Transform { + matrix := lib3mf.Transform{} + + // Set up an identity matrix with translation + identityMatrix := [4][3]float32{ + {1.0, 0.0, 0.0}, // Row for X axis + {0.0, 1.0, 0.0}, // Row for Y axis + {0.0, 0.0, 1.0}, // Row for Z axis + {x, y, z}, // Translation + } + + // Fill the matrix fields with values + for i := 0; i < 4; i++ { + for j := 0; j < 3; j++ { + matrix.Fields[i][j] = identityMatrix[i][j] + } + } + + return matrix +} + +func main() { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatalf("Error loading 3MF library: %v", err) + } + + // Check the lib3mf version + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatalf("Error fetching lib3mf version: %v", err) + } + fmt.Printf("lib3mf version: %d.%d.%d\n", nMajor, nMinor, nMicro) + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatalf("Error creating 3MF model: %v", err) + } + + // Initialize a mesh object and set its name + meshObject, err := model.AddMeshObject() + if err != nil { + log.Fatalf("Error adding mesh object: %v", err) + } + err = meshObject.SetName("Box") + if err != nil { + log.Fatalf("Error setting mesh object name: %v", err) + } + + // Define the size of the box + fSizeX, fSizeY, fSizeZ := float32(10.0), float32(20.0), float32(30.0) + + // Create vertices + vertices := []lib3mf.Position{ + createVertex(meshObject, 0, 0, 0), + createVertex(meshObject, fSizeX, 0, 0), + createVertex(meshObject, fSizeX, fSizeY, 0), + createVertex(meshObject, 0, fSizeY, 0), + createVertex(meshObject, 0, 0, fSizeZ), + createVertex(meshObject, fSizeX, 0, fSizeZ), + createVertex(meshObject, fSizeX, fSizeY, fSizeZ), + createVertex(meshObject, 0, fSizeY, fSizeZ), + } + + // Define triangles by vertices indices + triangleIndices := [][3]uint32{ + {2, 1, 0}, {0, 3, 2}, {4, 5, 6}, {6, 7, 4}, + {0, 1, 5}, {5, 4, 0}, {2, 3, 7}, {7, 6, 2}, + {1, 2, 6}, {6, 5, 1}, {3, 0, 4}, {4, 7, 3}, + } + + // Create triangles + triangles := make([]lib3mf.Triangle, 0, len(triangleIndices)) + for _, indices := range triangleIndices { + triangle := addTriangle(meshObject, indices[0], indices[1], indices[2]) + triangles = append(triangles, triangle) + } + + // Set geometry to the mesh object after creating vertices and triangles + err = meshObject.SetGeometry(vertices, triangles) + if err != nil { + log.Fatalf("Error setting geometry for mesh object: %v", err) + } + + // Create a components object + componentsObject, err := model.AddComponentsObject() + if err != nil { + log.Fatalf("Error adding components object: %v", err) + } + + // Add components with different transformations + _, err = componentsObject.AddComponent(meshObject.Object, createTranslationMatrix(0.0, 0.0, 0.0)) + if err != nil { + log.Fatalf("Error adding component with transformation: %v", err) + } + _, err = componentsObject.AddComponent(meshObject.Object, createTranslationMatrix(40.0, 60.0, 80.0)) + if err != nil { + log.Fatalf("Error adding component with transformation: %v", err) + } + _, err = componentsObject.AddComponent(meshObject.Object, createTranslationMatrix(120.0, 30.0, 70.0)) + if err != nil { + log.Fatalf("Error adding component with transformation: %v", err) + } + + // Add the components object to the model as a build item + _, err = model.AddBuildItem(componentsObject.Object, createTranslationMatrix(0.0, 0.0, 0.0)) + if err != nil { + log.Fatalf("Error adding build item with transformation: %v", err) + } + + // Writing to files (3MF) + writer3MF, err := model.QueryWriter("3mf") + if err != nil { + log.Fatalf("Error querying writer for 3MF format: %v", err) + } + err = writer3MF.WriteToFile("components.3mf") + if err != nil { + log.Fatalf("Error writing to file 'components.3mf': %v", err) + } + + // Dump to a STL file + writerSTL, err := model.QueryWriter("stl") + if err != nil { + log.Fatalf("Error querying writer for STL format: %v", err) + } + err = writerSTL.WriteToFile("components.stl") + if err != nil { + log.Fatalf("Error writing to file 'components.stl': %v", err) + } + + fmt.Println("3MF and STL files with components written successfully to 'components.3mf' and 'components.stl'") +} diff --git a/SDK/Examples/Go/create_cube.go b/SDK/Examples/Go/create_cube.go new file mode 100644 index 000000000..aa0f7e727 --- /dev/null +++ b/SDK/Examples/Go/create_cube.go @@ -0,0 +1,151 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: Create a simple cube + +Interface version: 2.3.2 ++++ +*/ + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" +) + +// createVertex creates a vertex on the mesh object and returns it. +func createVertex(mesh lib3mf.MeshObject, x, y, z float32) lib3mf.Position { + position := lib3mf.Position{ + Coordinates: [3]float32{x, y, z}, + } + _, err := mesh.AddVertex(position) + if err != nil { + log.Fatalf("Error adding vertex at (%f, %f, %f): %v", x, y, z, err) + } + return position +} + +// addTriangle adds a triangle to the mesh object using three vertex indices. +func addTriangle(mesh lib3mf.MeshObject, p1, p2, p3 uint32) lib3mf.Triangle { + triangle := lib3mf.Triangle{ + Indices: [3]uint32{p1, p2, p3}, + } + _, err := mesh.AddTriangle(triangle) + if err != nil { + log.Fatalf("Error adding triangle with vertices %d, %d, %d: %v", p1, p2, p3, err) + } + return triangle +} + +func main() { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatalf("Error loading 3MF library: %v", err) + } + + // Check the lib3mf version + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatalf("Error fetching lib3mf version: %v", err) + } + fmt.Printf("lib3mf version: %d.%d.%d\n", nMajor, nMinor, nMicro) + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatalf("Error creating 3MF model: %v", err) + } + + // Initialize a mesh object and set its name + meshObject, err := model.AddMeshObject() + if err != nil { + log.Fatalf("Error adding mesh object: %v", err) + } + err = meshObject.SetName("Box") + if err != nil { + log.Fatalf("Error setting mesh object name: %v", err) + } + + // Define the size of the cube + fSizeX, fSizeY, fSizeZ := float32(100.0), float32(200.0), float32(300.0) + + // Create vertices + vertices := []lib3mf.Position{ + createVertex(meshObject, 0, 0, 0), + createVertex(meshObject, fSizeX, 0, 0), + createVertex(meshObject, fSizeX, fSizeY, 0), + createVertex(meshObject, 0, fSizeY, 0), + createVertex(meshObject, 0, 0, fSizeZ), + createVertex(meshObject, fSizeX, 0, fSizeZ), + createVertex(meshObject, fSizeX, fSizeY, fSizeZ), + createVertex(meshObject, 0, fSizeY, fSizeZ), + } + + // Define triangles by vertices indices + triangleIndices := [][3]uint32{ + {2, 1, 0}, {0, 3, 2}, {4, 5, 6}, {6, 7, 4}, + {0, 1, 5}, {5, 4, 0}, {2, 3, 7}, {7, 6, 2}, + {1, 2, 6}, {6, 5, 1}, {3, 0, 4}, {4, 7, 3}, + } + + // Create triangles + triangles := make([]lib3mf.Triangle, 0, len(triangleIndices)) + for _, indices := range triangleIndices { + triangle := addTriangle(meshObject, indices[0], indices[1], indices[2]) + triangles = append(triangles, triangle) + } + + // Set geometry to the mesh object after creating vertices and triangles + err = meshObject.SetGeometry(vertices, triangles) + if err != nil { + log.Fatalf("Error setting geometry for mesh object: %v", err) + } + + // Get identity transform + transform, _ := wrapper.GetIdentityTransform() + + // Add build item with an identity transform + _, err = model.AddBuildItem(meshObject.Object, transform) + if err != nil { + log.Fatalf("Error adding build item with identity transform: %v", err) + } + + // Save the model to a 3MF file + writer, err := model.QueryWriter("3mf") + if err != nil { + log.Fatalf("Error querying writer for 3MF format: %v", err) + } + err = writer.WriteToFile("cube.3mf") + if err != nil { + log.Fatalf("Error writing to file 'cube.3mf': %v", err) + } + + fmt.Println("3MF file with a cube written successfully to 'cube.3mf'") +} diff --git a/SDK/Examples/Go/extract_info.go b/SDK/Examples/Go/extract_info.go new file mode 100644 index 000000000..b46b09970 --- /dev/null +++ b/SDK/Examples/Go/extract_info.go @@ -0,0 +1,214 @@ +/* ++++ + +Copyright (C) 2019 3MF Consortium (Vijai Kumar Suriyababu) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions, and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: Extract info from a 3MF model + +Interface version: 2.3.2 ++++ +*/ + +package main + +import ( + "fmt" + lib3mf "github.com/3MFConsortium/lib3mf.go/v2" + "log" + "os" +) + +// read3MFFileToModel reads a 3MF file into the provided model. +func read3MFFileToModel(model lib3mf.Model, filePath string) { + reader, err := model.QueryReader("3mf") + if err != nil { + log.Fatalf("Error querying 3MF reader: %v", err) + } + err = reader.SetStrictModeActive(false) + if err != nil { + log.Fatalf("Error setting strict mode: %v", err) + } + err = reader.ReadFromFile(filePath) + if err != nil { + log.Fatalf("Error reading from file %s: %v", filePath, err) + } +} + +// getVersion prints the lib3mf library version information. +func getVersion(wrapper lib3mf.Wrapper) { + nMajor, nMinor, nMicro, err := wrapper.GetLibraryVersion() + if err != nil { + log.Fatalf("Error fetching lib3mf version: %v", err) + } + versionString := fmt.Sprintf("lib3mf version: %d.%d.%d", nMajor, nMinor, nMicro) + + hasInfo, preReleaseInfo, err := wrapper.GetPrereleaseInformation() + if err != nil { + log.Fatalf("Error fetching prerelease information: %v", err) + } + if hasInfo { + versionString += "-" + preReleaseInfo + } + + hasInfo, buildInfo, err := wrapper.GetBuildInformation() + if err != nil { + log.Fatalf("Error fetching build information: %v", err) + } + if hasInfo { + versionString += "+" + buildInfo + } + + fmt.Println(versionString) +} + +// showMetadataInformation prints metadata information from the model. +func showMetadataInformation(metadataGroup lib3mf.MetaDataGroup) { + count, err := metadataGroup.GetMetaDataCount() + if err != nil { + log.Fatalf("Error getting metadata count: %v", err) + } + for i := uint32(0); i < count; i++ { + metadata, err := metadataGroup.GetMetaData(i) + if err != nil { + log.Fatalf("Error getting metadata: %v", err) + } + name, err := metadata.GetName() + if err != nil { + log.Fatalf("Error getting metadata name: %v", err) + } + value, err := metadata.GetValue() + if err != nil { + log.Fatalf("Error getting metadata value: %v", err) + } + fmt.Printf("Metadata: %s = %s\n", name, value) + } +} + +// showSliceStackInformation prints information about slice stacks in the model. +func showSliceStackInformation(model lib3mf.Model) { + sliceStacks, err := model.GetSliceStacks() + if err != nil { + log.Fatalf("Error getting slice stacks: %v", err) + } + for { + hasNext, err := sliceStacks.MoveNext() + if err != nil { + log.Fatalf("Error moving to next slice stack: %v", err) + } + if !hasNext { + break + } + + sliceStack, err := sliceStacks.GetCurrentSliceStack() + if err != nil { + log.Fatalf("Error getting current slice stack: %v", err) + } + resourceID, err := sliceStack.GetResourceID() + if err != nil { + log.Fatalf("Error getting slice stack resource ID: %v", err) + } + fmt.Printf("Slice Stack: %d\n", resourceID) + } +} + +// showObjectInformation prints information about objects in the model. +func showObjectInformation(model lib3mf.Model) { + objectIterator, err := model.GetObjects() + if err != nil { + log.Fatalf("Error getting objects iterator: %v", err) + } + + for { + hasNext, err := objectIterator.MoveNext() + if err != nil { + log.Fatalf("Error moving to next object: %v", err) + } + if !hasNext { + break + } + + obj, err := objectIterator.GetCurrentObject() + if err != nil { + log.Fatalf("Error getting current object: %v", err) + } + resourceID, err := obj.GetResourceID() + if err != nil { + log.Fatalf("Error getting object resource ID: %v", err) + } + isMesh, err := obj.IsMeshObject() + if err != nil { + log.Fatalf("Error checking if object is mesh: %v", err) + } + isComponent, err := obj.IsComponentsObject() + if err != nil { + log.Fatalf("Error checking if object is component: %v", err) + } + if isMesh { + fmt.Printf("Mesh Object: %d\n", resourceID) + } else if isComponent { + fmt.Printf("Components Object: %d\n", resourceID) + } else { + fmt.Printf("Unknown Object: %d\n", resourceID) + } + } +} + +func extractInfo(filePath string) { + // Get a wrapper object + wrapper, err := lib3mf.GetWrapper() + if err != nil { + log.Fatalf("Error loading 3MF library: %v", err) + } + + // Create a new 3MF model + model, err := wrapper.CreateModel() + if err != nil { + log.Fatalf("Error creating 3MF model: %v", err) + } + + // Read the 3MF file into the model + read3MFFileToModel(model, filePath) + + // Print library version + getVersion(wrapper) + + // Show metadata information + metaDataGroup, _ := model.GetMetaDataGroup() + showMetadataInformation(metaDataGroup) + + // Show slice stack information + showSliceStackInformation(model) + + // Show object information + showObjectInformation(model) +} + +func main() { + if len(os.Args) != 2 { + fmt.Println("Usage: go run extract_info.go model.3mf") + os.Exit(1) + } + extractInfo(os.Args[1]) +}