diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2260343e7..4b0c6d77b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,12 +1,32 @@ on: [push, pull_request] +env: + # Set this to "on" to enable integration tests and "off" to skip integration tests + RUN_INTEGRATION_TESTS: "on" name: Build jobs: + set-lib3mf-version: + runs-on: ubuntu-20.04 + outputs: + lib3mf-version: ${{ steps.set-version.outputs.LIB3MF_VERSION }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Run version extraction script and set environment variable + id: set-version + run: | + LIB3MF_VERSION=$(python CI/extract_version.py) + echo "LIB3MF_VERSION=$LIB3MF_VERSION" >> $GITHUB_OUTPUT + - name: Echo version for debug + run: echo "LIB3MF_VERSION=${{ steps.set-version.outputs.LIB3MF_VERSION }}" + build-linux-memtest: runs-on: ubuntu-20.04 + needs: [set-lib3mf-version] steps: - run: sudo apt update - run: sudo apt install -y valgrind uuid-dev - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: sh cmake/GenerateMake.sh @@ -15,22 +35,23 @@ jobs: build-linux-ubi8-gcc12: runs-on: ubuntu-20.04 + needs: [set-lib3mf-version] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - run: sudo apt update - run: sudo apt install -y uuid-dev - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - - run: mkdir -p build + - run: mkdir -p build - run: zip -r build/bindings.zip Autogenerated/Bindings - - name: Archive bindings - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: bindings.zip path: build/bindings.zip - - - name: Set up Docker Buildx + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Docker Build @@ -39,30 +60,62 @@ jobs: context: . file: ./CI/Dockerfile platforms: linux/amd64 - tags: lib3mf_ubi8:latest - load: true - - - name: Docker Extract + tags: lib3mf_ubi8:latest + load: true + - + name: Docker Extract uses: shrink/actions-docker-extract@v3.0.0 id: extract - with: + with: image: lib3mf_ubi8:latest path: out.zip destination: dist - + - run: unzip out.zip working-directory: ./dist - - - name: Upload Artifact - uses: actions/upload-artifact@v2 + - name: Upload Artifact + uses: actions/upload-artifact@v4 with: name: lib3mf.so - path: dist/lib3mf.so.2 + path: dist/lib3mf.so.2 + - name: Extract File Name (CPacked Archive) + run: | + ZIP_FILE=$(ls dist/lib3mf-*.zip) + echo "ARTIFACT_NAME_ZIP=$(basename ${ZIP_FILE})" >> $GITHUB_ENV + shell: bash + - name: Upload Artifact (CPacked Archive) + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME_ZIP }} + path: dist/${{ env.ARTIFACT_NAME_ZIP }} + - name: Extract File Name (Debian) + run: | + DEB_FILE=$(ls dist/lib3mf-*.deb) + echo "ARTIFACT_NAME_DEB=$(basename ${DEB_FILE})" >> $GITHUB_ENV + shell: bash + - name: Upload Artifact (Debian Archive) + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME_DEB }} + path: dist/${{ env.ARTIFACT_NAME_DEB }} + - name: Extract File Name (RPM) + run: | + RPM_FILE=$(ls dist/lib3mf-*.rpm) + echo "ARTIFACT_NAME_RPM=$(basename ${RPM_FILE})" >> $GITHUB_ENV + shell: bash + - name: Upload Artifact (RPM Archive) + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME_RPM }} + path: dist/${{ env.ARTIFACT_NAME_RPM }} build-macos: runs-on: macos-latest + needs: [set-lib3mf-version] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: sh cmake/GenerateMake.sh "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" @@ -70,16 +123,29 @@ jobs: working-directory: ./build - run: ctest -V working-directory: ./build + - run: cpack -G ZIP -C Release + working-directory: ./build + - name: Extract File Name + run: | + ZIP_FILE=$(ls build/lib3mf-*.zip) + echo "ARTIFACT_NAME=$(basename ${ZIP_FILE})" >> $GITHUB_ENV + shell: bash - name: Archive Mac binary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf.dylib path: build/lib3mf.dylib + - name: Upload Artifact (CPacked Archive) + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }} + path: build/${{ env.ARTIFACT_NAME }} build-macos-debug: runs-on: macos-latest + needs: [set-lib3mf-version] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: sh cmake/GenerateMake.sh "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64 -DCMAKE_BUILD_TYPE=Debug" @@ -88,13 +154,14 @@ jobs: - run: ctest -V working-directory: ./build - name: Archive Mac binary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf.debug.dylib path: build/lib3mf.dylib codecoverage-macos: runs-on: macos-latest + needs: [set-lib3mf-version] steps: - uses: actions/checkout@v4 with: @@ -108,7 +175,7 @@ jobs: working-directory: ./build - run: ./Tests/codecoverage/run_codecoverage.sh - name: Archive Code Coverage Results - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: codecoverage.zip path: build/codecoverage.zip @@ -122,8 +189,11 @@ jobs: build-windows-release: runs-on: windows-2019 + needs: [set-lib3mf-version] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: ./cmake/GenerateVS2019.bat @@ -131,20 +201,34 @@ jobs: working-directory: ./build - run: ctest -V working-directory: ./build + - run: cpack -G ZIP -C Release + working-directory: ./build + - name: Extract File Name + run: | + $zipFile = Get-ChildItem build\lib3mf-*.zip -Name + echo "ARTIFACT_NAME=$zipFile" | Out-File -FilePath $env:GITHUB_ENV -Append + shell: pwsh - name: Archive Windows Release binary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf.dll path: build/Release/lib3mf.dll - name: Archive Windows Release lib - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf.lib path: build/Release/lib3mf.lib + - name: Upload Artifact (CPacked Archive) + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }} + path: build/${{ env.ARTIFACT_NAME }} + build-windows-debug: runs-on: windows-2019 + needs: [set-lib3mf-version] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: ./cmake/GenerateVS2019.bat @@ -153,22 +237,23 @@ jobs: - run: ctest -V working-directory: ./build - name: Archive Windows Debug binary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf.debug.dll path: build/Debug/lib3mf.dll - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: lib3mf.pdb path: build/Debug/lib3mf.pdb - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: - name: lib3mf.debug.lib - path: build/Debug/lib3mf.lib + name: lib3mf.debug.lib + path: build/Debug/lib3mf.lib build-windows-32bit: runs-on: windows-2019 + needs: [set-lib3mf-version] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: ./cmake/GenerateVS2019_32bit.bat @@ -177,20 +262,21 @@ jobs: - run: ctest -V working-directory: ./build_32bit - name: Archive Windows 32 bit Release binary - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf_32bit.dll path: build_32bit/Release/lib3mf.dll - name: Archive Windows 32 bit Release lib - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lib3mf_32bit.lib path: build_32bit/Release/lib3mf.lib build-mingw-w64: runs-on: windows-2019 + needs: [set-lib3mf-version] steps: - run: choco install mingw -y - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - run: ./cmake/GenerateMinGW.bat @@ -198,44 +284,52 @@ jobs: working-directory: ./build - run: ctest -V working-directory: ./build + + assemble-sdk: runs-on: ubuntu-20.04 - needs: [build-windows-release, build-macos, build-linux-ubi8-gcc12] + needs: [set-lib3mf-version, build-windows-release, build-macos, build-linux-ubi8-gcc12] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - run: sudo apt install -y zip unzip - run: mkdir build - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: false - name: Download all workflow run artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: path: ./build - run: ls -Rl ./build - run: unzip bindings.zip/bindings.zip working-directory: ./build - run: bash SDK/GenerateSDK_github.sh - - name: Archive SDK artifact - uses: actions/upload-artifact@v2 + - name: Archive SDK artifact (Comprehensive) + uses: actions/upload-artifact@v4 with: name: lib3mf_sdk.zip path: build/lib3mf_sdk.zip + + deploy-linux: runs-on: ubuntu-20.04 - needs: [assemble-sdk] + needs: [set-lib3mf-version, assemble-sdk] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - - run: sudo apt install -y zip unzip + - run: sudo apt install -y zip unzip file - run: pwd - run: ls -Rl . - name: Download lib3mf_sdk artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: lib3mf_sdk.zip path: lib3mf_sdk.zip - run: ls -Rl . - name: Unpack the SDK run: | - unzip lib3mf_sdk.zip/lib3mf_sdk.zip + unzip lib3mf_sdk.zip/lib3mf_sdk.zip - name: Build CppDynamic run: | sh Examples/CppDynamic/GenerateMake.sh @@ -248,18 +342,75 @@ jobs: cd Examples/Cpp/build cmake --build . ./Example_ExtractInfo ../../Files/Helix.3mf + - name: Python Bindings + run: | + python Examples/Python/extract_info.py Examples/Files/Helix.3mf + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + - run: ls -Rl + - name: Download lib3mf cpack (Linux) + uses: actions/download-artifact@v4 + with: + name: lib3mf-${{ env.LIB3MF_VERSION }}-Linux.zip + path: lib3mf-${{ env.LIB3MF_VERSION }}-Linux.zip + - name: Unpack the cpacked SDK + run: | + unzip lib3mf-${{ env.LIB3MF_VERSION }}-Linux.zip/lib3mf-${{ env.LIB3MF_VERSION }}-Linux.zip && ls -Rl + - name: Build CppDynamic - CPack (Linux) + run: | + sh SDK/CPackExamples/CppDynamic/GenerateMake.sh + cd SDK/CPackExamples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + - name: Build Cpp - CPack (Linux) + run: | + sh SDK/CPackExamples/Cpp/GenerateMake.sh + cd SDK/CPackExamples/Cpp/build + cmake --build . + ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + - name: Download lib3mf (Debian Linux) + uses: actions/download-artifact@v4 + with: + name: lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb + path: lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb + - name: Check the file type + run: | + file lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb + file lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb/lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb + - run: pwd + - run: ls -Rl . + - name: Install the debian package + run: | + sudo dpkg -i lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb/lib3mf-${{ env.LIB3MF_VERSION }}-Linux.deb + - name: Build CppDynamic - CPack (Debian) + run: | + sh SDK/CPackExamples/CppDynamic/GenerateMake.sh + cd SDK/CPackExamples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + - name: Build Cpp - CPack (Debian) + run: | + sh SDK/CPackExamples/Cpp/GenerateMake.sh + cd SDK/CPackExamples/Cpp/build + cmake --build . + ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + deploy-windows: runs-on: windows-2019 - needs: [assemble-sdk] + needs: [set-lib3mf-version, assemble-sdk] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - name: Download lib3mf_sdk artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: lib3mf_sdk.zip path: lib3mf_sdk.zip - name: Unpack the SDK run: | - unzip lib3mf_sdk.zip/lib3mf_sdk.zip + unzip lib3mf_sdk.zip/lib3mf_sdk.zip - name: Build CppDynamic run: | ./Examples/CppDynamic/GenerateVS2019.bat @@ -272,18 +423,48 @@ jobs: cd Examples/Cpp/build cmake --build . --config Release ./Release/Example_ExtractInfo.exe ../../Files/Helix.3mf + - name: Python Bindings + run: | + python Examples/Python/extract_info.py Examples/Files/Helix.3mf + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + - name: Download lib3mf cpack (Windows) + uses: actions/download-artifact@v4 + with: + name: lib3mf-${{ env.LIB3MF_VERSION }}-Windows.zip + path: lib3mf-${{ env.LIB3MF_VERSION }}-Windows.zip + - name: Unpack the cpacked SDK + run: | + unzip lib3mf-${{ env.LIB3MF_VERSION }}-Windows.zip/lib3mf-${{ env.LIB3MF_VERSION }}-Windows.zip + - name: Build CppDynamic - CPack (Windows) + run: | + ./SDK/CPackExamples/CppDynamic/GenerateVS2019.bat + cd SDK/CPackExamples/CppDynamic/build + cmake --build . --config Release + ./Release/Example_ExtractInfo.exe ../../../Examples/Files/Helix.3mf + - name: Build Cpp - CPack (Windows) + run: | + ./SDK/CPackExamples/Cpp/GenerateVS2019.bat + cd SDK/CPackExamples/Cpp/build + cmake --build . --config Release + ./Release/Example_ExtractInfo.exe ../../../Examples/Files/Helix.3mf + deploy-macos: runs-on: macos-latest - needs: [assemble-sdk] + needs: [set-lib3mf-version, assemble-sdk] + env: + LIB3MF_VERSION: ${{ needs.set-lib3mf-version.outputs.lib3mf-version }} steps: - name: Download lib3mf_sdk artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: lib3mf_sdk.zip path: lib3mf_sdk.zip - name: Unpack the SDK run: | - unzip lib3mf_sdk.zip/lib3mf_sdk.zip + unzip lib3mf_sdk.zip/lib3mf_sdk.zip - name: Build CppDynamic run: | sh Examples/CppDynamic/GenerateMake.sh @@ -295,4 +476,381 @@ jobs: sh Examples/Cpp/GenerateMake.sh cd Examples/Cpp/build cmake --build . - ./Example_ExtractInfo ../../Files/Helix.3mf \ No newline at end of file + ./Example_ExtractInfo ../../Files/Helix.3mf + - name: Python Bindings + run: | + python Examples/Python/extract_info.py Examples/Files/Helix.3mf + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + - name: Download lib3mf cpack (Darwin) + uses: actions/download-artifact@v4 + with: + name: lib3mf-${{ env.LIB3MF_VERSION }}-Darwin.zip + path: lib3mf-${{ env.LIB3MF_VERSION }}-Darwin.zip + - run: ls -Rl . + - name: Unpack the cpacked SDK (Darwin) + run: | + unzip lib3mf-${{ env.LIB3MF_VERSION }}-Darwin.zip/lib3mf-${{ env.LIB3MF_VERSION }}-Darwin.zip + - name: Build CppDynamic - CPack (Darwin) + run: | + sh SDK/CPackExamples/CppDynamic/GenerateMake.sh + cd SDK/CPackExamples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + - name: Build Cpp - CPack (Darwin) + run: | + sh SDK/CPackExamples/Cpp/GenerateMake.sh + cd SDK/CPackExamples/Cpp/build + cmake --build . + ./Example_ExtractInfo ../../../Examples/Files/Helix.3mf + + + set-integration-tests-status: + runs-on: ubuntu-20.04 + needs: [ deploy-linux, deploy-windows, deploy-macos ] + outputs: + run_integration_tests: ${{ steps.set-status.outputs.run_integration_tests }} + steps: + - name: Set status + id: set-status + run: | + if [ "${{ env.RUN_INTEGRATION_TESTS }}" == "on" ]; then + echo "run_integration_tests=true" >> $GITHUB_OUTPUT + else + echo "run_integration_tests=false" >> $GITHUB_OUTPUT + fi + + integration-tests-latest-release: + runs-on: ubuntu-20.04 + needs: [set-integration-tests-status] + if: needs.set-integration-tests-status.outputs.run_integration_tests == 'true' # Single check before the job starts + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + + - name: Get latest lib3mf SDK release info from GitHub API + id: get_lib3mf_release + run: | + LATEST_LIB3MF_URL=$(curl -s https://api.github.com/repos/3MFConsortium/lib3mf/releases/latest | grep "browser_download_url.*zip" | cut -d '"' -f 4) + echo "LATEST_LIB3MF_URL=${LATEST_LIB3MF_URL}" >> $GITHUB_ENV + + - name: Download latest lib3mf SDK zip + run: | + wget ${{ env.LATEST_LIB3MF_URL }} -O latest_lib3mf_sdk.zip + + - name: Unpack the SDK + run: | + unzip latest_lib3mf_sdk.zip -d lib3mf_sdk + + - name: Build CppDynamic + run: | + sh lib3mf_sdk/Examples/CppDynamic/GenerateMake.sh + cd lib3mf_sdk/Examples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../Files/Helix.3mf + + - name: Download and unzip test suite + run: | + wget https://github.com/3MFConsortium/test_suites/releases/download/v2.0.0/3MF_Conformance_Test_Suites_v2.0.0.zip + unzip 3MF_Conformance_Test_Suites_v2.0.0.zip -d test_suites + + - name: List files + run: | + ls -al + + - name: Copy integration test script + run: | + cp CI/integration_test.py test_suites/ && cp -r lib3mf_sdk/Examples/CppDynamic/build/* test_suites/ + + - name: Run integration tests + run: | + cd test_suites && /usr/bin/time -v python integration_test.py 2>&1 | tee latest_sdk_test.log + + - name: Print results (Checks the total python script execution time) + run: | + LATEST_TIME=$(grep "Elapsed (wall clock) time" test_suites/latest_sdk_test.log | awk '{print $8}') + LATEST_TOTAL_SECONDS=$(echo $LATEST_TIME | awk -F: '{ print ($1 * 60) + $2 }') + echo "Latest SDK execution time in seconds: ${LATEST_TOTAL_SECONDS}" + + + integration-tests-last-two-releases: + runs-on: ubuntu-20.04 + needs: [set-integration-tests-status] + if: needs.set-integration-tests-status.outputs.run_integration_tests == 'true' # Single check before the job starts + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + + - name: Get latest lib3mf SDK release info from GitHub API + id: get_latest_release + run: | + LATEST_LIB3MF_URL=$(curl -s https://api.github.com/repos/3MFConsortium/lib3mf/releases/latest | grep "browser_download_url.*zip" | cut -d '"' -f 4) + echo "LATEST_LIB3MF_URL=${LATEST_LIB3MF_URL}" >> $GITHUB_ENV + + - name: Get second latest lib3mf SDK release info from GitHub API + id: get_second_latest_release + run: | + SECOND_LATEST_LIB3MF_URL=$(curl -s https://api.github.com/repos/3MFConsortium/lib3mf/releases | grep "browser_download_url.*zip" | cut -d '"' -f 4 | sed -n '2p') + echo "SECOND_LATEST_LIB3MF_URL=${SECOND_LATEST_LIB3MF_URL}" >> $GITHUB_ENV + + - name: Download latest lib3mf SDK zip + run: | + wget ${{ env.LATEST_LIB3MF_URL }} -O latest_lib3mf_sdk.zip + + - name: Download second latest lib3mf SDK zip + run: | + wget ${{ env.SECOND_LATEST_LIB3MF_URL }} -O second_latest_lib3mf_sdk.zip + + - name: Unpack the latest SDK + run: | + unzip latest_lib3mf_sdk.zip -d latest_lib3mf_sdk + + - name: Unpack the second latest SDK + run: | + unzip second_latest_lib3mf_sdk.zip -d second_latest_lib3mf_sdk + + - name: Download and unzip test suite + run: | + wget https://github.com/3MFConsortium/test_suites/releases/download/v2.0.0/3MF_Conformance_Test_Suites_v2.0.0.zip + unzip 3MF_Conformance_Test_Suites_v2.0.0.zip -d test_suites + + - name: Copy integration test script + run: | + cp CI/integration_test.py test_suites/ + + - name: Build and run CppDynamic with latest SDK + run: | + sh latest_lib3mf_sdk/Examples/CppDynamic/GenerateMake.sh + cd latest_lib3mf_sdk/Examples/CppDynamic/build + cmake --build . + cp -r * ../../../../test_suites/ + + - name: Run integration tests with latest SDK + run: | + cd test_suites + /usr/bin/time -v python integration_test.py 2>&1 | tee latest_sdk_test.log + + - name: Clean up latest SDK binaries + run: | + find test_suites -maxdepth 1 -type f ! -name 'integration_test.py' ! -name 'latest_sdk_test.log' ! -name 'second_latest_sdk_test.log' -exec rm -rf {} + + + - name: Build and run CppDynamic with second latest SDK + run: | + sh second_latest_lib3mf_sdk/Examples/CppDynamic/GenerateMake.sh + cd second_latest_lib3mf_sdk/Examples/CppDynamic/build + cmake --build . + echo "First debug" + ls -al ../../../../ + echo "This will fail" + cp -r * ../../../../test_suites/ + + - name: Run integration tests with second latest SDK + run: | + cd test_suites + /usr/bin/time -v python integration_test.py 2>&1 | tee second_latest_sdk_test.log + + - name: Compare results (Checks the total python script execution time) + run: | + LATEST_TIME=$(grep "Elapsed (wall clock) time" test_suites/latest_sdk_test.log | awk '{print $8}') + SECOND_LATEST_TIME=$(grep "Elapsed (wall clock) time" test_suites/second_latest_sdk_test.log | awk '{print $8}') + LATEST_TOTAL_SECONDS=$(echo $LATEST_TIME | awk -F: '{ print ($1 * 60) + $2 }') + SECOND_LATEST_TOTAL_SECONDS=$(echo $SECOND_LATEST_TIME | awk -F: '{ print ($1 * 60) + $2 }') + echo "Latest SDK execution time in seconds: ${LATEST_TOTAL_SECONDS}" + echo "Second Latest SDK execution time in seconds: ${SECOND_LATEST_TOTAL_SECONDS}" + # Compare the total seconds + if (( $(echo "$LATEST_TOTAL_SECONDS < $SECOND_LATEST_TOTAL_SECONDS" | bc -l) )); then + echo "New release is better" + else + echo "New release is worse" + fi + + integration-tests-latest-commit: + runs-on: ubuntu-20.04 + needs: [set-integration-tests-status] + if: needs.set-integration-tests-status.outputs.run_integration_tests == 'true' # Single check before the job starts + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + + - name: Download lib3mf_sdk artifact + uses: actions/download-artifact@v4 + with: + name: lib3mf_sdk.zip + path: lib3mf_sdk.zip + + - name: Unpack the SDK + run: | + unzip lib3mf_sdk.zip/lib3mf_sdk.zip -d lib3mf_sdk && ls -al + + - name: Build CppDynamic + run: | + sh lib3mf_sdk/Examples/CppDynamic/GenerateMake.sh + cd lib3mf_sdk/Examples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../Files/Helix.3mf + + - name: Download and unzip test suite + run: | + wget https://github.com/3MFConsortium/test_suites/releases/download/v2.0.0/3MF_Conformance_Test_Suites_v2.0.0.zip + unzip 3MF_Conformance_Test_Suites_v2.0.0.zip -d test_suites + + - name: Copy integration test script + run: | + cp CI/integration_test.py test_suites/ && cp -r lib3mf_sdk/Examples/CppDynamic/build/* test_suites/ + + - name: Run integration tests + run: | + cd test_suites && /usr/bin/time -v python integration_test.py 2>&1 | tee latest_sdk_test.log + + - name: Print results (Checks the total python script execution time) + run: | + LATEST_TIME=$(grep "Elapsed (wall clock) time" test_suites/latest_sdk_test.log | awk '{print $8}') + LATEST_TOTAL_SECONDS=$(echo $LATEST_TIME | awk -F: '{ print ($1 * 60) + $2 }') + echo "Latest SDK execution time in seconds: ${LATEST_TOTAL_SECONDS}" + + integration-test-last-commit-and-last-release: + runs-on: ubuntu-20.04 + needs: [set-integration-tests-status] + if: needs.set-integration-tests-status.outputs.run_integration_tests == 'true' # Single check before the job starts + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + + - name: Download lib3mf_sdk artifact + uses: actions/download-artifact@v4 + with: + name: lib3mf_sdk.zip + path: lib3mf_sdk.zip + + - name: Unpack the SDK + run: | + unzip lib3mf_sdk.zip/lib3mf_sdk.zip -d latest_commit_lib3mf_sdk && ls -al + + - name: Build CppDynamic with latest commit SDK + run: | + sh latest_commit_lib3mf_sdk/Examples/CppDynamic/GenerateMake.sh + cd latest_commit_lib3mf_sdk/Examples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../Files/Helix.3mf + + - name: Download and unzip test suite + run: | + wget https://github.com/3MFConsortium/test_suites/releases/download/v2.0.0/3MF_Conformance_Test_Suites_v2.0.0.zip + unzip 3MF_Conformance_Test_Suites_v2.0.0.zip -d test_suites + + - name: Copy integration test script + run: | + cp CI/integration_test.py test_suites/ + + - name: Copy latest commit SDK to test suite + run: | + cp -r latest_commit_lib3mf_sdk/Examples/CppDynamic/build/* test_suites/ + + - name: Run integration tests with latest commit SDK + run: | + cd test_suites && /usr/bin/time -v python integration_test.py 2>&1 | tee latest_commit_sdk_test.log + + - name: Clean up latest SDK binaries + run: | + find test_suites -maxdepth 1 -type f ! -name 'integration_test.py' ! -name '*.log' -exec rm -rf {} + + + - name: Get latest lib3mf SDK release info from GitHub API + id: get_latest_release + run: | + LATEST_LIB3MF_URL=$(curl -s https://api.github.com/repos/3MFConsortium/lib3mf/releases/latest | grep "browser_download_url.*zip" | cut -d '"' -f 4) + echo "LATEST_LIB3MF_URL=${LATEST_LIB3MF_URL}" >> $GITHUB_ENV + LATEST_RELEASE_NAME=$(curl -s https://api.github.com/repos/3MFConsortium/lib3mf/releases/latest | grep '"tag_name"' | cut -d '"' -f 4) + echo "LATEST_RELEASE_NAME=${LATEST_RELEASE_NAME}" >> $GITHUB_ENV + + - name: Download latest lib3mf SDK release zip + run: | + wget ${{ env.LATEST_LIB3MF_URL }} -O latest_release_lib3mf_sdk.zip + + - name: Unpack the SDK from latest release + run: | + unzip latest_release_lib3mf_sdk.zip -d latest_release_lib3mf_sdk + + - name: Build CppDynamic with latest release SDK + run: | + sh latest_release_lib3mf_sdk/Examples/CppDynamic/GenerateMake.sh + cd latest_release_lib3mf_sdk/Examples/CppDynamic/build + cmake --build . + ./Example_ExtractInfo ../../Files/Helix.3mf + + - name: Copy latest release SDK to test suite + run: | + cp -r latest_release_lib3mf_sdk/Examples/CppDynamic/build/* test_suites/ + + - name: Run integration tests with latest release SDK + run: | + cd test_suites && /usr/bin/time -v python integration_test.py 2>&1 | tee latest_release_sdk_test.log + + - name: Compare results (Checks the total python script execution time) + id: compare_results + run: | + LATEST_COMMIT_TIME=$(grep "Elapsed (wall clock) time" test_suites/latest_commit_sdk_test.log | awk '{print $8}') + LATEST_RELEASE_TIME=$(grep "Elapsed (wall clock) time" test_suites/latest_release_sdk_test.log | awk '{print $8}') + LATEST_COMMIT_TOTAL_SECONDS=$(echo $LATEST_COMMIT_TIME | awk -F: '{ print ($1 * 60) + $2 }') + LATEST_RELEASE_TOTAL_SECONDS=$(echo $LATEST_RELEASE_TIME | awk -F: '{ print ($1 * 60) + $2 }') + echo "Latest commit SDK execution time in seconds: ${LATEST_COMMIT_TOTAL_SECONDS}" + echo "Latest release SDK execution time in seconds: ${LATEST_RELEASE_TOTAL_SECONDS}" + # Compare the total seconds + if (( $(echo "$LATEST_COMMIT_TOTAL_SECONDS < $LATEST_RELEASE_TOTAL_SECONDS" | bc -l) )); then + echo "Latest commit is better" + else + echo "Latest release is better" + fi + echo "Latest commit with SHA ${{ github.sha }} ran in ${LATEST_COMMIT_TOTAL_SECONDS} seconds" > results.txt + echo "Latest release with tag ${{ env.LATEST_RELEASE_NAME }} ran in ${LATEST_RELEASE_TOTAL_SECONDS} seconds" >> results.txt + + - name: Upload results artifact + uses: actions/upload-artifact@v4 + with: + name: integration-test-results + path: results.txt \ No newline at end of file diff --git a/.gitignore b/.gitignore index a41019361..87ee8d189 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ Include/Model/COM/NMR_COMVersion.h debug .DS_Store .vscode +.idea +cmake-build-* \ No newline at end of file diff --git a/CI/Dockerfile b/CI/Dockerfile index 846332f66..efedb0943 100644 --- a/CI/Dockerfile +++ b/CI/Dockerfile @@ -14,6 +14,7 @@ RUN \ tar \ gzip \ zip \ + rpm-build \ ${GCCTOOLSET} \ && microdnf clean all @@ -53,14 +54,28 @@ RUN cmake --build . RUN ctest -V . +# Add this line to generate a ZIP package with cpack +RUN cpack -G ZIP -C Release + +# Generate a debian package +RUN cpack -G DEB -C Release + +# Generate a RPM package +RUN cpack -G RPM -C Release + WORKDIR "/../../" RUN mkdir -p out RUN cp ./lib3mf-repo/build/lib3mf.so.2 ./out/ -RUN cd out && zip -r ../out.zip . +RUN cp ./lib3mf-repo/build/lib3mf-*-Linux.zip ./out/ +RUN cp ./lib3mf-repo/build/lib3mf-*-Linux.deb ./out/ + +RUN cp ./lib3mf-repo/build/lib3mf-*-Linux.rpm ./out/ + +RUN cd out && zip -r ../out.zip . diff --git a/CI/extract_version.py b/CI/extract_version.py new file mode 100644 index 000000000..4fc0b0d53 --- /dev/null +++ b/CI/extract_version.py @@ -0,0 +1,32 @@ +import os +import re + +def extract_version_from_cmake(): + cmake_file = 'CMakeLists.txt' + + if not os.path.exists(cmake_file): + raise FileNotFoundError(f"{cmake_file} not found in the current directory") + + with open(cmake_file, 'r') as file: + content = file.read() + + major = re.search(r'set\(LIB3MF_VERSION_MAJOR\s+([0-9]+)\)', content) + minor = re.search(r'set\(LIB3MF_VERSION_MINOR\s+([0-9]+)\)', content) + micro = re.search(r'set\(LIB3MF_VERSION_MICRO\s+([0-9]+)\)', content) + prerelease = re.search(r'set\(LIB3MF_VERSION_PRERELEASE\s+"([^"]*)"\)', content) + + if not major or not minor or not micro: + raise ValueError("Could not find version components in CMakeLists.txt") + + version = f"{major.group(1)}.{minor.group(1)}.{micro.group(1)}" + if prerelease and prerelease.group(1): + version += f"-{prerelease.group(1)}" + + return version + +if __name__ == "__main__": + try: + version = extract_version_from_cmake() + print(version) + except Exception as e: + print(f"Error: {str(e)}") \ No newline at end of file diff --git a/CI/integration_test.py b/CI/integration_test.py new file mode 100644 index 000000000..dbfb7bb3f --- /dev/null +++ b/CI/integration_test.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +""" +@original author: weismam + +lib3mf_integration: + tests whether a large number of 3MF files is parsed correctly by lib3MF / + the Example_ExtractInfo from the SDK + +""" + +import datetime, time +import subprocess +import os + + +def listFiles(root, extension): + lFiles = [] + for path, _, files in os.walk(root): + for name in files: + [_, fileextension] = os.path.splitext(name) + if extension == fileextension: + lFiles.append(os.path.join(path, name)) + return lFiles + + +# returns a list of files of the following pattern: $root/{*}/$inbetweenFolder/{*}/*.$extension +def listFilesIn(root, inbetweenFolder, extension): + lFiles = [] + for path, subdirs, _ in os.walk(root): + for subdir in subdirs: + if subdir == inbetweenFolder: + lFiles += listFiles(os.path.join(path, subdir), extension) + lFiles = list(set(lFiles)) + lFiles.sort() + return lFiles + + +def ExtractInfo(execCommand, fileName): + tStart = time.time() + proc = subprocess.Popen([execCommand, fileName], stdout=subprocess.PIPE) + pOut = "" + errLines = [] + for line in proc.stdout: + utf8line = line.decode("utf-8") + if "error" in utf8line: + errLines.append(utf8line) + if "warning" in utf8line: + errLines.append(utf8line) + pOut += utf8line + + proc.wait() + info = dict.fromkeys({'success', 'time', 'stdout'}) + info['time'] = time.time() - tStart + info['returncode'] = proc.returncode + info['success'] = len(errLines) == 0 + info['errLines'] = errLines + info['stdout'] = str(pOut) + return info + + +if __name__ == "__main__": + tStart = datetime.datetime.now() + + root = os.path.dirname(os.path.realpath(__file__)) + + execCommand = os.path.join(root, "Example_ExtractInfo") + + print("Execute once for testing") + os.system(execCommand) + print("Execution done") + + positives = [] + negatives = [] + + for suite in os.listdir(root): + suite_path = os.path.join(root, suite) + if os.path.isdir(suite_path) and suite.startswith("suite"): + positives += listFilesIn(suite_path, "positive_test_cases", ".3mf") + negatives += listFilesIn(suite_path, "negative_test_cases", ".3mf") + + nPos = len(positives) + nNeg = len(negatives) + nFiles = nPos + nNeg + + print(execCommand) + + brokenPositives = [] + iFile = 0 + for fileName in positives: + iFile += 1 + print("{:3.0f}%: {:s}".format(100 * (iFile / nFiles), fileName), flush=True) + info = ExtractInfo(execCommand, fileName) + if not info['returncode'] == 0: + print("Fatal Error: MUSTPASS file \"{:s}\" does not work with returncode {:d}:".format(fileName, + info['returncode'])) + brokenPositives.append(info) + if not info['success']: + print("Error: MUSTPASS file \"{:s}\" does not work:".format(fileName)) + print('Contains {:d} problem{:s}:'.format(len(info['errLines']), ['s', ''][len(info['errLines']) == 0])) + for errLine in info['errLines']: + print(errLine, end='') + print('=== Output === ') + print(info['stdout']) + print('=== /Output === ') + brokenPositives.append(info) + + runningNegatives = [] + for fileName in negatives: + iFile += 1 + print("{:3.0f}%: {:s}".format(100 * (iFile / nFiles), fileName), flush=True) + info = ExtractInfo(execCommand, fileName) + if not info['returncode'] >= 0: + print("Fatal Error: MUSTFAIL file \"{:s}\" does not work with returncode {:d}:".format(fileName, + info['returncode'])) + if info['success'] and info['returncode'] == 0: + print("Error: MUSTFAIL file \"{:s}\" works".format(fileName)) + runningNegatives.append(info) + + duration = datetime.datetime.now() - tStart + print("Tested a total of {:d} = ({:d} positive + {:d} negative) files in {:s}.".format( + nFiles, nPos, nNeg, str(duration).split('.')[0])) + + if nPos > 0: + print(" {:3d} / {:3d} MUSTPASS files passed ({:6.2f}%)".format(nPos - len(brokenPositives), nPos, + 100 * (1 - len(brokenPositives) / nPos))) + if len(negatives) > 0: + print(" {:3d} / {:3d} MUSTFAIL files failed ({:6.2f}%)".format(nNeg - len(runningNegatives), nNeg, + 100 * (1 - len(runningNegatives) / nNeg))) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6384b06e7..f7983c519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,10 +46,10 @@ if (${MSVC}) # using Visual Studio C++ # this ensures that the min/max macros of minwindef.h are not used - add_definitions(-DNOMINMAX /W3) + add_definitions(-DNOMINMAX) + + #add_definitions(/W3) - add_definitions(/W3) - # add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS) # set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") @@ -131,6 +131,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/I target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Include) if (USE_INCLUDED_LIBZIP) + # Something goes here to check if submodules exist and initialize the submodules if it does not target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/libzip/Include) if(MSVC) target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) @@ -226,6 +227,8 @@ endif(WIN32) configure_file(lib3mf.pc.in lib3mf.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/lib3mf.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +install(FILES cmake/lib3mfConfig.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/lib3mf) install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" @@ -249,3 +252,21 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${STARTUPPROJECT}) ENDIF() endif() + + +######################################################### +set(CPACK_PACKAGE_NAME "lib3mf") +set(CPACK_PACKAGE_VENDOR "3MF Consortium") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "lib3mf - An implementation of the 3D Manufacturing Format file standard") +set(CPACK_PACKAGE_VERSION "${LIB3MF_VERSION_MAJOR}.${LIB3MF_VERSION_MINOR}.${LIB3MF_VERSION_MICRO}") +set(CPACK_PACKAGE_VERSION_MAJOR "${LIB3MF_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${LIB3MF_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${LIB3MF_VERSION_MICRO}") +set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") +set(CPACK_PACKAGE_CONTACT "lib3mf@3mf.io") +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "3MF Consortium") +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Source") + +######################################################### +include(CPack) diff --git a/SDK/CPackExamples/Cpp/CMakeLists.txt b/SDK/CPackExamples/Cpp/CMakeLists.txt new file mode 100644 index 000000000..e75ad0874 --- /dev/null +++ b/SDK/CPackExamples/Cpp/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required (VERSION 3.5) +project(Examples) +set(CMAKE_CXX_STANDARD 11) + +# Determine the platform and set lib3mf_DIR accordingly +if(WIN32) + # Path for Windows + set(lib3mf_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib3mf-2.3.1-Windows/lib/cmake/lib3mf") + find_package(lib3mf REQUIRED COMPONENTS Cpp) +elseif(APPLE) + # Path for macOS (Darwin) + set(lib3mf_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib3mf-2.3.1-Darwin/lib/cmake/lib3mf") + find_package(lib3mf REQUIRED COMPONENTS Cpp) +else() + # Path for Linux (Here we check twice to test for Debian / RPM packages properly) + find_package(lib3mf QUIET COMPONENTS Cpp) + # Check if the package was not found + if(NOT lib3mf_FOUND) + # lib3mf not found, so set lib3mf_DIR to the fallback directory + set(lib3mf_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib3mf-2.3.1-Linux/lib/cmake/lib3mf") + # Find package (lib3mf) + find_package(lib3mf REQUIRED COMPONENTS Cpp) + endif() +endif() + +add_definitions(-DTEXTURESPATH="${CMAKE_CURRENT_SOURCE_DIR}/../Files/Textures/") + +add_executable(Example_ColorCube Source/ColorCube.cpp) +target_link_libraries(Example_ColorCube lib3mf::lib3mf) +copy_lib3mf_libraries(Example_ColorCube) + +add_executable(Example_Components Source/Components.cpp) +target_link_libraries(Example_Components lib3mf::lib3mf) +copy_lib3mf_libraries(Example_Components) + +add_executable(Example_Converter Source/Converter.cpp) +target_link_libraries(Example_Converter lib3mf::lib3mf) +copy_lib3mf_libraries(Example_Converter) + +add_executable(Example_Cube Source/Cube.cpp) +target_link_libraries(Example_Cube lib3mf::lib3mf) +copy_lib3mf_libraries(Example_Cube) + +add_executable(Example_SecureCube Source/SecureCube.cpp) +target_link_libraries(Example_SecureCube lib3mf::lib3mf) +copy_lib3mf_libraries(Example_SecureCube) + +add_executable(Example_ExtractInfo Source/ExtractInfo.cpp) +target_link_libraries(Example_ExtractInfo lib3mf::lib3mf) +copy_lib3mf_libraries(Example_ExtractInfo) + +add_executable(Example_TextureCube Source/TextureCube.cpp) +target_link_libraries(Example_TextureCube lib3mf::lib3mf) +copy_lib3mf_libraries(Example_TextureCube) + +add_executable(Example_Slice Source/Slice.cpp) +target_link_libraries(Example_Slice lib3mf::lib3mf) +copy_lib3mf_libraries(Example_Slice) + +add_executable(Example_BeamLattice Source/BeamLattice.cpp) +target_link_libraries(Example_BeamLattice lib3mf::lib3mf) +copy_lib3mf_libraries(Example_BeamLattice) + +if (${MSVC}) + IF(${CMAKE_VERSION} VERSION_LESS 3.6.3) + MESSAGE ("Note: You need to manually select a StartUp-project in Visual Studio.") + ELSE() + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Example_Cube) + ENDIF() +endif() diff --git a/SDK/CPackExamples/Cpp/GenerateMake.sh b/SDK/CPackExamples/Cpp/GenerateMake.sh new file mode 100644 index 000000000..16fe9cf2b --- /dev/null +++ b/SDK/CPackExamples/Cpp/GenerateMake.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +basepath="$(cd "$(dirname "$0")" && pwd)" +builddir="$basepath/build" +mkdir -p $builddir +cd $builddir +cmake .. -G "Unix Makefiles" "$@" \ No newline at end of file diff --git a/SDK/CPackExamples/Cpp/GenerateVS2015.bat b/SDK/CPackExamples/Cpp/GenerateVS2015.bat new file mode 100644 index 000000000..e6933cdeb --- /dev/null +++ b/SDK/CPackExamples/Cpp/GenerateVS2015.bat @@ -0,0 +1,10 @@ +@echo off +set startingDir=%CD% + +set basepath=%~dp0 +set builddir=%basepath%\build +if not exist %builddir% (mkdir %builddir%) +cd %builddir% +cmake -G "Visual Studio 14 2015 Win64" .. %* + +cd %startingDir% diff --git a/SDK/CPackExamples/Cpp/GenerateVS2017.bat b/SDK/CPackExamples/Cpp/GenerateVS2017.bat new file mode 100644 index 000000000..4014d2f99 --- /dev/null +++ b/SDK/CPackExamples/Cpp/GenerateVS2017.bat @@ -0,0 +1,10 @@ +@echo off +set startingDir=%CD% + +set basepath=%~dp0 +set builddir=%basepath%\build +if not exist %builddir% (mkdir %builddir%) +cd %builddir% +cmake -G "Visual Studio 15 2017 Win64" .. %* + +cd %startingDir% diff --git a/SDK/CPackExamples/Cpp/GenerateVS2019.bat b/SDK/CPackExamples/Cpp/GenerateVS2019.bat new file mode 100644 index 000000000..efa4f308f --- /dev/null +++ b/SDK/CPackExamples/Cpp/GenerateVS2019.bat @@ -0,0 +1,10 @@ +@echo off +set startingDir=%CD% + +set basepath=%~dp0 +set builddir=%basepath%\build +if not exist %builddir% (mkdir %builddir%) +cd %builddir% +cmake -G "Visual Studio 16 2019" .. %* + +cd %startingDir% diff --git a/SDK/CPackExamples/Cpp/Source/BeamLattice.cpp b/SDK/CPackExamples/Cpp/Source/BeamLattice.cpp new file mode 100644 index 000000000..c1509cc88 --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/BeamLattice.cpp @@ -0,0 +1,159 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +BeamLattice.cpp : 3MF beamlattice creation example + +--*/ + +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and beams +sLib3MFPosition fnCreateVertex(float x, float y, float z) +{ + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFBeam fnCreateBeam(int v0, int v1, double r0, double r1, eLib3MFBeamLatticeCapMode c0, eLib3MFBeamLatticeCapMode c1) +{ + sLib3MFBeam result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Radii[0] = r0; + result.m_Radii[1] = r1; + result.m_CapModes[0] = c0; + result.m_CapModes[1] = c1; + return result; +} + + +void BeamLatticeExample() { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Beamlattice example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Beamlattice"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(0); + std::vector beams(12); + + float fSizeX = 100.0f; + float fSizeY = 200.0f; + float fSizeZ = 300.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(0.0f, 0.0f, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, 0.0f, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(0.0f, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(0.0f, 0.0f, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, 0.0f, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(0.0f, fSizeY, fSizeZ); + + // Manually create beams + double r0 = 1.0; + double r1 = 1.5; + double r2 = 2.0; + double r3 = 2.5; + beams[0] = fnCreateBeam(2, 1, r0, r0, eBeamLatticeCapMode::Butt, eBeamLatticeCapMode::Butt); + beams[1] = fnCreateBeam(0, 3, r0, r1, eBeamLatticeCapMode::Sphere, eBeamLatticeCapMode::Butt); + beams[2] = fnCreateBeam(4, 5, r0, r2, eBeamLatticeCapMode::Sphere, eBeamLatticeCapMode::Butt); + beams[3] = fnCreateBeam(6, 7, r0, r3, eBeamLatticeCapMode::HemiSphere, eBeamLatticeCapMode::Butt); + beams[4] = fnCreateBeam(0, 1, r1, r0, eBeamLatticeCapMode::HemiSphere, eBeamLatticeCapMode::Butt); + beams[5] = fnCreateBeam(5, 4, r1, r1, eBeamLatticeCapMode::Sphere, eBeamLatticeCapMode::HemiSphere); + beams[6] = fnCreateBeam(2, 3, r1, r2, eBeamLatticeCapMode::Sphere, eBeamLatticeCapMode::Sphere); + beams[7] = fnCreateBeam(7, 6, r1, r3, eBeamLatticeCapMode::Butt, eBeamLatticeCapMode::Butt); + beams[8] = fnCreateBeam(1, 2, r2, r2, eBeamLatticeCapMode::Butt, eBeamLatticeCapMode::Butt); + beams[9] = fnCreateBeam(6, 5, r2, r3, eBeamLatticeCapMode::HemiSphere, eBeamLatticeCapMode::Butt); + beams[10] = fnCreateBeam(3, 0, r3, r0, eBeamLatticeCapMode::Butt, eBeamLatticeCapMode::Sphere); + beams[11] = fnCreateBeam(4, 7, r3, r1, eBeamLatticeCapMode::HemiSphere, eBeamLatticeCapMode::HemiSphere); + meshObject->SetGeometry(vertices, triangles); + + // Set beamlattice geometry and metadata + PBeamLattice beamLattice = meshObject->BeamLattice(); + beamLattice->SetBeams(beams); + beamLattice->SetMinLength(0.005); + + PBeamSet set = beamLattice->AddBeamSet(); + set->SetName("Special Beams"); + set->SetIdentifier("bs1"); + std::vector references = { 2,0,5 }; + set->SetReferences(references); + + // Add build item + model->AddBuildItem(meshObject.get(), wrapper->GetIdentityTransform()); + + // Write file + PWriter writer = model->QueryWriter("3mf"); + writer->WriteToFile("beamlattice.3mf"); + + std::cout << "done" << std::endl; +} + +int main() { + try { + BeamLatticeExample(); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/ColorCube.cpp b/SDK/CPackExamples/Cpp/Source/ColorCube.cpp new file mode 100644 index 000000000..f1c46baca --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/ColorCube.cpp @@ -0,0 +1,183 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +ColorCube.cpp : 3MF Color Cube creation example + +--*/ + +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and triangles +sLib3MFPosition fnCreateVertex(float x, float y, float z) +{ + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFTriangle fnCreateTriangle(int v0, int v1, int v2) +{ + sLib3MFTriangle result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Indices[2] = v2; + return result; +} + +sLib3MFTriangleProperties fnCreateTriangleColor(PColorGroup colorGroup, Lib3MF_uint32 colorID1, Lib3MF_uint32 colorID2, Lib3MF_uint32 colorID3) +{ + sLib3MFTriangleProperties sTriangleProperty; + sTriangleProperty.m_ResourceID = colorGroup->GetResourceID(); + sTriangleProperty.m_PropertyIDs[0] = colorID1; + sTriangleProperty.m_PropertyIDs[1] = colorID2; + sTriangleProperty.m_PropertyIDs[2] = colorID3; + return sTriangleProperty; +} + + +void CubeExample() { + PWrapper wrapper = wrapper->loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Color Cube example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Colored Box"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(12); + + float fSizeX = 100.0f; + float fSizeY = 200.0f; + float fSizeZ = 300.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(0.0f, 0.0f, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, 0.0f, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(0.0f, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(0.0f, 0.0f, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, 0.0f, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(0.0f, fSizeY, fSizeZ); + + // Manually create triangles + triangles[0] = fnCreateTriangle(2, 1, 0); + triangles[1] = fnCreateTriangle(0, 3, 2); + triangles[2] = fnCreateTriangle(4, 5, 6); + triangles[3] = fnCreateTriangle(6, 7, 4); + triangles[4] = fnCreateTriangle(0, 1, 5); + triangles[5] = fnCreateTriangle(5, 4, 0); + triangles[6] = fnCreateTriangle(2, 3, 7); + triangles[7] = fnCreateTriangle(7, 6, 2); + triangles[8] = fnCreateTriangle(1, 2, 6); + triangles[9] = fnCreateTriangle(6, 5, 1); + triangles[10] = fnCreateTriangle(3, 0, 4); + triangles[11] = fnCreateTriangle(4, 7, 3); + + meshObject->SetGeometry(vertices, triangles); + + // define colors + PColorGroup colorGroup = model->AddColorGroup(); + Lib3MF_uint32 idRed = colorGroup->AddColor(wrapper->RGBAToColor(255, 0, 0, 255)); + Lib3MF_uint32 idGreen = colorGroup->AddColor(wrapper->RGBAToColor(0, 255, 0, 255)); + Lib3MF_uint32 idBlue = colorGroup->AddColor(wrapper->RGBAToColor(0, 0, 255, 255)); + Lib3MF_uint32 idOrange = colorGroup->AddColor(wrapper->RGBAToColor(255, 128, 0, 255)); + Lib3MF_uint32 idYellow = colorGroup->AddColor(wrapper->RGBAToColor(255, 255, 0, 255)); + + sLib3MFTriangleProperties sTriangleColorRed = fnCreateTriangleColor(colorGroup, idRed, idRed, idRed); + sLib3MFTriangleProperties sTriangleColorGreen = fnCreateTriangleColor(colorGroup, idGreen, idGreen, idGreen); + sLib3MFTriangleProperties sTriangleColorBlue = fnCreateTriangleColor(colorGroup, idBlue, idBlue, idBlue); + + sLib3MFTriangleProperties sTriangleColor1 = fnCreateTriangleColor(colorGroup, idOrange, idRed, idYellow); + sLib3MFTriangleProperties sTriangleColor2 = fnCreateTriangleColor(colorGroup, idYellow, idGreen, idOrange); + + // One-colored Triangles + meshObject->SetTriangleProperties(0, sTriangleColorRed); + meshObject->SetTriangleProperties(1, sTriangleColorRed); + meshObject->SetTriangleProperties(2, sTriangleColorGreen); + meshObject->SetTriangleProperties(3, sTriangleColorGreen); + meshObject->SetTriangleProperties(4, sTriangleColorBlue); + meshObject->SetTriangleProperties(5, sTriangleColorBlue); + // Gradient-colored Triangles + meshObject->SetTriangleProperties(6, sTriangleColor1); + meshObject->SetTriangleProperties(7, sTriangleColor2); + meshObject->SetTriangleProperties(8, sTriangleColor1); + meshObject->SetTriangleProperties(9, sTriangleColor2); + meshObject->SetTriangleProperties(10, sTriangleColor1); + meshObject->SetTriangleProperties(11, sTriangleColor2); + + // Object Level Property + meshObject->SetObjectLevelProperty(sTriangleColorRed.m_ResourceID, sTriangleColorRed.m_PropertyIDs[0]); + + // Add build item + model->AddBuildItem(meshObject.get(), wrapper->GetIdentityTransform()); + + PWriter writer = model->QueryWriter("3mf"); + writer->WriteToFile("colorcube.3mf"); + + std::cout << "done" << std::endl; +} + +int main() { + try { + CubeExample(); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/Components.cpp b/SDK/CPackExamples/Cpp/Source/Components.cpp new file mode 100644 index 000000000..f91499024 --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/Components.cpp @@ -0,0 +1,183 @@ + +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +Components.cpp : 3MF Components example + +--*/ + +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and triangles +sLib3MFPosition fnCreateVertex(float x, float y, float z) +{ + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFTriangle fnCreateTriangle(int v0, int v1, int v2) +{ + sLib3MFTriangle result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Indices[2] = v2; + return result; +} + +sLib3MFTransform createTranslationMatrix(float x, float y, float z) +{ + sLib3MFTransform mMatrix; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + mMatrix.m_Fields[i][j] = (i == j) ? 1.0f : 0.0f; + } + } + + mMatrix.m_Fields[3][0] = x; + mMatrix.m_Fields[3][1] = y; + mMatrix.m_Fields[3][2] = z; + + return mMatrix; +} + + +void ComponentsExample() { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Components example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Box"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(12); + + float fSizeX = 10.0f; + float fSizeY = 20.0f; + float fSizeZ = 30.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(0.0f, 0.0f, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, 0.0f, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(0.0f, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(0.0f, 0.0f, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, 0.0f, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(0.0f, fSizeY, fSizeZ); + + // Manually create triangles + triangles[0] = fnCreateTriangle(2, 1, 0); + triangles[1] = fnCreateTriangle(0, 3, 2); + triangles[2] = fnCreateTriangle(4, 5, 6); + triangles[3] = fnCreateTriangle(6, 7, 4); + triangles[4] = fnCreateTriangle(0, 1, 5); + triangles[5] = fnCreateTriangle(5, 4, 0); + triangles[6] = fnCreateTriangle(2, 3, 7); + triangles[7] = fnCreateTriangle(7, 6, 2); + triangles[8] = fnCreateTriangle(1, 2, 6); + triangles[9] = fnCreateTriangle(6, 5, 1); + triangles[10] = fnCreateTriangle(3, 0, 4); + triangles[11] = fnCreateTriangle(4, 7, 3); + + meshObject->SetGeometry(vertices, triangles); + + // Create Component Object + PComponentsObject componentsObject = model->AddComponentsObject(); + + // Add first component + componentsObject->AddComponent(meshObject.get(), createTranslationMatrix(0.0f, 0.0f, 0.0f)); + + // Add second component + componentsObject->AddComponent(meshObject.get(), createTranslationMatrix(40.0f, 60.0f, 80.0f)); + + // Add third component + componentsObject->AddComponent(meshObject.get(), createTranslationMatrix(120.0f, 30.0f, 70.0f)); + + + // Add componentsobject as build item + model->AddBuildItem(componentsObject.get(), createTranslationMatrix(0.0f, 0.0f, 0.0f)); + + // Add translated componentsobject as build item + model->AddBuildItem(componentsObject.get(), createTranslationMatrix(200.0f, 40.0f, 10.0f)); + + // Add translated meshobject as build item + model->AddBuildItem(meshObject.get(), createTranslationMatrix(-40.0f, 0.0f, 20.0f)); + + // Output scene as STL and 3MF + PWriter _3mfWriter = model->QueryWriter("3mf"); + std::cout << "writing components.3mf..." << std::endl; + _3mfWriter->WriteToFile("components.3mf"); + + PWriter stlWriter = model->QueryWriter("stl"); + std::cout << "writing components.stl..." << std::endl; + stlWriter->WriteToFile("components.stl"); + + std::cout << "done" << std::endl; +} + +int main() { + try { + ComponentsExample(); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} + diff --git a/SDK/CPackExamples/Cpp/Source/Converter.cpp b/SDK/CPackExamples/Cpp/Source/Converter.cpp new file mode 100644 index 000000000..2fefdbc56 --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/Converter.cpp @@ -0,0 +1,154 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +Converter.cpp : Can convert 3MFs to STL and back + +--*/ + +#include +#include +#include + +#ifndef __GNUC__ +#include +#endif + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +std::string FindExtension(std::string filename) { + // this emulates Windows' PathFindExtension + std::string::size_type idx; + idx = filename.rfind('.'); + + if (idx != std::string::npos) + { + return filename.substr(idx); + } + else + { + return ""; + } +} + + +int convert(std::string sFilename) { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Model Converter" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + // Extract Extension of filename + std::string sReaderName; + std::string sWriterName; + std::string sNewExtension; + std::string sExtension = FindExtension(sFilename); + std::transform(sExtension.begin(), sExtension.end(), sExtension.begin(), ::tolower); + + // Which Reader and Writer classes do we need? + if (sExtension == ".stl") { + sReaderName = "stl"; + sWriterName = "3mf"; + sNewExtension = ".3mf"; + } + if (sExtension == ".3mf") { + sReaderName = "3mf"; + sWriterName = "stl"; + sNewExtension = ".stl"; + } + if (sReaderName.length() == 0) { + std::cout << "unknown input file extension:" << sExtension << std::endl; + return -1; + } + + // Create new filename + std::string sOutputFilename = sFilename; + sOutputFilename.erase(sOutputFilename.length() - sExtension.length()); + sOutputFilename += sNewExtension; + + PModel model = wrapper->CreateModel(); + PReader reader = model->QueryReader(sReaderName); + + // Import Model from File + std::cout << "reading " << sFilename << "..." << std::endl; +#ifndef __GNUC__ + ULONGLONG nStartTicks = GetTickCount64(); +#endif + reader->ReadFromFile(sFilename); +#ifndef __GNUC__ + std::cout << "elapsed time: " << (GetTickCount64() - nStartTicks) << "ms" << std::endl; +#endif + + PWriter writer = model->QueryWriter(sWriterName); + std::cout << "writing " << sOutputFilename << "..." << std::endl; +#ifndef __GNUC__ + nStartTicks = GetTickCount64(); +#endif + writer->WriteToFile(sOutputFilename); +#ifndef __GNUC__ + std::cout << "elapsed time: " << (GetTickCount64() - nStartTicks) << "ms" << std::endl; +#endif + std::cout << "done" << std::endl; + return 0; +} + +int main(int argc, char** argv) { + // Parse Arguments + if (argc != 2) { + std::cout << "Usage: " << std::endl; + std::cout << "Convert 3MF to STL: Converter.exe model.3mf" << std::endl; + std::cout << "Convert STL to 3MF: Converter.exe model.stl" << std::endl; + return 0; + } + + try { + return convert(argv[1]); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/Cube.cpp b/SDK/CPackExamples/Cpp/Source/Cube.cpp new file mode 100644 index 000000000..18e99cb51 --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/Cube.cpp @@ -0,0 +1,140 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +Cube.cpp : 3MF Cube creation example + +--*/ + +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and triangles +sLib3MFPosition fnCreateVertex(float x, float y, float z) +{ + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFTriangle fnCreateTriangle(int v0, int v1, int v2) +{ + sLib3MFTriangle result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Indices[2] = v2; + return result; +} + + +void CubeExample() { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Cube example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Box"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(12); + + float fSizeX = 100.0f; + float fSizeY = 200.0f; + float fSizeZ = 300.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(0.0f, 0.0f, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, 0.0f, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(0.0f, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(0.0f, 0.0f, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, 0.0f, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(0.0f, fSizeY, fSizeZ); + + // Manually create triangles + triangles[0] = fnCreateTriangle(2, 1, 0); + triangles[1] = fnCreateTriangle(0, 3, 2); + triangles[2] = fnCreateTriangle(4, 5, 6); + triangles[3] = fnCreateTriangle(6, 7, 4); + triangles[4] = fnCreateTriangle(0, 1, 5); + triangles[5] = fnCreateTriangle(5, 4, 0); + triangles[6] = fnCreateTriangle(2, 3, 7); + triangles[7] = fnCreateTriangle(7, 6, 2); + triangles[8] = fnCreateTriangle(1, 2, 6); + triangles[9] = fnCreateTriangle(6, 5, 1); + triangles[10] = fnCreateTriangle(3, 0, 4); + triangles[11] = fnCreateTriangle(4, 7, 3); + + meshObject->SetGeometry(vertices, triangles); + + // Add build item + model->AddBuildItem(meshObject.get(), wrapper->GetIdentityTransform()); + + PWriter writer = model->QueryWriter("3mf"); + writer->WriteToFile("cube.3mf"); + + std::cout << "done" << std::endl; +} + +int main() { + try { + CubeExample(); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/ExtractInfo.cpp b/SDK/CPackExamples/Cpp/Source/ExtractInfo.cpp new file mode 100644 index 000000000..661cd30f2 --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/ExtractInfo.cpp @@ -0,0 +1,269 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +ExtractInfo.cpp : 3MF Read Example + +--*/ + +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +void ShowThumbnailInformation(PModel model) +{ + /* + // TODO: this is not yet implemented in Lib3MF + */ +} + + +void ShowMetaDataInformation(PMetaDataGroup metaDataGroup) +{ + Lib3MF_uint32 nMetaDataCount = metaDataGroup->GetMetaDataCount(); + + for (Lib3MF_uint32 iMeta = 0; iMeta < nMetaDataCount; iMeta++) { + + PMetaData metaData = metaDataGroup->GetMetaData(iMeta); + std::string sMetaDataValue = metaData->GetValue(); + std::string sMetaDataName = metaData->GetName(); + std::cout << "Metadatum: " << iMeta << ":" << std::endl; + std::cout << "Name = \"" << sMetaDataName << "\"" << std::endl; + std::cout << "Value = \"" << sMetaDataValue << "\"" << std::endl; + } +} + +void ShowSliceStack(PSliceStack sliceStack, std::string indent) +{ + std::cout << indent << "SliceStackID: " << sliceStack->GetResourceID() << std::endl; + if (sliceStack->GetSliceCount() > 0) { + std::cout << indent << " Slice count: " << sliceStack->GetSliceCount() << std::endl; + } + if (sliceStack->GetSliceRefCount() > 0) { + std::cout << indent << " Slice ref count: " << sliceStack->GetSliceRefCount() << std::endl; + for (Lib3MF_uint64 iSliceRef = 0; iSliceRef < sliceStack->GetSliceRefCount(); iSliceRef++) { + std::cout << indent << " Slice ref : " << sliceStack->GetSliceStackReference(iSliceRef)->GetResourceID() << std::endl; + } + } +} + +void ShowObjectProperties(PObject object) +{ + std::cout << " Name: \"" << object->GetName() << "\"" << std::endl; + std::cout << " PartNumber: \"" << object->GetPartNumber() << "\"" << std::endl; + + switch (object->GetType()) { + case eObjectType::Model: + std::cout << " Object type: model" << std::endl; + break; + case eObjectType::Support: + std::cout << " Object type: support" << std::endl; + break; + case eObjectType::SolidSupport: + std::cout << " Object type: solidsupport" << std::endl; + break; + case eObjectType::Other: + std::cout << " Object type: other" << std::endl; + break; + default: + std::cout << " Object type: invalid" << std::endl; + break; + } + + if (object->HasSlices(false)) { + PSliceStack sliceStack = object->GetSliceStack(); + ShowSliceStack(sliceStack, " "); + } + + if (object->GetMetaDataGroup()->GetMetaDataCount() > 0) { + ShowMetaDataInformation(object->GetMetaDataGroup()); + } +} + +void ShowMeshObjectInformation(PMeshObject meshObject) +{ + std::cout << "mesh object #" << meshObject->GetResourceID() << ": " << std::endl; + + ShowObjectProperties(meshObject); + + Lib3MF_uint64 nVertexCount = meshObject->GetVertexCount(); + Lib3MF_uint64 nTriangleCount = meshObject->GetTriangleCount(); + PBeamLattice beamLattice = meshObject->BeamLattice(); + + // Output data + std::cout << " Vertex count: " << nVertexCount << std::endl; + std::cout << " Triangle count: " << nTriangleCount << std::endl; + + Lib3MF_uint64 nBeamCount = beamLattice->GetBeamCount(); + if (nBeamCount > 0) { + std::cout << " Beam count: " << nBeamCount << std::endl; + Lib3MF_uint32 nRepresentationMesh; + if (beamLattice->GetRepresentation(nRepresentationMesh)) + std::cout << " |_Representation Mesh ID: " << nRepresentationMesh << std::endl; + eLib3MFBeamLatticeClipMode eClipMode; + Lib3MF_uint32 nClippingMesh; + beamLattice->GetClipping(eClipMode, nClippingMesh); + if (eClipMode != eBeamLatticeClipMode::NoClipMode) + std::cout << " |_Clipping Mesh ID: " << nClippingMesh << "(mode=" << (int)eClipMode << ")" << std::endl; + if (beamLattice->GetBeamSetCount() > 0) { + std::cout << " |_BeamSet count: " << beamLattice->GetBeamSetCount() << std::endl; + } + } + +} + +void ShowTransform(sLib3MFTransform transform, std::string indent) { + std::cout << indent << "Transformation: [ " << transform.m_Fields[0][0] << " " << transform.m_Fields[1][0] << " " << transform.m_Fields[2][0] << " " << transform.m_Fields[3][0] << " ]" << std::endl; + std::cout << indent << " [ " << transform.m_Fields[0][1] << " " << transform.m_Fields[1][1] << " " << transform.m_Fields[2][1] << " " << transform.m_Fields[3][1] << " ]" << std::endl; + std::cout << indent << " [ " << transform.m_Fields[0][2] << " " << transform.m_Fields[1][2] << " " << transform.m_Fields[2][2] << " " << transform.m_Fields[3][2] << " ]" << std::endl; +} + +void ShowComponentsObjectInformation(PComponentsObject componentsObject) +{ + std::cout << "components object #" << componentsObject->GetResourceID() << ": " << std::endl; + + ShowObjectProperties(componentsObject); + std::cout << " Component count: " << componentsObject->GetComponentCount() << std::endl; + for (Lib3MF_uint32 nIndex = 0; nIndex < componentsObject->GetComponentCount(); nIndex++) { + PComponent component = componentsObject->GetComponent(nIndex); + + std::cout << " Component " << nIndex << ": Object ID: " << component->GetObjectResourceID() << std::endl; + if (component->HasTransform()) { + ShowTransform(component->GetTransform(), " "); + } + else { + std::cout << " Transformation: none" << std::endl; + } + } +} + + +void ExtractInfoExample(std::string sFileName) { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Read example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + // Import Model from 3MF File + { + PReader reader = model->QueryReader("3mf"); + // And deactivate the strict mode (default is "false", anyway. This just demonstrates where/how to use it). + reader->SetStrictModeActive(false); + reader->ReadFromFile(sFileName); + + for (Lib3MF_uint32 iWarning = 0; iWarning < reader->GetWarningCount(); iWarning++) { + Lib3MF_uint32 nErrorCode; + std::string sWarningMessage = reader->GetWarning(iWarning, nErrorCode); + std::cout << "Encountered warning #" << nErrorCode << " : " << sWarningMessage << std::endl; + } + } + ShowThumbnailInformation(model); + + ShowMetaDataInformation(model->GetMetaDataGroup()); + + PSliceStackIterator sliceStacks = model->GetSliceStacks(); + while (sliceStacks->MoveNext()) { + PSliceStack sliceStack = sliceStacks->GetCurrentSliceStack(); + ShowSliceStack(sliceStack, ""); + } + + PObjectIterator objectIterator = model->GetObjects(); + while (objectIterator->MoveNext()) { + PObject object = objectIterator->GetCurrentObject(); + if (object->IsMeshObject()) { + ShowMeshObjectInformation(model->GetMeshObjectByID(object->GetResourceID())); + } + else if (object->IsComponentsObject()) { + ShowComponentsObjectInformation(model->GetComponentsObjectByID(object->GetResourceID())); + } + else { + std::cout << "unknown object #" << object->GetResourceID() << ": " << std::endl; + } + } + + + PBuildItemIterator buildItemIterator = model->GetBuildItems(); + while (buildItemIterator->MoveNext()) { + PBuildItem buildItem = buildItemIterator->GetCurrent(); + + std::cout << "Build item (Object #" << buildItem->GetObjectResourceID() << "): " << std::endl; + + if (buildItem->HasObjectTransform()) { + ShowTransform(buildItem->GetObjectTransform(), " "); + } + else { + std::cout << " Transformation: none" << std::endl; + } + std::cout << " Part number: \"" << buildItem->GetPartNumber() << "\"" << std::endl; + if (buildItem->GetMetaDataGroup()->GetMetaDataCount() > 0) { + ShowMetaDataInformation(buildItem->GetMetaDataGroup()); + } + } + + std::cout << "done" << std::endl; +} + + +int main(int argc, char** argv) { + // Parse Arguments + if (argc != 2) { + std::cout << "Usage: " << std::endl; + std::cout << "ExtractInfo.exe model.3mf" << std::endl; + return 0; + } + + try { + ExtractInfoExample(argv[1]); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/SecureCube.cpp b/SDK/CPackExamples/Cpp/Source/SecureCube.cpp new file mode 100644 index 000000000..de966b61f --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/SecureCube.cpp @@ -0,0 +1,487 @@ +/*++ + +Copyright (C) 2020 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +SecureCube.cpp : 3MF encrypted Cube creation and read example. This is a sample +skeleton code provided to guide you to the process of reading and writing a 3MF +file using the Secure Content spec. Encryption and decryption processes are abstracted +so to avoid binding this sample to any particular implementation. If you would like +to check a working version of the process, there's a unit tests available on the 3MF code +base that implements the entire workflow using LibreSSL: EncryptionMethods.cpp +Tip: you could also copy buffers around - file won't be valid but you will be able +to run the entire process. +--*/ + +#include +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +namespace SecureContentCallbacks { + // Sample random number generation callback. Do not use this beyond the scope of this example as it is not really a random number generation function. + static void NotRandomBytesAtAll(Lib3MF_uint64 byteData, Lib3MF_uint64 size, Lib3MF_pvoid userData, Lib3MF_uint64 * bytesWritten) { + static Lib3MF_uint8 random = 0; + Lib3MF_uint8 * buffer = (Lib3MF_uint8 *)byteData; + *bytesWritten = size; + while (size > 0) + *(buffer + (--size)) = ++random; + } + + // Structure to hold encryption context for keys + struct KeyWrappingCbData { + CWrapper * wrapper; + }; + + + // Structure to hold encryption context for resources + struct ContentEncryptionCbData { + CWrapper * wrapper; + std::map context; + }; + + // Sample callback to wrap the key of an encryption process + static void WriteKeyWrappingCbSample( + Lib3MF_AccessRight access, + Lib3MF_uint64 inSize, + const Lib3MF_uint8 * inBuffer, + const Lib3MF_uint64 outSize, + Lib3MF_uint64 * outNeeded, + Lib3MF_uint8 * outBuffer, + Lib3MF_pvoid userData, + Lib3MF_uint64 * status) { + + KeyWrappingCbData * cp = (KeyWrappingCbData *)userData; + + // Since we're using CAccessRight constructure, we have to account for + // the use of 'access' by calling Acquire on the CWrapper + CAccessRight accessRight(cp->wrapper, access); + cp->wrapper->Acquire(&accessRight); + + // This is going to be called for each consumer you've registered to + PConsumer consumer = accessRight.GetConsumer(); + std::string consumerId = consumer->GetConsumerID(); + std::cout << "ConsumerID " << consumer->GetConsumerID() << std::endl; + // You can also check for keyid and keyvalue + + // A call could be made to first identify what the output buffer size should be. + // In that case, outSize will be 0 or outBuffer will be null, and the proper size must be placed in outNeeded. + if (nullptr == outBuffer || outSize == 0) { + *outNeeded = inSize; + *status = inSize; + return; + } + + // Query the data about the encryption process to be done + eWrappingAlgorithm algorithm = accessRight.GetWrappingAlgorithm(); + eMgfAlgorithm mask = accessRight.GetMgfAlgorithm(); + eDigestMethod diges = accessRight.GetDigestMethod(); + + // You should deal with the encryption process of the key. + // Use the encryption process to wrap inBuffer (plain) into outBuffer (cipher) using details above. + // Use KeyWrappingCbData to hold any information you'll be needing at this point. + throw std::runtime_error("TODO: Add your encryption wrapping process here"); + //std::copy(inBuffer, inBuffer + outSize, outBuffer); + + // Finally, this function should use status to return the number of bytes needed, + // encrypted - or zero to indicate a failure. + *status = outSize; + } + + // Sample callback to encrypt contents of a resource + static void WriteContentEncryptionCbSample( + Lib3MF_ContentEncryptionParams params, + Lib3MF_uint64 inSize, + const Lib3MF_uint8 * inBuffer, + const Lib3MF_uint64 outSize, + Lib3MF_uint64 * outNeededSize, + Lib3MF_uint8 * outBuffer, + Lib3MF_pvoid userData, + Lib3MF_uint64 * status) { + + ContentEncryptionCbData * cb = (ContentEncryptionCbData *)userData; + + // Since we're using CAccessRight constructure, we have to account for + // the use of 'access' by calling Acquire on the CWrapper + CContentEncryptionParams cd(cb->wrapper, params); + cb->wrapper->Acquire(&cd); + + // A descriptor uniquely identifies the encryption process for a resource + Lib3MF_uint64 descriptor = cd.GetDescriptor(); + + // You can map the descriptor in use as you'll probably keep several + // contexts initialized at same time + auto localDescriptor = cb->context.find(cd.GetDescriptor()); + if (localDescriptor != cb->context.end()) + // Use an existing context + localDescriptor->second++; + else { + // Initialize a new context + + // Retrieve the encryption key, if there is one + std::vector key; + cd.GetKey(key); + + // You can also use keyuuid to look up a key externally + std::string keyUUID = cd.GetKeyUUID(); + + // Retrieve the additional authenticaton data, if it has been used to encrypt + std::vector aad; + cd.GetAdditionalAuthenticationData(aad); + + // TODO: Initialize the encryption context + cb->context[cd.GetDescriptor()] = 0; + } + + // Attention to the order in which params are tested, it matters + if (0 == inSize || nullptr == inBuffer) { + // When input buffer is null or input size is 0, this is a request + // to finalize this encryption process and generating the authentication tag + // TODO: generate proper tag + std::vector tag = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + cd.SetAuthenticationTag(tag); + //add tag verification status here + *status = tag.size(); + } else if (0 == outSize || nullptr == outBuffer) { + // If outSize is zero or outBuffer is null (but inSize and inBuffer are not), + // this is a call to figure out the output buffer size. + // Put the respective value in outNeededSize. + *outNeededSize = inSize; + *status = inSize; + } else { + // Else, perform the encryption process + throw std::runtime_error("TODO: Add your encryption process here"); + //std::copy(inBuffer, inBuffer + outSize, outBuffer); + *status = outSize; + } + //This function should use status to return the number of bytes needed, encrypted + // or verified - or zero to indicate a failure. + } + + static void ReadKeyWrappingCbSample( + Lib3MF_AccessRight access, + Lib3MF_uint64 inSize, + const Lib3MF_uint8 * inBuffer, + const Lib3MF_uint64 outSize, + Lib3MF_uint64 * outNeeded, + Lib3MF_uint8 * outBuffer, + Lib3MF_pvoid userData, + Lib3MF_uint64 * status) { + + // A call could be made to first identify what the output buffer size should be. + // In that case, outSize will be 0 or outBuffer will be null, and the proper size must be placed in outNeeded. + if (nullptr == outBuffer || outSize == 0) { + *outNeeded = inSize; + *status = inSize; + return; + } + + KeyWrappingCbData * cp = (KeyWrappingCbData *)userData; + + // Since we're using CAccessRight constructure, we have to account for + // the use of 'access' by calling Acquire on the CWrapper + CAccessRight accessRight(cp->wrapper, access); + cp->wrapper->Acquire(&accessRight); + + // This is going to be called for each consumer you've registered to + PConsumer consumer = accessRight.GetConsumer(); + std::string consumerId = consumer->GetConsumerID(); + std::cout << "ConsumerID " << consumer->GetConsumerID() << std::endl; + // You can also check for keyid and keyvalue + + // Query the data about the encryption process to be done + eWrappingAlgorithm algorithm = accessRight.GetWrappingAlgorithm(); + eMgfAlgorithm mask = accessRight.GetMgfAlgorithm(); + eDigestMethod diges = accessRight.GetDigestMethod(); + + // You should deal with the encryption process of the key. + // Use the encryption process to wrap inBuffer (cipher) into outBuffer (plain) using details above. + // Use KeyWrappingCbData to hold any information you'll be needing at this point. + throw std::runtime_error("TODO: Add your decryption wrapping process here"); + //std::copy(inBuffer, inBuffer + outSize, outBuffer); + + // Finally, this function should use status to return the number of bytes needed, + // decrypted - or zero to indicate a failure. + *status = outSize; + } + + static void ReadContentEncryptionCbSample( + Lib3MF_ContentEncryptionParams params, + Lib3MF_uint64 inSize, + const Lib3MF_uint8 * inBuffer, + const Lib3MF_uint64 outSize, + Lib3MF_uint64 * outNeededSize, + Lib3MF_uint8 * outBuffer, + Lib3MF_pvoid userData, + Lib3MF_uint64 * status) { + + ContentEncryptionCbData * cb = (ContentEncryptionCbData *)userData; + + // Since we're using CAccessRight constructure, we have to account for + // the use of 'access' by calling Acquire on the CWrapper + CContentEncryptionParams cd(cb->wrapper, params); + cb->wrapper->Acquire(&cd); + + // A descriptor uniquely identifies the encryption process for a resource + Lib3MF_uint64 descriptor = cd.GetDescriptor(); + + // You can map the descriptor in use as you'll probably keep several + // contexts initialized at same time + auto localDescriptor = cb->context.find(cd.GetDescriptor()); + if (localDescriptor != cb->context.end()) + // Use an existing context + localDescriptor->second++; + else { + // Initialize a new context + + // Retrieve the encryption key, if there is one + std::vector key; + cd.GetKey(key); + + // You can also use keyuuid to look up a key externally + std::string keyUUID = cd.GetKeyUUID(); + + // Retrieve the additional authenticaton data, if it has been used to encrypt + std::vector aad; + cd.GetAdditionalAuthenticationData(aad); + + // TODO: Initialize the encryption context + cb->context[cd.GetDescriptor()] = 0; + } + + // Attention to the order in which params are tested, it matters + if (0 == inSize || nullptr == inBuffer) { + // When input buffer is null or input size is 0, this is a request + // to finalize this encryption process and verify the authentication tag + std::vector tag; + cd.GetAuthenticationTag(tag); + // TODO: verify tag + *status = tag.size(); + } else if (0 == outSize || nullptr == outBuffer) { + // If outSize is zero or outBuffer is null (but inSize and inBuffer are not), + // this is a call to figure out the output buffer size. + // Put the respective value in outNeededSize. + *outNeededSize = inSize; + *status = inSize; + return; + } else { + // Else, perform the descryption process + throw std::runtime_error("TODO: Add your encryption process here"); + //std::copy(inBuffer, inBuffer + outSize, outBuffer); + *status = outSize; + } + //This function should use status to return the number of bytes needed, decrypted + // or verified - or zero to indicate a failure. + } +}; + +void printVersion(CWrapper & wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper.GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper.GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper.GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and triangles +sLib3MFPosition fnCreateVertex(float x, float y, float z) { + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFTriangle fnCreateTriangle(int v0, int v1, int v2) { + sLib3MFTriangle result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Indices[2] = v2; + return result; +} + +void WriteSecureContentExample(CWrapper & wrapper) { + + PModel model = wrapper.CreateModel(); + + // After initializing the model, set the random number generation callback + // A default one will be used if you don't, current implementation uses std::mt19937 + model->SetRandomNumberCallback(SecureContentCallbacks::NotRandomBytesAtAll, nullptr); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Box"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(12); + + float fSizeX = 100.0f; + float fSizeY = 200.0f; + float fSizeZ = 300.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(0.0f, 0.0f, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, 0.0f, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(0.0f, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(0.0f, 0.0f, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, 0.0f, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(0.0f, fSizeY, fSizeZ); + + // Manually create triangles + triangles[0] = fnCreateTriangle(2, 1, 0); + triangles[1] = fnCreateTriangle(0, 3, 2); + triangles[2] = fnCreateTriangle(4, 5, 6); + triangles[3] = fnCreateTriangle(6, 7, 4); + triangles[4] = fnCreateTriangle(0, 1, 5); + triangles[5] = fnCreateTriangle(5, 4, 0); + triangles[6] = fnCreateTriangle(2, 3, 7); + triangles[7] = fnCreateTriangle(7, 6, 2); + triangles[8] = fnCreateTriangle(1, 2, 6); + triangles[9] = fnCreateTriangle(6, 5, 1); + triangles[10] = fnCreateTriangle(3, 0, 4); + triangles[11] = fnCreateTriangle(4, 7, 3); + + meshObject->SetGeometry(vertices, triangles); + + // Add build item + model->AddBuildItem(meshObject.get(), wrapper.GetIdentityTransform()); + + // Move this mesh out of the root model file into another model + PPackagePart nonRootModel = model->FindOrCreatePackagePart("/3D/securecube.model"); + meshObject->SetPackagePart(nonRootModel.get()); + + // Locate the keystore and setup the resource as a secure content + PKeyStore keystore = model->GetKeyStore(); + + // Add you (client) as consumer of the resource. You'll need to do this + // to be able to have a chance to wrap the content key + // You can set optional public key and an optional key id + PConsumer consumer = keystore->AddConsumer("MyConsumerID", std::string(), std::string()); + + // Add a container for your secured resources. Resources within the same container will have a shared key. + PResourceDataGroup dataGroup = keystore->AddResourceDataGroup(); + + // Add access rights for your consumer into the datagroup + PAccessRight accessRight = dataGroup->AddAccessRight(consumer.get(), eWrappingAlgorithm::RSA_OAEP, eMgfAlgorithm::MGF1_SHA1, eDigestMethod::SHA1); + + // Specify additional authentication data that you could use to further validate your encryption process + std::vector aad = { '3','M','F','C','o','n','s','o','r','t','i','u','m',' ','S','a','m','p','l','e' }; + + // This will effectively add your nonRootModel as a secure content + PResourceData resourceData = keystore->AddResourceData(dataGroup.get(), nonRootModel.get(), eEncryptionAlgorithm::AES256_GCM, eCompression::Deflate, aad); + + // Query the writer and setup the encryption before saving results + PWriter writer = model->QueryWriter("3mf"); + + // Setup Key Wrapping process + SecureContentCallbacks::KeyWrappingCbData keyData; + keyData.wrapper = &wrapper; + writer->AddKeyWrappingCallback(consumer->GetConsumerID(), SecureContentCallbacks::WriteKeyWrappingCbSample, &keyData); + + // Content Encryption process + SecureContentCallbacks::ContentEncryptionCbData contentData; + contentData.wrapper = &wrapper; + writer->SetContentEncryptionCallback(SecureContentCallbacks::WriteContentEncryptionCbSample, &contentData); + + // You'll need to complete the callback code for this call to work properly. + writer->WriteToFile("secureCube.3mf"); + + std::cout << "Writing Done." << std::endl; +} + +void ReadSecureContentExample(CWrapper & wrapper) { + PModel model = wrapper.CreateModel(); + + // After initializing the model, set the random number generation callback + // A default one will be used if you don't, current implementation uses std::mt19937 + model->SetRandomNumberCallback(SecureContentCallbacks::NotRandomBytesAtAll, nullptr); + + // Query the reader and setup the encryption before saving results + PReader reader = model->QueryReader("3mf"); + + // Setup Key Wrapping process + SecureContentCallbacks::KeyWrappingCbData keyData; + keyData.wrapper = &wrapper; + reader->AddKeyWrappingCallback("MyConsumerID", SecureContentCallbacks::ReadKeyWrappingCbSample, &keyData); + + // Content Encryption process + SecureContentCallbacks::ContentEncryptionCbData contentData; + contentData.wrapper = &wrapper; + reader->SetContentEncryptionCallback(SecureContentCallbacks::ReadContentEncryptionCbSample, &contentData); + + // You'll need to complete the callback code for this call to work properly. + reader->ReadFromFile("secureCube.3mf"); + + PKeyStore keystore = model->GetKeyStore(); + + // If you know the part you're interested in, look for its resource data + PPackagePart partPath = model->FindOrCreatePackagePart("/3D/securecube.model"); + PResourceData resourceData = keystore->FindResourceData(partPath.get()); + + // You can retrieve additional authenticated data using in the encryption + // to further verify the consistency of the process. At this time, it is + // already verified. + std::vector aad; + resourceData->GetAdditionalAuthenticationData(aad); + std::cout << "Additional Authenticated Data: "; + for (auto it = aad.begin(); it != aad.end(); ++it) { + std::cout << (char)*it; + } + std::cout << std::endl; + + std::cout << "Reading Done." << std::endl; + +} + +int main() { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF SecureContent example" << std::endl; + printVersion(*wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + try { + WriteSecureContentExample(*wrapper); + ReadSecureContentExample(*wrapper); + } catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/Slice.cpp b/SDK/CPackExamples/Cpp/Source/Slice.cpp new file mode 100644 index 000000000..5b8a3828e --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/Slice.cpp @@ -0,0 +1,191 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +Slice.cpp : 3MF slice extension example + +--*/ + +#include +#include +#include +double const_pi() { return std::atan(1) * 4; } +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and beams +sLib3MFPosition fnCreateVertex(float x, float y, float z) +{ + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFTriangle fnCreateTriangle(int v0, int v1, int v2) +{ + sLib3MFTriangle result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Indices[2] = v2; + return result; +} + + +void SliceExample() { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Slice example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Sliced Object [outbox]"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(12); + + float fSizeX = 100.0f; + float fSizeY = 100.0f; + float fSizeZ = 300.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(-fSizeX, -fSizeY, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, -fSizeY, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(-fSizeX, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(-fSizeX, -fSizeY, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, -fSizeY, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(-fSizeX, fSizeY, fSizeZ); + + // Manually create triangles + triangles[0] = fnCreateTriangle(2, 1, 0); + triangles[1] = fnCreateTriangle(0, 3, 2); + triangles[2] = fnCreateTriangle(4, 5, 6); + triangles[3] = fnCreateTriangle(6, 7, 4); + triangles[4] = fnCreateTriangle(0, 1, 5); + triangles[5] = fnCreateTriangle(5, 4, 0); + triangles[6] = fnCreateTriangle(2, 3, 7); + triangles[7] = fnCreateTriangle(7, 6, 2); + triangles[8] = fnCreateTriangle(1, 2, 6); + triangles[9] = fnCreateTriangle(6, 5, 1); + triangles[10] = fnCreateTriangle(3, 0, 4); + triangles[11] = fnCreateTriangle(4, 7, 3); + + // Set Geometry + meshObject->SetGeometry(vertices, triangles); + + + PSliceStack sliceStack = model->AddSliceStack(0.0); + + + + + + // Define an ellipse + Lib3MF_uint32 nSliceVertices = 20; + std::vector origSliceVertices(nSliceVertices); + for (Lib3MF_uint32 iSliceVertex = 0; iSliceVertex < nSliceVertices; iSliceVertex++) { + double angle = 2 * (const_pi()* iSliceVertex) / nSliceVertices; + origSliceVertices[iSliceVertex].m_Coordinates[0] = (Lib3MF_single)(fSizeX / 2 * std::cos(angle)); + origSliceVertices[iSliceVertex].m_Coordinates[1] = (Lib3MF_single)(fSizeY *std::sin(angle)); + } + + std::vector polygonIndices(nSliceVertices + 1); + for (Lib3MF_uint32 iPolygonIndex = 0; iPolygonIndex < polygonIndices.size(); iPolygonIndex++) { + polygonIndices[iPolygonIndex] = iPolygonIndex % nSliceVertices; + } + + Lib3MF_uint32 nSlices = 10; + + for (Lib3MF_uint32 iSlice = 0; iSlice < nSlices; iSlice++) { + PSlice slice = sliceStack->AddSlice((iSlice + 1.0)*fSizeZ / nSlices); + + // Rotate the ellpise as we move up z + double angle = 2 * (const_pi()*iSlice) / nSlices; + std::vector sliceVertices(nSliceVertices); + for (Lib3MF_uint32 iSliceVertex = 0; iSliceVertex < nSliceVertices; iSliceVertex++) { + double x = origSliceVertices[iSliceVertex].m_Coordinates[0]; + double y = origSliceVertices[iSliceVertex].m_Coordinates[1]; + sliceVertices[iSliceVertex].m_Coordinates[0] = (Lib3MF_single)(std::cos(angle)*x - std::sin(angle)*y); + sliceVertices[iSliceVertex].m_Coordinates[1] = (Lib3MF_single)(std::sin(angle)*x + std::cos(angle)*y); + } + + slice->SetVertices(sliceVertices); + slice->AddPolygon(polygonIndices); + } + + + // Assign this slice stack to mesh..., the exact geometry of the part + meshObject->AssignSliceStack(sliceStack.get()); + // which is not an exact representation of the sliced geometry + meshObject->SetSlicesMeshResolution(eSlicesMeshResolution::Lowres); + + + // Add build item + model->AddBuildItem(meshObject.get(), wrapper->GetIdentityTransform()); + + // Write file + PWriter writer = model->QueryWriter("3mf"); + writer->WriteToFile("slice.3mf"); + + std::cout << "done" << std::endl; +} + +int main() { + try { + SliceExample(); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/CPackExamples/Cpp/Source/TextureCube.cpp b/SDK/CPackExamples/Cpp/Source/TextureCube.cpp new file mode 100644 index 000000000..a8cd51d76 --- /dev/null +++ b/SDK/CPackExamples/Cpp/Source/TextureCube.cpp @@ -0,0 +1,205 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +TextureCube.cpp : 3MF Texture Cube creation example. + +--*/ + +#include +#include +#include + +#include "lib3mf_implicit.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +// Utility functions to create vertices and triangles +sLib3MFPosition fnCreateVertex(float x, float y, float z) +{ + sLib3MFPosition result; + result.m_Coordinates[0] = x; + result.m_Coordinates[1] = y; + result.m_Coordinates[2] = z; + return result; +} + +sLib3MFTriangle fnCreateTriangle(int v0, int v1, int v2) +{ + sLib3MFTriangle result; + result.m_Indices[0] = v0; + result.m_Indices[1] = v1; + result.m_Indices[2] = v2; + return result; +} + +PTexture2DGroup fnLoadModelTexture(PModel model, const std::string sOPCPath, const std::string sFilePath, eLib3MFTextureType eType, eLib3MFTextureTileStyle eTileStyleU, eLib3MFTextureTileStyle eTileStyleV) +{ + std::string sRelationshipType_Texture = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; + PAttachment attachment = model->AddAttachment(sOPCPath, sRelationshipType_Texture); + attachment->ReadFromFile(sFilePath); + + PTexture2D texture2D = model->AddTexture2DFromAttachment(attachment.get()); + + texture2D->SetContentType(eType); + texture2D->SetTileStyleUV(eTileStyleU, eTileStyleV); + + PTexture2DGroup textureGroup = model->AddTexture2DGroup(texture2D.get()); + return textureGroup; +} + + +sLib3MFTriangleProperties fnCreateTexture(PTexture2DGroup textureGroup, double u1, double v1, double u2, double v2, double u3, double v3) +{ + sLib3MFTriangleProperties property; + property.m_ResourceID = textureGroup->GetResourceID(); + property.m_PropertyIDs[0] = textureGroup->AddTex2Coord(sLib3MFTex2Coord({ u1, v1 })); + property.m_PropertyIDs[1] = textureGroup->AddTex2Coord(sLib3MFTex2Coord({ u2, v2 })); + property.m_PropertyIDs[2] = textureGroup->AddTex2Coord(sLib3MFTex2Coord({ u3, v3 })); + return property; +} + +void TextureExample() { + PWrapper wrapper = CWrapper::loadLibrary(); + + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Texture Cube example" << std::endl; + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + PMeshObject meshObject = model->AddMeshObject(); + meshObject->SetName("Textured Box"); + + // Create mesh structure of a cube + std::vector vertices(8); + std::vector triangles(12); + + float fSizeX = 100.0f; + float fSizeY = 100.0f; + float fSizeZ = 100.0f; + + // Manually create vertices + vertices[0] = fnCreateVertex(0.0f, 0.0f, 0.0f); + vertices[1] = fnCreateVertex(fSizeX, 0.0f, 0.0f); + vertices[2] = fnCreateVertex(fSizeX, fSizeY, 0.0f); + vertices[3] = fnCreateVertex(0.0f, fSizeY, 0.0f); + vertices[4] = fnCreateVertex(0.0f, 0.0f, fSizeZ); + vertices[5] = fnCreateVertex(fSizeX, 0.0f, fSizeZ); + vertices[6] = fnCreateVertex(fSizeX, fSizeY, fSizeZ); + vertices[7] = fnCreateVertex(0.0f, fSizeY, fSizeZ); + + // Manually create triangles + triangles[0] = fnCreateTriangle(2, 1, 0); + triangles[1] = fnCreateTriangle(0, 3, 2); + triangles[2] = fnCreateTriangle(4, 5, 6); + triangles[3] = fnCreateTriangle(6, 7, 4); + triangles[4] = fnCreateTriangle(0, 1, 5); + triangles[5] = fnCreateTriangle(5, 4, 0); + triangles[6] = fnCreateTriangle(2, 3, 7); + triangles[7] = fnCreateTriangle(7, 6, 2); + triangles[8] = fnCreateTriangle(1, 2, 6); + triangles[9] = fnCreateTriangle(6, 5, 1); + triangles[10] = fnCreateTriangle(3, 0, 4); + triangles[11] = fnCreateTriangle(4, 7, 3); + + meshObject->SetGeometry(vertices, triangles); + + std::string sTextureFolder = TEXTURESPATH; + // add textures to 3mf package + std::cout << "sTextureFolder=\"" << sTextureFolder << "\"\n"; + auto textureGroup1 = fnLoadModelTexture(model, "/3D/Textures/tex1.png", sTextureFolder + "tex1.png", eTextureType::PNG, eTextureTileStyle::Wrap, eTextureTileStyle::Wrap); + auto textureGroup2 = fnLoadModelTexture(model, "/3D/Textures/tex2.png", sTextureFolder + "tex2.png", eTextureType::PNG, eTextureTileStyle::Mirror, eTextureTileStyle::Wrap); + auto textureGroup3 = fnLoadModelTexture(model, "/3D/Textures/tex3.png", sTextureFolder + "tex3.png", eTextureType::PNG, eTextureTileStyle::Wrap, eTextureTileStyle::Mirror); + auto textureGroup4 = fnLoadModelTexture(model, "/3D/Textures/tex4.png", sTextureFolder + "tex4.png", eTextureType::PNG, eTextureTileStyle::Clamp, eTextureTileStyle::Wrap); + auto textureGroup5 = fnLoadModelTexture(model, "/3D/Textures/tex5.png", sTextureFolder + "tex5.png", eTextureType::PNG, eTextureTileStyle::Wrap, eTextureTileStyle::Clamp); + auto textureGroup6 = fnLoadModelTexture(model, "/3D/Textures/tex6.png", sTextureFolder + "tex6.png", eTextureType::PNG, eTextureTileStyle::Clamp, eTextureTileStyle::Mirror); + + // Side 1 + meshObject->SetTriangleProperties(0, fnCreateTexture(textureGroup1, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0)); + meshObject->SetTriangleProperties(1, fnCreateTexture(textureGroup1, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0)); + + // Side 2 + meshObject->SetTriangleProperties(2, fnCreateTexture(textureGroup2, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0)); + meshObject->SetTriangleProperties(3, fnCreateTexture(textureGroup2, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0)); + + // Side 3 + // Go outside of bounds on this side + meshObject->SetTriangleProperties(4, fnCreateTexture(textureGroup3, -1.0, -1.0, 2.0, -1.0, 2.0, 2.0)); + meshObject->SetTriangleProperties(5, fnCreateTexture(textureGroup3, 2.0, 2.0, -1.0, 2.0, -1.0, -1.0)); + + // Side 4 + meshObject->SetTriangleProperties(6, fnCreateTexture(textureGroup4, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0)); + meshObject->SetTriangleProperties(7, fnCreateTexture(textureGroup4, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0)); + + // Side 5 + meshObject->SetTriangleProperties(8, fnCreateTexture(textureGroup5, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0)); + meshObject->SetTriangleProperties(9, fnCreateTexture(textureGroup5, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0)); + + // Side 6 + meshObject->SetTriangleProperties(10, fnCreateTexture(textureGroup6, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0)); + meshObject->SetTriangleProperties(11, fnCreateTexture(textureGroup6, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0)); + + // Object Level Property + meshObject->SetObjectLevelProperty(textureGroup1->GetResourceID(), 1); + + // Add build item + model->AddBuildItem(meshObject.get(), wrapper->GetIdentityTransform()); + + PWriter writer = model->QueryWriter("3mf"); + writer->WriteToFile("texturegroup.3mf"); + + std::cout << "done" << std::endl; +} + +int main() { + try { + TextureExample(); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} + + diff --git a/SDK/CPackExamples/CppDynamic/CMakeLists.txt b/SDK/CPackExamples/CppDynamic/CMakeLists.txt new file mode 100644 index 000000000..4fee96e92 --- /dev/null +++ b/SDK/CPackExamples/CppDynamic/CMakeLists.txt @@ -0,0 +1,75 @@ +#[[++ + +Copyright (C) 2019 3MF Consortium (Original Author) + +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. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.5.0-develop3. + +Abstract: This is an autogenerated CMake Project that demonstrates the + usage of the Dynamic C++ bindings of the 3MF Library + +Interface version: 2.2.0 + + +]] + +cmake_minimum_required(VERSION 3.5) + +project(Example_ExtractInfo) +set(CMAKE_CXX_STANDARD 11) + +# Determine the platform and set lib3mf_DIR accordingly +if(WIN32) + # Path for Windows + set(lib3mf_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib3mf-2.3.1-Windows/lib/cmake/lib3mf") + find_package(lib3mf REQUIRED COMPONENTS CppDynamic) +elseif(APPLE) + # Path for macOS (Darwin) + set(lib3mf_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib3mf-2.3.1-Darwin/lib/cmake/lib3mf") + find_package(lib3mf REQUIRED COMPONENTS CppDynamic) +else() + # Path for Linux (Here we check twice to test for Debian / RPM packages properly) + find_package(lib3mf QUIET COMPONENTS CppDynamic) + # Check if the package was not found + if(NOT lib3mf_FOUND) + # lib3mf not found, so set lib3mf_DIR to the fallback directory + set(lib3mf_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../lib3mf-2.3.1-Linux/lib/cmake/lib3mf") + # Find package (lib3mf) + find_package(lib3mf REQUIRED COMPONENTS CppDynamic) + endif() +endif() + +add_executable(Example_ExtractInfo "${CMAKE_CURRENT_SOURCE_DIR}/Source/ExtractInfo.cpp") + +# In case of CDynamic and CppDynamic, the lib3mf::lib3mf is simply an alias to an interface +target_link_libraries(Example_ExtractInfo lib3mf::lib3mf ${CMAKE_DL_LIBS}) + +if (${MSVC}) + IF(${CMAKE_VERSION} VERSION_LESS 3.6.3) + MESSAGE ("Note: You need to manually select a StartUp-project in Visual Studio.") + ELSE() + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Example_ExtractInfo) + ENDIF() +endif() + diff --git a/SDK/CPackExamples/CppDynamic/GenerateMake.sh b/SDK/CPackExamples/CppDynamic/GenerateMake.sh new file mode 100644 index 000000000..16fe9cf2b --- /dev/null +++ b/SDK/CPackExamples/CppDynamic/GenerateMake.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +basepath="$(cd "$(dirname "$0")" && pwd)" +builddir="$basepath/build" +mkdir -p $builddir +cd $builddir +cmake .. -G "Unix Makefiles" "$@" \ No newline at end of file diff --git a/SDK/CPackExamples/CppDynamic/GenerateVS2015.bat b/SDK/CPackExamples/CppDynamic/GenerateVS2015.bat new file mode 100644 index 000000000..e6933cdeb --- /dev/null +++ b/SDK/CPackExamples/CppDynamic/GenerateVS2015.bat @@ -0,0 +1,10 @@ +@echo off +set startingDir=%CD% + +set basepath=%~dp0 +set builddir=%basepath%\build +if not exist %builddir% (mkdir %builddir%) +cd %builddir% +cmake -G "Visual Studio 14 2015 Win64" .. %* + +cd %startingDir% diff --git a/SDK/CPackExamples/CppDynamic/GenerateVS2017.bat b/SDK/CPackExamples/CppDynamic/GenerateVS2017.bat new file mode 100644 index 000000000..4014d2f99 --- /dev/null +++ b/SDK/CPackExamples/CppDynamic/GenerateVS2017.bat @@ -0,0 +1,10 @@ +@echo off +set startingDir=%CD% + +set basepath=%~dp0 +set builddir=%basepath%\build +if not exist %builddir% (mkdir %builddir%) +cd %builddir% +cmake -G "Visual Studio 15 2017 Win64" .. %* + +cd %startingDir% diff --git a/SDK/CPackExamples/CppDynamic/GenerateVS2019.bat b/SDK/CPackExamples/CppDynamic/GenerateVS2019.bat new file mode 100644 index 000000000..efa4f308f --- /dev/null +++ b/SDK/CPackExamples/CppDynamic/GenerateVS2019.bat @@ -0,0 +1,10 @@ +@echo off +set startingDir=%CD% + +set basepath=%~dp0 +set builddir=%basepath%\build +if not exist %builddir% (mkdir %builddir%) +cd %builddir% +cmake -G "Visual Studio 16 2019" .. %* + +cd %startingDir% diff --git a/SDK/CPackExamples/CppDynamic/Source/ExtractInfo.cpp b/SDK/CPackExamples/CppDynamic/Source/ExtractInfo.cpp new file mode 100644 index 000000000..0a96b29f4 --- /dev/null +++ b/SDK/CPackExamples/CppDynamic/Source/ExtractInfo.cpp @@ -0,0 +1,271 @@ +/*++ + +Copyright (C) 2019 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* 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 MICROSOFT AND/OR NETFABB 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: + +ExtractInfo.cpp : 3MF Read Example + +--*/ + +#include +#include +#include + +#include "lib3mf_dynamic.hpp" + +using namespace Lib3MF; + + +void printVersion(PWrapper wrapper) { + Lib3MF_uint32 nMajor, nMinor, nMicro; + wrapper->GetLibraryVersion(nMajor, nMinor, nMicro); + std::cout << "lib3mf version = " << nMajor << "." << nMinor << "." << nMicro; + std::string sReleaseInfo, sBuildInfo; + if (wrapper->GetPrereleaseInformation(sReleaseInfo)) { + std::cout << "-" << sReleaseInfo; + } + if (wrapper->GetBuildInformation(sBuildInfo)) { + std::cout << "+" << sBuildInfo; + } + std::cout << std::endl; +} + +void ShowThumbnailInformation(PModel model) +{ + /* + // TODO: this is not yet implemented in lib3mf + */ +} + + +void ShowMetaDataInformation(PMetaDataGroup metaDataGroup) +{ + Lib3MF_uint32 nMetaDataCount = metaDataGroup->GetMetaDataCount(); + + for (Lib3MF_uint32 iMeta = 0; iMeta < nMetaDataCount; iMeta++) { + + PMetaData metaData = metaDataGroup->GetMetaData(iMeta); + std::string sMetaDataValue = metaData->GetValue(); + std::string sMetaDataName = metaData->GetName(); + std::cout << "Metadatum: " << iMeta << ":" << std::endl; + std::cout << "Name = \"" << sMetaDataName << "\"" << std::endl; + std::cout << "Value = \"" << sMetaDataValue << "\"" << std::endl; + } +} + +void ShowSliceStack(PSliceStack sliceStack, std::string indent) +{ + std::cout << indent << "SliceStackID: " << sliceStack->GetResourceID() << std::endl; + if (sliceStack->GetSliceCount() > 0) { + std::cout << indent << " Slice count: " << sliceStack->GetSliceCount() << std::endl; + } + if (sliceStack->GetSliceRefCount() > 0) { + std::cout << indent << " Slice ref count: " << sliceStack->GetSliceRefCount() << std::endl; + for (Lib3MF_uint64 iSliceRef = 0; iSliceRef < sliceStack->GetSliceRefCount(); iSliceRef++) { + std::cout << indent << " Slice ref : " << sliceStack->GetSliceStackReference(iSliceRef)->GetResourceID() << std::endl; + } + } +} + +void ShowObjectProperties(PObject object) +{ + std::cout << " Name: \"" << object->GetName() << "\"" << std::endl; + std::cout << " PartNumber: \"" << object->GetPartNumber() << "\"" << std::endl; + + switch (object->GetType()) { + case eObjectType::Model: + std::cout << " Object type: model" << std::endl; + break; + case eObjectType::Support: + std::cout << " Object type: support" << std::endl; + break; + case eObjectType::SolidSupport: + std::cout << " Object type: solidsupport" << std::endl; + break; + case eObjectType::Other: + std::cout << " Object type: other" << std::endl; + break; + default: + std::cout << " Object type: invalid" << std::endl; + break; + } + + if (object->HasSlices(false)) { + PSliceStack sliceStack = object->GetSliceStack(); + ShowSliceStack(sliceStack, " "); + } + + if (object->GetMetaDataGroup()->GetMetaDataCount() > 0) { + ShowMetaDataInformation(object->GetMetaDataGroup()); + } +} + +void ShowMeshObjectInformation(PMeshObject meshObject) +{ + std::cout << "mesh object #" << meshObject->GetResourceID() << ": " << std::endl; + + ShowObjectProperties(meshObject); + + Lib3MF_uint64 nVertexCount = meshObject->GetVertexCount(); + Lib3MF_uint64 nTriangleCount = meshObject->GetTriangleCount(); + PBeamLattice beamLattice = meshObject->BeamLattice(); + + // Output data + std::cout << " Vertex count: " << nVertexCount << std::endl; + std::cout << " Triangle count: " << nTriangleCount << std::endl; + + Lib3MF_uint64 nBeamCount = beamLattice->GetBeamCount(); + if (nBeamCount > 0) { + std::cout << " Beam count: " << nBeamCount << std::endl; + Lib3MF_uint32 nRepresentationMesh; + if (beamLattice->GetRepresentation(nRepresentationMesh)) + std::cout << " |_Representation Mesh ID: " << nRepresentationMesh << std::endl; + eLib3MFBeamLatticeClipMode eClipMode; + Lib3MF_uint32 nClippingMesh; + beamLattice->GetClipping(eClipMode, nClippingMesh); + if (eClipMode != eBeamLatticeClipMode::NoClipMode) + std::cout << " |_Clipping Mesh ID: " << nClippingMesh << "(mode=" << (int)eClipMode << ")" << std::endl; + if (beamLattice->GetBeamSetCount() > 0) { + std::cout << " |_BeamSet count: " << beamLattice->GetBeamSetCount() << std::endl; + } + } + +} + +void ShowTransform(sLib3MFTransform transform, std::string indent) { + std::cout << indent << "Transformation: [ " << transform.m_Fields[0][0] << " " << transform.m_Fields[1][0] << " " << transform.m_Fields[2][0] << " " << transform.m_Fields[3][0] << " ]" << std::endl; + std::cout << indent << " [ " << transform.m_Fields[0][1] << " " << transform.m_Fields[1][1] << " " << transform.m_Fields[2][1] << " " << transform.m_Fields[3][1] << " ]" << std::endl; + std::cout << indent << " [ " << transform.m_Fields[0][2] << " " << transform.m_Fields[1][2] << " " << transform.m_Fields[2][2] << " " << transform.m_Fields[3][2] << " ]" << std::endl; +} + +void ShowComponentsObjectInformation(PComponentsObject componentsObject) +{ + std::cout << "components object #" << componentsObject->GetResourceID() << ": " << std::endl; + + ShowObjectProperties(componentsObject); + std::cout << " Component count: " << componentsObject->GetComponentCount() << std::endl; + for (Lib3MF_uint32 nIndex = 0; nIndex < componentsObject->GetComponentCount(); nIndex++) { + PComponent component = componentsObject->GetComponent(nIndex); + + std::cout << " Component " << nIndex << ": Object ID: " << component->GetObjectResourceID() << std::endl; + if (component->HasTransform()) { + ShowTransform(component->GetTransform(), " "); + } + else { + std::cout << " Transformation: none" << std::endl; + } + } +} + + +void ExtractInfoExample(std::string sFileName) { + std::cout << "------------------------------------------------------------------" << std::endl; + std::cout << "3MF Read example" << std::endl; + + std::string libpath = ("."); // TODO: put the location of the Lib3MF-library file here. + auto wrapper = Lib3MF::CWrapper::loadLibrary(LIB3MF_LIBRARY_LOCATION); // TODO: add correct suffix of the library + + printVersion(wrapper); + std::cout << "------------------------------------------------------------------" << std::endl; + + PModel model = wrapper->CreateModel(); + + // Import Model from 3MF File + { + PReader reader = model->QueryReader("3mf"); + // And deactivate the strict mode (default is "false", anyway. This just demonstrates where/how to use it). + reader->SetStrictModeActive(false); + reader->ReadFromFile(sFileName); + + for (Lib3MF_uint32 iWarning = 0; iWarning < reader->GetWarningCount(); iWarning++) { + Lib3MF_uint32 nErrorCode; + std::string sWarningMessage = reader->GetWarning(iWarning, nErrorCode); + std::cout << "Encountered warning #" << nErrorCode << " : " << sWarningMessage << std::endl; + } + } + ShowThumbnailInformation(model); + + ShowMetaDataInformation(model->GetMetaDataGroup()); + + PSliceStackIterator sliceStacks = model->GetSliceStacks(); + while (sliceStacks->MoveNext()) { + PSliceStack sliceStack = sliceStacks->GetCurrentSliceStack(); + ShowSliceStack(sliceStack, ""); + } + + PObjectIterator objectIterator = model->GetObjects(); + while (objectIterator->MoveNext()) { + PObject object = objectIterator->GetCurrentObject(); + if (object->IsMeshObject()) { + ShowMeshObjectInformation(model->GetMeshObjectByID(object->GetResourceID())); + } + else if (object->IsComponentsObject()) { + ShowComponentsObjectInformation(model->GetComponentsObjectByID(object->GetResourceID())); + } + else { + std::cout << "unknown object #" << object->GetResourceID() << ": " << std::endl; + } + } + + + PBuildItemIterator buildItemIterator = model->GetBuildItems(); + while (buildItemIterator->MoveNext()) { + PBuildItem buildItem = buildItemIterator->GetCurrent(); + + std::cout << "Build item (Object #" << buildItem->GetObjectResourceID() << "): " << std::endl; + + if (buildItem->HasObjectTransform()) { + ShowTransform(buildItem->GetObjectTransform(), " "); + } + else { + std::cout << " Transformation: none" << std::endl; + } + std::cout << " Part number: \"" << buildItem->GetPartNumber() << "\"" << std::endl; + if (buildItem->GetMetaDataGroup()->GetMetaDataCount() > 0) { + ShowMetaDataInformation(buildItem->GetMetaDataGroup()); + } + } + + std::cout << "done" << std::endl; +} + + +int main(int argc, char** argv) { + // Parse Arguments + if (argc != 2) { + std::cout << "Usage: " << std::endl; + std::cout << "ExtractInfo.exe model.3mf" << std::endl; + return 0; + } + + try { + ExtractInfoExample(argv[1]); + } + catch (ELib3MFException &e) { + std::cout << e.what() << std::endl; + return e.getErrorCode(); + } + return 0; +} diff --git a/SDK/GenerateSDK_github.sh b/SDK/GenerateSDK_github.sh index 6a59330ea..00a0d6236 100644 --- a/SDK/GenerateSDK_github.sh +++ b/SDK/GenerateSDK_github.sh @@ -43,4 +43,4 @@ echo "GITRevision = "`git rev-parse HEAD` >> $VERSIONTXT echo Zip SDK artifacts cd $SDKARTIFACT -zip -r ../$OUTFILE ./* || failed "Error zipping SDK" +zip -r ../$OUTFILE ./* || failed "Error zipping SDK" \ No newline at end of file diff --git a/cmake/lib3mfConfig.cmake b/cmake/lib3mfConfig.cmake new file mode 100644 index 000000000..f1305b8f5 --- /dev/null +++ b/cmake/lib3mfConfig.cmake @@ -0,0 +1,104 @@ +# lib3mfConfig.cmake + +if(VCPKG_TOOLCHAIN) + message("Lib3MF - VCPKG Tool Chain") + set(LIB3MF_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") +else() + message("Lib3MF - General CMake Tool Chain") + set(LIB3MF_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../..") +endif() + + +# Initial setup for known components and default selection +set(lib3mf_known_components "C" "CDynamic" "Cpp" "CppDynamic") +set(lib3mf_selected_variant "Cpp") # Default variant + +# Check if any known component was specified and select it +foreach(comp ${lib3mf_FIND_COMPONENTS}) + if(comp IN_LIST lib3mf_known_components) + set(lib3mf_selected_variant ${comp}) + break() # Use the first specified known component + endif() +endforeach() + +# Configure paths based on the selected variant +set(lib3mf_INCLUDE_DIR "${LIB3MF_ROOT_DIR}/include/Bindings/${lib3mf_selected_variant}") +set(lib3mf_LIBRARY_DIR "${LIB3MF_ROOT_DIR}/lib") +set(lib3mf_BINARY_DIR "${LIB3MF_ROOT_DIR}/bin") + +# Adjust library file name based on platform +if(WIN32) + set(lib3mf_LIBRARY "${lib3mf_BINARY_DIR}/lib3mf.dll") + set(lib3mf_LIBRARY_IMPORT "${lib3mf_LIBRARY_DIR}/lib3mf.lib") # For importing symbols +elseif(APPLE) + set(lib3mf_LIBRARY "${lib3mf_LIBRARY_DIR}/lib3mf.dylib") +else() # Linux and others + set(lib3mf_LIBRARY "${lib3mf_LIBRARY_DIR}/lib3mf.so") +endif() + +# Print the chosen variant +message("***********************************") +message("LIB3MF Chosen Variant : " ${lib3mf_selected_variant}) +message("***********************************") + +# Create a special interface for dynamic loading scenarios +if("${lib3mf_selected_variant}" STREQUAL "CppDynamic" OR "${lib3mf_selected_variant}" STREQUAL "CDynamic") + if("${lib3mf_selected_variant}" STREQUAL "CDynamic") + message("*****************************************************************************") + message("") + message(" For CDynamic Variant, an additional source called lib3mf_dynamic.cc ") + message(" is required ") + message(" It is made available using the variable LIB3MF_CDYNAMIC_ADDITIONAL_SOURCE ") + message(" You must append this to your sources ") + message("") + message("*****************************************************************************") + set(LIB3MF_CDYNAMIC_ADDITIONAL_SOURCE "${LIB3MF_ROOT_DIR}/include/Bindings/CDynamic/lib3mf_dynamic.cc") + endif() + add_library(lib3mfdynamic INTERFACE) + # Now alias lib3mfdynamic to include the namespace + add_library(lib3mf::lib3mf ALIAS lib3mfdynamic) + # Set properties and compile definitions for lib3mfdynamic + target_compile_definitions(lib3mfdynamic INTERFACE + "LIB3MF_LIBRARY_LOCATION=\"${lib3mf_LIBRARY}\"" + ) + target_include_directories(lib3mfdynamic INTERFACE "${lib3mf_INCLUDE_DIR}") +else() + # Define the imported target for static linking + add_library(lib3mf::lib3mf SHARED IMPORTED) + set_target_properties(lib3mf::lib3mf PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${lib3mf_INCLUDE_DIR}" + IMPORTED_LOCATION "${lib3mf_LIBRARY}" + ) + if(WIN32) + set_property(TARGET lib3mf::lib3mf PROPERTY IMPORTED_IMPLIB "${lib3mf_LIBRARY_IMPORT}") + endif() + + # Define a custom function to handle library copying + function(copy_lib3mf_libraries target) + if(TARGET ${target}) + if(APPLE) + # On macOS, copy .dylib files, preserving symlinks only if they don't already exist in the target directory + file(GLOB LIB3MF_FILES "${lib3mf_LIBRARY_DIR}/lib3mf.dylib*") + foreach(file ${LIB3MF_FILES}) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${file}" "$/$(basename ${file})" + COMMENT "Copying $(basename ${file}) to target directory on macOS if it is different") + endforeach() + + + elseif(UNIX) + # On Unix-like systems (excluding macOS), copy .so files, preserving symlinks + add_custom_command(TARGET ${target} POST_BUILD + COMMAND sh -c "cp -P '${lib3mf_LIBRARY_DIR}/lib3mf.so'* '$'" + COMMENT "Copying all lib3mf.so* files to target directory on Linux") + else() + # On Windows, directly copy the .dll file without worrying about symlinks + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${lib3mf_LIBRARY}" "$" + COMMENT "Copying lib3mf.dll to target directory on Windows") + endif() + else() + message(WARNING "Target '${target}' not found. lib3mf library was not copied.") + endif() + endfunction() +endif()