mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-08 17:01:40 +00:00
Compare commits
343 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f2f1954f3 | |||
| b4323ba67a | |||
| b79070456f | |||
| 5cd4dbf603 | |||
| 2c18aebdcc | |||
| cb0a70ac7e | |||
| cf3f3c2c00 | |||
| 7029feca0a | |||
| f75526e6c3 | |||
| 6d4fe17975 | |||
| c1b4b5c1d0 | |||
| 994fa4bc03 | |||
| 3e066443be | |||
| d475553cd0 | |||
| 65e40b9923 | |||
| 29fdf629f9 | |||
| d9eb67b7a8 | |||
| bae1eddda9 | |||
| ad62dd6d91 | |||
| d565dc3ff3 | |||
| a340e6cf9a | |||
| 359693c562 | |||
| 016c278748 | |||
| f2b655fcf6 | |||
| 0bb6df6a6b | |||
| f07afda09c | |||
| 5b2721c1c4 | |||
| 0d5fa0c20c | |||
| 802ddf8ff9 | |||
| e20cea221b | |||
| ae66550cae | |||
| 5ccb655e10 | |||
| 278c1e5068 | |||
| 095e4a1677 | |||
| de5eb9641f | |||
| 4597be7fda | |||
| ca2e866c45 | |||
| e84767c22b | |||
| f7add7f412 | |||
| 8ffdb8f0b9 | |||
| e0e143bab5 | |||
| 8f3eaa35d7 | |||
| 81b1efdfd6 | |||
| 4a9af13626 | |||
| e9d577890e | |||
| 7f29850022 | |||
| 92c3b4c308 | |||
| 600d3a3460 | |||
| bea4e38e65 | |||
| 5386c9ec69 | |||
| ecf0a5a4e1 | |||
| 540f3acf52 | |||
| b3d29c4081 | |||
| 0d3849737c | |||
| a2db0a68a4 | |||
| 3e2869b6aa | |||
| db1a2705a3 | |||
| e7787b6aad | |||
| 6f0327d4b5 | |||
| 6341d7dd31 | |||
| 51971dd493 | |||
| 309e20009c | |||
| a06e5ecdf5 | |||
| 1ca2e12f92 | |||
| b9e5739088 | |||
| 30cdffc27a | |||
| 3607fbb762 | |||
| b73cdd1a0e | |||
| e21bd1ac96 | |||
| 633c613da2 | |||
| 8282f691e8 | |||
| 7bc64bef1a | |||
| 81a7c5b50e | |||
| 8f7c8dc1d2 | |||
| eea2398e07 | |||
| 35be7f4190 | |||
| 4f929e90a7 | |||
| f898089b27 | |||
| 4e4553201b | |||
| 29f5132510 | |||
| ae938d425b | |||
| 5161b0a3f8 | |||
| 3015f758ba | |||
| dbe68b48f2 | |||
| 094d96c735 | |||
| ed6e8d831e | |||
| 930678e3d2 | |||
| 4e3cc63de1 | |||
| 7b64dd338b | |||
| 71612e0780 | |||
| 9aae8f0329 | |||
| 175efcfffe | |||
| 643a2e1c1f | |||
| 3c5c8e44cb | |||
| fc89ffeb19 | |||
| 4bedb40d8a | |||
| a34c36d22e | |||
| e5ed7b53d4 | |||
| 069ef20c46 | |||
| 0b122ae1f5 | |||
| a0cc4b874b | |||
| 2f88778c9f | |||
| 0a752e09eb | |||
| 03662d2016 | |||
| 99f409280a | |||
| 0c9d3ea6d7 | |||
| 37fa4e9076 | |||
| 1ec273f508 | |||
| 574bcfa6c5 | |||
| 09ffdd2028 | |||
| d0c5bd5e85 | |||
| c03a7fe117 | |||
| 8596d53a4a | |||
| c78e16eafb | |||
| 68fea7bd45 | |||
| 90366f8236 | |||
| d3c21d7a81 | |||
| cd77c557e9 | |||
| 3110c98429 | |||
| 0e2a62d3b7 | |||
| f532624dc1 | |||
| f0e3056d0a | |||
| 298eca0ab6 | |||
| 1f17d73f3e | |||
| ababb49ae7 | |||
| 07a5c59359 | |||
| 8c8ef5c32b | |||
| 36fa53122a | |||
| 1dace18d26 | |||
| 5b4c1089cf | |||
| 83a3131276 | |||
| 6f6bebf387 | |||
| bfac12dbe7 | |||
| d33055bb72 | |||
| d08a4e0706 | |||
| 06eca938d7 | |||
| 543d9f8588 | |||
| b40e4378b3 | |||
| 1089de14f0 | |||
| 1b35533917 | |||
| c42a7f32d0 | |||
| 14e9846e35 | |||
| 37c6aea092 | |||
| 9b13dc8dd6 | |||
| 89c66f62bf | |||
| 18363c0d19 | |||
| 782aa4f88f | |||
| 13c71d7a8a | |||
| cb7e7331e6 | |||
| 4850e2a07c | |||
| fd980abcc2 | |||
| b022ca7e70 | |||
| 325db2e57f | |||
| 046ceb452a | |||
| 9e19735275 | |||
| 6ee05b646b | |||
| e62921171c | |||
| 6c47123781 | |||
| d448fa95e3 | |||
| 57de09d236 | |||
| a25f522cc3 | |||
| e067c2b834 | |||
| e2905d6f0e | |||
| ead061a98a | |||
| fae85e63cc | |||
| bccea6d932 | |||
| 1cc93784cd | |||
| f1e93581ec | |||
| 91ae631c7d | |||
| b599ee1040 | |||
| d2cc66fdf0 | |||
| 3824e7b449 | |||
| aa10784154 | |||
| 2018bcb3c1 | |||
| 11006f0ef5 | |||
| a44f72287b | |||
| c6849073d9 | |||
| e6258ab4a8 | |||
| 5db65f4041 | |||
| 89ce32fdfd | |||
| 763a31a31d | |||
| 4b8c612647 | |||
| 16723d11ca | |||
| 67238c4ac1 | |||
| 20229aa88c | |||
| 7788ae86c9 | |||
| 002b7c5d6e | |||
| e7db239647 | |||
| 158756e921 | |||
| a3efa7e9ee | |||
| 4896946e78 | |||
| 588f365366 | |||
| fa8770c14d | |||
| 0cf831133e | |||
| 98338e0831 | |||
| 5a3160eb62 | |||
| 80a9df8f52 | |||
| 8eb0060d4a | |||
| b2fc0c00cc | |||
| 51a338e4c8 | |||
| 2d5883f966 | |||
| 7169dcd053 | |||
| b9d4ea38b0 | |||
| c8f46cfb69 | |||
| 6242251d3c | |||
| 1689960257 | |||
| c509f1d3ee | |||
| 217bfed847 | |||
| 4621fc9269 | |||
| 1840e3d1a8 | |||
| becd38c77d | |||
| 7d6c6bd523 | |||
| b4558f7f75 | |||
| ebd5fe854f | |||
| 06cfd0931c | |||
| 90bd7d26ac | |||
| 3fbf01f446 | |||
| 26a1955cba | |||
| 27d9cae8ff | |||
| a425bb6da0 | |||
| a19d398c78 | |||
| a751c1cce3 | |||
| 9d4307d343 | |||
| ab7d9fdf4a | |||
| 5436467fc5 | |||
| 1c7e134d78 | |||
| 97ee4eaef0 | |||
| 30517770e0 | |||
| 5b1ec85f75 | |||
| 12123baafe | |||
| 0a1c54cedc | |||
| 12144f76d3 | |||
| ca6e950496 | |||
| c478da5257 | |||
| c28251a1b1 | |||
| 9bc0588d21 | |||
| 918cc65a0d | |||
| 7350ccd25e | |||
| c1f146966c | |||
| 24acfeaf45 | |||
| 5d748c5b04 | |||
| 5bcf8685ab | |||
| 8f64f75abb | |||
| 434ea74f22 | |||
| fbf2946b69 | |||
| 7af0ec13e5 | |||
| 29e5e0fd1a | |||
| d7de4f8df0 | |||
| 252bd6f4f6 | |||
| 3b28c92414 | |||
| fcbec74521 | |||
| 0f7f960b29 | |||
| b7fc5daf82 | |||
| 1d7a8ca249 | |||
| eaf7950677 | |||
| 95851ff94c | |||
| 3a6881ca17 | |||
| c5210a4f82 | |||
| e3cadab406 | |||
| d5b551874d | |||
| be3aef59fa | |||
| bcc631bf5f | |||
| 6c18ac9605 | |||
| 9d42f4c1ee | |||
| fdb2947345 | |||
| 5e784e4175 | |||
| fb81c88a5f | |||
| c014ce2b46 | |||
| ff65719074 | |||
| 3491e4c91b | |||
| 9f3adc9154 | |||
| f6c6203408 | |||
| 39a376df95 | |||
| 70a4fdbf95 | |||
| 0a41d2e016 | |||
| 0b5eab8549 | |||
| 6f9d54c2af | |||
| 9ea302cdf1 | |||
| c057e79f17 | |||
| f616c4049c | |||
| 20ccf2b455 | |||
| a07f56db6f | |||
| 724cdb446e | |||
| 95b3f7dac4 | |||
| 603cedc487 | |||
| fe6e9d86ae | |||
| 8e42f35db3 | |||
| 9d69a712ac | |||
| dc127471c5 | |||
| 95a094d596 | |||
| 4ec643d4e1 | |||
| c089c11cd6 | |||
| 8afba9a665 | |||
| 5c4046e054 | |||
| d82e1da907 | |||
| 8c2e1226c9 | |||
| ed8821486d | |||
| 742bfac144 | |||
| 1f4c4204d0 | |||
| 43f78605f2 | |||
| 6cd1375cf9 | |||
| 2d1250c18f | |||
| bee61cba11 | |||
| 33f072e0a7 | |||
| 490fa92dc0 | |||
| cf3393b6a0 | |||
| fbbc92c5d7 | |||
| 574827016a | |||
| 18c9140700 | |||
| 9e165319e6 | |||
| 64bf807baa | |||
| 136befde86 | |||
| 1a4d750ecd | |||
| 0a5dd78774 | |||
| 94427ebcb3 | |||
| 9147938c36 | |||
| ffcc02fb99 | |||
| e4e4f31d20 | |||
| 2dd002bcd8 | |||
| 7e5248da29 | |||
| 07a01bcf54 | |||
| 0a9e29ce45 | |||
| 0604db06e9 | |||
| 991556fbe3 | |||
| b66b04b001 | |||
| 5c9213426f | |||
| 69a4ad35a1 | |||
| 7f4d9e31c3 | |||
| ae79996eb6 | |||
| f5b2358c2e | |||
| f9c791de1b | |||
| 5b85a2c38b | |||
| bc46a8f85b | |||
| c57209fbce | |||
| 088a6b7204 | |||
| f2f4d626a5 | |||
| 572a5655cf | |||
| ccd91c0ff0 | |||
| a1d2c902f3 | |||
| 3ae361bb3b | |||
| 2d7521ed88 | |||
| 7098beb819 | |||
| f09ca658f1 |
@@ -110,7 +110,20 @@ jobs:
|
||||
run: |
|
||||
VERSION=v`cat VERSION`
|
||||
cd ./build/`uname`/*/10.15/install/ifcopenshell
|
||||
mkdir ~/output
|
||||
mkdir -p ~/output
|
||||
install_root="$PWD"
|
||||
|
||||
stage_runtime_payload() {
|
||||
dest="$1"
|
||||
while IFS= read -r runtime_file; do
|
||||
cp -L "$runtime_file" "$dest/"
|
||||
done < <(
|
||||
for runtime_dir in "$install_root/bin" "$install_root/lib" "$install_root/lib64"; do
|
||||
[ -d "$runtime_dir" ] || continue
|
||||
find "$runtime_dir" -type f \( -name "*.so" -o -name "*.so.*" -o -name "*.dylib" -o -name "*.dll" \)
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
ls -d python-* | while read py_version; do
|
||||
postfix=`echo ${py_version: -1} | sed s/[0-9]//`
|
||||
@@ -125,18 +138,25 @@ jobs:
|
||||
fi
|
||||
[ -d ifcopenshell/__pycache__ ] && rm -rf ifcopenshell/__pycache__
|
||||
find ifcopenshell -name "*.pyc" -delete
|
||||
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip ifcopenshell/*
|
||||
stage_runtime_payload ifcopenshell
|
||||
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip ifcopenshell
|
||||
mv *.zip ~/output
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
cd bin
|
||||
rm *.zip || true
|
||||
ls | while read exe; do
|
||||
zip -qq -r ${exe}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip $exe
|
||||
rm -f "$install_root"/bin/*.zip
|
||||
find "$install_root/bin" -maxdepth 1 -type f -perm /111 ! -name "*.zip" ! -name "*.so" ! -name "*.so.*" ! -name "*.dylib" ! -name "*.dll" | while read exe_path; do
|
||||
exe=`basename "$exe_path"`
|
||||
package_dir="$install_root/.package-${exe}"
|
||||
rm -rf "$package_dir"
|
||||
mkdir -p "$package_dir"
|
||||
cp "$exe_path" "$package_dir/"
|
||||
stage_runtime_payload "$package_dir"
|
||||
pushd "$package_dir" > /dev/null
|
||||
zip -qq -r "$HOME/output/${exe}-${VERSION}-${GITHUB_SHA:0:7}-macos${{ matrix.oldarch }}64.zip" .
|
||||
popd > /dev/null
|
||||
rm -rf "$package_dir"
|
||||
done
|
||||
mv *.zip ~/output
|
||||
cd ..
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
|
||||
@@ -86,7 +86,20 @@ jobs:
|
||||
run: |
|
||||
VERSION=v`cat VERSION`
|
||||
cd ./build/`uname`/*/install/ifcopenshell
|
||||
mkdir ~/output
|
||||
mkdir -p ~/output
|
||||
install_root="$PWD"
|
||||
|
||||
stage_runtime_payload() {
|
||||
dest="$1"
|
||||
while IFS= read -r runtime_file; do
|
||||
cp -L "$runtime_file" "$dest/"
|
||||
done < <(
|
||||
for runtime_dir in "$install_root/bin" "$install_root/lib" "$install_root/lib64"; do
|
||||
[ -d "$runtime_dir" ] || continue
|
||||
find "$runtime_dir" -type f \( -name "*.so" -o -name "*.so.*" -o -name "*.dylib" -o -name "*.dll" \)
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
ls -d python-* | while read py_version; do
|
||||
postfix=`echo ${py_version: -1} | sed s/[0-9]//`
|
||||
@@ -101,18 +114,24 @@ jobs:
|
||||
fi
|
||||
[ -d ifcopenshell/__pycache__ ] && rm -rf ifcopenshell/__pycache__
|
||||
find ifcopenshell -name "*.pyc" -delete
|
||||
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip ifcopenshell/*
|
||||
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip ifcopenshell
|
||||
mv *.zip ~/output
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
cd bin
|
||||
rm *.zip || true
|
||||
ls | while read exe; do
|
||||
zip -qq -r ${exe}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip $exe
|
||||
rm -f "$install_root"/bin/*.zip
|
||||
find "$install_root/bin" -maxdepth 1 -type f -perm /111 ! -name "*.zip" ! -name "*.so" ! -name "*.so.*" ! -name "*.dylib" ! -name "*.dll" | while read exe_path; do
|
||||
exe=`basename "$exe_path"`
|
||||
package_dir="$install_root/.package-${exe}"
|
||||
rm -rf "$package_dir"
|
||||
mkdir -p "$package_dir"
|
||||
cp "$exe_path" "$package_dir/"
|
||||
stage_runtime_payload "$package_dir"
|
||||
pushd "$package_dir" > /dev/null
|
||||
zip -qq -r "$HOME/output/${exe}-${VERSION}-${GITHUB_SHA:0:7}-linux64.zip" .
|
||||
popd > /dev/null
|
||||
rm -rf "$package_dir"
|
||||
done
|
||||
mv *.zip ~/output
|
||||
cd ..
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
|
||||
@@ -86,7 +86,20 @@ jobs:
|
||||
run: |
|
||||
VERSION=v`cat VERSION`
|
||||
cd ./build/`uname`/*/install/ifcopenshell
|
||||
mkdir ~/output
|
||||
mkdir -p ~/output
|
||||
install_root="$PWD"
|
||||
|
||||
stage_runtime_payload() {
|
||||
dest="$1"
|
||||
while IFS= read -r runtime_file; do
|
||||
cp -L "$runtime_file" "$dest/"
|
||||
done < <(
|
||||
for runtime_dir in "$install_root/bin" "$install_root/lib" "$install_root/lib64"; do
|
||||
[ -d "$runtime_dir" ] || continue
|
||||
find "$runtime_dir" -type f \( -name "*.so" -o -name "*.so.*" -o -name "*.dylib" -o -name "*.dll" \)
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
ls -d python-* | while read py_version; do
|
||||
postfix=`echo ${py_version: -1} | sed s/[0-9]//`
|
||||
@@ -101,18 +114,25 @@ jobs:
|
||||
fi
|
||||
[ -d ifcopenshell/__pycache__ ] && rm -rf ifcopenshell/__pycache__
|
||||
find ifcopenshell -name "*.pyc" -delete
|
||||
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip ifcopenshell/*
|
||||
stage_runtime_payload ifcopenshell
|
||||
zip -r -qq ifcopenshell-${py_version_major}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip ifcopenshell
|
||||
mv *.zip ~/output
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
cd bin
|
||||
rm *.zip || true
|
||||
ls | while read exe; do
|
||||
zip -qq -r ${exe}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip $exe
|
||||
rm -f "$install_root"/bin/*.zip
|
||||
find "$install_root/bin" -maxdepth 1 -type f -perm /111 ! -name "*.zip" ! -name "*.so" ! -name "*.so.*" ! -name "*.dylib" ! -name "*.dll" | while read exe_path; do
|
||||
exe=`basename "$exe_path"`
|
||||
package_dir="$install_root/.package-${exe}"
|
||||
rm -rf "$package_dir"
|
||||
mkdir -p "$package_dir"
|
||||
cp "$exe_path" "$package_dir/"
|
||||
stage_runtime_payload "$package_dir"
|
||||
pushd "$package_dir" > /dev/null
|
||||
zip -qq -r "$HOME/output/${exe}-${VERSION}-${GITHUB_SHA:0:7}-linuxarm64.zip" .
|
||||
popd > /dev/null
|
||||
rm -rf "$package_dir"
|
||||
done
|
||||
mv *.zip ~/output
|
||||
cd ..
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v6
|
||||
|
||||
@@ -68,6 +68,64 @@ jobs:
|
||||
cd win
|
||||
python build-all-win.py
|
||||
|
||||
- name: Repack .zip Archives With Runtime Plugins
|
||||
shell: powershell
|
||||
run: |
|
||||
$version = (Get-Content VERSION | Select-Object -First 1).Trim()
|
||||
$sha = $env:GITHUB_SHA.Substring(0, 7)
|
||||
$outputDir = Join-Path $env:USERPROFILE "output"
|
||||
$installDir = Get-ChildItem -Path . -Directory | Where-Object { $_.Name -like "_installed*" } | Select-Object -First 1
|
||||
if (-not $installDir) {
|
||||
throw "Install directory not found."
|
||||
}
|
||||
|
||||
$runtimeFiles = @()
|
||||
foreach ($runtimeDir in @((Join-Path $installDir.FullName "bin"), (Join-Path $installDir.FullName "lib"))) {
|
||||
if (Test-Path $runtimeDir) {
|
||||
$runtimeFiles += Get-ChildItem -Path $runtimeDir -Recurse -File | Where-Object { $_.Extension -ieq ".dll" }
|
||||
}
|
||||
}
|
||||
$runtimeFiles = $runtimeFiles | Sort-Object FullName -Unique
|
||||
|
||||
Get-ChildItem -Path $outputDir -Filter *.zip -ErrorAction SilentlyContinue | Remove-Item -Force
|
||||
|
||||
$binDir = Join-Path $installDir.FullName "bin"
|
||||
foreach ($exe in Get-ChildItem -Path $binDir -File -Filter *.exe) {
|
||||
$stageDir = Join-Path $env:RUNNER_TEMP ("package-" + $exe.BaseName)
|
||||
Remove-Item -Path $stageDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path $stageDir | Out-Null
|
||||
Copy-Item -Path $exe.FullName -Destination $stageDir
|
||||
foreach ($runtimeFile in $runtimeFiles) {
|
||||
Copy-Item -Path $runtimeFile.FullName -Destination $stageDir -Force
|
||||
}
|
||||
$zipPath = Join-Path $outputDir ("{0}-v{1}-{2}-win64.zip" -f $exe.BaseName, $version, $sha)
|
||||
Compress-Archive -Path (Join-Path $stageDir '*') -DestinationPath $zipPath -Force
|
||||
}
|
||||
|
||||
$depsRoot = Join-Path (Get-Location) "_deps"
|
||||
if (Test-Path $depsRoot) {
|
||||
foreach ($pythonRoot in Get-ChildItem -Path $depsRoot -Directory | Where-Object { $_.Name -like "python.*" }) {
|
||||
$pythonVersion = $pythonRoot.Name.Substring("python.".Length)
|
||||
$pythonVersionMajorMinor = ($pythonVersion.Split(".")[0..1] -join "")
|
||||
$sitePackages = Join-Path $pythonRoot.FullName "tools\\Lib\\site-packages"
|
||||
$packagePath = Join-Path $sitePackages "ifcopenshell"
|
||||
if (!(Test-Path $packagePath)) {
|
||||
continue
|
||||
}
|
||||
Get-ChildItem -Path $packagePath -Recurse -Filter *.pyc -ErrorAction SilentlyContinue | Remove-Item -Force
|
||||
foreach ($runtimeFile in $runtimeFiles) {
|
||||
Copy-Item -Path $runtimeFile.FullName -Destination $packagePath -Force
|
||||
}
|
||||
$zipPath = Join-Path $outputDir ("ifcopenshell-python-{0}-v{1}-{2}-win64.zip" -f $pythonVersionMajorMinor, $version, $sha)
|
||||
Push-Location $sitePackages
|
||||
try {
|
||||
Compress-Archive -Path "ifcopenshell" -DestinationPath $zipPath -Force
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- name: Pack Dependencies
|
||||
run: |
|
||||
cd ${{ matrix.deps_dir }}
|
||||
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
# Ensure Bonsai and ifcsverchok enable/disable works before uploading to extensions repo.
|
||||
|
||||
# Download Blender.
|
||||
wget -q -O blender.tar.xz https://download.blender.org/release/Blender5.0/blender-5.1.0-linux-x64.tar.xz
|
||||
wget -q -O blender.tar.xz https://download.blender.org/release/Blender5.1/blender-5.1.0-linux-x64.tar.xz
|
||||
tar -xf blender.tar.xz
|
||||
|
||||
# Setup Blender.
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
pip install -r requirements.txt
|
||||
python setup_extensions_repo.py --last-tag
|
||||
cd ..
|
||||
bonsai_zip="$(pwd)/$(ls bonsai_unstable_repo/bonsai_py311*-linux-x64.zip)"
|
||||
bonsai_zip="$(pwd)/$(ls bonsai_unstable_repo/bonsai_py313*-linux-x64.zip)"
|
||||
|
||||
# Install Bonsai.
|
||||
blender --command extension install-file -r user_default -e $bonsai_zip
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pyver: [py311, py312]
|
||||
pyver: [py311, py312, py313]
|
||||
config:
|
||||
- {
|
||||
name: "Windows Build",
|
||||
@@ -42,6 +42,11 @@ jobs:
|
||||
name: "MacOS ARM Build",
|
||||
short_name: macosm1,
|
||||
}
|
||||
exclude:
|
||||
# Python 3.13 is needed for Blender 5.1+ and Blender dropped Intel Mac support in 5.0.
|
||||
- pyver: py313
|
||||
config:
|
||||
short_name: macos
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6 # https://github.com/actions/setup-python
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifcedit-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/ifcedit &&
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/ifcedit/dist
|
||||
@@ -0,0 +1,36 @@
|
||||
name: ci-ifcmcp-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/ifcmcp &&
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/ifcmcp/dist
|
||||
verbose: true
|
||||
@@ -0,0 +1,35 @@
|
||||
name: ci-ifcquery-pypi
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Compile
|
||||
run: |
|
||||
pip install build
|
||||
cd src/ifcquery &&
|
||||
make dist IS_STABLE=TRUE
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: src/ifcquery/dist
|
||||
@@ -1,4 +1,4 @@
|
||||
name: ci-black-formatting
|
||||
name: ci-lint
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -30,6 +30,7 @@ jobs:
|
||||
uv tool install ruff
|
||||
uv tool install black
|
||||
uv tool install poethepoet
|
||||
uv tool install ty
|
||||
|
||||
# black doesn't catch all syntax errors, so we check them explicitly.
|
||||
- name: Check syntax errors
|
||||
@@ -57,6 +58,13 @@ jobs:
|
||||
black --diff --check . | black-codeclimate | python .github/workflows/black_to_github_annotations.py
|
||||
continue-on-error: true
|
||||
|
||||
- name: ty check
|
||||
id: ty
|
||||
run: |
|
||||
poe ty-venv
|
||||
poe ty
|
||||
continue-on-error: true
|
||||
|
||||
- name: Ruff check
|
||||
id: ruff
|
||||
run: |
|
||||
@@ -105,4 +113,7 @@ jobs:
|
||||
if [ "${{ steps.ruff.outcome }}" != "success" ]; then
|
||||
echo "::error::Ruff check failed, see Summary or 'ruff' step for the details." && ERROR=1
|
||||
fi
|
||||
if [ "${{ steps.ty.outcome }}" != "success" ]; then
|
||||
echo "::error::ty check failed, see 'ty check' step for the details." && ERROR=1
|
||||
fi
|
||||
exit $ERROR
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Release Pyodide WASM Wheel
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout IfcOpenShell
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build wheel
|
||||
working-directory: pyodide
|
||||
run: uv run pack_wheel.py --build
|
||||
|
||||
- name: Find wheel
|
||||
id: wheel
|
||||
run: |
|
||||
WHEEL=$(ls pyodide/dist/ifcopenshell-*.whl)
|
||||
echo "path=$WHEEL" >> $GITHUB_OUTPUT
|
||||
echo "name=$(basename $WHEEL)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout wasm-wheels
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: IfcOpenShell/wasm-wheels
|
||||
path: wasm-wheels
|
||||
token: ${{ secrets.BUILD_REPO_TOKEN }}
|
||||
|
||||
- name: Commit and push wheel to wasm-wheels
|
||||
run: |
|
||||
WHEEL_NAME="${{ steps.wheel.outputs.name }}"
|
||||
cp "${{ steps.wheel.outputs.path }}" "wasm-wheels/$WHEEL_NAME"
|
||||
cd wasm-wheels
|
||||
git config user.name "IfcOpenBot"
|
||||
git config user.email "ifcopenbot@ifcopenshell.org"
|
||||
git add "$WHEEL_NAME"
|
||||
git commit -m "Add $WHEEL_NAME"
|
||||
VERSION=$(cat ../VERSION)
|
||||
git tag "v${VERSION}"
|
||||
git push origin main
|
||||
git push origin "v${VERSION}"
|
||||
@@ -1,36 +0,0 @@
|
||||
name: Build and Deploy Stable Documentation
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd src/bonsai/docs
|
||||
pip install -r requirements.txt # Run pip install from the docs directory
|
||||
|
||||
- name: Build documentation
|
||||
run: |
|
||||
cd src/bonsai/docs
|
||||
make html
|
||||
|
||||
- name: Deploy to GitHub Pages (Stable)
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||
external_repository: IfcOpenShell/bonsaibim_org_docs
|
||||
publish_branch: main
|
||||
cname: docs.bonsaibim.org
|
||||
publish_dir: src/bonsai/docs/_build/html
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Deploy AI chat App to static page repo
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
pages: write
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/ifcchat/**'
|
||||
- '.github/workflows/publish-aichat-app.yaml'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout (recursive)
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Checkout intermediate Pages repo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: IfcOpenShell/aichat_ifcopenshell_org_static_html
|
||||
ref: gh-pages
|
||||
path: output
|
||||
token: ${{ secrets.WEBSITE_PUBLISH }}
|
||||
- name: Sync demo app into target subfolder
|
||||
run: |
|
||||
rsync -av --delete --exclude='.git/' src/ifcchat/ output/
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Download wheels
|
||||
working-directory: output/
|
||||
run: |
|
||||
pip download ifcquery==0.8.5 ifcopenshell-mcp==0.8.5 ifcedit==0.8.5 lark==1.3.1 isodate==0.7.2 --no-deps -d ./dist
|
||||
- name: Commit and push if changed
|
||||
working-directory: output
|
||||
run: |
|
||||
git config --global user.name 'IfcOpenBot'
|
||||
git config --global user.email 'IfcOpenBot@users.noreply.github.com'
|
||||
|
||||
git add .
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "$(git log --oneline -1)"
|
||||
git push origin gh-pages
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Deploy Pyodide Demo App to GitHub Pages
|
||||
name: Deploy Pyodide Demo App to static page repo
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
@@ -11,6 +11,7 @@ on:
|
||||
- '.github/workflows/publish-pyodide-demo-app.yml'
|
||||
branches:
|
||||
- v0.8.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
@@ -30,21 +31,27 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v6
|
||||
- name: Upload static files as artifact
|
||||
id: deployment
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
- name: Checkout intermediate Pages repo
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: src/pyodide/demo-app/
|
||||
repository: IfcOpenShell/wasm_ifcopenshell_org_static_html
|
||||
ref: gh-pages
|
||||
path: output
|
||||
token: ${{ secrets.WEBSITE_PUBLISH }}
|
||||
- name: Sync demo app into target subfolder
|
||||
run: |
|
||||
rsync -av --delete --exclude='.git/' src/pyodide/demo-app/ output/
|
||||
- name: Commit and push if changed
|
||||
working-directory: output
|
||||
run: |
|
||||
git config --global user.name 'IfcOpenBot'
|
||||
git config --global user.email 'IfcOpenBot@users.noreply.github.com'
|
||||
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
git add .
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "$(git log --oneline -1)"
|
||||
git push origin gh-pages
|
||||
|
||||
@@ -50,11 +50,14 @@ Contents
|
||||
| [ifcconvert](https://docs.ifcopenshell.org/ifcconvert.html) | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcconvert/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcconvert&expanded=true)
|
||||
| [ifccsv](https://docs.ifcopenshell.org/ifccsv.html) | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccsv/) |
|
||||
| [ifcdiff](https://docs.ifcopenshell.org/ifcdiff.html) | Compare changes between IFC models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcdiff/) |
|
||||
| [ifcedit](https://docs.ifcopenshell.org/ifcedit.html) | CLI wrapper for ifcopenshell.api IFC model mutation functions | LGPL-3.0-or-later | [](https://pypi.org/project/ifcedit/) |
|
||||
| [ifcfm](https://docs.ifcopenshell.org/ifcfm.html) | Extract IFC data for FM handover requirements | LGPL-3.0-or-later | [](https://pypi.org/project/ifcfm/) |
|
||||
| [ifcmax](https://docs.ifcopenshell.org/ifcmax.html) | Historic extension for IFC support in 3DS Max | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcmax.html)
|
||||
| [ifcopenshell-python](https://docs.ifcopenshell.org/ifcopenshell-python.html) | Python library for IFC manipulation | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcopenshell-python&expanded=true) [](https://pypi.org/project/ifcopenshell/) [](https://anaconda.org/conda-forge/ifcopenshell) [](https://anaconda.org/ifcopenshell/ifcopenshell) [](https://hub.docker.com/r/aecgeeks/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell-git) [Pyodide WASM Wheels](https://github.com/IfcOpenShell/wasm-wheels#pyodide-test-wheels) |
|
||||
| [ifcmcp](https://docs.ifcopenshell.org/ifcmcp.html) | MCP server for querying and editing IFC building models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcopenshell-mcp/) |
|
||||
| [ifcopenshell-python](https://docs.ifcopenshell.org/ifcopenshell-python.html) | Python library for IFC manipulation | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcopenshell-python&expanded=true) [](https://pypi.org/project/ifcopenshell/) [](https://anaconda.org/conda-forge/ifcopenshell) [](https://anaconda.org/ifcopenshell/ifcopenshell) [](https://hub.docker.com/r/aecgeeks/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell-git) [](https://github.com/IfcOpenShell/wasm-wheels) |
|
||||
| [ifcpatch](https://docs.ifcopenshell.org/ifcpatch.html) | Utility to run pre-packaged scripts to manipulate IFCs | LGPL-3.0-or-later | [](https://pypi.org/project/ifcpatch/) |
|
||||
| [ifcsverchok](https://docs.ifcopenshell.org/ifcsverchok.html) | Blender Add-on for visual node programming with IFC | GPL-3.0-or-later | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcsverchok&expanded=true)
|
||||
| [ifcquery](https://docs.ifcopenshell.org/ifcquery.html) | CLI tool for querying and inspecting IFC building models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcquery/) |
|
||||
| [ifcsverchok](https://docs.ifcopenshell.org/ifcsverchok.html) | Blender Add-on for visual node programming with IFC | GPL-3.0-or-later | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcsverchok&expanded=true)
|
||||
| [ifctester](https://docs.ifcopenshell.org/ifctester.html) | Library, CLI and webapp for IDS model auditing | LGPL-3.0-or-later | [](https://pypi.org/project/ifctester/) |
|
||||
|
||||
The IfcOpenShell C++ codebase is split into multiple interal libraries:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
mkdir -p build && cd build
|
||||
|
||||
cmake ../cmake \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DPython_EXECUTABLE=/home/dion/Projects/env/bin/python3.11 \
|
||||
-DPython_INCLUDE_DIR=/usr/include/python3.11 \
|
||||
-DBUILD_IFCPYTHON=ON \
|
||||
-DBUILD_IFCGEOM=ON \
|
||||
-DBUILD_CONVERT=ON \
|
||||
-DBUILD_GEOMSERVER=OFF \
|
||||
-DBUILD_EXAMPLES=OFF \
|
||||
-DWITH_OPENCASCADE=ON \
|
||||
-DWITH_CGAL=ON \
|
||||
-DWITH_MANIFOLD=ON \
|
||||
-DHDF5_SUPPORT=OFF \
|
||||
-DGLTF_SUPPORT=ON \
|
||||
-DIFCXML_SUPPORT=OFF \
|
||||
-DCOLLADA_SUPPORT=OFF \
|
||||
-DSCHEMA_VERSIONS="2x3;4;4x3_add2" \
|
||||
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||
-DOCC_LIBRARY_DIR=/usr/lib64/opencascade
|
||||
|
||||
ninja
|
||||
|
||||
cp ifcwrap/_ifcopenshell_wrapper*.so ifcwrap/ifcopenshell_wrapper.py \
|
||||
../src/ifcopenshell-python/ifcopenshell/
|
||||
+204
-204
@@ -18,28 +18,19 @@
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
if(NOT DEFINED CMAKE_CXX_STANDARD)
|
||||
if (NOT DEFINED CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
if(CMAKE_CXX_STANDARD LESS 17)
|
||||
if (CMAKE_CXX_STANDARD LESS 17)
|
||||
message(FATAL_ERROR "C++17 or newer is required.")
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) # not necessary, but encouraged
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(VERSION_OVERRIDE)
|
||||
file(READ "../VERSION" "RELEASE_VERSION_")
|
||||
string(STRIP "${RELEASE_VERSION_}" RELEASE_VERSION)
|
||||
message(STATUS "Detected version '${RELEASE_VERSION}'")
|
||||
else()
|
||||
set(RELEASE_VERSION "0.8.0")
|
||||
endif()
|
||||
|
||||
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
|
||||
|
||||
if(POLICY CMP0141) # 3.25+
|
||||
# Has to be set before `project` to take effect.
|
||||
cmake_policy(SET CMP0141 NEW) # Support for `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`.
|
||||
cmake_policy(SET CMP0141 NEW) # Support for `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`.
|
||||
endif()
|
||||
if(POLICY CMP0144) # 3.27
|
||||
cmake_policy(SET CMP0144 NEW) # find_package() uses upper-case <PACKAGENAME>_ROOT variables.
|
||||
@@ -48,8 +39,6 @@ if(POLICY CMP0167) # 3.30
|
||||
cmake_policy(SET CMP0167 OLD)
|
||||
endif()
|
||||
|
||||
project(IfcOpenShell VERSION ${RELEASE_VERSION})
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
@@ -65,64 +54,59 @@ endif()
|
||||
option(MINIMAL_BUILD "The build is to make a minimal version of IFC converter from OCCT into IFC." OFF)
|
||||
option(WASM_BUILD "Build a WebAssembly binary." OFF)
|
||||
|
||||
option(
|
||||
ENABLE_BUILD_OPTIMIZATIONS
|
||||
"Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds."
|
||||
OFF
|
||||
)
|
||||
option(BUILD_SHARED_LIBS "Build IfcParse and IfcGeom as shared libs (SO/DLL)." OFF)
|
||||
option(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
|
||||
option(BUILD_SHARED_LIBS "Build IfcOpenShell as shared libraries (required)." ON)
|
||||
option(MSVC_PARALLEL_BUILD "Multi-threaded compilation in Microsoft Visual Studio (/MP)" OFF)
|
||||
option(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OFF)
|
||||
option(USE_MMAP "Adds a command line options to parse IFC files from memory mapped files using Boost.Iostreams" OFF)
|
||||
option(NO_WARN "Disable all warnings" OFF)
|
||||
option(CREATE_BUNDLE "Copy .so files and don't create RPATHS or SOVERSION symlinks" )
|
||||
|
||||
option(BUILD_IFCGEOM "Build IfcGeom." ON)
|
||||
option(BUILD_IFCPYTHON "Build IfcPython." ON)
|
||||
option(BUILD_IFCPARSE_EXPERIMENTAL_WRAPPER "Build the experimental Clang-generated ifcparse Python wrapper." OFF)
|
||||
option(BUILD_CONVERT "Build IfcConvert executable." ON)
|
||||
option(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
|
||||
option(BUILD_EXAMPLES "Build example applications." OFF)
|
||||
option(BUILD_EXAMPLES "Build example applications." ON)
|
||||
option(BUILD_GEOMSERVER "Build IfcGeomServer executable (Open CASCADE is required)." ON)
|
||||
option(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." OFF)
|
||||
option(BUILD_QTVIEWER "Build IfcOpenShell Qt GUI Viewer" OFF) # QtViewer requires Qt6
|
||||
option(BUILD_IFCVIEWER "Build IfcViewer, a high-performance IFC viewer" OFF) # Requires Qt6 + OpenGL 4.5
|
||||
option(BUILD_IFCVIEWER_TESTS "Build unit tests for IfcViewer (fetches Catch2 v3)" OFF)
|
||||
option(BUILD_PACKAGE "" OFF)
|
||||
# Most users probably need just common schemas,
|
||||
# but we're keeping it `OFF` by default to avoid disruption
|
||||
# (e.g. all Python distribution would need to adapt this option to be set).
|
||||
option(
|
||||
BUILD_ONLY_COMMON_SCHEMAS
|
||||
"Build only common IFC schemas (2x3, 4, 4x3_add2). By default all schemas will be built."
|
||||
OFF
|
||||
)
|
||||
option(SCHEMA_VERSIONS "Explicitly specify schemas to build." "")
|
||||
|
||||
option(WITH_OPENCASCADE "Enable geometry interpretation using Open CASCADE" ON)
|
||||
option(WITH_CGAL "Enable geometry interpretation using CGAL" ON)
|
||||
option(WITH_MANIFOLD "Enable geometry interpretation using Manifold" OFF)
|
||||
option(COLLADA_SUPPORT "Build IfcConvert with COLLADA support (requires OpenCOLLADA)." ON)
|
||||
option(GLTF_SUPPORT "Build IfcConvert with glTF support (requires json.hpp)." OFF)
|
||||
option(HDF5_SUPPORT "Enable HDF5 support (requires HDF5, zlib)" ON)
|
||||
option(WITH_PROJ "Enable output of Earth-Centered Earth-Fixed glTF output using the PROJ library" OFF)
|
||||
option(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." ON)
|
||||
option(USD_SUPPORT "Build IfcConvert with USD support (requires pixar's USD library)." OFF)
|
||||
option(WITH_RELATIONSHIP_VALIDATION "Build IfcConvert with option to validate geometrical relationships." OFF)
|
||||
option(WITH_ROCKSDB "Support a RocksDB key-value store as a file backend in IfcOpenShell" OFF)
|
||||
option(WITH_ZSTD "Use Zstd compression in RocksDB writes" OFF)
|
||||
|
||||
option(USERSPACE_PYTHON_PREFIX "Installs IfcPython for the current user only instead of system-wide." OFF)
|
||||
option(USE_DEBUG_PYTHON "Use debug binaries when building Debug IfcPython on Windows." OFF)
|
||||
option(ADD_COMMIT_SHA "Add commit sha and branch in version number, requires git" OFF)
|
||||
option(
|
||||
VERSION_OVERRIDE
|
||||
"Override the version defined in buildinfo.cpp with the file VERSION in the repository root"
|
||||
OFF
|
||||
)
|
||||
option(USE_CCACHE "Enable use of ccache if it's available from PATH." ON)
|
||||
option(VERSION_OVERRIDE "Override the version defined in buildinfo.cpp with the file VERSION in the repository root" OFF)
|
||||
|
||||
set(PYTHON_MODULE_INSTALL_DIR
|
||||
""
|
||||
CACHE PATH
|
||||
set(
|
||||
PYTHON_MODULE_INSTALL_DIR
|
||||
"" CACHE PATH
|
||||
"Directory to install IfcPython package to. By default package is installed in found Python's site-packages."
|
||||
)
|
||||
|
||||
if (VERSION_OVERRIDE)
|
||||
file(READ "../VERSION" "RELEASE_VERSION_")
|
||||
string(STRIP "${RELEASE_VERSION_}" RELEASE_VERSION)
|
||||
message(STATUS "Detected version '${RELEASE_VERSION}'")
|
||||
else()
|
||||
set(RELEASE_VERSION "0.8.0")
|
||||
endif()
|
||||
|
||||
project(IfcOpenShell VERSION ${RELEASE_VERSION})
|
||||
|
||||
# Make sure CMake modules in this project are found first
|
||||
list(PREPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
|
||||
|
||||
@@ -134,17 +118,20 @@ if(MINIMAL_BUILD)
|
||||
set(COLLADA_SUPPORT OFF)
|
||||
set(GLTF_SUPPORT OFF)
|
||||
set(HDF5_SUPPORT OFF)
|
||||
set(IFCXML_SUPPORT OFF)
|
||||
set(USD_SUPPORT OFF)
|
||||
endif()
|
||||
|
||||
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND (NOT BUILD_IFCGEOM))
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
message(FATAL_ERROR "BUILD_SHARED_LIBS=OFF is not supported. Configure with -DBUILD_SHARED_LIBS=ON.")
|
||||
endif()
|
||||
|
||||
if((BUILD_CONVERT OR BUILD_GEOMSERVER OR BUILD_IFCPYTHON) AND(NOT BUILD_IFCGEOM))
|
||||
message(STATUS "'IfcGeom' is required with current outputs")
|
||||
set(BUILD_IFCGEOM ON)
|
||||
endif()
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(USE_CCACHE AND CCACHE_FOUND)
|
||||
if(CCACHE_FOUND)
|
||||
message(STATUS "`ccache` is found, using it as a compiler launcher.")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
|
||||
if(MSVC)
|
||||
@@ -153,15 +140,17 @@ if(USE_CCACHE AND CCACHE_FOUND)
|
||||
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
|
||||
# Not needed for Ninja.
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
file(COPY_FILE ${CCACHE_FOUND} ${CMAKE_BINARY_DIR}/cl.exe ONLY_IF_DIFFERENT)
|
||||
set(CMAKE_VS_GLOBALS "CLToolExe=cl.exe" "CLToolPath=${CMAKE_BINARY_DIR}" "UseMultiToolTask=true")
|
||||
file(COPY_FILE
|
||||
${CCACHE_FOUND} ${CMAKE_BINARY_DIR}/cl.exe
|
||||
ONLY_IF_DIFFERENT)
|
||||
set(CMAKE_VS_GLOBALS
|
||||
"CLToolExe=cl.exe"
|
||||
"CLToolPath=${CMAKE_BINARY_DIR}"
|
||||
"UseMultiToolTask=true"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
mark_as_advanced(CCACHE_FOUND)
|
||||
|
||||
# Variable to accumulate swig definitions from various submodules.
|
||||
set(SWIG_DEFINES "")
|
||||
|
||||
if(MSVC AND MSVC_PARALLEL_BUILD)
|
||||
add_definitions("/MP")
|
||||
@@ -177,28 +166,23 @@ endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(IFCOPENSHELL_EXPORT_TARGETS "${PROJECT_NAME}Targets")
|
||||
|
||||
# On Windows Release and Debug binaries are not compatible.
|
||||
# So we add a postfix to avoid issues and allow release and debug installations to coexist.
|
||||
if(WIN32)
|
||||
set(CMAKE_DEBUG_POSTFIX "_d")
|
||||
if(NOT INCLUDEDIR)
|
||||
set(INCLUDEDIR include)
|
||||
endif()
|
||||
if(NOT IS_ABSOLUTE ${INCLUDEDIR})
|
||||
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
endif()
|
||||
message(STATUS "INCLUDEDIR: ${INCLUDEDIR}")
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
add_definitions(-DIFC_SHARED_BUILD)
|
||||
if(MSVC)
|
||||
message(
|
||||
WARNING
|
||||
"Building DLLs against the static VC run-time. This is not recommended if the DLLs are to be redistributed."
|
||||
)
|
||||
# C4521: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
|
||||
add_definitions(-wd4251)
|
||||
endif()
|
||||
if(MSVC)
|
||||
message(WARNING "Building DLLs against the static VC run-time. This is not recommended if the DLLs are to be redistributed.")
|
||||
# C4521: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
# There will be couple hundreds of these so suppress them away, https://msdn.microsoft.com/en-us/library/esew7y1w.aspx
|
||||
add_definitions(-wd4251)
|
||||
endif()
|
||||
|
||||
UNIFY_ENVVARS_AND_CACHE(BOOST_ROOT)
|
||||
UNIFY_ENVVARS_AND_CACHE(BOOST_LIBRARYDIR)
|
||||
|
||||
if(NOT MINIMAL_BUILD)
|
||||
UNIFY_ENVVARS_AND_CACHE(PYTHON_INCLUDE_DIR)
|
||||
@@ -214,71 +198,75 @@ foreach(option_flag IN LISTS option_flags)
|
||||
convert_env_var_to_bool("${option_flag}")
|
||||
endforeach()
|
||||
|
||||
if(WITH_CGAL)
|
||||
find_package(CGAL REQUIRED)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_CGAL)
|
||||
list(APPEND GEOMETRY_KERNELS cgal)
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCGEOM AND WITH_OPENCASCADE)
|
||||
find_package(OpenCASCADE REQUIRED)
|
||||
add_definitions(-DIFOPSH_WITH_OPENCASCADE)
|
||||
# Map OpenCASCADE_LIBRARIES variable from OpenCASCADEConfig.cmake to OpenCASCADE_LIBRARIES used by kernel generic cmake file
|
||||
set(OpenCASCADE_LIBRARIES ${OpenCASCADE_LIBRARIES})
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_OPENCASCADE)
|
||||
list(APPEND GEOMETRY_KERNELS opencascade)
|
||||
endif()
|
||||
|
||||
set(GLTF_LIBRARIES "")
|
||||
message(STATUS "BUILD_IFCGEOM WITH_MANIFOLD: ${BUILD_IFCGEOM} ${WITH_MANIFOLD}")
|
||||
|
||||
if(BUILD_IFCGEOM AND WITH_MANIFOLD)
|
||||
find_package(manifold CONFIG REQUIRED)
|
||||
if(TARGET manifold::manifold)
|
||||
set(MANIFOLD_LIBRARIES manifold::manifold)
|
||||
elseif(TARGET manifold)
|
||||
set(MANIFOLD_LIBRARIES manifold)
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to determine manifold target")
|
||||
endif()
|
||||
list(APPEND GEOMETRY_KERNELS manifold)
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
list(APPEND GEOMETRY_KERNELS passthrough)
|
||||
endif()
|
||||
|
||||
if(GLTF_SUPPORT)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
set(GLTF_LIBRARIES nlohmann_json::nlohmann_json)
|
||||
add_definitions(-DWITH_GLTF)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_GLTF)
|
||||
endif()
|
||||
|
||||
# Add USD support to serializers
|
||||
set(USD_LIBRARIES "")
|
||||
if(USD_SUPPORT)
|
||||
find_package(USD REQUIRED)
|
||||
set(USD_LIBRARIES pxr::USD)
|
||||
endif(USD_SUPPORT)
|
||||
|
||||
set(ROCKSDB_LIBRARIES "")
|
||||
if(WITH_ROCKSDB)
|
||||
if (WITH_ROCKSDB)
|
||||
# Temporaily mess with CMAKE_FIND_PACKAGE_PREFER_CONFIG to help RocksDB
|
||||
# find it's zstd dependency on Windows.
|
||||
# Only do it on Windows, otherwise it might create problems as
|
||||
# findzstd and zstd-config target names do not match.
|
||||
# https://github.com/facebook/rocksdb/pull/13975
|
||||
if(WIN32)
|
||||
set(TEMP CMAKE_FIND_PACKAGE_PREFER_CONFIG)
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
|
||||
endif()
|
||||
find_package(RocksDB CONFIG REQUIRED)
|
||||
mark_as_advanced(RocksDB_DIR)
|
||||
if(WIN32)
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ${TEMP})
|
||||
endif()
|
||||
|
||||
message(STATUS "RocksDB: found at '${RocksDB_DIR}'.")
|
||||
add_library(IFCOPENSHELL_RocksDB INTERFACE)
|
||||
set(ROCKSDB_LIBRARIES "IFCOPENSHELL_RocksDB")
|
||||
target_compile_definitions(IFCOPENSHELL_RocksDB INTERFACE IFOPSH_WITH_ROCKSDB)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DIFOPSH_WITH_ROCKSDB)
|
||||
# Shared binaries for `rocksdb` only support limited API (only `c.h`), but we use `db.h` API.
|
||||
# So rocksdb supported only as a static library.
|
||||
# See https://github.com/facebook/rocksdb/issues/981.
|
||||
target_link_libraries(IFCOPENSHELL_RocksDB INTERFACE RocksDB::rocksdb)
|
||||
|
||||
if(WITH_ZSTD)
|
||||
if (WITH_ZSTD)
|
||||
# @todo do we actually need the zstd include dir or rather just pass
|
||||
# the libzstd.a along with the rocksdb library when needed and feature
|
||||
# detect based on rocksdb API?
|
||||
find_package(zstd CONFIG REQUIRED)
|
||||
mark_as_advanced(zstd_DIR)
|
||||
message(STATUS "zstd: found at '${zstd_DIR}'.")
|
||||
target_link_libraries(IFCOPENSHELL_RocksDB INTERFACE zstd::libzstd_static)
|
||||
endif()
|
||||
|
||||
install(TARGETS IFCOPENSHELL_RocksDB EXPORT ${IFCOPENSHELL_EXPORT_TARGETS})
|
||||
endif()
|
||||
|
||||
# Find Boost: On win32 the (hardcoded) default is to use static libraries and
|
||||
# runtime, when doing running conda-build we pick what conda prepared for us.
|
||||
if(WIN32 AND NOT DEFINED ENV{CONDA_BUILD})
|
||||
if(WIN32 AND("$ENV{CONDA_BUILD}" STREQUAL ""))
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
@@ -309,14 +297,8 @@ if(WASM_BUILD)
|
||||
else()
|
||||
# @todo review this, shouldn't this be all possible header-only now?
|
||||
# ... or rewritten using C++17 features?
|
||||
set(BOOST_COMPONENTS
|
||||
system
|
||||
program_options
|
||||
regex
|
||||
thread
|
||||
date_time
|
||||
iostreams
|
||||
)
|
||||
# set(BOOST_COMPONENTS system program_options regex thread date_time iostreams)
|
||||
set(BOOST_COMPONENTS program_options regex thread date_time iostreams)
|
||||
endif()
|
||||
|
||||
if(USE_MMAP)
|
||||
@@ -326,17 +308,6 @@ if(USE_MMAP)
|
||||
else()
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} iostreams)
|
||||
endif()
|
||||
|
||||
add_definitions(-DUSE_MMAP)
|
||||
endif()
|
||||
|
||||
# Handle CGAL after Boost settings are set, since CGAL will use them too.
|
||||
# Do `find_package(Boost)` only after this, to make sure `FindBoost` finds correct components.
|
||||
# Otherwise it will find components needed for CGAL and we might some libraries.
|
||||
if(WITH_CGAL)
|
||||
find_package(CGAL REQUIRED)
|
||||
set(CGAL_LIBRARIES IFCOPENSHELL_CGAL)
|
||||
list(APPEND GEOMETRY_KERNELS cgal)
|
||||
endif()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
@@ -345,15 +316,10 @@ message(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
|
||||
|
||||
if(COLLADA_SUPPORT)
|
||||
find_package(OpenCOLLADA REQUIRED)
|
||||
add_definitions(-DWITH_OPENCOLLADA)
|
||||
endif()
|
||||
|
||||
if(HDF5_SUPPORT)
|
||||
find_package(HDF5 REQUIRED COMPONENTS C CXX)
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} hdf5::hdf5_cpp)
|
||||
|
||||
add_definitions(-DWITH_HDF5)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_HDF5)
|
||||
endif(HDF5_SUPPORT)
|
||||
|
||||
if(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
@@ -368,20 +334,12 @@ if(ENABLE_BUILD_OPTIMIZATIONS)
|
||||
|
||||
# Linker
|
||||
# /OPT:REF enables also /OPT:ICF and disables INCREMENTAL
|
||||
set(LINKER_FLAGS_RELEASE "/LTCG /OPT:REF")
|
||||
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
|
||||
set(LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /OPT:NOICF")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}"
|
||||
)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
|
||||
"${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${LINKER_FLAGS_RELWITHDEBINFO}"
|
||||
)
|
||||
# /OPT:NOICF is recommended when /DEBUG is used (http://msdn.microsoft.com/en-us/library/xe4t6fc1.aspx)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:NOICF")
|
||||
else()
|
||||
# GCC-like: Release should use O3 but RelWithDebInfo 02 so enforce 03. Anything other useful that could be added here?
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
@@ -430,7 +388,7 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
# Enforce standards-conformance on VS > 2015, older Boost versions fail to compile with this
|
||||
if(MSVC_VERSION GREATER 1900 AND (Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
|
||||
if(MSVC_VERSION GREATER 1900 AND(Boost_MAJOR_VERSION GREATER 1 OR Boost_MINOR_VERSION GREATER 66))
|
||||
add_definitions(-permissive-)
|
||||
endif()
|
||||
|
||||
@@ -449,11 +407,11 @@ if(MSVC)
|
||||
# endforeach()
|
||||
# endif()
|
||||
|
||||
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||
# See #5158.
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.40)
|
||||
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
|
||||
endif()
|
||||
add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||
# See #5158.
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.40)
|
||||
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
|
||||
endif()
|
||||
else()
|
||||
add_definitions(-Wall -Wextra)
|
||||
|
||||
@@ -463,10 +421,7 @@ else()
|
||||
add_definitions(-Wno-maybe-uninitialized)
|
||||
endif()
|
||||
|
||||
if(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "GNU"
|
||||
AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0)
|
||||
)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 9.0))
|
||||
# OpenCascade spews a lot of deprecated-copy warnings
|
||||
add_definitions(-Wno-deprecated-copy)
|
||||
endif()
|
||||
@@ -477,45 +432,35 @@ else()
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
include_directories(${OPENCOLLADA_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${HDF5_INCLUDE_DIR})
|
||||
include_directories(${INCLUDE_DIRECTORIES}
|
||||
${Boost_INCLUDE_DIRS} ${HDF5_INCLUDE_DIR}
|
||||
${CGAL_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${MPFR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(NOT SCHEMA_VERSIONS)
|
||||
# `WASM_BUILD` - super arbitrarily try to keep size down at least a little bit
|
||||
if(BUILD_ONLY_COMMON_SCHEMAS OR WASM_BUILD)
|
||||
if(WASM_BUILD)
|
||||
# super arbitrarily try to keep size down at least a little bit
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x3_add2")
|
||||
else()
|
||||
set(SCHEMA_VERSIONS
|
||||
"2x3"
|
||||
"4"
|
||||
"4x1"
|
||||
"4x2"
|
||||
"4x3"
|
||||
"4x3_tc1"
|
||||
"4x3_add1"
|
||||
"4x3_add2"
|
||||
)
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3" "4x3_tc1" "4x3_add1" "4x3_add2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "IFC SCHEMA_VERSIONS that will be used for the build: ${SCHEMA_VERSIONS}.")
|
||||
|
||||
set(SCHEMA_DEFINITIONS "")
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
list(APPEND SCHEMA_DEFINITIONS "-DHAS_SCHEMA_${schema}")
|
||||
add_definitions(-DHAS_SCHEMA_${schema})
|
||||
endforeach()
|
||||
|
||||
string(REPLACE ";" ")(" schema_version_seq "(${SCHEMA_VERSIONS})")
|
||||
list(APPEND SCHEMA_DEFINITIONS "-DSCHEMA_SEQ=${schema_version_seq}")
|
||||
add_definitions(-DSCHEMA_SEQ=${schema_version_seq})
|
||||
|
||||
if(COMPILE_SCHEMA)
|
||||
# @todo, this appears to be untested at the moment
|
||||
find_package(PythonInterp)
|
||||
|
||||
if(NOT PYTHONINTERP_FOUND)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed."
|
||||
)
|
||||
message(FATAL_ERROR "A Python interpreter is necessary when COMPILE_SCHEMA is enabled. Disable COMPILE_SCHEMA or fix Python paths to proceed.")
|
||||
endif()
|
||||
|
||||
set(IFC_RELEASE_NOT_USED ${SCHEMA_VERSIONS})
|
||||
@@ -535,10 +480,7 @@ if(COMPILE_SCHEMA)
|
||||
|
||||
if("${PYPARSING_FOUND}" STREQUAL "-1")
|
||||
message(STATUS "Installing pyparsing")
|
||||
execute_process(
|
||||
COMMAND ${PYTHON_EXECUTABLE} -m pip "install" --user pyparsing
|
||||
RESULT_VARIABLE SUCCESS
|
||||
)
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
|
||||
|
||||
if(NOT "${SUCCESS}" STREQUAL "0")
|
||||
execute_process(COMMAND pip "install" --user pyparsing RESULT_VARIABLE SUCCESS)
|
||||
@@ -553,23 +495,19 @@ if(COMPILE_SCHEMA)
|
||||
|
||||
# Bootstrap the parser
|
||||
message(STATUS "Compiling schema, this will take a while...")
|
||||
execute_process(
|
||||
COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} bootstrap.py express.bnf
|
||||
WORKING_DIRECTORY ../src/ifcexpressparser
|
||||
OUTPUT_FILE express_parser.py
|
||||
RESULT_VARIABLE SUCCESS
|
||||
)
|
||||
RESULT_VARIABLE SUCCESS)
|
||||
|
||||
if(NOT "${SUCCESS}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Failed to bootstrap parser. Make sure pyparsing is installed")
|
||||
endif()
|
||||
|
||||
# Generate code
|
||||
execute_process(
|
||||
COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} ../ifcexpressparser/express_parser.py ../../${COMPILE_SCHEMA}
|
||||
WORKING_DIRECTORY ../src/ifcparse
|
||||
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME
|
||||
)
|
||||
OUTPUT_VARIABLE COMPILED_SCHEMA_NAME)
|
||||
|
||||
# Prevent the schema that had just been compiled from being excluded
|
||||
foreach(schema ${SCHEMA_VERSIONS})
|
||||
@@ -584,16 +522,39 @@ if(NOT Boost_VERSION LESS 105800)
|
||||
add_definitions(-DBOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(../src/plugin plugin)
|
||||
|
||||
add_subdirectory(../src/ifcparse ifcparse)
|
||||
set(IFCOPENSHELL_LIBRARIES IfcParse)
|
||||
|
||||
if(BUILD_IFCPARSE_EXPERIMENTAL_WRAPPER)
|
||||
add_subdirectory(../src/wrappergen wrappergen)
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
# CGAL::CGAL target already has dependencies resolved.
|
||||
if(WITH_CGAL AND CGAL_DIR)
|
||||
set(CGAL_LIBRARIES CGAL::CGAL)
|
||||
message(STATUS "Using found CGAL package at '${CGAL_DIR}'")
|
||||
elseif(WITH_CGAL AND NOT CGAL_DIR)
|
||||
find_library(libGMP NAMES gmp mpir PATHS ${GMP_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
find_library(libMPFR NAMES mpfr PATHS ${MPFR_LIBRARY_DIR} NO_DEFAULT_PATH)
|
||||
if(NOT libGMP)
|
||||
message(FATAL_ERROR "Unable to find GMP library files, aborting")
|
||||
endif()
|
||||
if(NOT libMPFR)
|
||||
message(FATAL_ERROR "Unable to find MPFR library files, aborting")
|
||||
endif()
|
||||
|
||||
list(APPEND CGAL_LIBRARIES "${libMPFR}")
|
||||
list(APPEND CGAL_LIBRARIES "${libGMP}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(../src/ifcgeom ifcgeom)
|
||||
endif(BUILD_IFCGEOM)
|
||||
|
||||
if(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
add_subdirectory(../src/serializers serializers)
|
||||
set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} ${SERIALIZER_SCHEMA_LIBRARIES})
|
||||
endif(BUILD_CONVERT OR BUILD_IFCPYTHON)
|
||||
|
||||
if(BUILD_CONVERT)
|
||||
@@ -613,8 +574,8 @@ if(ADD_COMMIT_SHA)
|
||||
endif()
|
||||
|
||||
if(GIT_FOUND)
|
||||
if(VERSION_OVERRIDE)
|
||||
set(git_branch ${RELEASE_VERSION})
|
||||
if (VERSION_OVERRIDE)
|
||||
set (git_branch ${RELEASE_VERSION})
|
||||
else()
|
||||
message("git found: ${GIT_EXECUTABLE} with version ${GIT_VERSION_STRING}")
|
||||
execute_process(
|
||||
@@ -626,8 +587,8 @@ if(ADD_COMMIT_SHA)
|
||||
string(REPLACE "\n" ";" git_branch_list "${git_branches}")
|
||||
|
||||
foreach(git_branch_candidate IN ITEMS ${git_branch_list})
|
||||
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
|
||||
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
|
||||
string(REPLACE "*" "" git_branch_candidate_temp "${git_branch_candidate}")
|
||||
string(STRIP "${git_branch_candidate_temp}" git_branch_candidate_2)
|
||||
if(NOT git_branch_candidate_2 MATCHES "^HEAD$")
|
||||
string(REPLACE "/" ";" git_branch_candidate_2_list "${git_branch_candidate_2}")
|
||||
list(GET git_branch_candidate_2_list -1 git_branch)
|
||||
@@ -645,13 +606,13 @@ if(ADD_COMMIT_SHA)
|
||||
message(STATUS "IfcOpenShell branch: \"${git_branch}\"")
|
||||
message(STATUS "IfcOpenShell commit: \"${git_sha}\"")
|
||||
|
||||
if("${git_branch}" STREQUAL "" OR "${git_sha}" STREQUAL "")
|
||||
if ("${git_branch}" STREQUAL "" OR "${git_sha}" STREQUAL "")
|
||||
message(FATAL_ERROR "Unable to determine commit sha and/or branch")
|
||||
endif()
|
||||
|
||||
target_compile_definitions(
|
||||
IfcParse
|
||||
PRIVATE -DIFCOPENSHELL_BRANCH=${git_branch} -DIFCOPENSHELL_COMMIT=${git_sha}
|
||||
target_compile_definitions(IfcParse PRIVATE
|
||||
-DIFCOPENSHELL_BRANCH=${git_branch}
|
||||
-DIFCOPENSHELL_COMMIT=${git_sha}
|
||||
)
|
||||
endif()
|
||||
endif(ADD_COMMIT_SHA)
|
||||
@@ -669,10 +630,6 @@ if(BUILD_DOCUMENTATION)
|
||||
add_subdirectory(../docs docs)
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCPYTHON)
|
||||
add_subdirectory(../src/ifcwrap ifcwrap)
|
||||
endif()
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(../src/examples examples)
|
||||
endif()
|
||||
@@ -685,32 +642,82 @@ if(BUILD_IFCPYTHON AND WITH_CGAL)
|
||||
add_subdirectory(../src/svgfill svgfill)
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCPYTHON)
|
||||
add_subdirectory(../src/ifcwrap ifcwrap)
|
||||
endif()
|
||||
|
||||
if(BUILD_QTVIEWER)
|
||||
add_subdirectory(../src/qtviewer qtviewer)
|
||||
endif()
|
||||
|
||||
if(BUILD_IFCGEOM)
|
||||
# install(FILES ${IFCGEOM_H_FILES}
|
||||
# DESTINATION ${INCLUDEDIR}/ifcgeom
|
||||
# )
|
||||
|
||||
install(FILES ${SCHEMA_AGNOSTIC_H_FILES}
|
||||
DESTINATION ${INCLUDEDIR}/ifcgeom
|
||||
)
|
||||
|
||||
file(GLOB SERIALIZATION_H_FILES ../src/ifcgeom/serialization/*.h)
|
||||
install(FILES ${SERIALIZATION_H_FILES}
|
||||
DESTINATION ${INCLUDEDIR}/ifcgeom/serialization
|
||||
)
|
||||
|
||||
foreach(kernel ${GEOMETRY_KERNELS})
|
||||
file(GLOB IFCGEOM_H_FILES ../src/ifcgeom/kernels/${kernel}/*.h)
|
||||
install(FILES ${IFCGEOM_H_FILES}
|
||||
DESTINATION ${INCLUDEDIR}/ifcgeom/kernels/${kernel}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
install(TARGETS ${IFCGEOM_SCHEMA_LIBRARIES} ${kernel_libraries} IfcGeom)
|
||||
endif(BUILD_IFCGEOM)
|
||||
if(BUILD_IFCVIEWER)
|
||||
if(BUILD_IFCVIEWER_TESTS)
|
||||
# Catch2 v3 — fetched on demand. Test option is OFF by default so the
|
||||
# default build remains offline-capable.
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
Catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v3.5.4
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(Catch2)
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
add_subdirectory(../src/ifcviewer ifcviewer)
|
||||
add_subdirectory(../src/ifcviewer-minimal ifcviewer-minimal)
|
||||
add_subdirectory(../src/ifcviewer-full ifcviewer-full)
|
||||
endif()
|
||||
|
||||
# Cmake uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE
|
||||
@ONLY
|
||||
)
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
endif()
|
||||
|
||||
# Packaging
|
||||
list(APPEND CPACK_SOURCE_IGNORE_FILES "/\\\\.git" "/build/" "/.pytest_cache/" "/__pycache__/")
|
||||
list(APPEND CPACK_SOURCE_IGNORE_FILES
|
||||
"/\\\\.git"
|
||||
"/build/"
|
||||
"/.pytest_cache/"
|
||||
"/__pycache__/"
|
||||
)
|
||||
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR}/..;/")
|
||||
set(CPACK_PACKAGE_NAME
|
||||
"${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}"
|
||||
)
|
||||
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}${EXTRA_VERSION}")
|
||||
set(CPACK_PACKAGE_FILE_NAME
|
||||
"${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}"
|
||||
)
|
||||
SET(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${EXTRA_VERSION}-${CMAKE_SYSTEM_NAME}")
|
||||
set(CPACK_PACKAGE_DIRECTORY "${PROJECT_BINARY_DIR}/assets")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IfcOpenShell")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "IfcOpenShell.")
|
||||
@@ -723,7 +730,6 @@ set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
|
||||
set(CPACK_GENERATOR "TGZ;DEB")
|
||||
set(CPACK_SOURCE_GENERATOR "TGZ")
|
||||
|
||||
set(BOOST_DEPS "")
|
||||
foreach(COMPONENT IN ITEMS ${BOOST_COMPONENTS})
|
||||
string(REPLACE "_" "-" COMP ${COMPONENT})
|
||||
set(BOOST_DEPS "${BOOST_DEPS}, libboost-${COMP}-dev")
|
||||
@@ -731,19 +737,13 @@ endforeach(COMPONENT)
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS
|
||||
"python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3-dev, python3-pytest ${BOOST_DEPS}"
|
||||
)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "python3, libxml2, libocct-foundation-dev, libocct-modeling-algorithms-dev, libocct-modeling-data-dev, libocct-ocaf-dev, libocct-visualization-dev, libocct-data-exchange-dev, libhdf5-serial-dev, libpython3-dev, python3-pytest ${BOOST_DEPS}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
|
||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "science")
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION
|
||||
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${EXTRA_VERSION}"
|
||||
)
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${EXTRA_VERSION}")
|
||||
set(CPACK_DEBIAN_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_SOURCE_DIR}/cmake/debian/postinst")
|
||||
|
||||
include(CPack)
|
||||
|
||||
include(package_export.cmake)
|
||||
|
||||
@@ -139,7 +139,8 @@ if(NOT OpenCOLLADA_DIR)
|
||||
endif()
|
||||
endif(NOT OpenCOLLADA_DIR)
|
||||
|
||||
if(OPENCOLLADA_FOUND)
|
||||
add_definitions(-DWITH_OPENCOLLADA)
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_OPENCOLLADA)
|
||||
if(OPENCOLLADA_FOUND AND NOT TARGET OpenCOLLADA::OpenCOLLADA)
|
||||
add_library(OpenCOLLADA::OpenCOLLADA INTERFACE IMPORTED)
|
||||
target_include_directories(OpenCOLLADA::OpenCOLLADA INTERFACE ${OPENCOLLADA_INCLUDE_DIRS})
|
||||
target_link_libraries(OpenCOLLADA::OpenCOLLADA INTERFACE ${OPENCOLLADA_LIBRARIES})
|
||||
endif()
|
||||
|
||||
+12
-6
@@ -6,7 +6,7 @@
|
||||
# Input variables could also be provided as environment variables.
|
||||
#
|
||||
# Output targets:
|
||||
# - `PROJ::proj`
|
||||
# - `proj::proj`
|
||||
#
|
||||
|
||||
# To avoid cyclic calls to this file
|
||||
@@ -34,10 +34,12 @@ if((NOT PROJ_INCLUDE_DIR AND NOT PROJ_LIBRARIES))
|
||||
message(FATAL_ERROR "Unable to find PROJ libraries in: ${PROJ_LIBRARY_DIR}")
|
||||
endif()
|
||||
|
||||
add_library(PROJ::proj INTERFACE IMPORTED)
|
||||
target_include_directories(PROJ::proj INTERFACE "${PROJ_INCLUDE_DIR}")
|
||||
target_link_libraries(PROJ::proj INTERFACE ${PROJ_LIBRARIES})
|
||||
target_link_directories(PROJ::proj INTERFACE "${PROJ_LIBRARY}")
|
||||
if(NOT TARGET proj::proj)
|
||||
add_library(proj::proj INTERFACE IMPORTED)
|
||||
target_include_directories(proj::proj INTERFACE "${PROJ_INCLUDE_DIR}")
|
||||
target_link_libraries(proj::proj INTERFACE ${PROJ_LIBRARIES})
|
||||
target_link_directories(proj::proj INTERFACE "${PROJ_LIBRARY}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
find_library(PROJ_LIBRARY NAMES proj PATHS ${PROJ_LIBRARY_DIR})
|
||||
@@ -50,7 +52,11 @@ else()
|
||||
|
||||
set(PROJ_INCLUDE_DIR ${PROJ_INCLUDE_DIR} CACHE FILEPATH "PROJ header files")
|
||||
message(STATUS "Looking for PROJ include files in: ${PROJ_INCLUDE_DIR}")
|
||||
include_directories(${PROJ_INCLUDE_DIR})
|
||||
if(NOT TARGET proj::proj)
|
||||
add_library(proj::proj INTERFACE IMPORTED)
|
||||
target_include_directories(proj::proj INTERFACE "${PROJ_INCLUDE_DIR}")
|
||||
target_link_libraries(proj::proj INTERFACE ${PROJ_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR})
|
||||
|
||||
@@ -64,7 +64,6 @@ set(USD_LIBRARIES
|
||||
find_library(USD_LIBRARY NAMES ${USD_LIBRARIES} PATHS ${USD_LIBRARY_DIR})
|
||||
if(USD_LIBRARY)
|
||||
message(STATUS "USD libraries ${USD_LIBRARIES} found in: ${USD_LIBRARY_DIR}")
|
||||
link_directories(${USD_LIBRARY_DIR})
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to find USD libraries in: ${USD_LIBRARY_DIR}")
|
||||
endif()
|
||||
@@ -82,5 +81,3 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(pxr::USD INTERFACE PXR_STATIC WITH_USD)
|
||||
|
||||
set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_USD)
|
||||
|
||||
@@ -37,7 +37,6 @@ cmake -G "Ninja" ^
|
||||
-D GLTF_SUPPORT:BOOL=ON ^
|
||||
-D BUILD_CONVERT:BOOL=ON ^
|
||||
-D BUILD_IFCMAX:BOOL=OFF ^
|
||||
-D IFCXML_SUPPORT:BOOL=ON ^
|
||||
-D Boost_LIBRARY_DIR:FILEPATH="%LIBRARY_PREFIX%\lib" ^
|
||||
-D Boost_INCLUDE_DIR:FILEPATH="%LIBRARY_PREFIX%\include" ^
|
||||
-D Boost_USE_STATIC_LIBS:BOOL=OFF ^
|
||||
|
||||
@@ -34,7 +34,6 @@ cmake ${CMAKE_ARGS} -G Ninja \
|
||||
-DEIGEN_DIR:FILEPATH=$PREFIX/include/eigen3 \
|
||||
-DCOLLADA_SUPPORT:BOOL=OFF \
|
||||
-DBUILD_EXAMPLES:BOOL=OFF \
|
||||
-DIFCXML_SUPPORT:BOOL=ON \
|
||||
-DGLTF_SUPPORT:BOOL=ON \
|
||||
-DBUILD_CONVERT:BOOL=ON \
|
||||
-DBUILD_IFCPYTHON:BOOL=ON \
|
||||
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
# Build fix: remove `boost_system` from CMake components
|
||||
|
||||
`Boost.System` became header-only in Boost 1.69. Boost 1.90.0 no longer ships a compiled library or CMake config for it, so `find_package(Boost REQUIRED COMPONENTS system ...)` fails.
|
||||
|
||||
## Fix
|
||||
|
||||
`cmake/CMakeLists.txt`:
|
||||
|
||||
```diff
|
||||
- set(BOOST_COMPONENTS system program_options regex thread date_time iostreams)
|
||||
+ set(BOOST_COMPONENTS program_options regex thread date_time iostreams)
|
||||
```
|
||||
|
||||
The headers are still available; no linking is needed.
|
||||
|
||||
# Build fix: add `template` keyword for dependent template member calls
|
||||
|
||||
Calling a template member function through a dependent expression (e.g. `storage->has_attribute_value<T>(...)` where `storage`'s type depends on a template parameter) requires the `template` keyword to disambiguate from a less-than comparison.
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
src/ifcparse/IfcParse.cpp:1856:67: error: expected primary-expression before '>' token
|
||||
1856 | if (storage->has_attribute_value<express::Base>(attr_index)) {
|
||||
| ^
|
||||
```
|
||||
|
||||
Six identical errors at lines 1856, 1865, 1896, 1905, 1934, 1943.
|
||||
|
||||
## Fix
|
||||
|
||||
`src/ifcparse/IfcParse.cpp`:
|
||||
|
||||
```diff
|
||||
-storage->has_attribute_value<express::Base>(attr_index)
|
||||
+storage->template has_attribute_value<express::Base>(attr_index)
|
||||
|
||||
-storage->has_attribute_value<Blank>(attr_index)
|
||||
+storage->template has_attribute_value<Blank>(attr_index)
|
||||
```
|
||||
|
||||
Applied at all six call sites in `in_memory_file_storage::read_from_stream`.
|
||||
|
||||
# Linker fix: missing explicit template instantiations for `InstanceStreamer`
|
||||
|
||||
`InstanceStreamer` is a class template with methods defined in `IfcParse.cpp`, not the header. Without explicit instantiations, the linker can't find the symbols when the SWIG wrapper loads.
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
ImportError: undefined symbol: _ZN8IfcParse16InstanceStreamerINS_10FileReaderINS_14FullBufferImplEEEEC1EPS3_PNS_7IfcFileE
|
||||
(IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(FileReader<FullBufferImpl>*, IfcFile*))
|
||||
```
|
||||
|
||||
## Fix
|
||||
|
||||
Cannot use `template class InstanceStreamer<...>` because some constructors have `static_assert` guards that reject certain reader types. Instead, instantiate each member function individually per reader type, only including the constructors valid for that type.
|
||||
|
||||
`src/ifcparse/IfcParse.cpp` (after the last `InstanceStreamer` method definition):
|
||||
|
||||
```cpp
|
||||
// FullBufferImpl
|
||||
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(IfcParse::IfcFile*);
|
||||
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(const std::string&, bool, IfcParse::IfcFile*);
|
||||
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(void*, int, IfcParse::IfcFile*);
|
||||
template IfcParse::InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(FileReader<FullBufferImpl>*, IfcParse::IfcFile*);
|
||||
// ... plus ensure_header, initialize_header, hasSemicolon, semicolonCount,
|
||||
// pushPage, bypassTypes, readInstance
|
||||
|
||||
// PushedSequentialImpl — same pattern, different valid constructors
|
||||
|
||||
// MMapFileReader (ifdef USE_MMAP) — same pattern
|
||||
```
|
||||
|
||||
# Linker fix: `FullBufferImpl` missing buffer constructor
|
||||
|
||||
SWIG's `stream_from_string` calls `InstanceStreamer<FileReader<FullBufferImpl>>(void*, int, IfcFile*)`, but the `(void*, int)` constructor previously hit a `static_assert` for `FullBufferImpl` — it only allowed `PushedSequentialImpl`.
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
ImportError: undefined symbol: _ZN8IfcParse16InstanceStreamerINS_10FileReaderINS_14FullBufferImplEEEEC1EPviPNS_7IfcFileE
|
||||
(InstanceStreamer<FileReader<FullBufferImpl>>::InstanceStreamer(void*, int, IfcFile*))
|
||||
```
|
||||
|
||||
## Fix
|
||||
|
||||
Three changes to make `FullBufferImpl` support buffer-based and default construction:
|
||||
|
||||
`src/ifcparse/FileReader.h` — add buffer constructor to `FullBufferImpl`:
|
||||
|
||||
```diff
|
||||
class IFC_PARSE_API FullBufferImpl {
|
||||
public:
|
||||
explicit FullBufferImpl(const std::string& fn);
|
||||
+ FullBufferImpl(void* data, size_t length);
|
||||
```
|
||||
|
||||
`src/ifcparse/FileReader.h` — add `FileReader(void*, size_t)` forwarding constructor:
|
||||
|
||||
```diff
|
||||
+ FileReader(void* data, size_t length)
|
||||
+ : cursor_(0) {
|
||||
+ if constexpr (std::is_same_v<Impl, FullBufferImpl>) {
|
||||
+ impl_ = std::make_shared<Impl>(data, length);
|
||||
+ } else {
|
||||
+ static_assert(...);
|
||||
+ }
|
||||
+ }
|
||||
```
|
||||
|
||||
`src/ifcparse/FileReader.cpp` — implement the constructor:
|
||||
|
||||
```cpp
|
||||
FullBufferImpl::FullBufferImpl(void* data, size_t length)
|
||||
: buf_(static_cast<char*>(data), static_cast<char*>(data) + length)
|
||||
, size_(length) {
|
||||
}
|
||||
```
|
||||
|
||||
`src/ifcparse/IfcParse.cpp` — extend the two `InstanceStreamer` constructors to accept `FullBufferImpl`:
|
||||
|
||||
```diff
|
||||
// InstanceStreamer(IfcFile*):
|
||||
+ } else if constexpr (std::is_same_v<Reader, FileReader<FullBufferImpl>>) {
|
||||
+ owned_stream_ = std::make_unique<Reader>(nullptr, (size_t)0);
|
||||
|
||||
// InstanceStreamer(void*, int, IfcFile*):
|
||||
+ } else if constexpr (std::is_same_v<Reader, FileReader<FullBufferImpl>>) {
|
||||
+ owned_stream_ = std::make_unique<Reader>(data, (size_t)length);
|
||||
```
|
||||
|
||||
# Runtime fix: segfault in `parse_context::push()` due to vector reallocation
|
||||
|
||||
`parse_context_pool` stores nodes in a `std::vector<parse_context>`. During parsing, `load()` takes a `parse_context&` parameter and calls `context.push()`, which calls `pool_->make()`. If the pool's vector reallocates (via `emplace_back`), all existing references into the vector — including the `context` reference held by the caller — become dangling. Subsequent access through the dangling reference causes a segfault.
|
||||
|
||||
Triggered by larger IFC files (e.g. `ISSUE_159_kleine_Wohnung_R22.ifc`, 9.5 MB) that cause enough pool growth to trigger reallocation.
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Thread 1 received signal SIGSEGV, Segmentation fault.
|
||||
0x... in IfcParse::parse_context::push()
|
||||
#1 in_memory_file_storage::load(...) // context& is dangling after reallocation
|
||||
#2 in_memory_file_storage::load(...) // parent call
|
||||
#3 InstanceStreamer::readInstance()
|
||||
```
|
||||
|
||||
## Fix
|
||||
|
||||
`src/ifcparse/storage.h` — change the pool container from `std::vector` to `std::deque`, which does not invalidate references on `push_back`/`emplace_back`:
|
||||
|
||||
```diff
|
||||
+#include <deque>
|
||||
|
||||
struct parse_context_pool {
|
||||
- std::vector<parse_context> nodes_;
|
||||
+ std::deque<parse_context> nodes_;
|
||||
```
|
||||
|
||||
# Runtime fix: `express::Base` comparison operators throw on null/expired instances
|
||||
|
||||
`express::Base::operator<` and `operator==` called `data()`, which throws `std::runtime_error("Trying to access deleted instance reference")` when the internal `weak_ptr` is expired. A default-constructed `express::Base` (the value-type equivalent of a null pointer) always has an expired `weak_ptr`.
|
||||
|
||||
## Why this model triggers it
|
||||
|
||||
The bug requires two conditions to coincide:
|
||||
|
||||
1. A representation is shared by **more than one product** (via `IfcRepresentationMap` / `IfcMappedItem`).
|
||||
2. At least one of those products has **no material association**, so `get_single_material_association()` returns `express::Base{}` (the null equivalent).
|
||||
|
||||
In `advanced_model.ifc`, Body representations like `#449` (Body/Brep) have a single `IfcRepresentationMap` (`#453`) with 13 `IfcMappedItem` usages, meaning 13 products share the geometry. Some of those products (e.g. `IfcFlowTerminal` instances) have no `IfcRelAssociatesMaterial`, so `get_single_material_association` returns `express::Base{}`.
|
||||
|
||||
Smaller or simpler models don't hit this because either:
|
||||
- Every representation maps to only 1 product → `reuse_ok_` short-circuits at `products.size() == 1` before reaching the material check.
|
||||
- Every product has a material association → no null `express::Base` is ever inserted into the set.
|
||||
|
||||
## Exact call sequence
|
||||
|
||||
```
|
||||
Iterator::initialize()
|
||||
try {
|
||||
mapping::get_representations(reps, filters_)
|
||||
addRepresentationsFromDefaultContexts(representations)
|
||||
→ collects reps from subcontexts in order:
|
||||
Axis (#115): 143 reps
|
||||
Body (#117): 7550 reps
|
||||
FootPrint (#119): 12 reps
|
||||
|
||||
for (auto representation : representations):
|
||||
|
||||
── Axis reps (indices 0–142) ──────────────────────────
|
||||
products_represented_by(rep, rmap)
|
||||
→ OfProductRepresentation: 1 product each
|
||||
filter_products(products, filters) → 1 product
|
||||
reuse_ok_(ifcproducts)
|
||||
→ products.size() == 1 → return true ← SHORT-CIRCUIT, no material check
|
||||
representation_mapped_to(rep) → null (no MappedItem)
|
||||
→ task created. 143 tasks accumulated.
|
||||
|
||||
── First Body rep #449 (Body/Brep) ────────────────────
|
||||
products_represented_by(#449, rmap)
|
||||
→ OfProductRepresentation: empty
|
||||
→ RepresentationMap: 1 map (#453)
|
||||
→ MapUsage: 13 MappedItems → traces through to 13 IfcProducts
|
||||
filter_products(products, filters) → 13 products
|
||||
reuse_ok_(ifcproducts) ← CRASH HERE
|
||||
→ products.size() == 1? NO (13 products)
|
||||
→ for each product:
|
||||
find_openings(product) → OK
|
||||
get_single_material_association(product)
|
||||
→ some products have no IfcRelAssociatesMaterial
|
||||
→ returns express::Base{} (expired weak_ptr)
|
||||
associated_single_materials.insert(result)
|
||||
→ std::set::insert calls operator<
|
||||
→ operator< calls data()
|
||||
→ data() calls data_.lock() → expired → THROWS
|
||||
"Trying to access deleted instance reference"
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
Logger::Error(e) ← exception caught here, get_representations aborted
|
||||
}
|
||||
|
||||
→ reps contains only the 143 Axis tasks created before the throw
|
||||
→ all 143 Axis reps have Curve2D geometry → map(representation) returns null
|
||||
→ no valid elements produced → initialize() returns false
|
||||
```
|
||||
|
||||
In the old pointer-based code, `reuse_ok_` used `std::set<const IfcUtil::IfcBaseEntity*>` and `get_single_material_association` returned `nullptr`. Inserting `nullptr` into a `std::set<T*>` is a plain pointer comparison — no dereference, no throw. The refactoring to `std::set<express::Base>` changed the comparison from pointer comparison to `express::Base::operator<`, which unconditionally dereferences through `data()`.
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
[Error] Trying to access deleted instance reference
|
||||
[Notice] Created 143 tasks for 143 products ← only Axis reps; all Body reps lost
|
||||
initialize() returned: False
|
||||
```
|
||||
|
||||
## Fix
|
||||
|
||||
`src/ifcparse/express.h` — use `weak_ptr::lock().get()` instead of `data()` so that expired pointers compare as `nullptr` (matching old raw-pointer semantics):
|
||||
|
||||
```diff
|
||||
bool operator<(const Base& other) const {
|
||||
- return data() < other.data();
|
||||
+ auto a = data_.lock();
|
||||
+ auto b = other.data_.lock();
|
||||
+ return a.get() < b.get();
|
||||
}
|
||||
|
||||
bool operator==(const Base& other) const {
|
||||
- return data() == other.data();
|
||||
+ auto a = data_.lock();
|
||||
+ auto b = other.data_.lock();
|
||||
+ return a.get() == b.get();
|
||||
}
|
||||
```
|
||||
|
||||
# Runtime fix: `entity_instance` missing `get_inverse` due to SWIG `%rename` collision
|
||||
|
||||
Accessing inverse attributes (e.g. `element.IsDecomposedBy`) on any entity raises `AttributeError: entity instance of type 'IFC2X3.IfcProject' has no attribute 'get_inverse'`.
|
||||
|
||||
## Why
|
||||
|
||||
`entity_instance_mixin.__getattr__` (line 106 of `entity_instance.py`) calls `self.get_inverse(name)` when it detects an inverse attribute. Since the mixin inherits into the SWIG-generated `entity_instance` class (via the `object = custom_base` hack in `IfcParseWrapper.i:936`), `self.get_inverse` must resolve to a method on the SWIG class.
|
||||
|
||||
However, `IfcParseWrapper.i:70` has a global rename:
|
||||
|
||||
```
|
||||
%rename("get_inverses_by_declaration") get_inverse;
|
||||
```
|
||||
|
||||
This was intended for `ifcopenshell::file::get_inverse` (which takes an entity + declaration and returns instances by reference), but SWIG `%rename` is global — it also renames the `%extend express::Base` method `get_inverse(const std::string& a)` at line 551. So the Python-side `entity_instance` class exposes the method as `get_inverses_by_declaration`, not `get_inverse`.
|
||||
|
||||
The old code (`v0.8.0`) didn't hit this because `__getattr__` called `self.wrapped_data.get_inverse(name)` on an inner `ifcopenshell_wrapper.entity_instance` object — but in that old layout, the inner object was constructed differently and the rename didn't apply the same way (or the method had a different path). In the new mixin approach, `self` **is** the SWIG object, so the rename is directly visible.
|
||||
|
||||
## Fix
|
||||
|
||||
`src/ifcwrap/IfcParseWrapper.i` — override the global rename specifically for `express::Base::get_inverse`, restoring the original name on entity instances:
|
||||
|
||||
```diff
|
||||
+%rename("get_inverse") express::Base::get_inverse;
|
||||
%rename("get_inverses_by_declaration") get_inverse;
|
||||
```
|
||||
|
||||
Add this line **before** the global rename (or anywhere before the `%extend express::Base` block). This scoped rename takes precedence for `express::Base`, so:
|
||||
- `entity_instance.get_inverse(name)` works as the mixin expects
|
||||
- `file.get_inverses_by_declaration(...)` keeps its intended name
|
||||
|
||||
## Python-side workaround
|
||||
|
||||
`entity_instance.py:106` — call the method by its SWIG-renamed name:
|
||||
|
||||
```diff
|
||||
- vs = self.get_inverse(name)
|
||||
+ vs = self.get_inverses_by_declaration(name)
|
||||
```
|
||||
|
||||
# Runtime fix: `entity_instance` class no longer importable from `entity_instance` module
|
||||
|
||||
The class rename from `entity_instance` to `entity_instance_mixin` broke external code that does `from ifcopenshell.entity_instance import entity_instance`.
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
ImportError: cannot import name 'entity_instance' from 'ifcopenshell.entity_instance'
|
||||
```
|
||||
|
||||
Triggered at import time via `ifcopenshell.util.pset` (and likely other modules).
|
||||
|
||||
## Fix
|
||||
|
||||
`src/ifcopenshell-python/ifcopenshell/entity_instance.py` — add a backwards-compatible alias at the bottom of the module:
|
||||
|
||||
```python
|
||||
entity_instance = entity_instance_mixin
|
||||
```
|
||||
+95
-51
@@ -162,6 +162,8 @@ USD_VERSION = "23.05"
|
||||
TBB_VERSION = "2021.9.0"
|
||||
ROCKSDB_VERSION = "9.11.2"
|
||||
ZSTD_VERSION = "1.5.7"
|
||||
MANIFOLD_VERSION = "3.2.1"
|
||||
|
||||
# binaries
|
||||
cp = "cp"
|
||||
bash = "bash"
|
||||
@@ -330,7 +332,7 @@ cecho(""" - IFC Schemas to compile. If not provided, fallback to default provide
|
||||
|
||||
dependency_tree: "dict[str, tuple[str, ...]]" = {
|
||||
"IfcParse": ("boost", "libxml2", "hdf5", "rocksdb"),
|
||||
"IfcGeom": ("IfcParse", "occ", "json", "cgal", "eigen", "OpenCOLLADA"),
|
||||
"IfcGeom": ("IfcParse", "occ", "manifold", "json", "cgal", "eigen", "OpenCOLLADA"),
|
||||
"IfcConvert": ("IfcGeom",),
|
||||
"OpenCOLLADA": ("libxml2", "pcre"),
|
||||
"IfcGeomServer": ("IfcGeom",),
|
||||
@@ -347,6 +349,7 @@ dependency_tree: "dict[str, tuple[str, ...]]" = {
|
||||
"eigen": (),
|
||||
"rocksdb": ("zstd",),
|
||||
"zstd": (),
|
||||
"manifold": (),
|
||||
# 'usd': ('boost', 'oneTBB')
|
||||
}
|
||||
|
||||
@@ -583,6 +586,11 @@ def run_cmake(arg1, cmake_args: "list[str]", cmake_dir: Union[str, None] = None,
|
||||
]
|
||||
)
|
||||
|
||||
if not any("BUILD_SHARED_LIBS" in f for f in cmake_args):
|
||||
cmake_flags.append(
|
||||
f"-DBUILD_SHARED_LIBS={OFF_ON[not BUILD_STATIC]}",
|
||||
)
|
||||
|
||||
run(
|
||||
[
|
||||
*wasm,
|
||||
@@ -591,7 +599,6 @@ def run_cmake(arg1, cmake_args: "list[str]", cmake_dir: Union[str, None] = None,
|
||||
*cmake_flags,
|
||||
*cmake_args,
|
||||
f"-DCMAKE_BUILD_TYPE={BUILD_CFG}",
|
||||
f"-DBUILD_SHARED_LIBS={OFF_ON[not BUILD_STATIC]}",
|
||||
f"-DCMAKE_SHARED_LINKER_FLAGS={os.environ['LDFLAGS']}",
|
||||
],
|
||||
cwd=cwd,
|
||||
@@ -994,6 +1001,29 @@ elif "occ" in targets:
|
||||
download_name=f"OCE-{OCE_VERSION}.tar.gz",
|
||||
)
|
||||
|
||||
if "manifold" in targets:
|
||||
dependency_name = f"manifold-{MANIFOLD_VERSION}"
|
||||
build_dependency(
|
||||
name=dependency_name,
|
||||
mode="cmake",
|
||||
build_tool_args=[
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/{dependency_name}",
|
||||
"-DMANIFOLD_PAR=OFF",
|
||||
"-DMANIFOLD_CROSS_SECTION=OFF",
|
||||
"-DMANIFOLD_PYBIND=OFF",
|
||||
"-DMANIFOLD_JSBIND=OFF",
|
||||
"-DMANIFOLD_CBIND=OFF",
|
||||
"-DMANIFOLD_TEST=OFF",
|
||||
"-DMANIFOLD_EXPORT=OFF",
|
||||
"-DMANIFOLD_DOWNLOADS=OFF",
|
||||
*MAC_CROSS_COMPILE_INTEL_ARGS,
|
||||
],
|
||||
download_url="https://github.com/elalish/manifold.git",
|
||||
download_name="manifold",
|
||||
download_tool=download_tool_git,
|
||||
revision=f"v{MANIFOLD_VERSION}",
|
||||
)
|
||||
|
||||
if "libxml2" in targets:
|
||||
OLD_CC = ""
|
||||
if MAC_CROSS_COMPILE_INTEL:
|
||||
@@ -1094,10 +1124,19 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
|
||||
f"http://www.python.org/ftp/python/{PYTHON_VERSION}/",
|
||||
f"Python-{PYTHON_VERSION}.tgz",
|
||||
)
|
||||
python_bin = INSTALL_DIR / f"python-{PYTHON_VERSION}" / "bin" / "python3"
|
||||
python_install = INSTALL_DIR / f"python-{PYTHON_VERSION}"
|
||||
python_bin = python_install / "bin" / "python3"
|
||||
# `_ssl` module is present -> we will be able to install `numpy` later
|
||||
# to verify IfcOpenShell installation
|
||||
run([str(python_bin), "-c", "import _ssl"])
|
||||
try:
|
||||
run([str(python_bin), "-c", "import _ssl"])
|
||||
except RuntimeError:
|
||||
print(
|
||||
"ERROR: Python was built without SSL support (_ssl module is missing). "
|
||||
f"To fix this: remove the installed Python at {python_install}; "
|
||||
"install OpenSSL development libraries and re-run."
|
||||
)
|
||||
raise
|
||||
|
||||
if MAC_CROSS_COMPILE_INTEL:
|
||||
assert original_path
|
||||
@@ -1295,16 +1334,17 @@ if os.environ.get("NO_CLEAN", "").lower() not in {"1", "on", "true"}:
|
||||
if os.path.exists(IFCOS_DIR):
|
||||
shutil.rmtree(IFCOS_DIR)
|
||||
os.makedirs(IFCOS_DIR, exist_ok=True)
|
||||
executables_dir = os.path.join(IFCOS_DIR, "executables")
|
||||
os.makedirs(executables_dir, exist_ok=True)
|
||||
ifcos_build_dir = os.path.join(IFCOS_DIR, "build")
|
||||
os.makedirs(ifcos_build_dir, exist_ok=True)
|
||||
|
||||
|
||||
cmake_args = [
|
||||
"-DUSE_MMAP=OFF",
|
||||
"-DBUILD_EXAMPLES=OFF",
|
||||
"-DBUILD_SHARED_LIBS=" + OFF_ON[not BUILD_STATIC],
|
||||
"-DBUILD_SHARED_LIBS=ON",
|
||||
"-DGLTF_SUPPORT=ON",
|
||||
"-DBoost_NO_BOOST_CMAKE=On",
|
||||
"-DCREATE_BUNDLE=On",
|
||||
"-DADD_COMMIT_SHA=" + ("On" if ADD_COMMIT_SHA else "Off"),
|
||||
"-DVERSION_OVERRIDE=" + ("On" if ADD_COMMIT_SHA else "Off"),
|
||||
*MAC_CROSS_COMPILE_INTEL_ARGS,
|
||||
@@ -1354,6 +1394,10 @@ elif "occ" in targets:
|
||||
occ_library_dir = f"{DEPS_DIR}/install/oce-{OCE_VERSION}/lib"
|
||||
cmake_args.extend(["-DOCC_INCLUDE_DIR=" + occ_include_dir, "-DOCC_LIBRARY_DIR=" + occ_library_dir])
|
||||
|
||||
if "manifold" in targets:
|
||||
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/manifold-{MANIFOLD_VERSION}")
|
||||
cmake_args.append("-DWITH_MANIFOLD=On")
|
||||
|
||||
if "OpenCOLLADA" in targets:
|
||||
# pcre is a dependency of OpenCOLLADA, but since we `find_package`,
|
||||
# we don't need to add it explicitly here as cmake will find it from the config.
|
||||
@@ -1399,40 +1443,33 @@ if "rocksdb" in targets:
|
||||
if "swig" in targets:
|
||||
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/swig-{SWIG_VERSION}")
|
||||
|
||||
ifcos_build_args = [
|
||||
f"-DBUILD_IFCGEOM={OFF_ON['IfcGeom' in targets]}",
|
||||
f"-DBUILD_GEOMSERVER={OFF_ON['IfcGeomServer' in targets]}",
|
||||
f"-DBUILD_CONVERT={OFF_ON['IfcConvert' in targets]}",
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/ifcopenshell",
|
||||
]
|
||||
|
||||
if not WASM and (not explicit_targets or {"IfcGeom", "IfcConvert", "IfcGeomServer"} & set(explicit_targets)):
|
||||
logger.info("\rConfiguring executables...")
|
||||
|
||||
exec_args = [
|
||||
f"-DBUILD_IFCGEOM={OFF_ON['IfcGeom' in targets]}",
|
||||
f"-DBUILD_GEOMSERVER={OFF_ON['IfcGeomServer' in targets]}",
|
||||
f"-DBUILD_CONVERT={OFF_ON['IfcConvert' in targets]}",
|
||||
*ifcos_build_args,
|
||||
f"-DBUILD_IFCPYTHON=OFF",
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/ifcopenshell",
|
||||
]
|
||||
|
||||
run_cmake("", exec_args + cmake_args + get_cmake_args_prefix_path(), cmake_dir=CMAKE_DIR, cwd=executables_dir)
|
||||
run_cmake("", exec_args + cmake_args + get_cmake_args_prefix_path(), cmake_dir=CMAKE_DIR, cwd=ifcos_build_dir)
|
||||
|
||||
logger.info("\rBuilding executables... ")
|
||||
|
||||
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "VERBOSE=1"], cwd=executables_dir)
|
||||
run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=executables_dir)
|
||||
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "VERBOSE=1"], cwd=ifcos_build_dir)
|
||||
run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=ifcos_build_dir)
|
||||
|
||||
if "IfcOpenShell-Python" in targets:
|
||||
# On OSX the actual Python library is not linked against.
|
||||
ADDITIONAL_ARGS = ""
|
||||
wrapper_ldflags = ""
|
||||
if platform.system() == "Darwin":
|
||||
ADDITIONAL_ARGS = "-Wl,-undefined,dynamic_lookup"
|
||||
|
||||
# NOTE: We don't use `CXXFLAGS` for wrappers, so wrapper is compiled with different flags
|
||||
# (e.g. ` -fdata-sections` is missing, which is set by default for executables)
|
||||
# So cache doesn't match and running build-all.py builds most of ifcopenshell libraries twice.
|
||||
os.environ["CPPFLAGS"] = f"{CXXFLAGS_MINIMAL} {ADDITIONAL_ARGS}"
|
||||
os.environ["CXXFLAGS"] = f"{CXXFLAGS_MINIMAL} {ADDITIONAL_ARGS}"
|
||||
os.environ["CFLAGS"] = f"{CFLAGS_MINIMAL} {ADDITIONAL_ARGS}"
|
||||
os.environ["LDFLAGS"] = f"{LDFLAGS} {ADDITIONAL_ARGS}"
|
||||
|
||||
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
|
||||
os.makedirs(python_dir, exist_ok=True)
|
||||
# On OSX the actual Python library is not linked against.
|
||||
wrapper_ldflags = "-Wl,-undefined,dynamic_lookup"
|
||||
|
||||
def compile_python_wrapper(
|
||||
python_version: str,
|
||||
@@ -1447,10 +1484,6 @@ if "IfcOpenShell-Python" in targets:
|
||||
|
||||
logger.info(f"\rConfiguring python {python_version} wrapper...")
|
||||
|
||||
cache_path = os.path.join(python_dir, "CMakeCache.txt")
|
||||
if os.path.exists(cache_path):
|
||||
os.remove(cache_path)
|
||||
|
||||
if python_path:
|
||||
# We couldn't just prefix PATH and have to provide all variables explicitly,
|
||||
# see ifcwrap/cmake for the details.
|
||||
@@ -1464,27 +1497,38 @@ if "IfcOpenShell-Python" in targets:
|
||||
)
|
||||
|
||||
assert python_include
|
||||
run_cmake(
|
||||
"",
|
||||
cmake_args
|
||||
+ get_cmake_args_prefix_path()
|
||||
+ [
|
||||
*([f"-DPYTHON_EXECUTABLE={python_executable}"] if python_executable else []),
|
||||
# Needed because pyodide is expecting setup.py to be in the root.
|
||||
*([f"-DPYTHON_MODULE_INSTALL_DIR={REPO_PATH}"] * WASM),
|
||||
f"-DPYTHON_INCLUDE_DIR={python_include}",
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/ifcopenshell/tmp",
|
||||
"-DUSERSPACE_PYTHON_PREFIX="
|
||||
+ ["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}],
|
||||
],
|
||||
cmake_dir=CMAKE_DIR,
|
||||
cwd=python_dir,
|
||||
)
|
||||
old_ldflags = os.environ["LDFLAGS"]
|
||||
if wrapper_ldflags:
|
||||
os.environ["LDFLAGS"] = f"{old_ldflags} {wrapper_ldflags}"
|
||||
|
||||
try:
|
||||
run_cmake(
|
||||
"",
|
||||
ifcos_build_args
|
||||
+ [
|
||||
"-DBUILD_IFCPYTHON=ON",
|
||||
]
|
||||
+ cmake_args
|
||||
+ get_cmake_args_prefix_path()
|
||||
+ [
|
||||
*([f"-DPYTHON_EXECUTABLE={python_executable}"] if python_executable else []),
|
||||
# Needed because pyodide is expecting setup.py to be in the root.
|
||||
*([f"-DPYTHON_MODULE_INSTALL_DIR={REPO_PATH}"] * WASM),
|
||||
f"-DPYTHON_INCLUDE_DIR={python_include}",
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/ifcopenshell/tmp",
|
||||
"-DUSERSPACE_PYTHON_PREFIX="
|
||||
+ ["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}],
|
||||
],
|
||||
cmake_dir=CMAKE_DIR,
|
||||
cwd=ifcos_build_dir,
|
||||
)
|
||||
finally:
|
||||
os.environ["LDFLAGS"] = old_ldflags
|
||||
|
||||
logger.info(f"\rBuilding python {python_version} wrapper... ")
|
||||
|
||||
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "ifcopenshell_wrapper", "VERBOSE=1"], cwd=python_dir)
|
||||
run([make, "install/local"], cwd=os.path.join(python_dir, "ifcwrap"))
|
||||
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "ifcopenshell_wrapper", "VERBOSE=1"], cwd=ifcos_build_dir)
|
||||
run([make, "install/local"], cwd=os.path.join(ifcos_build_dir, "ifcwrap"))
|
||||
|
||||
if python_executable:
|
||||
run([python_executable, "-m", "ensurepip"])
|
||||
@@ -1515,7 +1559,7 @@ if "IfcOpenShell-Python" in targets:
|
||||
)
|
||||
# Copy setup.py where pyodide build system expects it.
|
||||
shutil.copy(REPO_PATH / "pyodide" / "setup.py", REPO_PATH)
|
||||
# Empty pyproject so it's contents won't affect the resulting wheelthe the
|
||||
# Empty pyproject so it's contents won't affect the resulting wheel
|
||||
# otherwise the wheel will use version and dependencies from toml, not setup.py.
|
||||
(REPO_PATH / "pyproject.toml").write_text("")
|
||||
|
||||
|
||||
@@ -14,18 +14,11 @@ source .venv/bin/activate
|
||||
uv pip install pyodide-build
|
||||
# `uv run` is required, so xbuildenv would skip using `pip`.
|
||||
uv run pyodide xbuildenv install
|
||||
uv run pyodide xbuildenv install-emscripten
|
||||
|
||||
# Emscripten doesn't come with xbuildenv.
|
||||
if [ ! -d emsdk ]; then
|
||||
git clone https://github.com/emscripten-core/emsdk
|
||||
fi
|
||||
pushd emsdk
|
||||
PYODIDE_EMSCRIPTEN_VERSION=$(pyodide config get emscripten_version)
|
||||
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
|
||||
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
|
||||
source emsdk_env.sh
|
||||
EMSDK_ROOT=$(pyodide config get emscripten_dir)
|
||||
source ${EMSDK_ROOT}/emsdk_env.sh
|
||||
which emcc
|
||||
popd
|
||||
|
||||
mkdir -p packages/ifcopenshell
|
||||
VERSION=`cat IfcOpenShell/VERSION`
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
#
|
||||
# /// script
|
||||
# # Latest Pyodide build env versions are listed here:
|
||||
# # https://pyodide.github.io/pyodide/api/pyodide-cross-build-environments.json
|
||||
# # https://github.com/pyodide/pyodide-build/blob/main/pyodide_build/xbuildenv_releases.py
|
||||
# requires-python = "==3.13.2"
|
||||
# dependencies = [
|
||||
# "requests",
|
||||
# "setuptools",
|
||||
# ]
|
||||
# ///
|
||||
"""
|
||||
Pack an IfcOpenShell WASM wheel using Pyodide build system.
|
||||
|
||||
Usage:
|
||||
uv run make_wheel.py # Show this help
|
||||
uv run make_wheel.py --build # Build wheel
|
||||
uv run make_wheel.py --clean # Clean build artifacts and exit
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote
|
||||
|
||||
import requests
|
||||
|
||||
# Get repo root (parent of this script's parent directory)
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
PYODIDE_DIR = REPO_ROOT / "pyodide"
|
||||
BUILD_DIR = PYODIDE_DIR / "build"
|
||||
|
||||
# Hardcoded path (Windows packing workaround with --dev flag)
|
||||
PYODIDE_BUILD = Path(r"L:\Projects\Github\pyodide-build")
|
||||
|
||||
# Wheel platform tag (from PYODIDE_EMSCRIPTEN_VERSION in pyodide-build/Makefile.envs)
|
||||
WHEEL_PLATFORM_TAG = "emscripten_4_0_9_wasm32"
|
||||
|
||||
# Location where ifcopenshell will be extracted
|
||||
IFCOPENSHELL_DIR = PYODIDE_DIR / "ifcopenshell"
|
||||
|
||||
|
||||
class WheelBuilder:
|
||||
@staticmethod
|
||||
def extract_ifcopenshell_from_git(dst: Path) -> None:
|
||||
"""Extract ifcopenshell directory from git repo into destination."""
|
||||
Tools.rmrf(dst)
|
||||
|
||||
print(f"Extracting ifcopenshell from git to {dst}...")
|
||||
# Use git ls-files piped to git checkout-index to avoid copying
|
||||
# untracked or ignored files from the actual repo.
|
||||
ls_proc = subprocess.Popen(
|
||||
["git", "ls-files", "-z", "src/ifcopenshell-python/ifcopenshell"],
|
||||
cwd=REPO_ROOT,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
checkout_proc = subprocess.Popen(
|
||||
["git", "checkout-index", "-z", "--prefix", "pyodide/", "--stdin"],
|
||||
cwd=REPO_ROOT,
|
||||
stdin=ls_proc.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
assert ls_proc.stdout is not None
|
||||
ls_proc.stdout.close()
|
||||
checkout_proc.communicate()
|
||||
|
||||
if checkout_proc.returncode != 0:
|
||||
assert checkout_proc.stderr is not None
|
||||
raise RuntimeError(f"Failed to extract: {checkout_proc.stderr.decode()}")
|
||||
|
||||
# Move src/ifcopenshell-python/ifcopenshell to ifcopenshell.
|
||||
temp_src = PYODIDE_DIR / "src" / "ifcopenshell-python" / "ifcopenshell"
|
||||
shutil.move(temp_src, dst)
|
||||
|
||||
# Clean up temporary src directory.
|
||||
Tools.rmrf(PYODIDE_DIR / "src")
|
||||
|
||||
print("✓ Extracted ifcopenshell from git")
|
||||
|
||||
@staticmethod
|
||||
def get_wheel_url(makefile_path: Path) -> str:
|
||||
"""Get S3 wheel URL based on BINARY_VERSION and BUILD_COMMIT from Makefile."""
|
||||
|
||||
def parse_makefile_vars() -> dict[str, str]:
|
||||
content = makefile_path.read_text()
|
||||
vars: dict[str, str] = {}
|
||||
for match in re.finditer(r"^(BINARY_VERSION|BUILD_COMMIT):=(.+)$", content, re.MULTILINE):
|
||||
vars[match.group(1)] = match.group(2).strip()
|
||||
return vars
|
||||
|
||||
vars: dict[str, str] = parse_makefile_vars()
|
||||
binary_version = vars["BINARY_VERSION"]
|
||||
build_commit = vars["BUILD_COMMIT"]
|
||||
filename = f"ifcopenshell-{binary_version}+{build_commit}-cp313-cp313-pyodide_2025_0_wasm32.whl"
|
||||
encoded_filename = quote(filename, safe="")
|
||||
return f"https://s3.amazonaws.com/ifcopenshell-builds/{encoded_filename}"
|
||||
|
||||
@staticmethod
|
||||
def download_and_extract_so(url: str, build_dir: Path) -> tuple[Path, Path]:
|
||||
"""Download wheel from URL and extract .so and .py files."""
|
||||
py_wrapper_filename = "ifcopenshell_wrapper.py"
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
wheel_path = build_dir / url.rsplit("/", 1)[-1]
|
||||
|
||||
if wheel_path.exists():
|
||||
print(f"Using cached wheel: {wheel_path}")
|
||||
else:
|
||||
print(f"Downloading {url}...")
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
wheel_path.write_bytes(response.content)
|
||||
|
||||
print("Extracting _ifcopenshell_wrapper files...")
|
||||
with zipfile.ZipFile(wheel_path) as zf:
|
||||
so_files = [f for f in zf.namelist() if f.endswith(".so")]
|
||||
py_files = [f for f in zf.namelist() if f.endswith(py_wrapper_filename)]
|
||||
|
||||
assert so_files, "No .so file found in wheel"
|
||||
assert py_files, f"No {py_wrapper_filename} file found in wheel"
|
||||
|
||||
so_file = so_files[0]
|
||||
so_dst = build_dir / Path(so_file).name
|
||||
so_dst.write_bytes(zf.read(so_file))
|
||||
|
||||
py_file = py_files[0]
|
||||
py_dst = build_dir / Path(py_file).name
|
||||
py_dst.write_bytes(zf.read(py_file))
|
||||
|
||||
return so_dst, py_dst
|
||||
|
||||
|
||||
class Tools:
|
||||
@staticmethod
|
||||
def run(
|
||||
cmd: list[str],
|
||||
cwd: Path | None = None,
|
||||
) -> None:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
subprocess.check_call(cmd, cwd=cwd)
|
||||
|
||||
@staticmethod
|
||||
def create_symlink(dst: Path, src: Path) -> None:
|
||||
Tools.rmrf(dst)
|
||||
dst.symlink_to(src)
|
||||
|
||||
@staticmethod
|
||||
def rmrf(path: Path) -> None:
|
||||
if path.exists() or path.is_symlink():
|
||||
if path.is_dir() and not path.is_symlink():
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
path.unlink()
|
||||
|
||||
|
||||
def clean() -> None:
|
||||
"""Remove build artifacts."""
|
||||
paths_to_remove = (
|
||||
BUILD_DIR,
|
||||
PYODIDE_DIR / ".pyodide_build",
|
||||
PYODIDE_DIR / "dist",
|
||||
PYODIDE_DIR / "ifcopenshell.egg-info",
|
||||
PYODIDE_DIR / "src",
|
||||
IFCOPENSHELL_DIR,
|
||||
)
|
||||
for path in paths_to_remove:
|
||||
if path.exists() or path.is_symlink():
|
||||
print(f"Removing {path}...")
|
||||
Tools.rmrf(path)
|
||||
print("✓ Clean complete")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__, add_help=False)
|
||||
parser.add_argument("--build", action="store_true", help="Build the wheel")
|
||||
parser.add_argument("--clean", action="store_true", help="Clean build folder")
|
||||
parser.add_argument(
|
||||
"--dev",
|
||||
action="store_true",
|
||||
help="Use editable pyodide-build from hardcoded path (Windows packing workaround)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.build and not args.clean:
|
||||
print(__doc__)
|
||||
return
|
||||
|
||||
if args.clean:
|
||||
clean()
|
||||
return
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
WheelBuilder.extract_ifcopenshell_from_git(IFCOPENSHELL_DIR)
|
||||
|
||||
print("Downloading and extracting _ifcopenshell_wrapper files...")
|
||||
makefile = REPO_ROOT / "src" / "ifcopenshell-python" / "Makefile"
|
||||
wheel_url = WheelBuilder.get_wheel_url(makefile)
|
||||
so_file, py_file = WheelBuilder.download_and_extract_so(wheel_url, BUILD_DIR)
|
||||
|
||||
Tools.create_symlink(IFCOPENSHELL_DIR / Path(so_file).name, so_file)
|
||||
Tools.create_symlink(IFCOPENSHELL_DIR / Path(py_file).name, py_file)
|
||||
|
||||
print("Installing pyodide-build...")
|
||||
if args.dev:
|
||||
Tools.run(["uv", "pip", "install", "-e", str(PYODIDE_BUILD)])
|
||||
else:
|
||||
Tools.run(["uv", "pip", "install", "pyodide-build"])
|
||||
|
||||
print("Building with pyodide...")
|
||||
# Use --no-isolation due to pyodide-build Windows support issues:
|
||||
# symlink_unisolated_packages fails with missing `_sysconfigdata_$(CPYTHON_ABI_FLAGS)_emscripten_wasm32-emscripten.py`.
|
||||
# Hardcode platform name since pyodide doesn't yet support overriding wheel tags on Windows.
|
||||
#
|
||||
# Use `LEGACY_PLATFORM` since pyodide 0.34.1 introduced new tag for wheels `pyemscripten`,
|
||||
# which doesn't work with pyodide itself yet - https://github.com/pyodide/pyodide/issues/6177.
|
||||
os.environ["USE_LEGACY_PLATFORM"] = "1"
|
||||
Tools.run(["pyodide", "build", f"-C--build-option=--plat-name={WHEEL_PLATFORM_TAG}"])
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
print(f"\n✓ Done! ({elapsed:.1f}s)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+39
-1
@@ -2,12 +2,16 @@
|
||||
# because `tool.setuptools.ext-modules` is still experimental in pyproject.toml
|
||||
# and we need it to get the wheel suffix right.
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import tomllib
|
||||
from setuptools import Extension, find_packages, setup
|
||||
from setuptools.command.build_ext import build_ext
|
||||
|
||||
REPO_FOLDER = Path(__file__).parent
|
||||
# Detect repo folder: if setup.py is in pyodide folder, go to parent
|
||||
SETUP_DIR = Path(__file__).parent
|
||||
REPO_FOLDER = SETUP_DIR.parent if SETUP_DIR.name == "pyodide" else SETUP_DIR
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
@@ -25,6 +29,39 @@ def get_dependencies() -> list[str]:
|
||||
return dependencies
|
||||
|
||||
|
||||
class UnixBuildExt(build_ext):
|
||||
"""Customize ``build_ext`` to support packing on Windows."""
|
||||
|
||||
def finalize_options(self):
|
||||
from distutils import sysconfig
|
||||
|
||||
super().finalize_options()
|
||||
if sys.platform == "win32":
|
||||
self.compiler = "unix"
|
||||
|
||||
# Configure sysconfig for Windows builds
|
||||
# CCSHARED is the only variable that's not customizable with env vars.
|
||||
# Basically avoiding this:
|
||||
# File ".venv\Lib\site-packages\setuptools\_distutils\sysconfig.py", line 366, in customize_compiler
|
||||
# compiler_so=cc_cmd + ' ' + ccshared,
|
||||
# ~~~~~~~~~~~~~^~~~~~~~~~
|
||||
# TypeError: can only concatenate str (not "NoneType") to str
|
||||
sysconfig.get_config_vars() # Initialize config cache
|
||||
if sysconfig._config_vars.get("CCSHARED") is None:
|
||||
sysconfig._config_vars["CCSHARED"] = "-fPIC"
|
||||
# Override compiler type before it's instantiated
|
||||
|
||||
# Set Emscripten compiler environment variables
|
||||
os.environ["CC"] = "emcc"
|
||||
os.environ["CXX"] = "em++"
|
||||
os.environ["CFLAGS"] = ""
|
||||
os.environ["CXXFLAGS"] = ""
|
||||
os.environ["LDSHARED"] = "emcc -shared"
|
||||
os.environ["AR"] = "emar"
|
||||
os.environ["ARFLAGS"] = "rcs"
|
||||
os.environ["SETUPTOOLS_EXT_SUFFIX"] = ".cpython-313-wasm32-emscripten.so"
|
||||
|
||||
|
||||
setup(
|
||||
name="ifcopenshell",
|
||||
version=get_version(),
|
||||
@@ -44,4 +81,5 @@ setup(
|
||||
},
|
||||
# Has to provide extension to get the correct wheel suffix.
|
||||
ext_modules=[Extension("ifcopenshell._ifcopenshell_wrapper", sources=[])],
|
||||
cmdclass={"build_ext": UnixBuildExt},
|
||||
)
|
||||
|
||||
+6
-4
@@ -3,8 +3,9 @@ name = "IfcOpenShell"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"black==26.3.1",
|
||||
"ruff==0.15.8",
|
||||
"ruff==0.15.9",
|
||||
"poethepoet",
|
||||
"ty==0.0.29",
|
||||
"gersemi==0.26.1",
|
||||
]
|
||||
|
||||
@@ -28,6 +29,9 @@ extend-exclude = '''
|
||||
reportInvalidTypeForm = false
|
||||
disableBytesTypePromotions = true
|
||||
reportUnnecessaryTypeIgnoreComment = true
|
||||
reportRedeclaration = false
|
||||
# Ignore warnings from bpy stubs missing actual source files.
|
||||
reportMissingModuleSource = false
|
||||
# Pylance doesn't respect gitignore, so we have to exclude files manually here
|
||||
# to avoid VS Code slowing down.
|
||||
# https://github.com/microsoft/pylance-release/issues/5169
|
||||
@@ -84,7 +88,6 @@ all = "ignore"
|
||||
# Structural rules (no deep type inference needed, easier to adapt).
|
||||
abstract-method-in-final-class = "error"
|
||||
ambiguous-protocol-member = "error"
|
||||
byte-string-type-annotation = "error"
|
||||
conflicting-declarations = "error"
|
||||
conflicting-metaclass = "error"
|
||||
cyclic-class-definition = "error"
|
||||
@@ -96,7 +99,6 @@ empty-body = "error"
|
||||
escape-character-in-forward-annotation = "error"
|
||||
final-on-non-method = "error"
|
||||
final-without-value = "error"
|
||||
fstring-type-annotation = "error"
|
||||
ignore-comment-unknown-rule = "error"
|
||||
implicit-concatenated-string-type-annotation = "error"
|
||||
inconsistent-mro = "error"
|
||||
@@ -224,7 +226,7 @@ ty.sequence = ["ty-bonsai", "ty-ios"]
|
||||
ty.help = "Run ty type checker. Requires ty-venv to be set up first."
|
||||
ty-bonsai = "ty check src/bonsai --python=src/bonsai/.venv"
|
||||
|
||||
ty-venv.sequence = ["ty-venv-bonsai", "ty-venv-ios"]
|
||||
ty-venv.sequence = ["bonsai-deps", "ty-venv-bonsai", "ty-venv-ios"]
|
||||
|
||||
ty-venv-bonsai.sequence = [
|
||||
{cmd = "uv venv src/bonsai/.venv --python=3.11 --allow-existing"},
|
||||
|
||||
@@ -316,7 +316,7 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
Returns:
|
||||
The BCF viewpoint definition.
|
||||
"""
|
||||
ifc_file = element.wrapped_data.file
|
||||
ifc_file = element.file
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
elem_placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
elem_placement[:3, 3] *= unit_scale
|
||||
|
||||
@@ -316,7 +316,7 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
Returns:
|
||||
The BCF viewpoint definition.
|
||||
"""
|
||||
ifc_file = element.wrapped_data.file
|
||||
ifc_file = element.file
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
elem_placement = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
||||
elem_placement[:3, 3] *= unit_scale
|
||||
|
||||
+5
-3
@@ -48,6 +48,7 @@ VERSION_PATCH:=$(shell cat '../../VERSION' | cut -d '.' -f 3)
|
||||
VERSION_DATE:=$(shell date '+%y%m%d')
|
||||
LAST_COMMIT_HASH:=$(shell git rev-parse HEAD)
|
||||
LAST_COMMIT_DATE:=$(shell git show -s --format=%cI)
|
||||
LAST_GIT_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
PYPI_IMP:=cp
|
||||
|
||||
ifdef PYVERSION
|
||||
@@ -63,6 +64,7 @@ PYNUMBER:=3$(PYMINOR)
|
||||
PYPI_VERSION:=3.$(PYMINOR)
|
||||
endif # def PYVERSION
|
||||
|
||||
IFCMERGE_VERSION:=2026-04-07
|
||||
|
||||
ifdef PLATFORM
|
||||
SUPPORTED_PLATFORMS := linux macos macosm1 win
|
||||
@@ -239,10 +241,9 @@ endif
|
||||
|
||||
# required for three-way git merging
|
||||
ifeq ($(PLATFORM), win)
|
||||
cd build/bonsai/libs/bin && wget https://github.com/brunopostle/ifcmerge/releases/download/2025-01-26/ifcmerge.zip
|
||||
cd build/bonsai/libs/bin && unzip ifcmerge.zip && rm ifcmerge.zip
|
||||
cd build/bonsai/libs/bin && wget https://github.com/brunopostle/ifcmerge/releases/download/$(IFCMERGE_VERSION)/ifcmerge.exe
|
||||
else
|
||||
cd build/bonsai/libs/bin && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/main/ifcmerge && chmod +x ifcmerge
|
||||
cd build/bonsai/libs/bin && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/$(IFCMERGE_VERSION)/ifcmerge && chmod +x ifcmerge
|
||||
endif
|
||||
|
||||
# Generate translations module for Bonsai build
|
||||
@@ -261,6 +262,7 @@ else
|
||||
$(SED) "s/0.0.0/$(VERSION)-alpha$(VERSION_DATE)/" build/bonsai/blender_manifest.toml
|
||||
$(SED) "s/8888888/$(LAST_COMMIT_HASH)/" build/bonsai/__init__.py
|
||||
$(SED) "s/9999999/$(LAST_COMMIT_DATE)/" build/bonsai/__init__.py
|
||||
$(SED) "s/7777777/$(LAST_GIT_BRANCH)/" build/bonsai/__init__.py
|
||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
|
||||
endif
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ from typing import TYPE_CHECKING, Any, Union
|
||||
|
||||
last_commit_hash = "8888888"
|
||||
last_commit_date = "9999999"
|
||||
last_git_branch = "7777777"
|
||||
|
||||
|
||||
def get_last_commit_hash() -> Union[str, None]:
|
||||
@@ -60,6 +61,15 @@ def get_last_commit_date() -> Union[str, None]:
|
||||
return last_commit_date
|
||||
|
||||
|
||||
def get_git_branch() -> Union[str, None]:
|
||||
# Using this weird way to write 7777777,
|
||||
# so makefile won't accidentally replace it here
|
||||
# we'll be able to distinguish branch from placeholder value.
|
||||
if last_git_branch == str(7_777777):
|
||||
return None
|
||||
return last_git_branch
|
||||
|
||||
|
||||
# Accessed from bonsai extension:
|
||||
bbim_semver: dict[str, Any] = {}
|
||||
|
||||
@@ -125,6 +135,7 @@ def get_debug_info(*, bonsai_failed_to_load: bool = False) -> dict[str, Any]:
|
||||
"bonsai_version": bbim_version,
|
||||
"bonsai_commit_hash": get_last_commit_hash(),
|
||||
"bonsai_commit_date": get_last_commit_date(),
|
||||
"bonsai_git_branch": get_git_branch(),
|
||||
"last_actions": last_actions,
|
||||
"last_error": last_error,
|
||||
}
|
||||
@@ -251,10 +262,12 @@ if IN_BLENDER:
|
||||
|
||||
global last_commit_hash
|
||||
global last_commit_date
|
||||
global last_git_branch
|
||||
path = Path(__file__).resolve().parent
|
||||
repo = git.Repo(str(path), search_parent_directories=True)
|
||||
last_commit_hash = repo.head.object.hexsha
|
||||
last_commit_date = repo.head.object.committed_datetime.isoformat()
|
||||
last_git_branch = repo.active_branch.name
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ def import_attributes(
|
||||
info = {a.name(): None for a in attributes}
|
||||
info["type"] = element
|
||||
else:
|
||||
assert (entity := element.wrapped_data.declaration().as_entity())
|
||||
assert (entity := element.declaration().as_entity())
|
||||
attributes = entity.all_attributes()
|
||||
info = element.get_info()
|
||||
for attribute in attributes:
|
||||
|
||||
@@ -101,7 +101,7 @@ class AggregateDecorator:
|
||||
cls.is_installed = False
|
||||
|
||||
def dotted_line_shader(self):
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth("FLOAT", "v_ArcLength")
|
||||
|
||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||
|
||||
@@ -73,6 +73,22 @@ def poll_related_object(self: "BIMObjectAggregateProperties", related_obj: bpy.t
|
||||
return True
|
||||
|
||||
|
||||
def update_relating_object(self, context):
|
||||
if self.relating_object:
|
||||
ifc_id = tool.Blender.get_object_bim_props(self.relating_object).ifc_definition_id
|
||||
if ifc_id:
|
||||
bpy.ops.bim.aggregate_assign_object(relating_object=ifc_id)
|
||||
bpy.ops.bim.disable_editing_aggregate()
|
||||
|
||||
|
||||
def update_related_object(self, context):
|
||||
if self.related_object:
|
||||
ifc_id = tool.Blender.get_object_bim_props(self.related_object).ifc_definition_id
|
||||
if ifc_id:
|
||||
bpy.ops.bim.aggregate_assign_object(related_object=ifc_id)
|
||||
bpy.ops.bim.disable_editing_aggregate()
|
||||
|
||||
|
||||
def update_aggregate_decorator(self, context):
|
||||
if self.aggregate_decorator:
|
||||
AggregateDecorator.install(bpy.context)
|
||||
@@ -89,12 +105,15 @@ def update_aggregate_mode_decorator(self, context):
|
||||
|
||||
class BIMObjectAggregateProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
relating_object: PointerProperty(name="Relating Whole", type=bpy.types.Object, poll=poll_relating_object)
|
||||
relating_object: PointerProperty(
|
||||
name="Relating Whole", type=bpy.types.Object, poll=poll_relating_object, update=update_relating_object
|
||||
)
|
||||
related_object: PointerProperty(
|
||||
name="Related Part",
|
||||
description="Related Part, will be used to derive the Relating Object",
|
||||
type=bpy.types.Object,
|
||||
poll=poll_related_object,
|
||||
update=update_related_object,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@@ -295,13 +295,13 @@ class ExplorerShowUIPopup(bpy.types.Operator):
|
||||
bl_description = "Show Explorer UI to select element as attribute value or edit it."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
ifc_class: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
"""Element IFC class."""
|
||||
attribute_name: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
attribute_name: bpy.props.StringProperty()
|
||||
"""IFC class attribute name."""
|
||||
data_path: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
data_path: bpy.props.StringProperty()
|
||||
"""Full data path"""
|
||||
preselect_ifc_id: bpy.props.IntProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
preselect_ifc_id: bpy.props.IntProperty(options={"SKIP_SAVE"})
|
||||
"""IFC id to preselect in the popup."""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@@ -41,7 +41,7 @@ class BIMAttributeProperties(PropertyGroup):
|
||||
|
||||
|
||||
class ExplorerEntity(PropertyGroup):
|
||||
ifc_definition_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
ifc_definition_id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ifc_definition_id: int
|
||||
@@ -60,7 +60,7 @@ class BIMExplorerProperties(PropertyGroup):
|
||||
self.property_unset("editing_entity_id")
|
||||
self.entity_attributes.clear()
|
||||
|
||||
is_loaded: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
is_loaded: BoolProperty(
|
||||
name="Toggle Explorer UI",
|
||||
update=update_is_loaded,
|
||||
)
|
||||
@@ -76,15 +76,15 @@ class BIMExplorerProperties(PropertyGroup):
|
||||
def update_ifc_class(self, context: object) -> None:
|
||||
tool.Attribute.refresh_uilist_entities()
|
||||
|
||||
ifc_class: EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
ifc_class: EnumProperty(
|
||||
name="IFC Class To Search",
|
||||
items=get_ifc_class,
|
||||
update=update_ifc_class,
|
||||
)
|
||||
entities: CollectionProperty(type=ExplorerEntity) # pyright: ignore[reportRedeclaration]
|
||||
active_entity_index: IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
editing_entity_id: IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
entity_attributes: CollectionProperty(type=Attribute) # pyright: ignore[reportRedeclaration]
|
||||
entities: CollectionProperty(type=ExplorerEntity)
|
||||
active_entity_index: IntProperty()
|
||||
editing_entity_id: IntProperty()
|
||||
entity_attributes: CollectionProperty(type=Attribute)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
is_loaded: bool
|
||||
|
||||
@@ -201,16 +201,10 @@ class ExecuteIfcClash(bpy.types.Operator, ExportHelper):
|
||||
"ALT+click to run a quick clash without selecting a file to save."
|
||||
)
|
||||
|
||||
filter_glob: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
default="*.bcf;*.json", options={"HIDDEN"}
|
||||
)
|
||||
format: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
name="Format", items=[(i, i, "") for i in ("bcf", "json")]
|
||||
)
|
||||
filepath: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
subtype="FILE_PATH", options={"SKIP_SAVE"}
|
||||
)
|
||||
quick_clash: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
filter_glob: bpy.props.StringProperty(default="*.bcf;*.json", options={"HIDDEN"})
|
||||
format: bpy.props.EnumProperty(name="Format", items=[(i, i, "") for i in ("bcf", "json")])
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE"})
|
||||
quick_clash: bpy.props.BoolProperty(
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ from bonsai.bim.prop import BIMFilterGroup, StrProperty
|
||||
|
||||
|
||||
class ClashSource(PropertyGroup):
|
||||
name: StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: StringProperty(
|
||||
name="File",
|
||||
description="Absolute filepath to existing .ifc file to use as a clash source.",
|
||||
)
|
||||
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups") # pyright: ignore[reportRedeclaration]
|
||||
mode: EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
|
||||
mode: EnumProperty(
|
||||
items=[
|
||||
("a", "All Elements", "All elements will be used for clashing"),
|
||||
("i", "Include", "Only the selected elements are included for clashing"),
|
||||
@@ -62,7 +62,7 @@ class Clash(PropertyGroup):
|
||||
b_global_id: StringProperty(name="B")
|
||||
a_name: StringProperty(name="A Name")
|
||||
b_name: StringProperty(name="B Name")
|
||||
clash_type: EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
clash_type: EnumProperty(
|
||||
name="Clash Type",
|
||||
items=tuple((i, i, "") for i in CLASH_TYPE_ITEMS),
|
||||
)
|
||||
|
||||
@@ -206,7 +206,7 @@ class CostSchedulesData:
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
if quantity.is_a("IfcPhysicalSimpleQuantity"):
|
||||
measure_class = (
|
||||
quantity.wrapped_data.declaration()
|
||||
quantity.declaration()
|
||||
.as_entity()
|
||||
.attribute_by_index(3)
|
||||
.type_of_attribute()
|
||||
|
||||
@@ -87,7 +87,7 @@ class CopyCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Copy Cost Schedule"
|
||||
bl_description = "Create a duplicate of the provided cost schedule."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
cost_schedule: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
cost_schedule: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
cost_schedule: int
|
||||
|
||||
@@ -90,7 +90,7 @@ class PrintIfcFile(bpy.types.Operator):
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
print(tool.Ifc.get().wrapped_data.to_string())
|
||||
print(tool.Ifc.get().to_string())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -260,14 +260,14 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
)
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
geometry_library: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
geometry_library: bpy.props.EnumProperty(
|
||||
name="Geometry Library",
|
||||
description="Geometry library to use for testing shape creation.",
|
||||
items=[(i, i, "") for i in get_args(ifcopenshell.geom.GEOMETRY_LIBRARY)],
|
||||
# By default use the same library as used for importing ifc project.
|
||||
default="hybrid-cgal-simple-opencascade",
|
||||
)
|
||||
custom_geometry_library: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
custom_geometry_library: bpy.props.StringProperty(
|
||||
name="Custom Geometry Library",
|
||||
description="Provide a custom geometry library name, will override the 'geometry library' property.",
|
||||
)
|
||||
@@ -781,7 +781,7 @@ class PurgeUnusedObjects(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Purge Unused Objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
object_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
object_type: bpy.props.EnumProperty(
|
||||
name="Object Type",
|
||||
items=((s, s.capitalize(), "") for s in get_args(tool.Debug.PurgeMergeObjectType)),
|
||||
)
|
||||
@@ -827,7 +827,7 @@ class MergeIdenticalObjects(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
object_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
object_type: bpy.props.EnumProperty(
|
||||
name="Object Type",
|
||||
items=((s, s.capitalize(), "") for s in get_args(tool.Debug.PurgeMergeObjectType)),
|
||||
)
|
||||
@@ -1073,7 +1073,7 @@ class ChangeLogLevel(bpy.types.Operator):
|
||||
bl_options = {"REGISTER"}
|
||||
bl_description = "Change general log level across all Python code in Blender"
|
||||
|
||||
log_level: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
log_level: bpy.props.EnumProperty(
|
||||
name="Log Level",
|
||||
items=[(i, i, "") for i in get_args(LogLevelType)],
|
||||
default="WARNING",
|
||||
|
||||
@@ -246,17 +246,17 @@ class CreateDrawing(bpy.types.Operator):
|
||||
+ "Add the CTRL modifier to optionally open drawings to view them as\n"
|
||||
+ "they are created"
|
||||
)
|
||||
print_all: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
print_all: bpy.props.BoolProperty(
|
||||
name="Print All",
|
||||
default=False,
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
open_viewer: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
open_viewer: bpy.props.BoolProperty(
|
||||
name="Open in Viewer",
|
||||
default=False,
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
sync: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
sync: bpy.props.BoolProperty(
|
||||
name="Sync Before Creating Drawing",
|
||||
description="Could save some time if you're sure IFC and current Blender session are already in sync",
|
||||
default=True,
|
||||
@@ -2322,14 +2322,14 @@ class ActivateDrawingBase(tool.Ifc.Operator):
|
||||
+ "SHIFT+CLICK to load a quick preview of the drawing view"
|
||||
)
|
||||
|
||||
drawing: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
should_view_from_camera: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
drawing: bpy.props.IntProperty()
|
||||
should_view_from_camera: bpy.props.BoolProperty(
|
||||
name="Should View From Camera",
|
||||
description="Move view to the activated drawing's camera position.",
|
||||
default=True,
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
use_quick_preview: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
use_quick_preview: bpy.props.BoolProperty(
|
||||
name="Use Quick Preview",
|
||||
description="Just move the camera to the drawing view, without loading anything else.",
|
||||
default=False,
|
||||
@@ -3635,14 +3635,12 @@ class ToggleTargetView(bpy.types.Operator):
|
||||
bl_label = "Toggle Target View"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
target_view: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
toggle_all: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
target_view: bpy.props.StringProperty()
|
||||
toggle_all: bpy.props.BoolProperty(
|
||||
default=False,
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
items=[(i, i, "") for i in get_args(ToggleOption)]
|
||||
)
|
||||
option: bpy.props.EnumProperty(items=[(i, i, "") for i in get_args(ToggleOption)])
|
||||
|
||||
if TYPE_CHECKING:
|
||||
target_view: str
|
||||
|
||||
@@ -860,13 +860,13 @@ class BIMTextProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
literals: CollectionProperty(name="Literals", type=LiteralProps)
|
||||
newline_at: IntProperty(name="Newline At")
|
||||
symbol: EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
symbol: EnumProperty(
|
||||
name="Symbol",
|
||||
description="Symbol from symbols.svg to use for this text.",
|
||||
items=[(s, s, "") for s in ["NO SYMBOL", "CUSTOM SYMBOL"] + tool.Drawing.DEFAULT_SYMBOLS],
|
||||
default="NO SYMBOL",
|
||||
)
|
||||
custom_symbol: StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
custom_symbol: StringProperty(
|
||||
name="Custom Symbol",
|
||||
description="Non-default symbol to use for this text.",
|
||||
)
|
||||
|
||||
@@ -85,7 +85,7 @@ class EditObjectPlacement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_mesh_separate"
|
||||
bl_label = "IFC Mesh Separate"
|
||||
blender_op = bpy.ops.mesh.separate.get_rna_type()
|
||||
blender_op = bpy.ops.mesh.separate.get_rna_type() # ty: ignore[missing-argument]
|
||||
bl_description = blender_op.description + ".\nAlso makes sure changes are in sync with IFC."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
blender_type_prop = blender_op.properties["type"]
|
||||
@@ -246,7 +246,7 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
class OverrideOriginSet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_origin_set"
|
||||
blender_op = bpy.ops.object.origin_set.get_rna_type()
|
||||
blender_op = bpy.ops.object.origin_set.get_rna_type() # ty: ignore[missing-argument]
|
||||
bl_label = "IFC Origin Set"
|
||||
bl_description = (
|
||||
blender_op.description + ".\nAlso makes sure changes are in sync with IFC (operator works only on IFC objects)"
|
||||
@@ -790,7 +790,7 @@ def lock_error_message(name: str) -> str:
|
||||
|
||||
|
||||
def calc_delete_is_batch(ifc_file: ifcopenshell.file, context: bpy.types.Context) -> bool:
|
||||
total_elements = len(tool.Ifc.get().wrapped_data.entity_names())
|
||||
total_elements = len(tool.Ifc.get().entity_names())
|
||||
total_polygons = sum([len(o.data.polygons) for o in context.selected_objects if o.type == "MESH"])
|
||||
# These numbers are a bit arbitrary, but basically batching is only
|
||||
# really necessary on large models and large geometry removals.
|
||||
@@ -801,7 +801,7 @@ def calc_delete_is_batch(ifc_file: ifcopenshell.file, context: bpy.types.Context
|
||||
class OverrideDelete(bpy.types.Operator):
|
||||
bl_idname = "bim.override_object_delete"
|
||||
bl_label = "IFC Delete"
|
||||
blender_op = bpy.ops.object.delete.get_rna_type()
|
||||
blender_op = bpy.ops.object.delete.get_rna_type() # ty: ignore[missing-argument]
|
||||
bl_description = (
|
||||
blender_op.description
|
||||
+ ".\nAlso makes sure changes in sync with IFC."
|
||||
@@ -821,7 +821,7 @@ class OverrideDelete(bpy.types.Operator):
|
||||
def poll(cls, context):
|
||||
# Match `object.delete` poll for consistency.
|
||||
# `object.delete` poll just checks for OBJECT mode.
|
||||
poll = bpy.ops.object.delete.poll()
|
||||
poll = bpy.ops.object.delete.poll() # ty: ignore[missing-argument]
|
||||
if poll:
|
||||
return True
|
||||
cls.poll_message_set("Only available in OBJECT mode")
|
||||
@@ -1045,7 +1045,7 @@ class SelectedIdsData(NamedTuple):
|
||||
class OverrideOutlinerDelete(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_outliner_delete"
|
||||
bl_label = "IFC Delete"
|
||||
blender_op = bpy.ops.outliner.delete.get_rna_type()
|
||||
blender_op = bpy.ops.outliner.delete.get_rna_type() # ty: ignore[missing-argument]
|
||||
bl_description = (
|
||||
blender_op.description
|
||||
+ ".\nAlso makes sure changes in sync with IFC."
|
||||
@@ -1060,7 +1060,7 @@ class OverrideOutlinerDelete(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def poll(cls, context) -> bool:
|
||||
# Match `outliner.delete` poll for consistency.
|
||||
# `outliner.delete` just checks `area.type` == `OUTLINER`.
|
||||
poll = bpy.ops.outliner.delete.poll()
|
||||
poll = bpy.ops.outliner.delete.poll() # ty: ignore[missing-argument]
|
||||
if poll:
|
||||
return True
|
||||
cls.poll_message_set("Only available from Outliner.")
|
||||
@@ -1164,7 +1164,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
def poll(cls, context) -> bool:
|
||||
# Match `object.duplicate_move` poll for consistency.
|
||||
# `object.duplicate_move` poll checks for OBJECT mode.
|
||||
poll = bpy.ops.object.duplicate_move.poll()
|
||||
poll = bpy.ops.object.duplicate_move.poll() # ty: ignore[missing-argument]
|
||||
if poll:
|
||||
return True
|
||||
cls.poll_message_set("Only available in OBJECT mode")
|
||||
@@ -1908,7 +1908,7 @@ class RefreshLinkedAggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.override_object_join"
|
||||
bl_label = "IFC Join"
|
||||
blender_op = bpy.ops.mesh.separate.get_rna_type()
|
||||
blender_op = bpy.ops.mesh.separate.get_rna_type() # ty: ignore[missing-argument]
|
||||
bl_description = (
|
||||
blender_op.description
|
||||
+ ".\nAlso makes sure changes are in sync with IFC."
|
||||
@@ -1926,7 +1926,7 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not bpy.ops.object.join.poll():
|
||||
if not bpy.ops.object.join.poll(): # ty: ignore[missing-argument]
|
||||
cls.poll_message_set("Active object is not EDITable.")
|
||||
return False
|
||||
if not context.selected_editable_objects:
|
||||
|
||||
@@ -43,11 +43,11 @@ class ToggleGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Toggle Group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
ifc_definition_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
group_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
ifc_definition_id: bpy.props.IntProperty()
|
||||
group_type: bpy.props.EnumProperty(
|
||||
items=[(i, i, "") for i in get_args(tool.Group.GroupType)],
|
||||
)
|
||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
option: bpy.props.EnumProperty(
|
||||
items=[(i, i, "") for i in get_args(tool.Group.ToggleOption)],
|
||||
)
|
||||
|
||||
|
||||
@@ -34,8 +34,10 @@ classes = (
|
||||
operator.Fetch,
|
||||
operator.Merge,
|
||||
operator.ObjectLog,
|
||||
operator.SelectConflictEntity,
|
||||
operator.Push,
|
||||
operator.RefreshGit,
|
||||
operator.RenameBranch,
|
||||
operator.SwitchRevision,
|
||||
operator.InstallGit,
|
||||
operator.RunGitDiff,
|
||||
|
||||
@@ -21,65 +21,71 @@ class IfcGitData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
repo = None
|
||||
if bool(tool.Ifc.get()):
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
if os.path.isfile(path_ifc):
|
||||
repo = tool.IfcGit.repo_from_path(path_ifc)
|
||||
|
||||
cls.data = {
|
||||
"repo": cls.repo(),
|
||||
"remotes": cls.remotes(),
|
||||
"branch_names": cls.branch_names(),
|
||||
"remote_names": cls.remote_names(),
|
||||
"remote_urls": cls.remote_urls(),
|
||||
"repo": repo,
|
||||
"remotes": repo.remotes if repo else None,
|
||||
"branch_names": cls.branch_names(repo),
|
||||
"tag_names": cls.tag_names(repo),
|
||||
"remote_names": cls.remote_names(repo),
|
||||
"remote_urls": {r.name: r.url for r in repo.remotes} if repo else {},
|
||||
"path_ifc": cls.path_ifc(),
|
||||
"branches_by_hexsha": cls.branches_by_hexsha(),
|
||||
"tags_by_hexsha": cls.tags_by_hexsha(),
|
||||
"name_ifc": cls.name_ifc(),
|
||||
"name_ifc": cls.name_ifc(repo),
|
||||
"dir_name": cls.dir_name(),
|
||||
"base_name": cls.base_name(),
|
||||
"working_dir": cls.working_dir(),
|
||||
"untracked_files": cls.untracked_files(),
|
||||
"is_detached": cls.is_detached(),
|
||||
"active_branch_name": cls.active_branch_name(),
|
||||
"is_dirty": cls.is_dirty(),
|
||||
"commit": cls.commit(),
|
||||
"current_revision": cls.current_revision(),
|
||||
"working_dir": repo.working_dir if repo else None,
|
||||
"ifc_is_untracked": cls.ifc_is_untracked(repo),
|
||||
"is_detached": repo.head.is_detached if repo else None,
|
||||
"active_branch_name": repo.active_branch.name if repo and not repo.head.is_detached else None,
|
||||
"is_dirty": cls.is_dirty(repo),
|
||||
"current_revision": cls.current_revision(repo),
|
||||
"git_exe": cls.git_exe(),
|
||||
"ifcmerge_exe": cls.ifcmerge_exe(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def repo(cls):
|
||||
if bool(tool.Ifc.get()):
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
if os.path.isfile(path_ifc):
|
||||
return tool.IfcGit.repo_from_path(path_ifc)
|
||||
return None
|
||||
def branch_names(cls, repo):
|
||||
if not repo or not repo.heads:
|
||||
return []
|
||||
names = sorted([b.name for b in repo.branches])
|
||||
if "main" in names:
|
||||
names.remove("main")
|
||||
names = ["main"] + names
|
||||
if repo.remotes:
|
||||
for remote in repo.remotes:
|
||||
for ref in remote.refs:
|
||||
names.append(ref.name)
|
||||
return names
|
||||
|
||||
@classmethod
|
||||
def remotes(cls):
|
||||
if cls.repo():
|
||||
return cls.repo().remotes
|
||||
return None
|
||||
def tag_names(cls, repo):
|
||||
if not repo:
|
||||
return []
|
||||
return [t.name for t in repo.tags]
|
||||
|
||||
@classmethod
|
||||
def branch_names(cls):
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def remote_names(cls):
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def remote_urls(cls):
|
||||
result = {}
|
||||
if cls.repo():
|
||||
for remote in cls.repo().remotes:
|
||||
result[remote.name] = remote.url
|
||||
return result
|
||||
def remote_names(cls, repo):
|
||||
if not repo:
|
||||
return []
|
||||
names = sorted([r.name for r in repo.remotes])
|
||||
if "origin" in names:
|
||||
names.remove("origin")
|
||||
names = ["origin"] + names
|
||||
return names
|
||||
|
||||
@classmethod
|
||||
def path_ifc(cls):
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
if os.path.isfile(path_ifc):
|
||||
return tool.Ifc.get_path()
|
||||
return path_ifc
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
@@ -88,7 +94,8 @@ class IfcGitData:
|
||||
if tool.IfcGitRepo.repo.branches:
|
||||
return tool.IfcGit.branches_by_hexsha(tool.IfcGitRepo.repo)
|
||||
except AttributeError:
|
||||
return {}
|
||||
pass
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
def tags_by_hexsha(cls):
|
||||
@@ -97,12 +104,11 @@ class IfcGitData:
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
def name_ifc(cls):
|
||||
if bool(tool.Ifc.get()):
|
||||
def name_ifc(cls, repo):
|
||||
if bool(tool.Ifc.get()) and repo:
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
if tool.IfcGitRepo.repo and os.path.isfile(path_ifc):
|
||||
working_dir = tool.IfcGitRepo.repo.working_dir
|
||||
return os.path.relpath(path_ifc, working_dir)
|
||||
if os.path.isfile(path_ifc):
|
||||
return os.path.relpath(path_ifc, repo.working_dir)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
@@ -122,49 +128,28 @@ class IfcGitData:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def working_dir(cls):
|
||||
if cls.repo():
|
||||
return cls.repo().working_dir
|
||||
def ifc_is_untracked(cls, repo):
|
||||
"""Return True if the IFC file exists in the repo but has not been added to git."""
|
||||
if not repo:
|
||||
return False
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
if not os.path.isfile(path_ifc):
|
||||
return False
|
||||
return not bool(repo.git.ls_files(path_ifc))
|
||||
|
||||
@classmethod
|
||||
def untracked_files(cls):
|
||||
if cls.repo():
|
||||
return cls.repo().untracked_files
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def is_detached(cls):
|
||||
if cls.repo():
|
||||
return cls.repo().head.is_detached
|
||||
|
||||
@classmethod
|
||||
def active_branch_name(cls):
|
||||
if cls.repo() and not cls.is_detached():
|
||||
return cls.repo().active_branch.name
|
||||
|
||||
@classmethod
|
||||
def is_dirty(cls):
|
||||
if cls.repo() and cls.git_exe():
|
||||
def is_dirty(cls, repo):
|
||||
if repo and cls.git_exe():
|
||||
path_ifc = tool.Ifc.get_path()
|
||||
if os.path.isfile(path_ifc):
|
||||
return cls.repo().is_dirty(path=path_ifc)
|
||||
return repo.is_dirty(path=path_ifc)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def commit(cls):
|
||||
def current_revision(cls, repo):
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
if cls.repo() and len(props.ifcgit_commits) > 0:
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
try:
|
||||
return cls.repo().commit(rev=item.hexsha)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def current_revision(cls):
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
if cls.repo() and cls.repo().head.is_valid() and len(props.ifcgit_commits) > 0:
|
||||
return tool.IfcGitRepo.repo.commit()
|
||||
if repo and repo.head.is_valid() and len(props.ifcgit_commits) > 0:
|
||||
return repo.commit()
|
||||
|
||||
@classmethod
|
||||
def git_exe(cls):
|
||||
|
||||
@@ -120,11 +120,11 @@ class CommitChanges(bpy.types.Operator):
|
||||
if props.commit_message == "":
|
||||
return False
|
||||
if repo:
|
||||
if props.new_branch_name in [branch.name for branch in repo.branches]:
|
||||
if props.new_branch_name in IfcGitData.data["branch_names"]:
|
||||
cls.poll_message_set("Branch already exists!")
|
||||
return False
|
||||
elif not tool.IfcGit.is_valid_ref_format(props.new_branch_name):
|
||||
if repo.head.is_detached:
|
||||
if IfcGitData.data["is_detached"]:
|
||||
cls.poll_message_set("Branch name is invalid or empty!")
|
||||
return False
|
||||
elif props.new_branch_name != "":
|
||||
@@ -134,10 +134,17 @@ class CommitChanges(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.commit_changes(tool.IfcGit, tool.Ifc, repo)
|
||||
core.refresh_revision_list(tool.IfcGit, repo, tool.Ifc)
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
commit_message = props.commit_message
|
||||
new_branch_name = props.new_branch_name
|
||||
core.commit_changes(tool.IfcGit, tool.Ifc, commit_message, new_branch_name)
|
||||
props.new_branch_name = ""
|
||||
props.commit_message = ""
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
IfcGitData.load()
|
||||
if new_branch_name:
|
||||
props.display_branch = new_branch_name
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -157,7 +164,7 @@ class AddTag(bpy.types.Operator):
|
||||
repo = IfcGitData.data["repo"]
|
||||
if repo and (
|
||||
not tool.IfcGit.is_valid_ref_format(props.new_tag_name)
|
||||
or props.new_tag_name in [tag.name for tag in repo.tags]
|
||||
or props.new_tag_name in IfcGitData.data["tag_names"]
|
||||
):
|
||||
return False
|
||||
return True
|
||||
@@ -165,8 +172,12 @@ class AddTag(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.add_tag(tool.IfcGit, repo)
|
||||
core.refresh_revision_list(tool.IfcGit, repo, tool.Ifc)
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
core.add_tag(tool.IfcGit, repo, item.hexsha, props.new_tag_name, props.new_tag_message)
|
||||
props.new_tag_name = ""
|
||||
props.new_tag_message = ""
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -183,7 +194,7 @@ class DeleteTag(bpy.types.Operator):
|
||||
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.delete_tag(tool.IfcGit, repo, self.tag_name)
|
||||
core.refresh_revision_list(tool.IfcGit, repo, tool.Ifc)
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -191,7 +202,7 @@ class DeleteTag(bpy.types.Operator):
|
||||
class RefreshGit(bpy.types.Operator):
|
||||
"""Refresh revision list"""
|
||||
|
||||
bl_label = ""
|
||||
bl_label = "Refresh"
|
||||
bl_idname = "ifcgit.refresh"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@@ -205,8 +216,7 @@ class RefreshGit(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.refresh_revision_list(tool.IfcGit, repo, tool.Ifc)
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
tool.IfcGit.decolourise()
|
||||
return {"FINISHED"}
|
||||
@@ -215,7 +225,7 @@ class RefreshGit(bpy.types.Operator):
|
||||
class DisplayRevision(bpy.types.Operator):
|
||||
"""Colourise objects by selected revision"""
|
||||
|
||||
bl_label = ""
|
||||
bl_label = "Colourise Revision"
|
||||
bl_idname = "ifcgit.display_revision"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@@ -250,7 +260,7 @@ class DisplayUncommitted(bpy.types.Operator):
|
||||
class SwitchRevision(bpy.types.Operator):
|
||||
"""Switches the repository to the selected revision and reloads the IFC file"""
|
||||
|
||||
bl_label = ""
|
||||
bl_label = "Switch Revision"
|
||||
bl_idname = "ifcgit.switch_revision"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@@ -268,7 +278,7 @@ class SwitchRevision(bpy.types.Operator):
|
||||
|
||||
|
||||
class Merge(bpy.types.Operator):
|
||||
"""Merges the selected branch into working branch"""
|
||||
"""Merges the selected branch into working branch.\nCtrl+click to preview without merging"""
|
||||
|
||||
bl_label = "Merge this branch"
|
||||
bl_idname = "ifcgit.merge"
|
||||
@@ -282,15 +292,84 @@ class Merge(bpy.types.Operator):
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
def invoke(self, context, event):
|
||||
if event.ctrl:
|
||||
core.dry_run_merge(tool.IfcGit, tool.Ifc, self)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
return self.execute(context)
|
||||
|
||||
if core.merge_branch(tool.IfcGit, tool.Ifc, self):
|
||||
def execute(self, context):
|
||||
if core.merge_branch(tool.IfcGit, tool.Ifc, self) is not False:
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
else:
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class SelectConflictEntity(bpy.types.Operator):
|
||||
"""Select the conflicting entity in the viewport"""
|
||||
|
||||
bl_label = "Select Conflict Entity"
|
||||
bl_idname = "ifcgit.select_conflict_entity"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
step_id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
step_id: int
|
||||
|
||||
def execute(self, context):
|
||||
model = tool.Ifc.get()
|
||||
if not model:
|
||||
return {"CANCELLED"}
|
||||
|
||||
try:
|
||||
entity = model.by_id(self.step_id)
|
||||
except Exception:
|
||||
self.report({"WARNING"}, f"Entity #{self.step_id} not found (may have been deleted locally)")
|
||||
return {"CANCELLED"}
|
||||
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
if obj is None:
|
||||
# Walk inverse references up to 5 hops to find nearest entity with a Blender object
|
||||
visited = {entity.id()}
|
||||
queue = [entity]
|
||||
for _ in range(5):
|
||||
next_queue = []
|
||||
for ent in queue:
|
||||
for inv in model.get_inverse(ent):
|
||||
if inv.id() in visited:
|
||||
continue
|
||||
visited.add(inv.id())
|
||||
obj = tool.Ifc.get_object(inv)
|
||||
if obj is not None:
|
||||
break
|
||||
next_queue.append(inv)
|
||||
if obj is not None:
|
||||
break
|
||||
if obj is not None:
|
||||
break
|
||||
queue = next_queue
|
||||
|
||||
if obj is None:
|
||||
self.report({"INFO"}, f"No viewport representation found for #{self.step_id} ({entity.is_a()})")
|
||||
return {"CANCELLED"}
|
||||
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
for area in context.screen.areas:
|
||||
if area.type == "VIEW_3D":
|
||||
region = next((r for r in area.regions if r.type == "WINDOW"), None)
|
||||
if region:
|
||||
with context.temp_override(area=area, region=region):
|
||||
bpy.ops.view3d.view_selected()
|
||||
break
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class Push(bpy.types.Operator):
|
||||
"""Pushes the working branch to selected remote"""
|
||||
|
||||
@@ -314,9 +393,9 @@ class Fetch(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
repo = IfcGitData.data["repo"]
|
||||
remote = repo.remotes[props.select_remote]
|
||||
remote.fetch()
|
||||
core.fetch(tool.IfcGit, props.select_remote)
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -336,7 +415,7 @@ class AddRemote(bpy.types.Operator):
|
||||
not repo
|
||||
or not tool.IfcGit.is_valid_ref_format(props.remote_name)
|
||||
or not props.remote_url
|
||||
or props.remote_name in [remote.name for remote in repo.remotes]
|
||||
or props.remote_name in IfcGitData.data["remote_names"]
|
||||
):
|
||||
return False
|
||||
return True
|
||||
@@ -344,8 +423,11 @@ class AddRemote(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.add_remote(tool.IfcGit, repo)
|
||||
core.refresh_revision_list(tool.IfcGit, repo, tool.Ifc)
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
core.add_remote(tool.IfcGit, repo, props.remote_name, props.remote_url)
|
||||
props.remote_name = ""
|
||||
props.remote_url = ""
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -360,8 +442,19 @@ class DeleteRemote(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.delete_remote(tool.IfcGit, repo)
|
||||
core.refresh_revision_list(tool.IfcGit, repo, tool.Ifc)
|
||||
props = tool.IfcGit.get_ifcgit_props()
|
||||
remote_name = props.select_remote
|
||||
if props.display_branch.startswith(remote_name + "/"):
|
||||
active = IfcGitData.data["active_branch_name"]
|
||||
if active:
|
||||
props.display_branch = active
|
||||
else:
|
||||
local_branches = [b for b in IfcGitData.data["branch_names"] if "/" not in b]
|
||||
if local_branches:
|
||||
props.display_branch = local_branches[0]
|
||||
core.delete_remote(tool.IfcGit, repo, remote_name)
|
||||
tool.IfcGit.select_first_remote()
|
||||
core.refresh_revision_list(tool.IfcGit, tool.Ifc)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -375,8 +468,8 @@ class ObjectLog(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not (obj := context.active_object):
|
||||
cls.poll_message_set("No Active Object")
|
||||
if not (obj := context.active_object) or not obj.select_get():
|
||||
cls.poll_message_set("No selected object")
|
||||
elif not tool.Blender.get_ifc_definition_id(obj):
|
||||
cls.poll_message_set("Active Object doesn't have an IFC definition")
|
||||
else:
|
||||
@@ -422,7 +515,7 @@ class RunGitDiff(bpy.types.Operator):
|
||||
)
|
||||
bl_options = set()
|
||||
|
||||
save_to_temp: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
save_to_temp: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||
|
||||
if TYPE_CHECKING:
|
||||
save_to_temp: bool
|
||||
@@ -445,3 +538,37 @@ class RunGitDiff(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
core.run_git_diff(tool.IfcGit, self, self.save_to_temp)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RenameBranch(bpy.types.Operator):
|
||||
"""Rename the current branch"""
|
||||
|
||||
bl_label = "Rename Branch"
|
||||
bl_idname = "ifcgit.rename_branch"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
new_name: bpy.props.StringProperty(name="New name")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
new_name: str
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
IfcGitData.make_sure_is_loaded()
|
||||
if not IfcGitData.data["repo"]:
|
||||
return False
|
||||
if IfcGitData.data["is_detached"]:
|
||||
return False
|
||||
if IfcGitData.data["is_dirty"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.new_name = IfcGitData.data["active_branch_name"]
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
repo = IfcGitData.data["repo"]
|
||||
core.rename_branch(tool.IfcGit, repo, self.new_name)
|
||||
refresh()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,28 +17,14 @@ from bonsai.bim.module.ifcgit.data import IfcGitData
|
||||
def git_branches(self: "IfcGitProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||
# NOTE "Python must keep a reference to the strings returned by
|
||||
# the callback or Blender will misbehave or even crash"
|
||||
IfcGitData.data["branch_names"] = sorted([branch.name for branch in IfcGitData.data["repo"].heads])
|
||||
|
||||
if "main" in IfcGitData.data["branch_names"]:
|
||||
IfcGitData.data["branch_names"].remove("main")
|
||||
IfcGitData.data["branch_names"] = ["main"] + IfcGitData.data["branch_names"]
|
||||
|
||||
if IfcGitData.data["remotes"]:
|
||||
for remote in IfcGitData.data["remotes"]:
|
||||
for remote_branch in remote.refs:
|
||||
IfcGitData.data["branch_names"].append(remote_branch.name)
|
||||
|
||||
return [(myname, myname, myname) for myname in IfcGitData.data["branch_names"]]
|
||||
# Branch list (local + remote, main first) is computed once in IfcGitData.load()
|
||||
IfcGitData.make_sure_is_loaded()
|
||||
return [(name, name, name) for name in IfcGitData.data["branch_names"]]
|
||||
|
||||
|
||||
def git_remotes(self: "IfcGitProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS:
|
||||
IfcGitData.data["remote_names"] = sorted([remote.name for remote in IfcGitData.data["remotes"]])
|
||||
|
||||
if "origin" in IfcGitData.data["remote_names"]:
|
||||
IfcGitData.data["remote_names"].remove("origin")
|
||||
IfcGitData.data["remote_names"] = ["origin"] + IfcGitData.data["remote_names"]
|
||||
|
||||
return [(myname, myname, myname) for myname in IfcGitData.data["remote_names"]]
|
||||
IfcGitData.make_sure_is_loaded()
|
||||
return [(name, name, name) for name in IfcGitData.data["remote_names"]]
|
||||
|
||||
|
||||
def update_revlist(self: "IfcGitProperties", context: bpy.types.Context) -> None:
|
||||
@@ -90,6 +76,7 @@ class IfcGitListItem(PropertyGroup):
|
||||
name="Commit Message",
|
||||
default="",
|
||||
)
|
||||
committed_date: IntProperty(name="Committed Date", default=0)
|
||||
tags: CollectionProperty(type=IfcGitTag, name="List of revision tags")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -98,6 +85,7 @@ class IfcGitListItem(PropertyGroup):
|
||||
author_name: str
|
||||
author_email: str
|
||||
message: str
|
||||
committed_date: int
|
||||
tags: bpy.types.bpy_prop_collection_idprop[IfcGitTag]
|
||||
|
||||
|
||||
@@ -151,6 +139,11 @@ class IfcGitProperties(PropertyGroup):
|
||||
],
|
||||
update=update_revlist,
|
||||
)
|
||||
merge_conflicts: StringProperty(
|
||||
name="Merge Conflicts",
|
||||
description="JSON report from last failed merge attempt",
|
||||
default="",
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ifcgit_commits: bpy.types.bpy_prop_collection_idprop[IfcGitListItem]
|
||||
@@ -165,3 +158,4 @@ class IfcGitProperties(PropertyGroup):
|
||||
display_branch: str
|
||||
select_remote: str
|
||||
ifcgit_filter: Literal["all", "tagged", "relevant"]
|
||||
merge_conflicts: str
|
||||
|
||||
@@ -52,7 +52,7 @@ class IFCGIT_PT_panel(bpy.types.Panel):
|
||||
if IfcGitData.data["repo"] and os.path.exists(IfcGitData.data["repo"].git_dir):
|
||||
name_ifc = IfcGitData.data["name_ifc"]
|
||||
row.label(text=IfcGitData.data["working_dir"], icon="SYSTEM")
|
||||
if name_ifc in IfcGitData.data["untracked_files"]:
|
||||
if IfcGitData.data["ifc_is_untracked"]:
|
||||
row.operator(
|
||||
"ifcgit.addfile",
|
||||
text="Add '" + name_ifc + "' to repository",
|
||||
@@ -112,15 +112,13 @@ class IFCGIT_PT_panel(bpy.types.Panel):
|
||||
row.label(text="Working branch: Detached HEAD")
|
||||
else:
|
||||
row.label(text="Working branch: " + IfcGitData.data["active_branch_name"])
|
||||
row.operator("ifcgit.rename_branch", icon="GREASEPENCIL", text="")
|
||||
|
||||
grouped = layout.row()
|
||||
column = grouped.column()
|
||||
row = column.row()
|
||||
row = layout.row()
|
||||
row.prop(props, "display_branch", text="Browse branch")
|
||||
row.prop(props, "ifcgit_filter", text="Filter revisions")
|
||||
|
||||
row = column.row()
|
||||
row.template_list(
|
||||
layout.template_list(
|
||||
"COMMIT_UL_List",
|
||||
"The_List",
|
||||
props,
|
||||
@@ -128,20 +126,64 @@ class IFCGIT_PT_panel(bpy.types.Panel):
|
||||
props,
|
||||
"commit_index",
|
||||
)
|
||||
column = grouped.column()
|
||||
row = column.row()
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("ifcgit.refresh", icon="FILE_REFRESH")
|
||||
|
||||
if not is_dirty:
|
||||
|
||||
row = column.row()
|
||||
row.operator("ifcgit.display_revision", icon="SELECT_DIFFERENCE")
|
||||
|
||||
row = column.row()
|
||||
row.operator("ifcgit.switch_revision", icon="CURRENT_FILE")
|
||||
row.operator("ifcgit.merge", icon="SYSTEM")
|
||||
|
||||
row = column.row()
|
||||
row.operator("ifcgit.merge", icon="EXPERIMENTAL", text="")
|
||||
conflicts = tool.IfcGit.get_merge_conflicts()
|
||||
if conflicts is not None:
|
||||
box = layout.box()
|
||||
box.alert = True
|
||||
row = box.row()
|
||||
row.label(
|
||||
text=f"Merge failed \u2014 {len(conflicts)} conflict(s)",
|
||||
icon="ERROR",
|
||||
)
|
||||
for conflict in conflicts:
|
||||
col = box.column(align=True)
|
||||
conflict_type = conflict.get("type", "")
|
||||
entity_id = conflict.get("entity_id", "?")
|
||||
local_id = conflict.get("original_local_id")
|
||||
|
||||
if conflict_type == "attribute_conflict":
|
||||
entity_class = conflict.get("entity_class", "Entity")
|
||||
attr_idx = conflict.get("attribute_index", "?")
|
||||
desc = f"#{entity_id} {entity_class}: attribute {attr_idx} conflict"
|
||||
elif conflict_type == "entity_deleted_and_modified":
|
||||
entity_class = conflict.get("entity_class", "Entity")
|
||||
desc = f"#{entity_id} {entity_class}: " + conflict.get("message", "deleted/modified conflict")
|
||||
elif conflict_type == "class_changed":
|
||||
desc = (
|
||||
f"#{entity_id}: class changed "
|
||||
+ conflict.get("base_class", "?")
|
||||
+ " \u2192 "
|
||||
+ conflict.get("modified_class", "?")
|
||||
)
|
||||
elif conflict_type == "required_entity_deleted":
|
||||
desc = f"#{entity_id}: " + conflict.get("message", "required entity deleted")
|
||||
else:
|
||||
desc = f"#{entity_id}: {conflict_type}"
|
||||
|
||||
row = col.row(align=True)
|
||||
row.label(text=desc)
|
||||
if local_id:
|
||||
op = row.operator(
|
||||
"ifcgit.select_conflict_entity",
|
||||
text="",
|
||||
icon="RESTRICT_SELECT_OFF",
|
||||
)
|
||||
op.step_id = local_id
|
||||
|
||||
if conflict_type == "attribute_conflict":
|
||||
sub = col.column(align=True)
|
||||
sub.scale_y = 0.75
|
||||
sub.label(text=f" Base: {conflict.get('base_value', '')}")
|
||||
sub.label(text=f" Local: {conflict.get('local_value', '')}")
|
||||
sub.label(text=f" Remote: {conflict.get('remote_value', '')}")
|
||||
|
||||
if not props.ifcgit_commits:
|
||||
return
|
||||
@@ -216,13 +258,7 @@ class COMMIT_UL_List(bpy.types.UIList):
|
||||
):
|
||||
|
||||
current_revision = IfcGitData.data["current_revision"]
|
||||
|
||||
# TODO Figure how this "item" can be acesse in "data.py"
|
||||
# so it's possible to move the ".commit"
|
||||
try:
|
||||
commit = IfcGitData.data["repo"].commit(rev=item.hexsha)
|
||||
except ValueError:
|
||||
return
|
||||
current_hexsha = current_revision.hexsha if current_revision else None
|
||||
|
||||
lookup = IfcGitData.data["branches_by_hexsha"]
|
||||
refs = ""
|
||||
@@ -236,11 +272,11 @@ class COMMIT_UL_List(bpy.types.UIList):
|
||||
for tag in lookup[item.hexsha]:
|
||||
refs += "{" + tag.name + "} "
|
||||
|
||||
if commit == current_revision:
|
||||
layout.label(text="[HEAD] " + refs + commit.message.split("\n")[0], icon="DECORATE_KEYFRAME")
|
||||
if item.hexsha == current_hexsha:
|
||||
layout.label(text="[HEAD] " + refs + item.message.split("\n")[0], icon="DECORATE_KEYFRAME")
|
||||
else:
|
||||
layout.label(text=refs + commit.message.split("\n")[0], icon="DECORATE_ANIMATE")
|
||||
layout.label(text=time.strftime("%c", time.localtime(commit.committed_date)))
|
||||
layout.label(text=refs + item.message.split("\n")[0], icon="DECORATE_ANIMATE")
|
||||
layout.label(text=time.strftime("%c", time.localtime(item.committed_date)))
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
|
||||
|
||||
@@ -272,21 +272,21 @@ class RadianceRender(bpy.types.Operator):
|
||||
+ '''" map_u map_v
|
||||
0
|
||||
1 0.5
|
||||
|
||||
|
||||
# This is a multiplier to colour balance the env map
|
||||
# In this case, it provides a rough ground luminance from 3k-5k
|
||||
env_map colorfunc env_colour
|
||||
4 100 100 100 .
|
||||
0
|
||||
0
|
||||
|
||||
|
||||
# .37 .57 1.5 is measured from a HDRI image
|
||||
# It is multiplied by a factor such that grey(r,g,b) = 1
|
||||
skyfunc colorfunc sky_colour
|
||||
4 .64 .99 2.6 .
|
||||
0
|
||||
0
|
||||
|
||||
|
||||
void mixpict composite
|
||||
7 env_colour sky_colour grey "'''
|
||||
+ hdr_mask_path
|
||||
@@ -295,22 +295,22 @@ void mixpict composite
|
||||
+ """" map_u map_v
|
||||
0
|
||||
2 0.5 1
|
||||
|
||||
|
||||
composite glow env_map_glow
|
||||
0
|
||||
0
|
||||
4 1 1 1 0
|
||||
|
||||
|
||||
env_map_glow source sky
|
||||
0
|
||||
0
|
||||
4 0 0 1 180
|
||||
|
||||
|
||||
env_colour glow ground_glow
|
||||
0
|
||||
0
|
||||
4 1 1 1 0
|
||||
|
||||
|
||||
ground_glow source ground
|
||||
0
|
||||
0
|
||||
@@ -566,7 +566,7 @@ class LightPickCoordinates(bpy.types.Operator):
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
use_current_location: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
use_current_location: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||
|
||||
if TYPE_CHECKING:
|
||||
use_current_location: bool
|
||||
|
||||
@@ -136,7 +136,7 @@ class SplitAlongEdge(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"Will unassign element from a type if type has a representation."
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
mode: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
mode: bpy.props.EnumProperty(
|
||||
default="BOOLEAN",
|
||||
items=tuple((i, i, "") for i in get_args(SplitAlongEdgeMode)),
|
||||
)
|
||||
@@ -359,7 +359,7 @@ class ConfirmQuickFavoriteOperator(bpy.types.Operator):
|
||||
bl_idname = "bim.confirm_quick_favorite_operator"
|
||||
bl_label = "Confirm Operator"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
index: int
|
||||
@@ -452,10 +452,8 @@ class MoveQuickFavoritesItem(bpy.types.Operator):
|
||||
bl_idname = "bim.move_quick_favorites_item"
|
||||
bl_label = "Move Quick Favorites Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
direction: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
items=[("UP", "Up", ""), ("DOWN", "Down", "")]
|
||||
)
|
||||
index: bpy.props.IntProperty()
|
||||
direction: bpy.props.EnumProperty(items=[("UP", "Up", ""), ("DOWN", "Down", "")])
|
||||
|
||||
if TYPE_CHECKING:
|
||||
index: int
|
||||
@@ -474,7 +472,7 @@ class RemoveQuickFavoritesItem(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_quick_favorites_item"
|
||||
bl_label = "Remove Quick Favorites Item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
index: int
|
||||
|
||||
@@ -36,9 +36,9 @@ QuickFavoriteValueType = Literal["float_value", "bool_value", "int_value", "stri
|
||||
|
||||
|
||||
class QuickFavoriteEnumItem(PropertyGroup):
|
||||
name: StringProperty(name="Name", default="") # pyright: ignore[reportRedeclaration]
|
||||
display_name: StringProperty(name="Display Name", default="") # pyright: ignore[reportRedeclaration]
|
||||
description: StringProperty(name="Description", default="") # pyright: ignore[reportRedeclaration]
|
||||
name: StringProperty(name="Name", default="")
|
||||
display_name: StringProperty(name="Display Name", default="")
|
||||
description: StringProperty(name="Description", default="")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
@@ -51,19 +51,19 @@ def get_enum_items(self: "QuickFavoriteProperty", context: bpy.types.Context | N
|
||||
|
||||
|
||||
class QuickFavoriteProperty(PropertyGroup):
|
||||
name: StringProperty(name="Name", default="") # pyright: ignore[reportRedeclaration]
|
||||
display_name: StringProperty(name="Display Name", default="") # pyright: ignore[reportRedeclaration]
|
||||
value_prop: EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: StringProperty(name="Name", default="")
|
||||
display_name: StringProperty(name="Display Name", default="")
|
||||
value_prop: EnumProperty(
|
||||
name="Value Prop",
|
||||
items=tuple((v, v, "") for v in get_args(QuickFavoriteValueType)),
|
||||
)
|
||||
string_value: StringProperty(name="String Value", default="") # pyright: ignore[reportRedeclaration]
|
||||
float_value: FloatProperty(name="Float Value", default=0.0) # pyright: ignore[reportRedeclaration]
|
||||
int_value: IntProperty(name="Int Value", default=0) # pyright: ignore[reportRedeclaration]
|
||||
bool_value: BoolProperty(name="Bool Value", default=False) # pyright: ignore[reportRedeclaration]
|
||||
enum_value: EnumProperty(name="Enum Value", items=get_enum_items) # pyright: ignore[reportRedeclaration]
|
||||
enum_items: CollectionProperty(type=QuickFavoriteEnumItem) # pyright: ignore[reportRedeclaration]
|
||||
is_active: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
string_value: StringProperty(name="String Value", default="")
|
||||
float_value: FloatProperty(name="Float Value", default=0.0)
|
||||
int_value: IntProperty(name="Int Value", default=0)
|
||||
bool_value: BoolProperty(name="Bool Value", default=False)
|
||||
enum_value: EnumProperty(name="Enum Value", items=get_enum_items)
|
||||
enum_items: CollectionProperty(type=QuickFavoriteEnumItem)
|
||||
is_active: BoolProperty(
|
||||
name="Is Active",
|
||||
description="Only active properties will be added to the operator when invoked from Quick Favorites",
|
||||
default=False,
|
||||
@@ -100,20 +100,20 @@ def get_operator_suggestions(self: "QuickFavoritesItem", context: bpy.types.Cont
|
||||
|
||||
|
||||
class QuickFavoritesItem(PropertyGroup):
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False) # pyright: ignore[reportRedeclaration]
|
||||
search: StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||
search: StringProperty(
|
||||
name="Search",
|
||||
default="",
|
||||
search=get_operator_suggestions,
|
||||
# Resetting `search_options`, allowing users only to use suggestions.
|
||||
search_options=set(),
|
||||
)
|
||||
properties: CollectionProperty(type=QuickFavoriteProperty) # pyright: ignore[reportRedeclaration]
|
||||
operator_id: StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
properties: CollectionProperty(type=QuickFavoriteProperty)
|
||||
operator_id: StringProperty(
|
||||
name="Operator ID",
|
||||
default="",
|
||||
)
|
||||
label: StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
label: StringProperty(
|
||||
name="Label",
|
||||
description="Label that will be used in Quick Favorites for this operator",
|
||||
default="",
|
||||
@@ -139,15 +139,15 @@ class QuickFavoritesItem(PropertyGroup):
|
||||
|
||||
|
||||
class BIMMiscProperties(PropertyGroup):
|
||||
total_storeys: IntProperty( # pyright: ignore[reportRedeclaration]
|
||||
total_storeys: IntProperty(
|
||||
name="Total Storeys",
|
||||
description="Number of storeys above object's storey to take into account for resizing",
|
||||
default=1,
|
||||
)
|
||||
override_colour: FloatVectorProperty( # pyright: ignore[reportRedeclaration]
|
||||
override_colour: FloatVectorProperty(
|
||||
name="Override Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
|
||||
)
|
||||
quick_favorites: CollectionProperty(type=QuickFavoritesItem) # pyright: ignore[reportRedeclaration]
|
||||
quick_favorites: CollectionProperty(type=QuickFavoritesItem)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
total_storeys: int
|
||||
|
||||
@@ -545,7 +545,7 @@ class ChangeTypePage(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.change_type_page"
|
||||
bl_label = "Change Type Page"
|
||||
bl_options = {"REGISTER"}
|
||||
page: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
page: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
page: int
|
||||
|
||||
@@ -271,7 +271,7 @@ class ExtendProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.extend_profile"
|
||||
bl_label = "Extend Profile"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
join_type: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
join_type: bpy.props.EnumProperty(
|
||||
items=[("-", "Unjoin", ""), ("L", "L", ""), ("V", "V", ""), ("T", "T", "")],
|
||||
default="-",
|
||||
)
|
||||
|
||||
@@ -1729,20 +1729,20 @@ def poll_sverchok_nodes(self: "BIMExternalParametricGeometryProperties", node_tr
|
||||
|
||||
|
||||
class BIMExternalParametricGeometryProperties(bpy.types.PropertyGroup):
|
||||
is_editing: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
is_editing: bpy.props.BoolProperty(
|
||||
name="Is Editing Paramteric Geometry",
|
||||
description="Toggle editing parametric geometry.",
|
||||
default=False,
|
||||
update=update_is_editing,
|
||||
)
|
||||
geometry_source: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
geometry_source: bpy.props.EnumProperty(
|
||||
name="Geometry Source",
|
||||
items=[
|
||||
("GEONODES", "Geometry Nodes", ""),
|
||||
("IFCSVERCHOK", "IFC Sverchok", ""),
|
||||
],
|
||||
)
|
||||
geo_nodes: bpy.props.PointerProperty( # pyright: ignore[reportRedeclaration]
|
||||
geo_nodes: bpy.props.PointerProperty(
|
||||
name="Geometry Nodes",
|
||||
description="Geometry nodes tree to use as a source for representation.",
|
||||
type=bpy.types.GeometryNodeTree,
|
||||
@@ -1750,7 +1750,7 @@ class BIMExternalParametricGeometryProperties(bpy.types.PropertyGroup):
|
||||
poll=lambda self, node_tree: not node_tree.name.startswith("BBIM_EPG"),
|
||||
)
|
||||
|
||||
sverchok_nodes: bpy.props.PointerProperty( # pyright: ignore[reportRedeclaration]
|
||||
sverchok_nodes: bpy.props.PointerProperty(
|
||||
name="Sverchok Nodes",
|
||||
description="Sverchok node tree to use as a source for representation.",
|
||||
type=bpy.types.NodeTree,
|
||||
|
||||
@@ -101,7 +101,7 @@ class NestDecorator:
|
||||
cls.is_installed = False
|
||||
|
||||
def dotted_line_shader(self):
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth("FLOAT", "v_ArcLength")
|
||||
|
||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||
|
||||
@@ -33,7 +33,7 @@ class EnableEditingPerson(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_person"
|
||||
bl_label = "Enable Editing Person"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
person: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
person: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
person: int
|
||||
@@ -75,7 +75,7 @@ class RemovePerson(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_person"
|
||||
bl_label = "Remove Person"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
person: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
person: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
person: int
|
||||
@@ -88,7 +88,7 @@ class AddPersonAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.add_person_attribute"
|
||||
bl_label = "Add Person Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: bpy.props.EnumProperty(
|
||||
items=tuple((i, i, "") for i in get_args(tool.Owner.PersonAttributeType)),
|
||||
)
|
||||
|
||||
@@ -104,10 +104,10 @@ class RemovePersonAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_person_attribute"
|
||||
bl_label = "Remove Person Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: bpy.props.EnumProperty(
|
||||
items=tuple((i, i, "") for i in get_args(tool.Owner.PersonAttributeType)),
|
||||
)
|
||||
id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: tool.Owner.PersonAttributeType # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
@@ -122,7 +122,7 @@ class EnableEditingRole(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_role"
|
||||
bl_label = "Enable Editing Role"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
role: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
role: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
role: int
|
||||
@@ -146,7 +146,7 @@ class AddRole(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_role"
|
||||
bl_label = "Add Role"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
parent: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
parent: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
parent: int
|
||||
@@ -168,7 +168,7 @@ class RemoveRole(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_role"
|
||||
bl_label = "Remove Role"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
role: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
role: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
role: int
|
||||
@@ -181,8 +181,8 @@ class AddAddress(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_address"
|
||||
bl_label = "Add Address"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
parent: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
ifc_class: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
parent: bpy.props.IntProperty()
|
||||
ifc_class: bpy.props.EnumProperty(
|
||||
items=tuple((i, i, "") for i in get_args(ADDRESS_TYPE)),
|
||||
)
|
||||
|
||||
@@ -198,7 +198,7 @@ class AddAddressAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.add_address_attribute"
|
||||
bl_label = "Add Address Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: bpy.props.EnumProperty(
|
||||
items=tuple((i, i, "") for i in get_args(tool.Owner.AddressAttributeType)),
|
||||
)
|
||||
|
||||
@@ -214,10 +214,10 @@ class RemoveAddressAttribute(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_address_attribute"
|
||||
bl_label = "Remove Address Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: bpy.props.EnumProperty(
|
||||
items=tuple((i, i, "") for i in get_args(tool.Owner.AddressAttributeType)),
|
||||
)
|
||||
id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: tool.Owner.AddressAttributeType # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
@@ -232,7 +232,7 @@ class EnableEditingAddress(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_address"
|
||||
bl_label = "Enable Editing Address"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
address: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
address: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
address: int
|
||||
@@ -265,7 +265,7 @@ class RemoveAddress(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_address"
|
||||
bl_label = "Remove Address"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
address: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
address: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
address: int
|
||||
@@ -278,7 +278,7 @@ class EnableEditingOrganisation(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_organisation"
|
||||
bl_label = "Enable Editing Organisation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
organisation: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
organisation: int
|
||||
@@ -320,7 +320,7 @@ class RemoveOrganisation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_organisation"
|
||||
bl_label = "Remove Organisation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
organisation: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
organisation: int
|
||||
@@ -333,8 +333,8 @@ class AddPersonAndOrganisation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_person_and_organisation"
|
||||
bl_label = "Add Person And Organisation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
person: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
person: bpy.props.IntProperty()
|
||||
organisation: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
person: int
|
||||
@@ -350,7 +350,7 @@ class RemovePersonAndOrganisation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_person_and_organisation"
|
||||
bl_label = "Remove Person And Organisation"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
person_and_organisation: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
person_and_organisation: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
person_and_organisation: int
|
||||
@@ -365,7 +365,7 @@ class SetUser(bpy.types.Operator):
|
||||
bl_idname = "bim.set_user"
|
||||
bl_label = "Set User"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
user: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
user: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
user: int
|
||||
@@ -401,7 +401,7 @@ class EnableEditingActor(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_actor"
|
||||
bl_label = "Enable Editing Actor"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
actor: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
actor: int
|
||||
@@ -434,7 +434,7 @@ class RemoveActor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_actor"
|
||||
bl_label = "Remove Actor"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
actor: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
actor: int
|
||||
@@ -447,7 +447,7 @@ class AssignActor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_actor"
|
||||
bl_label = "Assign Actor"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
actor: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
actor: int
|
||||
@@ -462,7 +462,7 @@ class UnassignActor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.unassign_actor"
|
||||
bl_label = "Unassign Actor"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
actor: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
actor: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
actor: int
|
||||
@@ -481,7 +481,7 @@ class RemoveApplication(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"Remove provided IfcApplication."
|
||||
"\n\nFor safety will only work on applications without inverses (they are typically marked as '(unused)'."
|
||||
)
|
||||
application_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
application_id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
application_id: int
|
||||
@@ -525,7 +525,7 @@ class EnableEditingApplication(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_application"
|
||||
bl_label = "Enable Editing Application"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
application_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
application_id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
application_id: int
|
||||
|
||||
@@ -86,9 +86,7 @@ class NewProject(bpy.types.Operator):
|
||||
bl_label = "New Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Start a new IFC project in a fresh session"
|
||||
preset: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
items=[(i, i, "") for i in get_args(PresetType)]
|
||||
)
|
||||
preset: bpy.props.EnumProperty(items=[(i, i, "") for i in get_args(PresetType)])
|
||||
|
||||
if TYPE_CHECKING:
|
||||
preset: PresetType
|
||||
@@ -178,13 +176,9 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
||||
bl_description = (
|
||||
"Select an IFC file that can be used as a library.\n\nALT+click to reload the current loaded library file."
|
||||
)
|
||||
filter_glob: bpy.props.StringProperty(
|
||||
default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
append_all: bpy.props.BoolProperty(default=False) # pyright: ignore[reportRedeclaration]
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path", default=False
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
append_all: bpy.props.BoolProperty(default=False)
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
filter_glob: str
|
||||
@@ -568,7 +562,7 @@ class AppendLibraryElementByQuery(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.append_library_element_by_query"
|
||||
bl_label = "Append Library Element By Query"
|
||||
|
||||
query: bpy.props.StringProperty(name="Query") # pyright: ignore[reportRedeclaration]
|
||||
query: bpy.props.StringProperty(name="Query")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
query: str
|
||||
@@ -600,11 +594,9 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"Append element to the current project.\n\n"
|
||||
"ALT+CLICK to skip reusing materials, profiles, styles based on their name (may result in duplicates)"
|
||||
)
|
||||
definition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
prop_index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
assume_unique_by_name: bpy.props.BoolProperty(
|
||||
name="Assume Unique By Name", default=True, options={"SKIP_SAVE"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
definition: bpy.props.IntProperty()
|
||||
prop_index: bpy.props.IntProperty()
|
||||
assume_unique_by_name: bpy.props.BoolProperty(name="Assume Unique By Name", default=True, options={"SKIP_SAVE"})
|
||||
|
||||
if TYPE_CHECKING:
|
||||
definition: int
|
||||
@@ -959,28 +951,24 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
||||
bl_label = "Load Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Load an existing IFC project"
|
||||
filepath: bpy.props.StringProperty(
|
||||
subtype="FILE_PATH", options={"SKIP_SAVE"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
filter_glob: bpy.props.StringProperty(
|
||||
default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
is_advanced: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE"})
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
|
||||
is_advanced: bpy.props.BoolProperty(
|
||||
name="Enable Advanced Mode",
|
||||
description="Load IFC file with advanced settings. Checking this option will skip loading IFC file and will open advanced load settings",
|
||||
default=False,
|
||||
)
|
||||
use_relative_path: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path",
|
||||
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
||||
default=False,
|
||||
)
|
||||
should_start_fresh_session: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
should_start_fresh_session: bpy.props.BoolProperty(
|
||||
name="Should Start Fresh Session",
|
||||
description="Clear current Blender session before loading IFC. Not supported with 'Use Relative Path' option",
|
||||
default=True,
|
||||
)
|
||||
import_without_ifc_data: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
import_without_ifc_data: bpy.props.BoolProperty(
|
||||
name="Import Without IFC Data",
|
||||
description=(
|
||||
"Import IFC objects as Blender objects without any IFC metadata and authoring capabilities."
|
||||
@@ -988,9 +976,7 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
||||
),
|
||||
default=False,
|
||||
)
|
||||
use_detailed_tooltip: bpy.props.BoolProperty(
|
||||
default=False, options={"HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
use_detailed_tooltip: bpy.props.BoolProperty(default=False, options={"HIDDEN"})
|
||||
filename_ext = ".ifc"
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -1300,7 +1286,7 @@ class ToggleFilterCategories(bpy.types.Operator):
|
||||
bl_idname = "bim.toggle_filter_categories"
|
||||
bl_label = "Toggle Filter Categories"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
should_select: bpy.props.BoolProperty(name="Should Select", default=True) # pyright: ignore[reportRedeclaration]
|
||||
should_select: bpy.props.BoolProperty(name="Should Select", default=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
should_select: bool
|
||||
@@ -1327,7 +1313,7 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
||||
default=False,
|
||||
)
|
||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
||||
query: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
query: bpy.props.StringProperty(
|
||||
name="Query",
|
||||
description=(
|
||||
"Custom selector query to use to load element from a linked model. E.g. 'IfcElement'.\n\n"
|
||||
@@ -1404,7 +1390,7 @@ class UnlinkIfc(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Remove the selected file from the link list"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
link_index: int
|
||||
@@ -1428,7 +1414,7 @@ class UnloadLink(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Unload the selected linked file"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
link_index: int
|
||||
@@ -1454,9 +1440,9 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Load the selected file"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) # pyright: ignore[reportRedeclaration]
|
||||
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
||||
query: bpy.props.StringProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
link_index: int
|
||||
@@ -1631,7 +1617,7 @@ class ReloadLink(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Reload the selected file"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
link_index: int
|
||||
@@ -1647,7 +1633,7 @@ class ToggleLinkSelectability(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Toggle selectability"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
link_index: int
|
||||
@@ -1679,8 +1665,8 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Toggle visibility between SOLID and WIREFRAME"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
mode: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
mode: bpy.props.EnumProperty(
|
||||
name="Visibility Mode",
|
||||
items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")),
|
||||
)
|
||||
@@ -1821,7 +1807,7 @@ class SelectLinkHandle(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Select link empty object handle"
|
||||
|
||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||
link_index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
link_index: int
|
||||
@@ -1843,7 +1829,7 @@ class SelectLinkedModelElement(bpy.types.Operator):
|
||||
bl_options = {"REGISTER"}
|
||||
bl_description = "Select an element in the currently selected linked model by providing GlobalId."
|
||||
|
||||
guid: bpy.props.StringProperty(name="GlobalId") # pyright: ignore[reportRedeclaration]
|
||||
guid: bpy.props.StringProperty(name="GlobalId")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
guid: str
|
||||
@@ -1882,21 +1868,11 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filename_ext = ".ifc"
|
||||
supported_filexts = (".ifc", ".ifczip", ".ifcjson")
|
||||
filter_glob: bpy.props.StringProperty(
|
||||
default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
json_version: bpy.props.EnumProperty(
|
||||
items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version"
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
json_compact: bpy.props.BoolProperty(
|
||||
name="Export Compact IFCJSON", default=False
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
should_save_as: bpy.props.BoolProperty(
|
||||
name="Should Save As", default=False, options={"HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path", default=False
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
filter_glob: bpy.props.StringProperty(default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"})
|
||||
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
|
||||
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
|
||||
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
filter_glob: str
|
||||
@@ -2053,7 +2029,7 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
|
||||
bl_description = "Operator is used to load a project .cache.blend to then link it to the IFC file."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
query: bpy.props.StringProperty()
|
||||
"""See ``bim.link_ifc``."""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -2443,8 +2419,8 @@ class HideQueriedLinkedElement(bpy.types.Operator):
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
unhide_all: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
hide_all_except: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
unhide_all: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||
hide_all_except: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||
|
||||
if TYPE_CHECKING:
|
||||
unhide_all: bool
|
||||
@@ -2918,12 +2894,8 @@ class IFCFileHandlerOperator(bpy.types.Operator):
|
||||
bl_label = "Import .ifc file"
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
|
||||
directory: bpy.props.StringProperty(
|
||||
subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
files: bpy.props.CollectionProperty(
|
||||
type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"}
|
||||
) # pyright: ignore[reportRedeclaration]
|
||||
directory: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"})
|
||||
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"})
|
||||
|
||||
if TYPE_CHECKING:
|
||||
directory: str
|
||||
@@ -2978,7 +2950,7 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
|
||||
bl_label = "Measure Tool"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
measure_type: bpy.props.StringProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
measure_type: str
|
||||
@@ -3077,7 +3049,7 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
|
||||
bl_label = "Measure Face Area Tool"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
measure_type: bpy.props.StringProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
measure_type: str
|
||||
@@ -3379,7 +3351,7 @@ class LoadBlendMetadataAndIFC(bpy.types.Operator):
|
||||
bl_idname = "bim.load_blend_metadata_and_ifc"
|
||||
bl_label = "Load Blend Metadata and IFC"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(name="IFC File Path", default="") # pyright: ignore[reportRedeclaration]
|
||||
filepath: bpy.props.StringProperty(name="IFC File Path", default="")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
filepath: str
|
||||
|
||||
@@ -345,7 +345,7 @@ class BIMProjectProperties(PropertyGroup):
|
||||
),
|
||||
default=False,
|
||||
)
|
||||
should_cache: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
should_cache: BoolProperty(
|
||||
name="Cache",
|
||||
description=(
|
||||
"Cache loaded geometry to .h5 file in your cache directory (see in preferences) "
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bpy
|
||||
@@ -384,6 +385,18 @@ class BIM_PT_new_project_wizard(Panel):
|
||||
row = self.layout.row()
|
||||
row.operator("bim.create_project")
|
||||
|
||||
if shutil.which("git"):
|
||||
git_props = context.scene.IfcGitProperties
|
||||
box = self.layout.box()
|
||||
row = box.row()
|
||||
row.label(text="Clone a remote Git repository")
|
||||
row = box.row()
|
||||
row.prop(git_props, "remote_url")
|
||||
row = box.row()
|
||||
row.prop(git_props, "local_folder")
|
||||
row = box.row()
|
||||
row.operator("ifcgit.clone_repo", icon="IMPORT")
|
||||
|
||||
|
||||
class BIM_PT_project_library(Panel):
|
||||
bl_label = "Project Library"
|
||||
|
||||
@@ -240,7 +240,7 @@ class CopyPropertyToSelection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Copy Property To Selection"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
name: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
@@ -280,10 +280,10 @@ class BIM_OT_add_property_to_edit(bpy.types.Operator):
|
||||
bl_label = "Add Property to Edit"
|
||||
bl_idname = "bim.add_property_to_edit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
option: bpy.props.EnumProperty(
|
||||
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
||||
)
|
||||
index: bpy.props.IntProperty(default=-1) # pyright: ignore[reportRedeclaration]
|
||||
index: bpy.props.IntProperty(default=-1)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
option: tool.Pset.BulkOperationType
|
||||
@@ -307,9 +307,9 @@ class BIM_OT_remove_property_to_edit(bpy.types.Operator):
|
||||
bl_label = "Remove Property from Editing"
|
||||
bl_idname = "bim.remove_property_to_edit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
index2: bpy.props.IntProperty(default=-1) # pyright: ignore[reportRedeclaration]
|
||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
index: bpy.props.IntProperty()
|
||||
index2: bpy.props.IntProperty(default=-1)
|
||||
option: bpy.props.EnumProperty(
|
||||
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
||||
)
|
||||
|
||||
@@ -336,7 +336,7 @@ class BIM_OT_bulk_edit_clear_list(bpy.types.Operator):
|
||||
bl_label = "Clear List of Properties"
|
||||
bl_idname = "bim.pset_bulk_edit_clear_list"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
option: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
option: bpy.props.EnumProperty(
|
||||
items=[(t, t, "") for t in tool.Pset.BULK_OPERATION_TYPES],
|
||||
)
|
||||
|
||||
|
||||
@@ -368,9 +368,9 @@ class GlobalPsetProperties(PropertyGroup):
|
||||
qto_filter: StringProperty(name="Qto Filter", options={"TEXTEDIT_UPDATE"})
|
||||
|
||||
# Bulk operations.
|
||||
psets_to_delete: CollectionProperty(type=DeletePsetEntry) # pyright: ignore[reportRedeclaration]
|
||||
psets_to_rename: CollectionProperty(type=RenamePropertyEntry) # pyright: ignore[reportRedeclaration]
|
||||
psets_to_add_edit: CollectionProperty(type=AddEditPropertyEntry) # pyright: ignore[reportRedeclaration]
|
||||
psets_to_delete: CollectionProperty(type=DeletePsetEntry)
|
||||
psets_to_rename: CollectionProperty(type=RenamePropertyEntry)
|
||||
psets_to_add_edit: CollectionProperty(type=AddEditPropertyEntry)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pset_filter: str
|
||||
|
||||
@@ -95,7 +95,7 @@ class ColourByPropertyData:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return default
|
||||
keys = [a.name() for a in element.wrapped_data.declaration().as_entity().all_attributes()]
|
||||
keys = [a.name() for a in element.declaration().as_entity().all_attributes()]
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
for pset, properties in psets.items():
|
||||
if pset.endswith("Common"):
|
||||
@@ -126,7 +126,7 @@ class SelectSimilarData:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return []
|
||||
keys = [a.name() for a in element.wrapped_data.declaration().as_entity().all_attributes()]
|
||||
keys = [a.name() for a in element.declaration().as_entity().all_attributes()]
|
||||
psets = ifcopenshell.util.element.get_psets(element, psets_only=True)
|
||||
for pset, properties in psets.items():
|
||||
if pset.endswith("Common"):
|
||||
|
||||
@@ -799,7 +799,7 @@ class SelectQueryElements(Operator):
|
||||
bl_description = "Select elements matching an provided selector query"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
query: StringProperty(name="Query") # pyright: ignore[reportRedeclaration]
|
||||
query: StringProperty(name="Query")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
query: str
|
||||
@@ -829,12 +829,12 @@ class SaveSearch(Operator, tool.Ifc.Operator):
|
||||
# Extra item so it will be easy to select current text.
|
||||
return [text] + SaveSearch.name_search_items
|
||||
|
||||
name: StringProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: StringProperty(
|
||||
name="Name",
|
||||
search=get_name_search_items,
|
||||
search_options={"SORT"},
|
||||
)
|
||||
module: StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
module: StringProperty()
|
||||
|
||||
def update_use_all_ifcgroups(self, context: object = None) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
@@ -845,7 +845,7 @@ class SaveSearch(Operator, tool.Ifc.Operator):
|
||||
}
|
||||
self.name_search_items[:] = natsorted(groups)
|
||||
|
||||
use_all_ifcgroups: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
use_all_ifcgroups: BoolProperty(
|
||||
name="Use Any IfcGroup",
|
||||
description=(
|
||||
"By default we're targeting only IfcGroups with SEARCH ObjectType "
|
||||
|
||||
@@ -106,7 +106,7 @@ class ActivateStatusFilters(bpy.types.Operator):
|
||||
bl_description = "Filter and display objects based on currently selected IFC statuses"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
only_if_enabled: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
only_if_enabled: bpy.props.BoolProperty(
|
||||
name="Only If Filters are Enabled",
|
||||
description="Activate status filters only in case if they were enabled from the UI before.",
|
||||
default=False,
|
||||
@@ -137,7 +137,7 @@ class SelectStatusFilter(bpy.types.Operator):
|
||||
bl_description = "Select elements with currently selected status"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
status: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
status: bpy.props.StringProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
status: tool.Sequence.ElementStatusUI
|
||||
@@ -156,7 +156,7 @@ class AssignStatus(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Assign status to the selected elements.\n\nAlt+CLICK to unassign the status."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
should_override_previous_status: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
should_override_previous_status: bpy.props.BoolProperty(
|
||||
name="Override Previous Status",
|
||||
description=(
|
||||
"Whether assigning new status should override previous one.\n\n"
|
||||
@@ -165,8 +165,8 @@ class AssignStatus(bpy.types.Operator, tool.Ifc.Operator):
|
||||
),
|
||||
default=True,
|
||||
)
|
||||
status: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
should_unassign_status: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
status: bpy.props.StringProperty()
|
||||
should_unassign_status: bpy.props.BoolProperty(
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
|
||||
@@ -415,7 +415,7 @@ class CopyWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Copy Work Schedule"
|
||||
bl_description = "Create a duplicate of the provided work schedule."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
work_schedule: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
work_schedule: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
work_schedule: int
|
||||
|
||||
@@ -412,7 +412,7 @@ WorkPlanEditingType = Literal["-", "ATTRIBUTES", "SCHEDULES", "WORK_SCHEDULE", "
|
||||
|
||||
class BIMWorkPlanProperties(PropertyGroup):
|
||||
work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute)
|
||||
editing_type: EnumProperty( # pyright: ignore[reportRedeclaration]
|
||||
editing_type: EnumProperty(
|
||||
items=[(i, i, "") for i in get_args(WorkPlanEditingType)],
|
||||
)
|
||||
work_plans: CollectionProperty(name="Work Plans", type=WorkPlan)
|
||||
@@ -430,8 +430,8 @@ class BIMWorkPlanProperties(PropertyGroup):
|
||||
|
||||
|
||||
class IFCStatus(PropertyGroup):
|
||||
name: StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
is_visible: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
name: StringProperty()
|
||||
is_visible: BoolProperty(
|
||||
name="Is Visible", default=True, update=lambda x, y: (None, bpy.ops.bim.activate_status_filters())[0]
|
||||
)
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ class CopyToContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Copy to Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
container: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
container: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
container: int
|
||||
|
||||
@@ -167,7 +167,7 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_structural_boundary_condition"
|
||||
bl_label = "Enable Editing Structural Boundary Condition"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
boundary_condition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
boundary_condition: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
boundary_condition: int
|
||||
@@ -186,7 +186,7 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.edit_structural_boundary_condition"
|
||||
bl_label = "Edit Structural Boundary Condition"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
connection: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
connection: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
connection: int
|
||||
@@ -917,7 +917,7 @@ class EnableEditingBoundaryCondition(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_boundary_condition"
|
||||
bl_label = "Enable Editing Boundary Condition"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
boundary_condition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
boundary_condition: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
boundary_condition: int
|
||||
|
||||
@@ -83,7 +83,7 @@ class DecorationShader:
|
||||
PARALLEL DISTRIBUTED FORCE,
|
||||
DISTRIBUTED MOMENT,
|
||||
"""
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty:ignore[too-many-positional-arguments]
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth("VEC3", "forces")
|
||||
vert_out.smooth("VEC3", "co")
|
||||
|
||||
@@ -203,7 +203,7 @@ class DecorationShader:
|
||||
"""param: pattern: type of pattern
|
||||
SINGLE FORCE,
|
||||
SINGLE MOMENT"""
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty: ignore[too-many-positional-arguments]
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth("VEC3", "co")
|
||||
|
||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||
@@ -253,7 +253,7 @@ class DecorationShader:
|
||||
|
||||
def get_planar_shader(self) -> gpu.types.GPUShader:
|
||||
"""shader for planar loads"""
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") # ty: ignore[too-many-positional-arguments]
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth("VEC3", "co")
|
||||
|
||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||
|
||||
@@ -54,7 +54,7 @@ class AddSystem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Add System"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
parent_system_id: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
parent_system_id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
parent_system_id: int
|
||||
|
||||
@@ -26,16 +26,16 @@ from bpy.types import PropertyGroup
|
||||
|
||||
|
||||
class WebProperties(PropertyGroup):
|
||||
webserver_port: IntProperty( # pyright: ignore[reportRedeclaration]
|
||||
webserver_port: IntProperty(
|
||||
name="Webserver Port",
|
||||
min=0,
|
||||
max=65535,
|
||||
)
|
||||
is_running: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
is_running: BoolProperty(
|
||||
name="Webserver Running Status",
|
||||
default=False,
|
||||
)
|
||||
is_connected: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
is_connected: BoolProperty(
|
||||
name="Connection Status",
|
||||
default=False,
|
||||
)
|
||||
|
||||
@@ -159,9 +159,9 @@ class SelectURIAttribute(bpy.types.Operator, ImportHelper):
|
||||
bl_label = "Select URI Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Select a local file"
|
||||
attribute_data_path: bpy.props.StringProperty(name="Data Path") # pyright: ignore[reportRedeclaration]
|
||||
attribute_data_path: bpy.props.StringProperty(name="Data Path")
|
||||
"""Full data path to `Attribute`/string property."""
|
||||
use_relative_path: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
use_relative_path: bpy.props.BoolProperty(
|
||||
name="Use Relative Path",
|
||||
default=False,
|
||||
)
|
||||
@@ -601,7 +601,7 @@ class CreateMacBonsaiApp(bpy.types.Operator):
|
||||
"ALT+click to uninstall Bonsai app if it was installed previously."
|
||||
)
|
||||
|
||||
uninstall: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
|
||||
uninstall: bpy.props.BoolProperty(options={"SKIP_SAVE"})
|
||||
|
||||
if TYPE_CHECKING:
|
||||
uninstall: bool
|
||||
@@ -1667,7 +1667,7 @@ class BIM_OT_attribute_add_subitem(bpy.types.Operator):
|
||||
bl_description = "Add subitem to the current attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
data_path: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
data_path: bpy.props.StringProperty()
|
||||
"""Full data path."""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -1691,9 +1691,9 @@ class BIM_OT_attribute_remove_subitem(bpy.types.Operator):
|
||||
bl_description = "Add subitem to the current attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
data_path: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||
data_path: bpy.props.StringProperty()
|
||||
"""Full data path."""
|
||||
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
data_path: str
|
||||
|
||||
@@ -333,7 +333,7 @@ class Attribute(PropertyGroup):
|
||||
filter_glob: StringProperty()
|
||||
is_null: BoolProperty(name="Is Null", update=update_is_null)
|
||||
is_selected: BoolProperty(name="Is Selected", default=False)
|
||||
subitems_values: CollectionProperty(type=StrProperty) # pyright: ignore[reportRedeclaration]
|
||||
subitems_values: CollectionProperty(type=StrProperty)
|
||||
|
||||
# Attribute parameters.
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
@@ -342,7 +342,7 @@ class Attribute(PropertyGroup):
|
||||
value_max: FloatProperty(description="This is used to validate int_value and float_value")
|
||||
value_max_constraint: BoolProperty(default=False, description="True if the numerical value has an upper bound")
|
||||
special_type: StringProperty(name="Special Value Type", default="")
|
||||
use_explorer_ui: BoolProperty() # pyright: ignore[reportRedeclaration]
|
||||
use_explorer_ui: BoolProperty()
|
||||
metadata: StringProperty(name="Metadata", description="For storing some additional information about the attribute")
|
||||
update: StringProperty(name="Update", description="Custom update function to be executed")
|
||||
|
||||
|
||||
@@ -665,7 +665,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
name="Disable Undo When Saving (Faster saves, no undo for you!)", default=False
|
||||
)
|
||||
should_stream: BoolProperty(name="Stream Data From IFC-SPF (Only for advanced users)", default=False)
|
||||
should_always_cache: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
should_always_cache: BoolProperty(
|
||||
name="Always Cache Geometry",
|
||||
description="Whether to always cache geometry regardless of 'Cache' setting during Advanced Project Load.",
|
||||
)
|
||||
|
||||
@@ -56,42 +56,50 @@ def discard_uncommitted(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None:
|
||||
ifcgit.load_project(path_ifc)
|
||||
|
||||
|
||||
def commit_changes(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], repo: git.Repo) -> None:
|
||||
def commit_changes(
|
||||
ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], commit_message: str, new_branch_name: str = ""
|
||||
) -> None:
|
||||
"""Commit and create new branches as required"""
|
||||
path_ifc = ifc.get_path()
|
||||
|
||||
if repo.head.is_detached:
|
||||
ifcgit.git_commit(path_ifc)
|
||||
ifcgit.create_new_branch()
|
||||
if ifcgit.is_head_detached():
|
||||
ifcgit.git_commit(path_ifc, commit_message)
|
||||
ifcgit.create_new_branch(new_branch_name)
|
||||
else:
|
||||
ifcgit.checkout_new_branch(path_ifc)
|
||||
ifcgit.git_commit(path_ifc)
|
||||
if new_branch_name:
|
||||
ifcgit.checkout_new_branch(path_ifc, new_branch_name)
|
||||
ifcgit.git_commit(path_ifc, commit_message)
|
||||
|
||||
|
||||
def add_tag(ifcgit: type[tool.IfcGit], repo: git.Repo) -> None:
|
||||
ifcgit.add_tag(repo)
|
||||
def add_tag(ifcgit: type[tool.IfcGit], repo: git.Repo, hexsha: str, tag_name: str, tag_message: str = "") -> None:
|
||||
ifcgit.add_tag(repo, hexsha, tag_name, tag_message)
|
||||
|
||||
|
||||
def delete_tag(ifcgit: type[tool.IfcGit], repo: git.Repo, tag_name: git.TagReference) -> None:
|
||||
ifcgit.delete_tag(repo, tag_name)
|
||||
|
||||
|
||||
def add_remote(ifcgit: type[tool.IfcGit], repo: git.Repo) -> None:
|
||||
ifcgit.add_remote(repo)
|
||||
def add_remote(ifcgit: type[tool.IfcGit], repo: git.Repo, remote_name: str, remote_url: str) -> None:
|
||||
ifcgit.add_remote(repo, remote_name, remote_url)
|
||||
|
||||
|
||||
def delete_remote(ifcgit: type[tool.IfcGit], repo: git.Repo) -> None:
|
||||
ifcgit.delete_remote(repo)
|
||||
def delete_remote(ifcgit: type[tool.IfcGit], repo: git.Repo, remote_name: str) -> None:
|
||||
ifcgit.delete_remote(repo, remote_name)
|
||||
|
||||
|
||||
def rename_branch(ifcgit: type[tool.IfcGit], repo: git.Repo, new_name: str) -> None:
|
||||
ifcgit.rename_branch(repo, new_name)
|
||||
|
||||
|
||||
def push(ifcgit: type[tool.IfcGit], repo: git.Repo, remote_name: str, operator: bpy.types.Operator) -> None:
|
||||
error_message = ifcgit.push(repo, remote_name, repo.active_branch.name)
|
||||
error_message = ifcgit.push(repo, remote_name, ifcgit.get_active_branch_name())
|
||||
if error_message:
|
||||
operator.report({"ERROR"}, error_message)
|
||||
|
||||
|
||||
def refresh_revision_list(ifcgit: type[tool.IfcGit], repo: git.Repo, ifc: type[tool.Ifc]) -> None:
|
||||
if repo.heads:
|
||||
def refresh_revision_list(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None:
|
||||
ifcgit.clear_merge_conflicts()
|
||||
if ifcgit.repo_has_commits():
|
||||
ifcgit.refresh_revision_list(ifc.get_path())
|
||||
|
||||
|
||||
@@ -125,10 +133,76 @@ def switch_revision(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None:
|
||||
ifcgit.decolourise()
|
||||
|
||||
|
||||
def merge_branch(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], operator: bpy.types.Operator) -> None:
|
||||
def merge_branch(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], operator: bpy.types.Operator) -> bool | None:
|
||||
path_ifc = ifc.get_path()
|
||||
ifcgit.config_ifcmerge()
|
||||
ifcgit.execute_merge(path_ifc, operator)
|
||||
|
||||
branch_name = ifcgit.get_selected_branch()
|
||||
if branch_name is None:
|
||||
return
|
||||
|
||||
mergetool = ifcgit.get_merge_tool(branch_name)
|
||||
merge_result = ifcgit.git_merge(branch_name)
|
||||
|
||||
if merge_result == "error":
|
||||
operator.report({"ERROR"}, "Unknown IFC Merge failure")
|
||||
return False
|
||||
elif merge_result == "conflict":
|
||||
conflicts = ifcgit.git_mergetool(mergetool, path_ifc)
|
||||
if conflicts is not None:
|
||||
ifcgit.git_merge_abort()
|
||||
if conflicts:
|
||||
ifcgit.store_merge_conflicts(conflicts)
|
||||
operator.report({"WARNING"}, "Merge failed — see the conflict report in the panel below")
|
||||
else:
|
||||
operator.report({"ERROR"}, "Merge tool failed — check that ifcmerge is installed correctly")
|
||||
return False
|
||||
ifcgit.commit_merge(path_ifc)
|
||||
|
||||
ifcgit.clear_merge_conflicts()
|
||||
ifcgit.set_display_branch()
|
||||
ifcgit.git_checkout(path_ifc)
|
||||
ifcgit.load_project(path_ifc)
|
||||
ifcgit.refresh_revision_list(path_ifc)
|
||||
ifcgit.decolourise()
|
||||
|
||||
|
||||
def dry_run_merge(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], operator: bpy.types.Operator) -> None:
|
||||
path_ifc = ifc.get_path()
|
||||
ifcgit.config_ifcmerge()
|
||||
|
||||
branch_name = ifcgit.get_selected_branch()
|
||||
if branch_name is None:
|
||||
return
|
||||
|
||||
mergetool = ifcgit.get_merge_tool(branch_name)
|
||||
merge_result = ifcgit.git_merge_no_commit(branch_name)
|
||||
|
||||
if merge_result == "error":
|
||||
try:
|
||||
ifcgit.git_merge_abort()
|
||||
except Exception:
|
||||
pass
|
||||
operator.report({"ERROR"}, "Unknown IFC Merge failure")
|
||||
return
|
||||
|
||||
if merge_result == "conflict":
|
||||
conflicts = ifcgit.git_mergetool(mergetool, path_ifc)
|
||||
ifcgit.git_merge_abort()
|
||||
if conflicts is not None:
|
||||
ifcgit.store_merge_conflicts(conflicts)
|
||||
operator.report({"WARNING"}, "Merge preview: conflicts found — see the panel below")
|
||||
else:
|
||||
ifcgit.clear_merge_conflicts()
|
||||
operator.report({"INFO"}, "Merge preview: no conflicts")
|
||||
else:
|
||||
# Clean merge or already up to date — abort the pending merge state if any
|
||||
try:
|
||||
ifcgit.git_merge_abort()
|
||||
except Exception:
|
||||
pass
|
||||
ifcgit.clear_merge_conflicts()
|
||||
operator.report({"INFO"}, "Merge preview: no conflicts")
|
||||
|
||||
|
||||
def entity_log(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], step_id: int, operator: bpy.types.Operator) -> None:
|
||||
@@ -145,5 +219,9 @@ def install_git(ifcgit: type[tool.IfcGit], operator: bpy.types.Operator) -> None
|
||||
print("install_git() not implemented")
|
||||
|
||||
|
||||
def fetch(ifcgit: type[tool.IfcGit], remote_name: str) -> None:
|
||||
ifcgit.fetch(remote_name)
|
||||
|
||||
|
||||
def run_git_diff(ifcgit: type[tool.IfcGit], operator: bpy.types.Operator, save_to_temp: bool) -> None:
|
||||
ifcgit.run_git_diff(operator, save_to_temp)
|
||||
|
||||
@@ -535,6 +535,60 @@ class Ifc:
|
||||
def get_all_element_occurrences(cls, element): pass
|
||||
|
||||
|
||||
@interface
|
||||
class IfcGit:
|
||||
def add_file_to_repo(cls, repo, path_file): pass
|
||||
def add_remote(cls, repo, remote_name, remote_url): pass
|
||||
def add_tag(cls, repo, hexsha, tag_name, tag_message): pass
|
||||
def branches_by_hexsha(cls, repo): pass
|
||||
def checkout_new_branch(cls, path_file, branch_name): pass
|
||||
def clear_commits_list(cls): pass
|
||||
def clone_repo(cls, remote_url, local_folder): pass
|
||||
def colourise(cls, step_ids): pass
|
||||
def config_ifcmerge(cls): pass
|
||||
def create_new_branch(cls, branch_name): pass
|
||||
def decolourise(cls): pass
|
||||
def delete_remote(cls, repo, remote_name): pass
|
||||
def delete_tag(cls, repo, tag_name): pass
|
||||
def dos2unix(cls, path_file): pass
|
||||
def commit_merge(cls, path_ifc): pass
|
||||
def entity_log(cls, path_ifc, step_id): pass
|
||||
def fetch(cls, remote_name): pass
|
||||
def get_commits_list(cls, path_ifc, lookup): pass
|
||||
def get_merge_tool(cls, branch_name): pass
|
||||
def get_selected_branch(cls): pass
|
||||
def git_merge(cls, branch_name): pass
|
||||
def git_merge_abort(cls): pass
|
||||
def git_merge_no_commit(cls, branch_name): pass
|
||||
def git_mergetool(cls, mergetool, path_ifc): pass
|
||||
def store_merge_conflicts(cls, conflicts): pass
|
||||
def clear_merge_conflicts(cls): pass
|
||||
def get_merge_conflicts(cls): pass
|
||||
def set_display_branch(cls): pass
|
||||
def get_active_branch_name(cls): pass
|
||||
def get_ifcgit_props(cls): pass
|
||||
def get_modified_step_ids(cls, step_ids): pass
|
||||
def get_path_dir(cls, path_ifc): pass
|
||||
def get_revisions_step_ids(cls): pass
|
||||
def is_head_detached(cls): pass
|
||||
def repo_has_commits(cls): pass
|
||||
def git_checkout(cls, path_file): pass
|
||||
def git_commit(cls, path_file, commit_message): pass
|
||||
def ifc_diff_ids(cls, repo, hash_a, hash_b, path_ifc): pass
|
||||
def init_repo(cls, path_dir): pass
|
||||
def install_git_windows(cls, operator): pass
|
||||
def is_valid_ref_format(cls, string): pass
|
||||
def load_anyifc(cls, repo): pass
|
||||
def load_project(cls, path_ifc): pass
|
||||
def push(cls, repo, remote_name, branch_name): pass
|
||||
def refresh_revision_list(cls, path_ifc): pass
|
||||
def repo_from_path(cls, path): pass
|
||||
def run_git_diff(cls, operator, save_to_temp): pass
|
||||
def switch_to_revision_item(cls): pass
|
||||
def tags_by_hexsha(cls, repo): pass
|
||||
def update_step_ids(cls, step_ids, modified_step_ids): pass
|
||||
|
||||
|
||||
@interface
|
||||
class Layer:
|
||||
pass
|
||||
|
||||
@@ -1809,7 +1809,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
item = tool.Ifc.get().by_id(props.ifc_definition_id)
|
||||
allowed_attributes = [
|
||||
a.name()
|
||||
for a in item.wrapped_data.declaration().as_entity().all_attributes()
|
||||
for a in item.declaration().as_entity().all_attributes()
|
||||
if a.type_of_attribute()._is("IfcLengthMeasure")
|
||||
]
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Literal, Union
|
||||
from typing import TYPE_CHECKING, Any, Union
|
||||
|
||||
import bpy
|
||||
|
||||
@@ -128,39 +129,27 @@ class IfcGit:
|
||||
cls.dos2unix(path_file)
|
||||
repo.index.add(os.path.normpath(path_file))
|
||||
repo.index.commit(message="Added " + os.path.relpath(path_file, repo.working_dir))
|
||||
bpy.ops.ifcgit.refresh()
|
||||
|
||||
@classmethod
|
||||
def git_checkout(cls, path_file: str) -> None:
|
||||
IfcGitRepo.repo.git.checkout(path_file)
|
||||
|
||||
@classmethod
|
||||
def checkout_new_branch(cls, path_file: str) -> None:
|
||||
def checkout_new_branch(cls, path_file: str, branch_name: str) -> None:
|
||||
"""Create a branch and move uncommitted changes to this branch"""
|
||||
props = cls.get_ifcgit_props()
|
||||
if props.new_branch_name:
|
||||
IfcGitRepo.repo.git.checkout(b=props.new_branch_name)
|
||||
props.display_branch = props.new_branch_name
|
||||
props.new_branch_name = ""
|
||||
bpy.ops.ifcgit.refresh()
|
||||
IfcGitRepo.repo.git.checkout(b=branch_name)
|
||||
|
||||
@classmethod
|
||||
def git_commit(cls, path_file: str) -> None:
|
||||
props = cls.get_ifcgit_props()
|
||||
def git_commit(cls, path_file: str, commit_message: str) -> None:
|
||||
repo = IfcGitRepo.repo
|
||||
if os.name == "nt":
|
||||
cls.dos2unix(path_file)
|
||||
repo.index.add(os.path.normpath(path_file))
|
||||
repo.index.commit(message=props.commit_message)
|
||||
props.commit_message = ""
|
||||
repo.index.commit(message=commit_message)
|
||||
|
||||
@classmethod
|
||||
def add_tag(cls, repo: git.Repo) -> None:
|
||||
props = cls.get_ifcgit_props()
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
repo.create_tag(props.new_tag_name, ref=item.hexsha, message=props.new_tag_message)
|
||||
props.new_tag_name = ""
|
||||
props.new_tag_message = ""
|
||||
def add_tag(cls, repo: git.Repo, hexsha: str, tag_name: str, tag_message: str = "") -> None:
|
||||
repo.create_tag(tag_name, ref=hexsha, message=tag_message)
|
||||
|
||||
@classmethod
|
||||
def delete_tag(cls, repo: git.Repo, tag_name: git.TagReference) -> None:
|
||||
@@ -168,20 +157,17 @@ class IfcGit:
|
||||
repo.delete_tag(tag_name)
|
||||
|
||||
@classmethod
|
||||
def add_remote(cls, repo: git.Repo) -> None:
|
||||
props = cls.get_ifcgit_props()
|
||||
repo.create_remote(name=props.remote_name, url=props.remote_url)
|
||||
props.remote_name = ""
|
||||
props.remote_url = ""
|
||||
def rename_branch(cls, repo: git.Repo, new_name: str) -> None:
|
||||
repo.active_branch.rename(new_name)
|
||||
|
||||
@classmethod
|
||||
def delete_remote(cls, repo: git.Repo) -> None:
|
||||
props = cls.get_ifcgit_props()
|
||||
remote_name = props.select_remote
|
||||
def add_remote(cls, repo: git.Repo, remote_name: str, remote_url: str) -> None:
|
||||
repo.create_remote(name=remote_name, url=remote_url)
|
||||
|
||||
@classmethod
|
||||
def delete_remote(cls, repo: git.Repo, remote_name: str) -> None:
|
||||
if remote_name in repo.remotes:
|
||||
repo.delete_remote(remote_name)
|
||||
if repo.remotes:
|
||||
props.select_remote = repo.remotes[0].name
|
||||
|
||||
@classmethod
|
||||
def push(cls, repo: git.Repo, remote_name: str, branch_name: str) -> Union[str, None]:
|
||||
@@ -193,16 +179,25 @@ class IfcGit:
|
||||
return exc.stderr
|
||||
|
||||
@classmethod
|
||||
def create_new_branch(cls) -> None:
|
||||
"""Convert a detached HEAD into a branch"""
|
||||
props = cls.get_ifcgit_props()
|
||||
repo = IfcGitRepo.repo
|
||||
new_branch = repo.create_head(props.new_branch_name)
|
||||
new_branch.checkout()
|
||||
props.display_branch = props.new_branch_name
|
||||
props.new_branch_name = ""
|
||||
def is_head_detached(cls) -> bool:
|
||||
return bool(IfcGitRepo.repo.head.is_detached)
|
||||
|
||||
bpy.ops.ifcgit.refresh()
|
||||
@classmethod
|
||||
def repo_has_commits(cls) -> bool:
|
||||
if IfcGitRepo.repo:
|
||||
return bool(IfcGitRepo.repo.heads)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_active_branch_name(cls) -> str:
|
||||
return IfcGitRepo.repo.active_branch.name
|
||||
|
||||
@classmethod
|
||||
def create_new_branch(cls, branch_name: str) -> None:
|
||||
"""Convert a detached HEAD into a branch"""
|
||||
repo = IfcGitRepo.repo
|
||||
new_branch = repo.create_head(branch_name)
|
||||
new_branch.checkout()
|
||||
|
||||
@classmethod
|
||||
def clear_commits_list(cls) -> None:
|
||||
@@ -222,7 +217,7 @@ class IfcGit:
|
||||
rev=[props.display_branch],
|
||||
)
|
||||
)
|
||||
commits_relevant = list(
|
||||
commits_relevant = set(
|
||||
git.objects.commit.Commit.iter_items(
|
||||
repo=repo,
|
||||
rev=[props.display_branch],
|
||||
@@ -230,11 +225,17 @@ class IfcGit:
|
||||
)
|
||||
)
|
||||
|
||||
def is_relevant(commit):
|
||||
if commit in commits_relevant:
|
||||
return True
|
||||
# Merge commits are relevant too
|
||||
return len(commit.parents) > 1 and any(p in commits_relevant for p in commit.parents)
|
||||
|
||||
for commit in commits:
|
||||
|
||||
if props.ifcgit_filter == "tagged" and commit.hexsha not in lookup:
|
||||
continue
|
||||
elif props.ifcgit_filter == "relevant" and commit not in commits_relevant:
|
||||
elif props.ifcgit_filter == "relevant" and not is_relevant(commit):
|
||||
continue
|
||||
|
||||
props.ifcgit_commits.add()
|
||||
@@ -243,7 +244,8 @@ class IfcGit:
|
||||
list_item.message = commit.message
|
||||
list_item.author_name = commit.author.name
|
||||
list_item.author_email = commit.author.email
|
||||
if commit in commits_relevant:
|
||||
list_item.committed_date = int(commit.committed_date)
|
||||
if is_relevant(commit):
|
||||
list_item.relevant = True
|
||||
if commit.hexsha in lookup:
|
||||
for tag in lookup[commit.hexsha]:
|
||||
@@ -284,16 +286,25 @@ class IfcGit:
|
||||
if re.match("^Ifc", obj.name):
|
||||
bpy.data.objects.remove(obj, do_unlink=True)
|
||||
|
||||
bpy.data.orphans_purge(do_recursive=True) # ty:ignore[unknown-argument]
|
||||
bpy.data.orphans_purge(do_recursive=True)
|
||||
|
||||
import bonsai.bim.handler
|
||||
from bonsai.bim.module.model.data import AuthoringData
|
||||
from bonsai.bim.module.root.data import IfcClassData
|
||||
|
||||
AuthoringData.type_thumbnails = {}
|
||||
|
||||
IfcClassData.is_loaded = False
|
||||
|
||||
settings = import_ifc.IfcImportSettings.factory(bpy.context, path_ifc, logging.getLogger("ImportIFC"))
|
||||
settings.should_setup_viewport_camera = False
|
||||
ifc_importer = import_ifc.IfcImporter(settings)
|
||||
ifc_importer.execute()
|
||||
tool.Project.load_project_pset_templates()
|
||||
tool.Project.load_default_thumbnails()
|
||||
tool.Project.set_default_context()
|
||||
tool.Project.set_default_modeling_dimensions()
|
||||
tool.Root.reload_grid_decorator()
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
@classmethod
|
||||
@@ -393,20 +404,43 @@ class IfcGit:
|
||||
model = tool.Ifc.get()
|
||||
modified_step_ids = {"modified": set()}
|
||||
|
||||
for step_id in step_ids["modified"] | step_ids["added"]:
|
||||
try:
|
||||
entity = model.by_id(step_id)
|
||||
except:
|
||||
continue
|
||||
if entity.is_a("IfcProductDefinitionShape"):
|
||||
def collect(entity, depth=0):
|
||||
if depth > 2:
|
||||
return
|
||||
if entity.is_a("IfcProduct"):
|
||||
modified_step_ids["modified"].add(entity.id())
|
||||
elif entity.is_a("IfcProductDefinitionShape"):
|
||||
for product in entity.ShapeOfProduct:
|
||||
modified_step_ids["modified"].add(product.id())
|
||||
elif entity.is_a("IfcObjectPlacement"):
|
||||
for product in entity.PlacesObject:
|
||||
modified_step_ids["modified"].add(product.id())
|
||||
elif entity.is_a("IfcTypeProduct") and entity.Types:
|
||||
for related_object in entity.Types[0].RelatedObjects:
|
||||
modified_step_ids["modified"].add(related_object.id())
|
||||
elif entity.is_a("IfcTypeProduct"):
|
||||
for rel in entity.Types:
|
||||
for obj in rel.RelatedObjects:
|
||||
modified_step_ids["modified"].add(obj.id())
|
||||
elif entity.is_a("IfcShapeRepresentation"):
|
||||
for prod_rep in entity.OfProductRepresentation:
|
||||
for product in prod_rep.ShapeOfProduct:
|
||||
modified_step_ids["modified"].add(product.id())
|
||||
elif entity.is_a("IfcRepresentationItem"):
|
||||
for referencing in model.get_inverse(entity):
|
||||
if referencing.is_a("IfcShapeRepresentation"):
|
||||
collect(referencing, depth + 1)
|
||||
elif entity.is_a("IfcPropertySet"):
|
||||
for rel in entity.DefinesOccurrence:
|
||||
for obj in rel.RelatedObjects:
|
||||
modified_step_ids["modified"].add(obj.id())
|
||||
elif entity.is_a("IfcProperty"):
|
||||
for pset in entity.PartOfPset:
|
||||
collect(pset, depth + 1)
|
||||
|
||||
for step_id in step_ids["modified"] | step_ids["added"]:
|
||||
try:
|
||||
entity = model.by_id(step_id)
|
||||
except:
|
||||
continue
|
||||
collect(entity)
|
||||
|
||||
return modified_step_ids
|
||||
|
||||
@@ -458,38 +492,56 @@ class IfcGit:
|
||||
if item.hexsha in lookup:
|
||||
for branch in lookup[item.hexsha]:
|
||||
if branch.name == props.display_branch:
|
||||
if isinstance(branch, git.RemoteReference):
|
||||
# Checking out a remote branch tip goes to detached HEAD.
|
||||
# Pre-fill the new branch name field with the local equivalent
|
||||
# so the user isn't blocked from committing without a hint.
|
||||
local_name = branch.remote_head
|
||||
props.new_branch_name = cls._unique_branch_name(repo, local_name)
|
||||
branch.checkout()
|
||||
return
|
||||
# NOTE this is calling the git binary in a subprocess
|
||||
repo.git.checkout(item.hexsha)
|
||||
|
||||
@classmethod
|
||||
def _unique_branch_name(cls, repo: git.Repo, name: str) -> str:
|
||||
"""Return name if unused, otherwise name-2, name-3, etc."""
|
||||
existing = {h.name for h in repo.heads}
|
||||
if name not in existing:
|
||||
return name
|
||||
i = 2
|
||||
while f"{name}-{i}" in existing:
|
||||
i += 1
|
||||
return f"{name}-{i}"
|
||||
|
||||
@classmethod
|
||||
def delete_collection(cls, blender_collection: bpy.types.Collection) -> None:
|
||||
for obj in blender_collection.objects:
|
||||
bpy.data.objects.remove(obj, do_unlink=True)
|
||||
bpy.data.collections.remove(blender_collection)
|
||||
|
||||
@classmethod
|
||||
def is_valid_branch_name(cls, new_branch_name: str):
|
||||
"""Check if a branch name is valid and doesn't conflict with existing branches"""
|
||||
if not cls.is_valid_ref_format(new_branch_name):
|
||||
return False
|
||||
if new_branch_name in [branch.name for branch in IfcGitRepo.repo.branches]:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def config_ifcmerge(cls) -> None:
|
||||
config_reader = IfcGitRepo.repo.config_reader()
|
||||
section = 'mergetool "ifcmerge"'
|
||||
new_cmd = "ifcmerge $BASE $LOCAL $REMOTE $MERGED > $MERGED.ifcmerge"
|
||||
if not config_reader.has_section(section):
|
||||
with IfcGitRepo.repo.config_writer() as config_writer:
|
||||
config_writer.set_value(section, "cmd", "ifcmerge $BASE $LOCAL $REMOTE $MERGED")
|
||||
config_writer.set_value(section, "cmd", new_cmd)
|
||||
config_writer.set_value(section, "trustExitCode", True)
|
||||
elif config_reader.get_value(section, "cmd") != new_cmd:
|
||||
with IfcGitRepo.repo.config_writer() as config_writer:
|
||||
config_writer.set_value(section, "cmd", new_cmd)
|
||||
config_writer.set_value(section, "trustExitCode", True)
|
||||
section = 'mergetool "ifcmerge-forward"'
|
||||
new_cmd = "ifcmerge --prioritise-local $BASE $LOCAL $REMOTE $MERGED > $MERGED.ifcmerge"
|
||||
if not config_reader.has_section(section):
|
||||
with IfcGitRepo.repo.config_writer() as config_writer:
|
||||
config_writer.set_value(section, "cmd", "ifcmerge $BASE $REMOTE $LOCAL $MERGED")
|
||||
config_writer.set_value(section, "cmd", new_cmd)
|
||||
config_writer.set_value(section, "trustExitCode", True)
|
||||
elif config_reader.get_value(section, "cmd") != new_cmd:
|
||||
with IfcGitRepo.repo.config_writer() as config_writer:
|
||||
config_writer.set_value(section, "cmd", new_cmd)
|
||||
config_writer.set_value(section, "trustExitCode", True)
|
||||
|
||||
@classmethod
|
||||
@@ -519,49 +571,117 @@ class IfcGit:
|
||||
output.write(line + b"\n")
|
||||
|
||||
@classmethod
|
||||
def execute_merge(cls, path_ifc: str, operator: bpy.types.Operator) -> Union[None, Literal[False]]:
|
||||
def get_selected_branch(cls) -> Union[str, None]:
|
||||
"""Return the name of the branch at the selected commit matching display_branch, or None."""
|
||||
props = cls.get_ifcgit_props()
|
||||
repo = IfcGitRepo.repo
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
lookup = cls.branches_by_hexsha(repo)
|
||||
if item.hexsha in lookup:
|
||||
for branch in lookup[item.hexsha]:
|
||||
if branch.name == props.display_branch:
|
||||
# this is a branch!
|
||||
if re.match("^(origin/)?(HEAD|main|master)$", branch.name):
|
||||
# preserve remote IDs in origin/main or main
|
||||
mergetool = "ifcmerge"
|
||||
else:
|
||||
# rewrite remote IDs
|
||||
mergetool = "ifcmerge-forward"
|
||||
try:
|
||||
# NOTE this is calling the git binary in a subprocess
|
||||
repo.git.merge(branch)
|
||||
except git.exc.GitCommandError:
|
||||
# merge is expected to fail, run ifcmerge
|
||||
try:
|
||||
repo.git.mergetool(tool=mergetool)
|
||||
except git.exc.GitCommandError as exc:
|
||||
message = re.sub("( stderr: '|')", "", exc.stderr)
|
||||
# ifcmerge failed, rollback
|
||||
repo.git.merge(abort=True)
|
||||
if item.hexsha not in lookup:
|
||||
return None
|
||||
for branch in lookup[item.hexsha]:
|
||||
if branch.name == props.display_branch:
|
||||
return branch.name
|
||||
return None
|
||||
|
||||
operator.report({"ERROR"}, "IFC Merge failed:" + message)
|
||||
return False
|
||||
else:
|
||||
if os.name == "nt":
|
||||
cls.dos2unix(path_ifc)
|
||||
repo.index.add(os.path.normpath(path_ifc))
|
||||
repo.git.commit("--no-edit")
|
||||
except git.exc.GitError:
|
||||
operator.report({"ERROR"}, "Unknown IFC Merge failure")
|
||||
return False
|
||||
@classmethod
|
||||
def get_merge_tool(cls, branch_name: str) -> str:
|
||||
if re.match("^(origin/)?(HEAD|main|master)$", branch_name):
|
||||
return "ifcmerge"
|
||||
return "ifcmerge-forward"
|
||||
|
||||
props.display_branch = repo.active_branch.name
|
||||
@classmethod
|
||||
def git_merge(cls, branch_name: str) -> Union[str, None]:
|
||||
"""Attempt a git merge. Returns None on clean merge, 'conflict' on expected
|
||||
GitCommandError, or 'error' on an unknown GitError."""
|
||||
repo = IfcGitRepo.repo
|
||||
branch = repo.refs[branch_name]
|
||||
try:
|
||||
repo.git.merge(branch)
|
||||
return None
|
||||
except git.exc.GitCommandError:
|
||||
return "conflict"
|
||||
except git.exc.GitError:
|
||||
return "error"
|
||||
|
||||
cls.load_project(path_ifc)
|
||||
cls.refresh_revision_list(path_ifc)
|
||||
cls.decolourise()
|
||||
@classmethod
|
||||
def git_merge_no_commit(cls, branch_name: str) -> Union[str, None]:
|
||||
"""Attempt a git merge without committing (always leaves a merge state to abort).
|
||||
Returns None on clean merge, 'conflict' on conflict, or 'error' on unknown failure."""
|
||||
repo = IfcGitRepo.repo
|
||||
branch = repo.refs[branch_name]
|
||||
try:
|
||||
repo.git.merge(branch, no_commit=True, no_ff=True)
|
||||
return None
|
||||
except git.exc.GitCommandError:
|
||||
return "conflict"
|
||||
except git.exc.GitError:
|
||||
return "error"
|
||||
|
||||
@classmethod
|
||||
def git_mergetool(cls, mergetool: str, path_ifc: str) -> Union[list, None]:
|
||||
"""Run ifcmerge tool. Returns None on success, list of conflict dicts on failure."""
|
||||
repo = IfcGitRepo.repo
|
||||
report_path = path_ifc + ".ifcmerge"
|
||||
try:
|
||||
repo.git.mergetool(tool=mergetool)
|
||||
except git.exc.GitCommandError as e:
|
||||
print(f"ifcgit: mergetool failed: {e}")
|
||||
|
||||
conflicts = None
|
||||
if os.path.exists(report_path):
|
||||
try:
|
||||
with open(report_path) as f:
|
||||
content = f.read().strip()
|
||||
if content:
|
||||
data = json.loads(content)
|
||||
conflicts = data.get("conflicts", [])
|
||||
except (json.JSONDecodeError, OSError):
|
||||
pass
|
||||
try:
|
||||
os.remove(report_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if conflicts is None and repo.index.unmerged_blobs():
|
||||
conflicts = []
|
||||
|
||||
return conflicts
|
||||
|
||||
@classmethod
|
||||
def store_merge_conflicts(cls, conflicts: list) -> None:
|
||||
cls.get_ifcgit_props().merge_conflicts = json.dumps(conflicts)
|
||||
|
||||
@classmethod
|
||||
def clear_merge_conflicts(cls) -> None:
|
||||
cls.get_ifcgit_props().merge_conflicts = ""
|
||||
|
||||
@classmethod
|
||||
def get_merge_conflicts(cls) -> Union[list, None]:
|
||||
raw = cls.get_ifcgit_props().merge_conflicts
|
||||
if not raw:
|
||||
return None
|
||||
try:
|
||||
return json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def git_merge_abort(cls) -> None:
|
||||
IfcGitRepo.repo.git.merge(abort=True)
|
||||
|
||||
@classmethod
|
||||
def commit_merge(cls, path_ifc: str) -> None:
|
||||
repo = IfcGitRepo.repo
|
||||
if os.name == "nt":
|
||||
cls.dos2unix(path_ifc)
|
||||
repo.index.add(os.path.normpath(path_ifc))
|
||||
repo.git.commit("--no-edit")
|
||||
|
||||
@classmethod
|
||||
def set_display_branch(cls) -> None:
|
||||
props = cls.get_ifcgit_props()
|
||||
props.display_branch = IfcGitRepo.repo.active_branch.name
|
||||
|
||||
@classmethod
|
||||
def entity_log(cls, path_ifc: str, step_id: int) -> str:
|
||||
@@ -589,6 +709,18 @@ class IfcGit:
|
||||
except FileNotFoundError:
|
||||
operator.report({"ERROR"}, "Winget is not available. Make sure Windows Package Manager is installed.")
|
||||
|
||||
@classmethod
|
||||
def select_first_remote(cls) -> None:
|
||||
props = cls.get_ifcgit_props()
|
||||
repo = IfcGitRepo.repo
|
||||
if repo and repo.remotes:
|
||||
props.select_remote = repo.remotes[0].name
|
||||
|
||||
@classmethod
|
||||
def fetch(cls, remote_name: str) -> None:
|
||||
repo = IfcGitRepo.repo
|
||||
repo.remotes[remote_name].fetch()
|
||||
|
||||
@classmethod
|
||||
def run_git_diff(cls, operator: bpy.types.Operator, save_to_temp: bool) -> None:
|
||||
path = tool.Ifc.get_path()
|
||||
|
||||
@@ -25,11 +25,12 @@ import bmesh
|
||||
import bpy
|
||||
import mathutils
|
||||
import numpy as np
|
||||
from bpy_extras import view3d_utils
|
||||
from mathutils import Vector
|
||||
|
||||
import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
from bpy_extras import view3d_utils
|
||||
|
||||
|
||||
class Raycast(bonsai.core.tool.Raycast):
|
||||
offset = 10
|
||||
@@ -200,7 +201,7 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
|
||||
if inter_world is None:
|
||||
print("No intersection with viewport near plane found for the segment.")
|
||||
return
|
||||
return None, None
|
||||
|
||||
init_2d = view3d_utils.location_3d_to_region_2d(region, rv3d, inter_world)
|
||||
|
||||
@@ -215,7 +216,7 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
if found_world is None:
|
||||
if init_2d is None:
|
||||
print("Initial projection invalid and iterative search failed.")
|
||||
return
|
||||
return None, None
|
||||
# fallback: clamp projected point to border via manual mapping
|
||||
final_2d = clamp_to_region_border(init_2d, region)
|
||||
final_world = None
|
||||
@@ -432,11 +433,8 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
seg_len_sq = sx * sx + sy * sy
|
||||
|
||||
if seg_len_sq == 0.0:
|
||||
# degenerate segment: return distance to p0
|
||||
dx = px - p0x
|
||||
dy = py - p0y
|
||||
dist = math.hypot(dx, dy)
|
||||
return dist, (p0x, p0y), 0.0
|
||||
# degenerate segment: skip it
|
||||
continue
|
||||
|
||||
# project (p - p0) onto seg: t = dot(p-p0, seg) / |seg|^2
|
||||
apx = px - p0x
|
||||
@@ -726,7 +724,8 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
if tool.Raycast.intersect_mouse_2d_bounding_box(mouse_pos, bbox_2d):
|
||||
if tool.Raycast.object_is_visible_in_clipping_plane(obj):
|
||||
snap_obj = cls.create_snap_obj(obj)
|
||||
objs_to_raycast.append(snap_obj)
|
||||
if snap_obj is not None:
|
||||
objs_to_raycast.append(snap_obj)
|
||||
|
||||
return objs_to_raycast
|
||||
|
||||
@@ -887,8 +886,21 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
|
||||
@classmethod
|
||||
def create_snap_obj(cls, obj):
|
||||
for snap_obj in cls.snap_objs:
|
||||
if obj.data is None or not isinstance(obj.data, bpy.types.Mesh):
|
||||
return None
|
||||
for i, snap_obj in enumerate(cls.snap_objs):
|
||||
if obj.name == snap_obj.obj.name:
|
||||
# Handle objects modified while a modal operator is active.
|
||||
# Example: adding a door or window alters the wall geometry.
|
||||
if len(obj.data.vertices) != len(snap_obj.verts_3d):
|
||||
cls.snap_objs.pop(i)
|
||||
snap_obj = SnapObj(obj)
|
||||
cls.snap_objs.append(snap_obj)
|
||||
for v1, v2 in zip(obj.data.vertices, snap_obj.verts_3d):
|
||||
if (obj.matrix_world @ v1.co) != v2:
|
||||
cls.snap_objs.pop(i)
|
||||
snap_obj = SnapObj(obj)
|
||||
cls.snap_objs.append(snap_obj)
|
||||
return snap_obj
|
||||
snap_obj = SnapObj(obj)
|
||||
cls.snap_objs.append(snap_obj)
|
||||
|
||||
@@ -7,7 +7,7 @@ Python code formatters
|
||||
For Python code formatting, we use `Black code formatter <https://pypi.org/project/black/>`__,
|
||||
black settings are stored in the repository's pyproject.toml.
|
||||
|
||||
We have GitHub workflow `ci-black-formatting` to maintain black formatting across the repository.
|
||||
We have GitHub workflow `ci-lint` to maintain black formatting across the repository.
|
||||
|
||||
``black`` can be installed using ``pip install black`` and files can be formatted with the following example command:
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ When adding or removing a supported Python version, update the following:
|
||||
|
||||
* - File
|
||||
- What to update
|
||||
* - ``.github/workflows/ci-black-formatting.yaml``
|
||||
* - ``.github/workflows/ci-lint.yaml``
|
||||
- ``MIN_IOS_PY_VERSION``
|
||||
* - ``.github/workflows/ci-ifcopenshell-python-pypi.yml``
|
||||
- ``pyver`` matrix
|
||||
@@ -44,6 +44,8 @@ When a new Blender version is released and supported:
|
||||
|
||||
* - File
|
||||
- What to update
|
||||
* - ``.github/workflows/ci-bonsai.yml``
|
||||
- ``pyver`` matrix
|
||||
* - ``.github/workflows/ci-bonsai-daily.yml``
|
||||
- Blender download URL
|
||||
|
||||
@@ -57,9 +59,47 @@ When Blender ships with a new Python version:
|
||||
|
||||
* - File
|
||||
- What to update
|
||||
* - ``.github/workflows/ci-black-formatting.yaml``
|
||||
* - ``.github/workflows/ci-lint.yaml``
|
||||
- ``MIN_BLENDER_PY_VERSION``
|
||||
* - ``src/bonsai/Makefile``
|
||||
- ``SUPPORTED_PYVERSIONS``
|
||||
* - ``src/bonsai/scripts/dev_environment.py``
|
||||
- ``PYTHON_VERSION`` mapping (Blender version, bundled Python version)
|
||||
|
||||
Release
|
||||
-------
|
||||
|
||||
Notes:
|
||||
|
||||
- Typically all packages are released at once using the same version schema
|
||||
- The ``README.md`` badges can serve as a visual reference for what versions have been released
|
||||
|
||||
Things to update:
|
||||
|
||||
- ``.github/workflows/ci-bcf-pypi.yml`` - release `bcf-client <https://pypi.org/project/bcf-client/>`_ to PyPI
|
||||
- ``.github/workflows/ci-bonsai.yml`` - release bonsai in GitHub releases
|
||||
- ``.github/workflows/ci-bsdd-pypi.yaml`` - release `bsdd <https://pypi.org/project/bsdd/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifc4d-pypi.yaml`` - release `ifc4d <https://pypi.org/project/ifc4d/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifc5d-pypi.yaml`` - release `ifc5d <https://pypi.org/project/ifc5d/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcclash-pypi.yaml`` - release `ifcclash <https://pypi.org/project/ifcclash/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcconvert.yml`` - release ifcconvert binaries in GitHub releases
|
||||
- ``.github/workflows/ci-ifccsv-pypi.yaml`` - release `ifccsv <https://pypi.org/project/ifccsv/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcdiff-pypi.yaml`` - release `ifcdiff <https://pypi.org/project/ifcdiff/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcedit-pypi.yaml`` - release `ifcedit <https://pypi.org/project/ifcedit/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcfm-pypi.yaml`` - release `ifcfm <https://pypi.org/project/ifcfm/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifccityjson-pypi.yaml`` - release `ifccityjson <https://pypi.org/project/ifccityjson/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcmcp-pypi.yaml`` - release `ifcopenshell-mcp <https://pypi.org/project/ifcopenshell-mcp/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcopenshell-python.yml`` - release ifcopenshell-python binaries in GitHub releases
|
||||
- ``.github/workflows/ci-ifcopenshell-python-pypi.yml`` - release `ifcopenshell <https://pypi.org/project/ifcopenshell/>`_ wheels to PyPI
|
||||
- ``.github/workflows/ci-ifcpatch-pypi.yaml`` - release `ifcpatch <https://pypi.org/project/ifcpatch/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcquery-pypi.yaml`` - release `ifcquery <https://pypi.org/project/ifcquery/>`_ to PyPI
|
||||
- ``.github/workflows/ci-ifcsverchok.yml`` - release ifcsverchok Blender add-on in GitHub releases
|
||||
- ``.github/workflows/ci-ifctester-pypi.yml`` - release `ifctester <https://pypi.org/project/ifctester/>`_ to PyPI
|
||||
- ``.github/workflows/ci-pyodide-wasm-release.yml`` - release pyodide wasm wheel to `wasm-wheels <https://github.com/IfcOpenShell/wasm-wheels>`_
|
||||
- Release Bonsai Blender extension - zip files from ci-bonsai.yml releases should be uploaded manually to `Blender extensions platform <https://extensions.blender.org/add-ons/bonsai/>`_
|
||||
- Publishing documentation and websites (see `website <https://github.com/IfcOpenShell/website>`_ repository):
|
||||
|
||||
- `ifcopenshell-docs.yml` - builds and publishes IfcOpenShell documentation to `docs.ifcopenshell.org <https://docs.ifcopenshell.org>`_ (`ifcopenshell_org_docs <https://github.com/IfcOpenShell/ifcopenshell_org_docs>`_ repo)
|
||||
- `bonsai-docs.yml` - builds and publishes Bonsai documentation to `docs.bonsaibim.org <https://docs.bonsaibim.org>`_ (`bonsaibim_org_docs <https://github.com/IfcOpenShell/bonsaibim_org_docs>`_ repo)
|
||||
- `main.yml` - publishes `bonsaibim.org <https://bonsaibim.org>`_ (`bonsaibim_org_static_html <https://github.com/IfcOpenShell/bonsaibim_org_static_html>`_ repo) and `ifcopenshell.org <https://ifcopenshell.org>`_ (`ifcopenshell_org_static_html <https://github.com/IfcOpenShell/ifcopenshell_org_static_html>`_ repo)
|
||||
- ``VERSION`` to the release version - **UPDATE THIS LAST** as all workflows above typically depend on it to set the version correctly
|
||||
|
||||
@@ -102,6 +102,13 @@ def geometry():
|
||||
prophet.verify()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ifcgit():
|
||||
prophet = Prophecy(bonsai.core.tool.IfcGit)
|
||||
yield prophet
|
||||
prophet.verify()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def georeference():
|
||||
prophet = Prophecy(bonsai.core.tool.Georeference)
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2025 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# Bonsai is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Bonsai is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
# This file was generated with the assistance of an AI coding tool.
|
||||
|
||||
import pytest
|
||||
|
||||
import bonsai.core.ifcgit as subject
|
||||
from test.core.bootstrap import ifc, ifcgit
|
||||
|
||||
|
||||
class MockOperator:
|
||||
def __init__(self):
|
||||
self.reports = []
|
||||
|
||||
def report(self, level, message):
|
||||
self.reports.append((level, message))
|
||||
|
||||
|
||||
class TestCreateRepo:
|
||||
def test_run(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.get_path_dir("path/to/model.ifc").should_be_called().will_return("path/to")
|
||||
ifcgit.init_repo("path/to").should_be_called()
|
||||
subject.create_repo(ifcgit, ifc)
|
||||
|
||||
|
||||
class TestAddFile:
|
||||
def test_run(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.repo_from_path("path/to/model.ifc").should_be_called().will_return("repo")
|
||||
ifcgit.add_file_to_repo("repo", "path/to/model.ifc").should_be_called()
|
||||
subject.add_file(ifcgit, ifc)
|
||||
|
||||
|
||||
class TestCloneRepo:
|
||||
def test_successful_clone(self, ifcgit):
|
||||
ifcgit.clone_repo("http://example.com/repo.git", "/local/folder").should_be_called().will_return("repo")
|
||||
ifcgit.load_anyifc("repo").should_be_called()
|
||||
op = MockOperator()
|
||||
subject.clone_repo(ifcgit, "http://example.com/repo.git", "/local/folder", operator=op)
|
||||
assert op.reports == [({"INFO"}, "Repository cloned")]
|
||||
|
||||
def test_failed_clone_reports_error(self, ifcgit):
|
||||
ifcgit.clone_repo("http://example.com/repo.git", "/local/folder").should_be_called().will_return(None)
|
||||
op = MockOperator()
|
||||
subject.clone_repo(ifcgit, "http://example.com/repo.git", "/local/folder", operator=op)
|
||||
assert op.reports == [({"ERROR"}, "Clone failed")]
|
||||
|
||||
|
||||
class TestDiscardUncommitted:
|
||||
def test_run(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.git_checkout("path/to/model.ifc").should_be_called()
|
||||
ifcgit.load_project("path/to/model.ifc").should_be_called()
|
||||
subject.discard_uncommitted(ifcgit, ifc)
|
||||
|
||||
|
||||
class TestCommitChanges:
|
||||
def test_commit_on_branch_without_new_branch(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.is_head_detached().should_be_called().will_return(False)
|
||||
ifcgit.git_commit("path/to/model.ifc", "my message").should_be_called()
|
||||
subject.commit_changes(ifcgit, ifc, "my message", "")
|
||||
|
||||
def test_commit_on_branch_with_new_branch(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.is_head_detached().should_be_called().will_return(False)
|
||||
ifcgit.checkout_new_branch("path/to/model.ifc", "feature").should_be_called()
|
||||
ifcgit.git_commit("path/to/model.ifc", "my message").should_be_called()
|
||||
subject.commit_changes(ifcgit, ifc, "my message", "feature")
|
||||
|
||||
def test_commit_on_detached_head(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.is_head_detached().should_be_called().will_return(True)
|
||||
ifcgit.git_commit("path/to/model.ifc", "my message").should_be_called()
|
||||
ifcgit.create_new_branch("feature").should_be_called()
|
||||
subject.commit_changes(ifcgit, ifc, "my message", "feature")
|
||||
|
||||
|
||||
class TestAddTag:
|
||||
def test_run(self, ifcgit):
|
||||
ifcgit.add_tag("repo", "abc123", "v1.0", "Release notes").should_be_called()
|
||||
subject.add_tag(ifcgit, "repo", "abc123", "v1.0", "Release notes")
|
||||
|
||||
|
||||
class TestDeleteTag:
|
||||
def test_run(self, ifcgit):
|
||||
ifcgit.delete_tag("repo", "v1.0").should_be_called()
|
||||
subject.delete_tag(ifcgit, "repo", "v1.0")
|
||||
|
||||
|
||||
class TestAddRemote:
|
||||
def test_run(self, ifcgit):
|
||||
ifcgit.add_remote("repo", "origin", "http://example.com").should_be_called()
|
||||
subject.add_remote(ifcgit, "repo", "origin", "http://example.com")
|
||||
|
||||
|
||||
class TestDeleteRemote:
|
||||
def test_run(self, ifcgit):
|
||||
ifcgit.delete_remote("repo", "origin").should_be_called()
|
||||
subject.delete_remote(ifcgit, "repo", "origin")
|
||||
|
||||
|
||||
class TestPush:
|
||||
def test_push_succeeds_silently(self, ifcgit):
|
||||
ifcgit.get_active_branch_name().should_be_called().will_return("main")
|
||||
ifcgit.push("repo", "origin", "main").should_be_called().will_return(None)
|
||||
subject.push(ifcgit, "repo", "origin", operator=None)
|
||||
|
||||
def test_push_failure_reports_error(self, ifcgit):
|
||||
ifcgit.get_active_branch_name().should_be_called().will_return("main")
|
||||
ifcgit.push("repo", "origin", "main").should_be_called().will_return("stderr: rejected")
|
||||
op = MockOperator()
|
||||
subject.push(ifcgit, "repo", "origin", operator=op)
|
||||
assert op.reports == [({"ERROR"}, "stderr: rejected")]
|
||||
|
||||
|
||||
class TestRefreshRevisionList:
|
||||
def test_refreshes_when_repo_has_heads(self, ifcgit, ifc):
|
||||
ifcgit.clear_merge_conflicts().should_be_called()
|
||||
ifcgit.repo_has_commits().should_be_called().will_return(True)
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.refresh_revision_list("path/to/model.ifc").should_be_called()
|
||||
subject.refresh_revision_list(ifcgit, ifc)
|
||||
|
||||
def test_skips_when_repo_has_no_heads(self, ifcgit, ifc):
|
||||
ifcgit.clear_merge_conflicts().should_be_called()
|
||||
ifcgit.repo_has_commits().should_be_called().will_return(False)
|
||||
subject.refresh_revision_list(ifcgit, ifc)
|
||||
# nothing else should be called — Prophecy will verify
|
||||
|
||||
|
||||
class TestColouriseRevision:
|
||||
def test_skips_when_no_step_ids(self, ifcgit):
|
||||
ifcgit.get_revisions_step_ids().should_be_called().will_return(None)
|
||||
subject.colourise_revision(ifcgit)
|
||||
|
||||
def test_colourises_with_step_ids(self, ifcgit):
|
||||
ifcgit.get_revisions_step_ids().should_be_called().will_return("step_ids")
|
||||
ifcgit.get_modified_step_ids("step_ids").should_be_called().will_return("modified_step_ids")
|
||||
ifcgit.update_step_ids("step_ids", "modified_step_ids").should_be_called().will_return("final_step_ids")
|
||||
ifcgit.colourise("final_step_ids").should_be_called()
|
||||
subject.colourise_revision(ifcgit)
|
||||
|
||||
|
||||
class TestColouriseUncommitted:
|
||||
def test_skips_when_no_step_ids(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.ifc_diff_ids("repo", None, "HEAD", "path/to/model.ifc").should_be_called().will_return(None)
|
||||
subject.colourise_uncommitted(ifcgit, ifc, "repo")
|
||||
|
||||
def test_colourises_with_step_ids(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.ifc_diff_ids("repo", None, "HEAD", "path/to/model.ifc").should_be_called().will_return("step_ids")
|
||||
ifcgit.get_modified_step_ids("step_ids").should_be_called().will_return("modified_step_ids")
|
||||
ifcgit.update_step_ids("step_ids", "modified_step_ids").should_be_called().will_return("final_step_ids")
|
||||
ifcgit.colourise("final_step_ids").should_be_called()
|
||||
subject.colourise_uncommitted(ifcgit, ifc, "repo")
|
||||
|
||||
|
||||
class TestSwitchRevision:
|
||||
def test_run(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.switch_to_revision_item().should_be_called()
|
||||
ifcgit.load_project("path/to/model.ifc").should_be_called()
|
||||
ifcgit.refresh_revision_list("path/to/model.ifc").should_be_called()
|
||||
ifcgit.decolourise().should_be_called()
|
||||
subject.switch_revision(ifcgit, ifc)
|
||||
|
||||
|
||||
class TestMergeBranch:
|
||||
def test_no_branch_at_selected_commit(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return(None)
|
||||
subject.merge_branch(ifcgit, ifc, operator=None)
|
||||
|
||||
def test_clean_merge(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge("feature").should_be_called().will_return(None)
|
||||
ifcgit.clear_merge_conflicts().should_be_called()
|
||||
ifcgit.set_display_branch().should_be_called()
|
||||
ifcgit.git_checkout("path/to/model.ifc").should_be_called()
|
||||
ifcgit.load_project("path/to/model.ifc").should_be_called()
|
||||
ifcgit.refresh_revision_list("path/to/model.ifc").should_be_called()
|
||||
ifcgit.decolourise().should_be_called()
|
||||
subject.merge_branch(ifcgit, ifc, operator=None)
|
||||
|
||||
def test_conflict_mergetool_success(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge("feature").should_be_called().will_return("conflict")
|
||||
ifcgit.git_mergetool("ifcmerge-forward", "path/to/model.ifc").should_be_called().will_return(None)
|
||||
ifcgit.commit_merge("path/to/model.ifc").should_be_called()
|
||||
ifcgit.clear_merge_conflicts().should_be_called()
|
||||
ifcgit.set_display_branch().should_be_called()
|
||||
ifcgit.git_checkout("path/to/model.ifc").should_be_called()
|
||||
ifcgit.load_project("path/to/model.ifc").should_be_called()
|
||||
ifcgit.refresh_revision_list("path/to/model.ifc").should_be_called()
|
||||
ifcgit.decolourise().should_be_called()
|
||||
subject.merge_branch(ifcgit, ifc, operator=None)
|
||||
|
||||
def test_conflict_mergetool_failure(self, ifcgit, ifc):
|
||||
conflicts = [{"type": "attribute_conflict", "entity_id": 42}]
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge("feature").should_be_called().will_return("conflict")
|
||||
ifcgit.git_mergetool("ifcmerge-forward", "path/to/model.ifc").should_be_called().will_return(conflicts)
|
||||
ifcgit.git_merge_abort().should_be_called()
|
||||
ifcgit.store_merge_conflicts(conflicts).should_be_called()
|
||||
op = MockOperator()
|
||||
subject.merge_branch(ifcgit, ifc, op)
|
||||
assert op.reports == [({"WARNING"}, "Merge failed — see the conflict report in the panel below")]
|
||||
|
||||
def test_unknown_merge_error(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge("feature").should_be_called().will_return("error")
|
||||
op = MockOperator()
|
||||
subject.merge_branch(ifcgit, ifc, op)
|
||||
assert op.reports == [({"ERROR"}, "Unknown IFC Merge failure")]
|
||||
|
||||
|
||||
class TestDryRunMerge:
|
||||
def test_no_branch_at_selected_commit(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return(None)
|
||||
subject.dry_run_merge(ifcgit, ifc, operator=None)
|
||||
|
||||
def test_clean_merge_preview(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge_no_commit("feature").should_be_called().will_return(None)
|
||||
ifcgit.git_merge_abort().should_be_called()
|
||||
ifcgit.clear_merge_conflicts().should_be_called()
|
||||
op = MockOperator()
|
||||
subject.dry_run_merge(ifcgit, ifc, op)
|
||||
assert op.reports == [({"INFO"}, "Merge preview: no conflicts")]
|
||||
|
||||
def test_conflict_preview_shows_report(self, ifcgit, ifc):
|
||||
conflicts = [{"type": "attribute_conflict", "entity_id": 42}]
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge_no_commit("feature").should_be_called().will_return("conflict")
|
||||
ifcgit.git_mergetool("ifcmerge-forward", "path/to/model.ifc").should_be_called().will_return(conflicts)
|
||||
ifcgit.git_merge_abort().should_be_called()
|
||||
ifcgit.store_merge_conflicts(conflicts).should_be_called()
|
||||
op = MockOperator()
|
||||
subject.dry_run_merge(ifcgit, ifc, op)
|
||||
assert op.reports == [({"WARNING"}, "Merge preview: conflicts found — see the panel below")]
|
||||
|
||||
def test_conflict_preview_mergetool_succeeds(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.config_ifcmerge().should_be_called()
|
||||
ifcgit.get_selected_branch().should_be_called().will_return("feature")
|
||||
ifcgit.get_merge_tool("feature").should_be_called().will_return("ifcmerge-forward")
|
||||
ifcgit.git_merge_no_commit("feature").should_be_called().will_return("conflict")
|
||||
ifcgit.git_mergetool("ifcmerge-forward", "path/to/model.ifc").should_be_called().will_return(None)
|
||||
ifcgit.git_merge_abort().should_be_called()
|
||||
ifcgit.clear_merge_conflicts().should_be_called()
|
||||
op = MockOperator()
|
||||
subject.dry_run_merge(ifcgit, ifc, op)
|
||||
assert op.reports == [({"INFO"}, "Merge preview: no conflicts")]
|
||||
|
||||
|
||||
class TestEntityLog:
|
||||
def test_run(self, ifcgit, ifc):
|
||||
ifc.get_path().should_be_called().will_return("path/to/model.ifc")
|
||||
ifcgit.entity_log("path/to/model.ifc", 42).should_be_called().will_return("log text")
|
||||
op = MockOperator()
|
||||
subject.entity_log(ifcgit, ifc, 42, op)
|
||||
assert op.reports == [({"ERROR"}, "log text")]
|
||||
|
||||
|
||||
class TestInstallGit:
|
||||
def test_windows(self, ifcgit):
|
||||
import unittest.mock as mock
|
||||
|
||||
with mock.patch("platform.system", return_value="Windows"):
|
||||
ifcgit.install_git_windows(operator="op").should_be_called()
|
||||
subject.install_git(ifcgit, "op")
|
||||
|
||||
def test_non_windows_does_nothing(self, ifcgit):
|
||||
import unittest.mock as mock
|
||||
|
||||
with mock.patch("platform.system", return_value="Linux"):
|
||||
subject.install_git(ifcgit, "op")
|
||||
# no tool method should be called — Prophecy will verify
|
||||
|
||||
|
||||
class TestFetch:
|
||||
def test_run(self, ifcgit):
|
||||
ifcgit.fetch("origin").should_be_called()
|
||||
subject.fetch(ifcgit, "origin")
|
||||
|
||||
|
||||
class TestRunGitDiff:
|
||||
def test_run(self, ifcgit):
|
||||
ifcgit.run_git_diff("operator", False).should_be_called()
|
||||
subject.run_git_diff(ifcgit, "operator", False)
|
||||
@@ -0,0 +1,581 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2025 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# Bonsai is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Bonsai is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
# This file was generated with the assistance of an AI coding tool.
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import bonsai.core.tool
|
||||
from bonsai.tool.ifcgit import IfcGit, IfcGitRepo
|
||||
from test.bim.bootstrap import NewFile
|
||||
|
||||
try:
|
||||
import git
|
||||
import git.exc
|
||||
|
||||
HAS_GIT = True
|
||||
except ImportError:
|
||||
HAS_GIT = False
|
||||
|
||||
import pytest
|
||||
|
||||
requires_git = pytest.mark.skipif(not HAS_GIT, reason="GitPython not available")
|
||||
|
||||
|
||||
def _make_repo(tmpdir: str) -> "git.Repo":
|
||||
"""Initialise a git repo with user config (needed for commits)."""
|
||||
repo = git.Repo.init(tmpdir)
|
||||
with repo.config_writer() as cfg:
|
||||
cfg.set_value("user", "name", "Test User")
|
||||
cfg.set_value("user", "email", "test@example.com")
|
||||
return repo
|
||||
|
||||
|
||||
def _commit_ifc(repo: "git.Repo", tmpdir: str, content: str, message: str) -> str:
|
||||
"""Write content to model.ifc, stage and commit it. Returns commit hexsha."""
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
with open(ifc_path, "w") as f:
|
||||
f.write(content)
|
||||
repo.index.add([os.path.normpath(ifc_path)])
|
||||
commit = repo.index.commit(message)
|
||||
return commit.hexsha
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Interface conformance
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestImplementsTool(NewFile):
|
||||
def test_run(self):
|
||||
assert isinstance(IfcGit(), bonsai.core.tool.IfcGit)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pure-logic (no git required)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestIsValidRefFormat(NewFile):
|
||||
def test_simple_name(self):
|
||||
assert IfcGit.is_valid_ref_format("main")
|
||||
|
||||
def test_name_with_slash(self):
|
||||
assert IfcGit.is_valid_ref_format("feature/my-feature")
|
||||
|
||||
def test_name_with_numbers(self):
|
||||
assert IfcGit.is_valid_ref_format("release-2024")
|
||||
|
||||
def test_rejects_leading_dot(self):
|
||||
assert not IfcGit.is_valid_ref_format(".hidden")
|
||||
|
||||
def test_rejects_leading_dash(self):
|
||||
assert not IfcGit.is_valid_ref_format("-branch")
|
||||
|
||||
def test_rejects_space(self):
|
||||
assert not IfcGit.is_valid_ref_format("my branch")
|
||||
|
||||
def test_rejects_double_dot(self):
|
||||
assert not IfcGit.is_valid_ref_format("my..branch")
|
||||
|
||||
def test_rejects_tilde(self):
|
||||
assert not IfcGit.is_valid_ref_format("my~branch")
|
||||
|
||||
def test_rejects_caret(self):
|
||||
assert not IfcGit.is_valid_ref_format("my^branch")
|
||||
|
||||
def test_rejects_trailing_dot(self):
|
||||
assert not IfcGit.is_valid_ref_format("branch.")
|
||||
|
||||
def test_rejects_trailing_slash(self):
|
||||
assert not IfcGit.is_valid_ref_format("branch/")
|
||||
|
||||
def test_rejects_dot_lock_suffix(self):
|
||||
assert not IfcGit.is_valid_ref_format("branch.lock")
|
||||
|
||||
def test_rejects_empty_string(self):
|
||||
assert not IfcGit.is_valid_ref_format("")
|
||||
|
||||
def test_rejects_at_brace(self):
|
||||
assert not IfcGit.is_valid_ref_format("branch@{upstream}")
|
||||
|
||||
|
||||
class TestGetPathDir(NewFile):
|
||||
def test_returns_parent_directory(self):
|
||||
assert IfcGit.get_path_dir("/some/path/model.ifc") == "/some/path"
|
||||
|
||||
def test_handles_nested_path(self):
|
||||
assert IfcGit.get_path_dir("/a/b/c/d.ifc") == "/a/b/c"
|
||||
|
||||
def test_returns_absolute_path(self):
|
||||
result = IfcGit.get_path_dir("/a/b/model.ifc")
|
||||
assert os.path.isabs(result)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# File I/O
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestDos2Unix(NewFile):
|
||||
def test_converts_crlf_to_lf(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".ifc", delete=False, mode="wb") as f:
|
||||
f.write(b"line1\r\nline2\r\nline3\r\n")
|
||||
path = f.name
|
||||
try:
|
||||
IfcGit.dos2unix(path)
|
||||
with open(path, "rb") as f:
|
||||
assert f.read() == b"line1\nline2\nline3\n"
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
def test_lf_only_file_is_unchanged(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".ifc", delete=False, mode="wb") as f:
|
||||
f.write(b"line1\nline2\nline3\n")
|
||||
path = f.name
|
||||
try:
|
||||
IfcGit.dos2unix(path)
|
||||
with open(path, "rb") as f:
|
||||
assert f.read() == b"line1\nline2\nline3\n"
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
def test_empty_file_unchanged(self):
|
||||
with tempfile.NamedTemporaryFile(suffix=".ifc", delete=False, mode="wb") as f:
|
||||
f.write(b"")
|
||||
path = f.name
|
||||
try:
|
||||
IfcGit.dos2unix(path)
|
||||
with open(path, "rb") as f:
|
||||
assert f.read() == b""
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Git repo initialisation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestInitRepo(NewFile):
|
||||
@requires_git
|
||||
def test_creates_git_repo(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
IfcGitRepo.repo = None
|
||||
IfcGit.init_repo(tmpdir)
|
||||
assert IfcGitRepo.repo is not None
|
||||
assert os.path.isdir(IfcGitRepo.repo.git_dir)
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_creates_info_attributes_file(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
IfcGitRepo.repo = None
|
||||
IfcGit.init_repo(tmpdir)
|
||||
attrs_path = os.path.join(IfcGitRepo.repo.git_dir, "info", "attributes")
|
||||
assert os.path.isfile(attrs_path)
|
||||
with open(attrs_path) as f:
|
||||
assert "*.ifc text" in f.read()
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
|
||||
class TestRepoFromPath(NewFile):
|
||||
@requires_git
|
||||
def test_finds_repo_from_file_in_root(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
IfcGitRepo.repo = None
|
||||
repo = _make_repo(tmpdir)
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
open(ifc_path, "w").close()
|
||||
result = IfcGit.repo_from_path(ifc_path)
|
||||
assert result is not None
|
||||
assert os.path.abspath(result.working_dir) == os.path.abspath(tmpdir)
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_finds_repo_from_subdirectory(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
IfcGitRepo.repo = None
|
||||
_make_repo(tmpdir)
|
||||
subdir = os.path.join(tmpdir, "sub", "dir")
|
||||
os.makedirs(subdir)
|
||||
result = IfcGit.repo_from_path(subdir)
|
||||
assert result is not None
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_returns_none_for_path_outside_any_repo(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
IfcGitRepo.repo = None
|
||||
# a plain directory with no .git anywhere above it (using /tmp directly
|
||||
# is safe since /tmp is not a git repo on this system)
|
||||
result = IfcGit.repo_from_path("/nonexistent/path/that/does/not/exist")
|
||||
assert result is None
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Git repo configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestConfigInfoAttributes(NewFile):
|
||||
@requires_git
|
||||
def test_creates_attributes_file(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
IfcGit.config_info_attributes(repo)
|
||||
attrs_path = os.path.join(repo.git_dir, "info", "attributes")
|
||||
assert os.path.isfile(attrs_path)
|
||||
with open(attrs_path) as f:
|
||||
assert "*.ifc text" in f.read()
|
||||
|
||||
@requires_git
|
||||
def test_does_not_overwrite_existing_attributes(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
attrs_path = os.path.join(repo.git_dir, "info", "attributes")
|
||||
os.makedirs(os.path.dirname(attrs_path), exist_ok=True)
|
||||
with open(attrs_path, "w") as f:
|
||||
f.write("*.png binary\n")
|
||||
IfcGit.config_info_attributes(repo)
|
||||
with open(attrs_path) as f:
|
||||
content = f.read()
|
||||
assert "*.png binary" in content # original content preserved
|
||||
|
||||
|
||||
class TestConfigPush(NewFile):
|
||||
@requires_git
|
||||
def test_sets_push_defaults(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
IfcGit.config_push(repo)
|
||||
reader = repo.config_reader()
|
||||
assert reader.get_value("push", "default") == "current"
|
||||
assert reader.get_value("push", "autoSetupRemote") is True
|
||||
|
||||
@requires_git
|
||||
def test_does_not_overwrite_existing_push_section(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
with repo.config_writer() as w:
|
||||
w.set_value("push", "default", "simple")
|
||||
IfcGit.config_push(repo)
|
||||
reader = repo.config_reader()
|
||||
assert reader.get_value("push", "default") == "simple"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Branch / tag lookups
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestBranchesByHexsha(NewFile):
|
||||
@requires_git
|
||||
def test_maps_head_commit_to_active_branch(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
result = IfcGit.branches_by_hexsha(repo)
|
||||
head_sha = repo.head.commit.hexsha
|
||||
assert head_sha in result
|
||||
names = [b.name for b in result[head_sha]]
|
||||
assert repo.active_branch.name in names
|
||||
|
||||
@requires_git
|
||||
def test_returns_empty_dict_when_no_commits(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
result = IfcGit.branches_by_hexsha(repo)
|
||||
assert result == {}
|
||||
|
||||
@requires_git
|
||||
def test_includes_both_branches_when_pointing_to_same_commit(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
repo.create_head("feature")
|
||||
result = IfcGit.branches_by_hexsha(repo)
|
||||
head_sha = repo.head.commit.hexsha
|
||||
names = [b.name for b in result[head_sha]]
|
||||
assert len(names) == 2
|
||||
|
||||
|
||||
class TestTagsByHexsha(NewFile):
|
||||
@requires_git
|
||||
def test_returns_empty_dict_when_no_tags(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
assert IfcGit.tags_by_hexsha(repo) == {}
|
||||
|
||||
@requires_git
|
||||
def test_maps_commit_to_annotated_tag(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
repo.create_tag("v1.0", message="Release 1.0")
|
||||
result = IfcGit.tags_by_hexsha(repo)
|
||||
head_sha = repo.head.commit.hexsha
|
||||
assert head_sha in result
|
||||
assert result[head_sha][0].name == "v1.0"
|
||||
|
||||
@requires_git
|
||||
def test_maps_commit_to_lightweight_tag(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
repo.create_tag("v0.1")
|
||||
result = IfcGit.tags_by_hexsha(repo)
|
||||
head_sha = repo.head.commit.hexsha
|
||||
assert head_sha in result
|
||||
|
||||
|
||||
class TestDeleteTag(NewFile):
|
||||
@requires_git
|
||||
def test_removes_existing_tag(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
repo.create_tag("v1.0")
|
||||
IfcGit.delete_tag(repo, "v1.0")
|
||||
assert "v1.0" not in [t.name for t in repo.tags]
|
||||
|
||||
@requires_git
|
||||
def test_does_not_raise_for_nonexistent_tag(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
IfcGit.delete_tag(repo, "does-not-exist") # must not raise
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# IFC diff parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestIfcDiffIds(NewFile):
|
||||
@requires_git
|
||||
def test_detects_modified_entity(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
sha_a = _commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
sha_b = _commit_ifc(repo, tmpdir, "#1=IFCPROJECT('xyz',$,$,$,$,$,$,$,$);\n", "update")
|
||||
result = IfcGit.ifc_diff_ids(repo, sha_a, sha_b, ifc_path)
|
||||
assert 1 in result["modified"]
|
||||
assert result["added"] == set()
|
||||
assert result["removed"] == set()
|
||||
|
||||
@requires_git
|
||||
def test_detects_added_entity(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
sha_a = _commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
sha_b = _commit_ifc(
|
||||
repo,
|
||||
tmpdir,
|
||||
"#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n#2=IFCSITE('new',$,$,$,$,$,$,$,$,$,$,$,$);\n",
|
||||
"add site",
|
||||
)
|
||||
result = IfcGit.ifc_diff_ids(repo, sha_a, sha_b, ifc_path)
|
||||
assert 2 in result["added"]
|
||||
assert 1 not in result["modified"]
|
||||
assert result["removed"] == set()
|
||||
|
||||
@requires_git
|
||||
def test_detects_removed_entity(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
sha_a = _commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
sha_b = _commit_ifc(repo, tmpdir, "", "clear")
|
||||
result = IfcGit.ifc_diff_ids(repo, sha_a, sha_b, ifc_path)
|
||||
assert 1 in result["removed"]
|
||||
assert result["added"] == set()
|
||||
assert result["modified"] == set()
|
||||
|
||||
@requires_git
|
||||
def test_no_changes_between_identical_commits(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
sha = _commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
result = IfcGit.ifc_diff_ids(repo, sha, sha, ifc_path)
|
||||
assert result["modified"] == set()
|
||||
assert result["added"] == set()
|
||||
assert result["removed"] == set()
|
||||
|
||||
@requires_git
|
||||
def test_diff_against_working_tree_with_none_hash_a(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
_commit_ifc(repo, tmpdir, "#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n", "init")
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
# Make an uncommitted change in working tree
|
||||
with open(ifc_path, "w") as f:
|
||||
f.write("#1=IFCPROJECT('xyz',$,$,$,$,$,$,$,$);\n")
|
||||
result = IfcGit.ifc_diff_ids(repo, None, "HEAD", ifc_path)
|
||||
assert 1 in result["modified"]
|
||||
|
||||
@requires_git
|
||||
def test_handles_multiple_changed_entities(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
sha_a = _commit_ifc(
|
||||
repo,
|
||||
tmpdir,
|
||||
"#1=IFCPROJECT('abc',$,$,$,$,$,$,$,$);\n#2=IFCSITE('s',$,$,$,$,$,$,$,$,$,$,$,$);\n",
|
||||
"init",
|
||||
)
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
sha_b = _commit_ifc(
|
||||
repo,
|
||||
tmpdir,
|
||||
"#1=IFCPROJECT('xyz',$,$,$,$,$,$,$,$);\n#2=IFCSITE('t',$,$,$,$,$,$,$,$,$,$,$,$);\n",
|
||||
"update both",
|
||||
)
|
||||
result = IfcGit.ifc_diff_ids(repo, sha_a, sha_b, ifc_path)
|
||||
assert 1 in result["modified"]
|
||||
assert 2 in result["modified"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Merge conflict report — store / clear / get
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestStoreClearGetMergeConflicts(NewFile):
|
||||
def test_round_trip(self):
|
||||
conflicts = [{"type": "attribute_conflict", "entity_id": 42}]
|
||||
IfcGit.store_merge_conflicts(conflicts)
|
||||
result = IfcGit.get_merge_conflicts()
|
||||
assert result == conflicts
|
||||
|
||||
def test_get_returns_none_when_empty(self):
|
||||
IfcGit.clear_merge_conflicts()
|
||||
assert IfcGit.get_merge_conflicts() is None
|
||||
|
||||
def test_clear_removes_stored_conflicts(self):
|
||||
IfcGit.store_merge_conflicts([{"type": "class_changed"}])
|
||||
IfcGit.clear_merge_conflicts()
|
||||
assert IfcGit.get_merge_conflicts() is None
|
||||
|
||||
def test_get_returns_none_on_corrupt_json(self):
|
||||
import bpy
|
||||
|
||||
bpy.context.scene.IfcGitProperties.merge_conflicts = "not valid json {"
|
||||
assert IfcGit.get_merge_conflicts() is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# git_mergetool — report file reading
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestGitMergetool:
|
||||
@requires_git
|
||||
def test_returns_none_when_report_file_absent(self):
|
||||
import unittest.mock as mock
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
mock_repo = mock.MagicMock()
|
||||
IfcGitRepo.repo = mock_repo
|
||||
result = IfcGit.git_mergetool("ifcmerge", ifc_path)
|
||||
assert result is None
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_returns_none_when_report_file_empty(self):
|
||||
import unittest.mock as mock
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
report_path = ifc_path + ".ifcmerge"
|
||||
open(report_path, "w").close()
|
||||
mock_repo = mock.MagicMock()
|
||||
IfcGitRepo.repo = mock_repo
|
||||
result = IfcGit.git_mergetool("ifcmerge", ifc_path)
|
||||
assert result is None
|
||||
assert not os.path.exists(report_path)
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_parses_conflict_report_and_deletes_file(self):
|
||||
import json
|
||||
import unittest.mock as mock
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ifc_path = os.path.join(tmpdir, "model.ifc")
|
||||
report_path = ifc_path + ".ifcmerge"
|
||||
conflicts = [{"type": "attribute_conflict", "entity_id": 5}]
|
||||
with open(report_path, "w") as f:
|
||||
json.dump({"status": "failed", "conflicts": conflicts}, f)
|
||||
mock_repo = mock.MagicMock()
|
||||
mock_repo.git.mergetool.side_effect = git.exc.GitCommandError("mergetool", 1)
|
||||
IfcGitRepo.repo = mock_repo
|
||||
result = IfcGit.git_mergetool("ifcmerge", ifc_path)
|
||||
assert result == conflicts
|
||||
assert not os.path.exists(report_path)
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# config_ifcmerge — cmd format and update
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestConfigIfcmerge:
|
||||
@requires_git
|
||||
def test_writes_redirect_cmd_on_first_call(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
IfcGitRepo.repo = repo
|
||||
IfcGit.config_ifcmerge()
|
||||
reader = repo.config_reader()
|
||||
cmd = reader.get_value('mergetool "ifcmerge"', "cmd")
|
||||
assert "> $MERGED.ifcmerge" in cmd
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_updates_cmd_missing_redirect(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
IfcGitRepo.repo = repo
|
||||
with repo.config_writer() as w:
|
||||
w.set_value('mergetool "ifcmerge"', "cmd", "ifcmerge $BASE $LOCAL $REMOTE $MERGED")
|
||||
w.set_value('mergetool "ifcmerge"', "trustExitCode", True)
|
||||
IfcGit.config_ifcmerge()
|
||||
reader = repo.config_reader()
|
||||
cmd = reader.get_value('mergetool "ifcmerge"', "cmd")
|
||||
assert "> $MERGED.ifcmerge" in cmd
|
||||
IfcGitRepo.repo = None
|
||||
|
||||
@requires_git
|
||||
def test_forward_tool_writes_redirect_cmd(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
repo = _make_repo(tmpdir)
|
||||
IfcGitRepo.repo = repo
|
||||
IfcGit.config_ifcmerge()
|
||||
reader = repo.config_reader()
|
||||
cmd = reader.get_value('mergetool "ifcmerge-forward"', "cmd")
|
||||
assert "--prioritise-local" in cmd
|
||||
assert "> $MERGED.ifcmerge" in cmd
|
||||
IfcGitRepo.repo = None
|
||||
@@ -39,12 +39,11 @@
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/macros.h"
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcBaseClass.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/macros.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
#include "ifcgeom/Serialization/Serialization.h"
|
||||
#include "../ifcgeom/Serialization/Serialization.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
@@ -57,18 +56,18 @@ void createGroundShape(TopoDS_Shape& shape);
|
||||
|
||||
int main() {
|
||||
|
||||
// The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several
|
||||
// The hierarchy_helper is a subclass of the regular file that provides several
|
||||
// convenience functions for working with geometry in IFC files.
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname("IfcAdvancedHouse.ifc");
|
||||
hierarchy_helper<IfcSchema> file;
|
||||
file.header().file_name().setname("IfcAdvancedHouse.ifc");
|
||||
|
||||
IfcSchema::IfcBuilding* building = file.addBuilding();
|
||||
auto building = file.addBuilding();
|
||||
// By adding a building, a hierarchy has been automatically created that consists of the following
|
||||
// structure: IfcProject > IfcSite > IfcBuilding
|
||||
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// project, which has been created automatically.
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("IfcAdvancedHouse"s);
|
||||
file.getSingle<IfcSchema::IfcProject>().setName("IfcAdvancedHouse"s);
|
||||
|
||||
// To demonstrate the ability to serialize arbitrary opencascade solids a building envelope is
|
||||
// constructed by applying boolean operations. Naturally, in IFC, building elements should be
|
||||
@@ -91,13 +90,13 @@ int main() {
|
||||
// IfcFacetedBRep. If it would not be a polyhedron, serialise() can only be successful when linked
|
||||
// to the IFC4 model and with `advanced` set to `true` which introduces IfcAdvancedFace. It would
|
||||
// return `0` otherwise.
|
||||
IfcSchema::IfcProductDefinitionShape* building_shape = IfcGeom::serialise(STRINGIFY(IfcSchema), building_shell, false)->as<IfcSchema::IfcProductDefinitionShape>();
|
||||
auto building_shape = IfcGeom::serialise(file, building_shell, false).as<IfcSchema::IfcProductDefinitionShape>();
|
||||
|
||||
file.addEntity(building_shape);
|
||||
IfcSchema::IfcRepresentation* rep = *building_shape->Representations()->begin();
|
||||
file.add_entity(building_shape);
|
||||
auto rep = building_shape.Representations().begin();
|
||||
rep->setContextOfItems(file.getRepresentationContext("model"));
|
||||
|
||||
building->setRepresentation(building_shape);
|
||||
building.setRepresentation(building_shape);
|
||||
|
||||
// A pale white colour is assigned to the building.
|
||||
setSurfaceColour(file, building_shape, 0.75, 0.73, 0.68);
|
||||
@@ -107,18 +106,18 @@ int main() {
|
||||
TopoDS_Shape shape;
|
||||
createGroundShape(shape);
|
||||
|
||||
auto ground_representation = IfcGeom::serialise(STRINGIFY(IfcSchema), shape, true);
|
||||
auto ground_representation = IfcGeom::serialise(file, shape, true);
|
||||
if (!ground_representation) {
|
||||
ground_representation = IfcGeom::tesselate(STRINGIFY(IfcSchema), shape, 100.);
|
||||
ground_representation = IfcGeom::tesselate(file, shape, 100.);
|
||||
}
|
||||
file.getSingle<IfcSchema::IfcSite>()->setRepresentation(ground_representation->as<IfcSchema::IfcProductDefinitionShape>());
|
||||
file.getSingle<IfcSchema::IfcSite>().setRepresentation(ground_representation.as<IfcSchema::IfcProductDefinitionShape>());
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle<IfcSchema::IfcSite>()->Representation()->Representations();
|
||||
for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
|
||||
(*it)->setContextOfItems(file.getRepresentationContext("Model"));
|
||||
auto ground_reps = file.getSingle<IfcSchema::IfcSite>().Representation().Representations();
|
||||
for (auto& rep : ground_reps) {
|
||||
rep.setContextOfItems(file.getRepresentationContext("Model"));
|
||||
}
|
||||
file.addEntity(ground_representation);
|
||||
setSurfaceColour(file, ground_representation->as<IfcSchema::IfcProductDefinitionShape>(), 0.15, 0.25, 0.05);
|
||||
file.add_entity(ground_representation);
|
||||
setSurfaceColour(file, ground_representation.as<IfcSchema::IfcProductDefinitionShape>(), 0.15, 0.25, 0.05);
|
||||
|
||||
/*
|
||||
// Note that IFC lacks elementary surfaces that STEP does have, such as spherical_surface.
|
||||
|
||||
+299
-210
@@ -30,8 +30,8 @@
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#pragma warning(disable : 4018 4267 4250 4984 4985)
|
||||
|
||||
#include "ifcparse/Ifc4x3_add2.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/Ifc4x3_add2.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <fstream>
|
||||
@@ -43,37 +43,49 @@ double to_radian(double deg) { return PI * deg / 180; }
|
||||
|
||||
// performs basic project setup including created the IfcProject object
|
||||
// and initializing the project units to FEET
|
||||
Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
|
||||
Schema::IfcProject setup_project(hierarchy_helper<Schema>& file) {
|
||||
std::vector<std::string> file_description;
|
||||
file_description.push_back("ViewDefinition[Alignment-basedReferenceView]");
|
||||
file.header().file_description()->setdescription(file_description);
|
||||
file.header().file_description().setdescription(file_description);
|
||||
|
||||
auto project = file.addProject();
|
||||
project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
|
||||
project->setDescription(std::string("C++ Example"));
|
||||
project.setName("FHWA Bridge Geometry Manual Example Alignment");
|
||||
project.setDescription("C++ Example");
|
||||
|
||||
// set up project units for feet
|
||||
// the call to file.addProject() sets up length units as millimeter.
|
||||
auto units_in_context = project->UnitsInContext();
|
||||
auto units = units_in_context->Units();
|
||||
auto begin = units->begin();
|
||||
auto units_in_context = project.UnitsInContext();
|
||||
auto units = units_in_context.Units();
|
||||
auto begin = units.begin();
|
||||
auto iter = begin;
|
||||
auto end = units->end();
|
||||
auto end = units.end();
|
||||
for (; iter != end; iter++) {
|
||||
auto unit = *iter;
|
||||
if (unit->as<Schema::IfcSIUnit>() && unit->as<Schema::IfcSIUnit>()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
|
||||
auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0);
|
||||
file.addEntity(dimensions);
|
||||
auto& unit = *iter;
|
||||
if (unit.as<Schema::IfcSIUnit>() && unit.as<Schema::IfcSIUnit>().UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
|
||||
auto dimensions = file.create<Schema::IfcDimensionalExponents>();
|
||||
dimensions.setLengthExponent(1);
|
||||
dimensions.setMassExponent(0);
|
||||
dimensions.setTimeExponent(0);
|
||||
dimensions.setElectricCurrentExponent(0);
|
||||
dimensions.setThermodynamicTemperatureExponent(0);
|
||||
dimensions.setAmountOfSubstanceExponent(0);
|
||||
dimensions.setLuminousIntensityExponent(0);
|
||||
|
||||
auto conversion_factor = file.create<Schema::IfcMeasureWithUnit>();
|
||||
auto length = file.create<Schema::IfcLengthMeasure>();
|
||||
length.set_attribute_value(0, 304.80);
|
||||
conversion_factor.setValueComponent(length);
|
||||
conversion_factor.setUnitComponent(unit);
|
||||
|
||||
auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as<Schema::IfcSIUnit>());
|
||||
file.addEntity(conversion_factor);
|
||||
|
||||
auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor);
|
||||
file.addEntity(conversion_based_unit);
|
||||
|
||||
units->remove(unit); // remove the millimeter unit
|
||||
units->push(conversion_based_unit); // add the feet unit
|
||||
units_in_context->setUnits(units); // update the UnitsInContext
|
||||
auto conversion_based_unit = file.create<Schema::IfcConversionBasedUnit>();
|
||||
conversion_based_unit.setDimensions(dimensions);
|
||||
conversion_based_unit.setUnitType(Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT);
|
||||
conversion_based_unit.setName("FEET");
|
||||
conversion_based_unit.setConversionFactor(conversion_factor);
|
||||
|
||||
units.erase(std::remove(units.begin(), units.end(), unit)); // remove the millimeter unit
|
||||
units.push_back(conversion_based_unit); // add the feet unit
|
||||
units_in_context.setUnits(units); // update the UnitsInContext
|
||||
|
||||
break; // Done!, the length unit was found, so break out of the loop
|
||||
}
|
||||
@@ -83,135 +95,198 @@ Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for horizontal alignment tangent runs
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_tangent(typename Schema::IfcCartesianPoint* p, double dir, double length) {
|
||||
std::pair<typename Schema::IfcCurveSegment, typename Schema::IfcAlignmentSegment> create_tangent(hierarchy_helper<Schema>& file, const typename Schema::IfcCartesianPoint& p, double dir, double length) {
|
||||
// geometry
|
||||
auto parent_curve = new Schema::IfcLine(
|
||||
new Schema::IfcCartesianPoint(std::vector<double>({0, 0})),
|
||||
new Schema::IfcVector(new Schema::IfcDirection(std::vector<double>{1.0, 0.0}), 1.0));
|
||||
auto parent_curve = file.create<Schema::IfcLine>();
|
||||
parent_curve.setPnt(file.addDoublet<Schema::IfcCartesianPoint>(0.0, 0.0));
|
||||
auto vec = file.create<Schema::IfcVector>();
|
||||
vec.setOrientation(file.addDoublet<Schema::IfcDirection>(1.0, 0.0));
|
||||
vec.setMagnitude(1.0);
|
||||
parent_curve.setDir(vec);
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{cos(dir), sin(dir)})),
|
||||
new Schema::IfcLengthMeasure(0.0), // start
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
auto place = file.create<Schema::IfcAxis2Placement2D>();
|
||||
place.setLocation(p);
|
||||
place.setRefDirection(file.addDoublet<Schema::IfcDirection>(cos(dir), sin(dir)));
|
||||
|
||||
auto curve_segment = file.create<Schema::IfcCurveSegment>();
|
||||
curve_segment.setTransition(Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT);
|
||||
curve_segment.setPlacement(place);
|
||||
curve_segment.setSegmentStart(file.addValue<Schema::IfcLengthMeasure>(0.0));
|
||||
curve_segment.setSegmentLength(file.addValue<Schema::IfcLengthMeasure>(length));
|
||||
curve_segment.setParentCurve(parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentHorizontalSegment(
|
||||
boost::none, boost::none, p, dir, 0.0, 0.0, length, boost::none, Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE);
|
||||
auto design_parameters = file.create<Schema::IfcAlignmentHorizontalSegment>();
|
||||
design_parameters.setStartPoint(p);
|
||||
design_parameters.setStartDirection(dir);
|
||||
design_parameters.setStartRadiusOfCurvature(0.0);
|
||||
design_parameters.setEndRadiusOfCurvature(0.0);
|
||||
design_parameters.setSegmentLength(length);
|
||||
design_parameters.setPredefinedType(Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_LINE);
|
||||
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(
|
||||
IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
auto alignment_segment = file.create<Schema::IfcAlignmentSegment>();
|
||||
alignment_segment.setGlobalId(ifcopenshell::global_id());
|
||||
alignment_segment.setDesignParameters(design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for horizontal alignment horizonal curves
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_hcurve(typename Schema::IfcCartesianPoint* pc, double dir, double radius, double lc) {
|
||||
std::pair<typename Schema::IfcCurveSegment, typename Schema::IfcAlignmentSegment> create_hcurve(hierarchy_helper<Schema>& file, const typename Schema::IfcCartesianPoint& pc, double dir, double radius, double lc) {
|
||||
// geometry
|
||||
double sign = radius / fabs(radius);
|
||||
auto parent_curve = new Schema::IfcCircle(
|
||||
new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>({0, 0})), new Schema::IfcDirection(std::vector<double>{1, 0})),
|
||||
fabs(radius));
|
||||
auto place = file.create<Schema::IfcAxis2Placement2D>();
|
||||
place.setLocation(file.addDoublet<Schema::IfcCartesianPoint>(0.0, 0.0));
|
||||
place.setRefDirection(file.addDoublet<Schema::IfcDirection>(1.0, 0.0));
|
||||
auto parent_curve = file.create<Schema::IfcCircle>();
|
||||
parent_curve.setPosition(place);
|
||||
parent_curve.setRadius(fabs(radius));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(pc, new Schema::IfcDirection(std::vector<double>{cos(dir), sin(dir)})),
|
||||
new Schema::IfcLengthMeasure(0.0),
|
||||
new Schema::IfcLengthMeasure(sign * lc),
|
||||
parent_curve);
|
||||
auto place2 = file.create<Schema::IfcAxis2Placement2D>();
|
||||
place2.setLocation(pc);
|
||||
place2.setRefDirection(file.addDoublet<Schema::IfcDirection>(cos(dir), sin(dir)));
|
||||
|
||||
auto curve_segment = file.create<Schema::IfcCurveSegment>();
|
||||
curve_segment.setTransition(Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT);
|
||||
curve_segment.setPlacement(place2);
|
||||
curve_segment.setSegmentStart(file.addValue<Schema::IfcLengthMeasure>(0.0));
|
||||
curve_segment.setSegmentLength(file.addValue<Schema::IfcLengthMeasure>(sign * lc));
|
||||
curve_segment.setParentCurve(parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentHorizontalSegment(boost::none, boost::none, pc, dir, radius, radius, lc, boost::none, Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
auto design_parameters = file.create<Schema::IfcAlignmentHorizontalSegment>();
|
||||
design_parameters.setStartPoint(pc);
|
||||
design_parameters.setStartDirection(dir);
|
||||
design_parameters.setStartRadiusOfCurvature(radius);
|
||||
design_parameters.setEndRadiusOfCurvature(radius);
|
||||
design_parameters.setSegmentLength(lc);
|
||||
design_parameters.setPredefinedType(Schema::IfcAlignmentHorizontalSegmentTypeEnum::IfcAlignmentHorizontalSegmentType_CIRCULARARC);
|
||||
|
||||
auto alignment_segment = file.create<Schema::IfcAlignmentSegment>();
|
||||
alignment_segment.setGlobalId(ifcopenshell::global_id());
|
||||
alignment_segment.setDesignParameters(design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for vertical profile gradient runs
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_gradient(typename Schema::IfcCartesianPoint* p, double slope, double length) {
|
||||
std::pair<typename Schema::IfcCurveSegment, typename Schema::IfcAlignmentSegment> create_gradient(hierarchy_helper<Schema>& file, const typename Schema::IfcCartesianPoint& p, double slope, double length) {
|
||||
// geometry
|
||||
auto parent_curve = new Schema::IfcLine(
|
||||
new Schema::IfcCartesianPoint(std::vector<double>({0, 0})),
|
||||
new Schema::IfcVector(new Schema::IfcDirection(std::vector<double>{1, 0}), 1.0));
|
||||
auto parent_curve = file.create<Schema::IfcLine>();
|
||||
parent_curve.setPnt(file.addDoublet<Schema::IfcCartesianPoint>(0.0, 0.0));
|
||||
auto vec = file.create<Schema::IfcVector>();
|
||||
vec.setOrientation(file.addDoublet<Schema::IfcDirection>(1.0, 0.0));
|
||||
vec.setMagnitude(1.0);
|
||||
parent_curve.setDir(vec);
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{sqrt(1 - slope * slope), slope})),
|
||||
new Schema::IfcLengthMeasure(0.0), // start
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
auto place2 = file.create<Schema::IfcAxis2Placement2D>();
|
||||
place2.setLocation(p);
|
||||
place2.setRefDirection(file.addDoublet<Schema::IfcDirection>(sqrt(1 - slope * slope), slope));
|
||||
|
||||
auto curve_segment = file.create<Schema::IfcCurveSegment>();
|
||||
curve_segment.setTransition(Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT);
|
||||
curve_segment.setPlacement(place2);
|
||||
curve_segment.setSegmentStart(file.addValue<Schema::IfcLengthMeasure>(0.0));
|
||||
curve_segment.setSegmentLength(file.addValue<Schema::IfcLengthMeasure>(length));
|
||||
curve_segment.setParentCurve(parent_curve);
|
||||
|
||||
// business logic
|
||||
auto design_parameters = new Schema::IfcAlignmentVerticalSegment(boost::none, boost::none, p->Coordinates()[0], length, p->Coordinates()[1], slope, slope, boost::none, Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
auto design_parameters = file.create<Schema::IfcAlignmentVerticalSegment>();
|
||||
design_parameters.setStartDistAlong(p.Coordinates()[0]);
|
||||
design_parameters.setHorizontalLength(length);
|
||||
design_parameters.setStartHeight(p.Coordinates()[1]);
|
||||
design_parameters.setStartGradient(slope);
|
||||
design_parameters.setEndGradient(slope);
|
||||
design_parameters.setPredefinedType(Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_CONSTANTGRADIENT);
|
||||
|
||||
auto alignment_segment = file.create<Schema::IfcAlignmentSegment>();
|
||||
alignment_segment.setGlobalId(ifcopenshell::global_id());
|
||||
alignment_segment.setDesignParameters(design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates geometry and business logic segments for vertical profile parabolic vertical curves
|
||||
std::pair<typename Schema::IfcCurveSegment*, typename Schema::IfcAlignmentSegment*> create_vcurve(typename Schema::IfcCartesianPoint* p, double start_slope, double end_slope, double length) {
|
||||
std::pair<typename Schema::IfcCurveSegment, typename Schema::IfcAlignmentSegment> create_vcurve(hierarchy_helper<Schema>& file, const typename Schema::IfcCartesianPoint& p, double start_slope, double end_slope, double length) {
|
||||
// geometry
|
||||
double A = p->Coordinates()[1];
|
||||
double A = p.Coordinates()[1];
|
||||
double B = start_slope;
|
||||
double C = (end_slope - start_slope) / (2 * length);
|
||||
|
||||
auto parent_curve = new Schema::IfcPolynomialCurve(
|
||||
new Schema::IfcAxis2Placement2D(new Schema::IfcCartesianPoint(std::vector<double>{0.0, 0.0}), new Schema::IfcDirection(std::vector<double>{1.0, 0.0})),
|
||||
std::vector<double>{0.0, 1.0},
|
||||
std::vector<double>{A, B, C},
|
||||
boost::none);
|
||||
auto place = file.create<Schema::IfcAxis2Placement2D>();
|
||||
place.setLocation(file.addDoublet<Schema::IfcCartesianPoint>(0.0, 0.0));
|
||||
place.setRefDirection(file.addDoublet<Schema::IfcDirection>(1.0, 0.0));
|
||||
|
||||
auto curve_segment = new Schema::IfcCurveSegment(
|
||||
Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT,
|
||||
new Schema::IfcAxis2Placement2D(p, new Schema::IfcDirection(std::vector<double>{sqrt(1 - start_slope * start_slope), start_slope})),
|
||||
new Schema::IfcLengthMeasure(0.0),
|
||||
new Schema::IfcLengthMeasure(length),
|
||||
parent_curve);
|
||||
auto parent_curve = file.create<Schema::IfcPolynomialCurve>();
|
||||
parent_curve.setPosition(place);
|
||||
parent_curve.setCoefficientsX(std::vector<double>{0.0, 1.0});
|
||||
parent_curve.setCoefficientsY(std::vector<double>{A, B, C});
|
||||
|
||||
auto place2 = file.create<Schema::IfcAxis2Placement2D>();
|
||||
place2.setLocation(p);
|
||||
place2.setRefDirection(file.addDoublet<Schema::IfcDirection>(sqrt(1 - start_slope * start_slope), start_slope));
|
||||
|
||||
auto curve_segment = file.create<Schema::IfcCurveSegment>();
|
||||
curve_segment.setTransition(Schema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT);
|
||||
curve_segment.setPlacement(place2);
|
||||
curve_segment.setSegmentStart(file.addValue<Schema::IfcLengthMeasure>(0.0));
|
||||
curve_segment.setSegmentLength(file.addValue<Schema::IfcLengthMeasure>(length));
|
||||
curve_segment.setParentCurve(parent_curve);
|
||||
|
||||
// business logic
|
||||
double k = (end_slope - start_slope) / length;
|
||||
auto design_parameters = new Schema::IfcAlignmentVerticalSegment(boost::none, boost::none, p->Coordinates()[0], length, p->Coordinates()[1], start_slope, end_slope, 1 / k, Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC);
|
||||
auto alignment_segment = new Schema::IfcAlignmentSegment(IfcParse::IfcGlobalId(), nullptr, boost::none, boost::none, boost::none, nullptr, nullptr, design_parameters);
|
||||
|
||||
auto design_parameters = file.create<Schema::IfcAlignmentVerticalSegment>();
|
||||
design_parameters.setStartDistAlong(p.Coordinates()[0]);
|
||||
design_parameters.setHorizontalLength(length);
|
||||
design_parameters.setStartHeight(p.Coordinates()[1]);
|
||||
design_parameters.setStartGradient(start_slope);
|
||||
design_parameters.setEndGradient(end_slope);
|
||||
design_parameters.setRadiusOfCurvature(1 / k);
|
||||
design_parameters.setPredefinedType(Schema::IfcAlignmentVerticalSegmentTypeEnum::IfcAlignmentVerticalSegmentType_PARABOLICARC);
|
||||
|
||||
auto alignment_segment = file.create<Schema::IfcAlignmentSegment>();
|
||||
alignment_segment.setGlobalId(ifcopenshell::global_id());
|
||||
alignment_segment.setDesignParameters(design_parameters);
|
||||
|
||||
return {curve_segment, alignment_segment};
|
||||
}
|
||||
|
||||
// creates representations for each IfcAlignmentSegment per CT 4.1.7.1.1.4
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Product_Shape/Product_Geometric_Representation/Alignment_Geometry/Alignment_Geometry_-_Segments/content.html
|
||||
void create_segment_representations(IfcHierarchyHelper<Schema>& file, Schema::IfcLocalPlacement* global_placement, Schema::IfcGeometricRepresentationSubContext* segment_axis_subcontext, typename aggregate_of<typename Schema::IfcSegment>::ptr curve_segments, typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr segments) {
|
||||
auto cs_iter = curve_segments->begin();
|
||||
auto s_iter = segments->begin();
|
||||
for (; cs_iter != curve_segments->end(); cs_iter++, s_iter++) {
|
||||
auto curve_segment = *cs_iter;
|
||||
auto alignment_segment = (*s_iter)->as<Schema::IfcAlignmentSegment>();
|
||||
void create_segment_representations(hierarchy_helper<Schema>& file, const Schema::IfcLocalPlacement& global_placement, const Schema::IfcGeometricRepresentationSubContext& segment_axis_subcontext, std::vector<Schema::IfcSegment>& curve_segments, std::vector<Schema::IfcObjectDefinition>& segments) {
|
||||
auto cs_iter = curve_segments.begin();
|
||||
auto s_iter = segments.begin();
|
||||
for (; cs_iter != curve_segments.end(); cs_iter++, s_iter++) {
|
||||
auto& curve_segment = *cs_iter;
|
||||
auto alignment_segment = (*s_iter).as<Schema::IfcAlignmentSegment>();
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
representation_items->push(curve_segment);
|
||||
auto axis_representation = file.create<Schema::IfcShapeRepresentation>();
|
||||
axis_representation.setContextOfItems(segment_axis_subcontext);
|
||||
axis_representation.setRepresentationIdentifier("Axis");
|
||||
axis_representation.setRepresentationType("Segment");
|
||||
axis_representation.setItems({curve_segment});
|
||||
|
||||
auto axis_representation = new Schema::IfcShapeRepresentation(segment_axis_subcontext, std::string("Axis"), std::string("Segment"), representation_items);
|
||||
file.addEntity(axis_representation);
|
||||
auto product = file.create<Schema::IfcProductDefinitionShape>();
|
||||
product.setRepresentations({axis_representation});
|
||||
|
||||
typename aggregate_of<typename Schema::IfcRepresentation>::ptr representations(new aggregate_of<typename Schema::IfcRepresentation>());
|
||||
representations->push(axis_representation);
|
||||
|
||||
auto product = new Schema::IfcProductDefinitionShape(boost::none, boost::none, representations);
|
||||
file.addEntity(product);
|
||||
|
||||
alignment_segment->setObjectPlacement(global_placement);
|
||||
alignment_segment->setRepresentation(product);
|
||||
alignment_segment.setObjectPlacement(global_placement);
|
||||
alignment_segment.setRepresentation(product);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
IfcHierarchyHelper<Schema> file;
|
||||
hierarchy_helper<Schema> file;
|
||||
|
||||
auto project = setup_project(file);
|
||||
|
||||
auto geometric_representation_context = file.getRepresentationContext(std::string("Model")); // creates the representation context if it doesn't already exist
|
||||
|
||||
auto axis_model_representation_subcontext = new Schema::IfcGeometricRepresentationSubContext(std::string("Axis"), std::string("Model"), geometric_representation_context, boost::none, Schema::IfcGeometricProjectionEnum::IfcGeometricProjection_MODEL_VIEW, boost::none);
|
||||
file.addEntity(axis_model_representation_subcontext);
|
||||
auto axis_model_representation_subcontext = file.create<Schema::IfcGeometricRepresentationSubContext>();
|
||||
axis_model_representation_subcontext.setContextIdentifier("Axis");
|
||||
axis_model_representation_subcontext.setContextType("Model");
|
||||
axis_model_representation_subcontext.setParentContext(geometric_representation_context);
|
||||
axis_model_representation_subcontext.setTargetView(Schema::IfcGeometricProjectionEnum::IfcGeometricProjection_MODEL_VIEW);
|
||||
|
||||
auto global_placement = file.addLocalPlacement();
|
||||
|
||||
@@ -251,86 +326,91 @@ int main() {
|
||||
double angle_4 = to_radian(289.0395);
|
||||
|
||||
// create containers to store the curve segments
|
||||
typename aggregate_of<typename Schema::IfcSegment>::ptr horizontal_curve_segments(new aggregate_of<typename Schema::IfcSegment>()); // geometry
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr horizontal_segments(new aggregate_of<typename Schema::IfcObjectDefinition>()); // business logic
|
||||
std::vector<Schema::IfcSegment> horizontal_curve_segments; // geometry
|
||||
std::vector<Schema::IfcObjectDefinition> horizontal_segments; // business logic
|
||||
|
||||
//
|
||||
// Build the horizontal alignment segments
|
||||
//
|
||||
|
||||
// POB to PC1
|
||||
auto curve_segment_1 = create_tangent(pob, angle_1, run_1);
|
||||
horizontal_curve_segments->push(curve_segment_1.first);
|
||||
horizontal_segments->push(curve_segment_1.second);
|
||||
auto curve_segment_1 = create_tangent(file, pob, angle_1, run_1);
|
||||
horizontal_curve_segments.push_back(curve_segment_1.first);
|
||||
horizontal_segments.push_back(curve_segment_1.second);
|
||||
|
||||
// Curve 1
|
||||
auto curve_segment_2 = create_hcurve(pc1, angle_1, rc_1, lc_1);
|
||||
horizontal_curve_segments->push(curve_segment_2.first);
|
||||
horizontal_segments->push(curve_segment_2.second);
|
||||
auto curve_segment_2 = create_hcurve(file, pc1, angle_1, rc_1, lc_1);
|
||||
horizontal_curve_segments.push_back(curve_segment_2.first);
|
||||
horizontal_segments.push_back(curve_segment_2.second);
|
||||
|
||||
// PT1 to PC2
|
||||
auto curve_segment_3 = create_tangent(pt1, angle_2, run_2);
|
||||
horizontal_curve_segments->push(curve_segment_3.first);
|
||||
horizontal_segments->push(curve_segment_3.second);
|
||||
auto curve_segment_3 = create_tangent(file, pt1, angle_2, run_2);
|
||||
horizontal_curve_segments.push_back(curve_segment_3.first);
|
||||
horizontal_segments.push_back(curve_segment_3.second);
|
||||
|
||||
// Curve 2
|
||||
auto curve_segment_4 = create_hcurve(pc2, angle_2, rc_2, lc_2);
|
||||
horizontal_curve_segments->push(curve_segment_4.first);
|
||||
horizontal_segments->push(curve_segment_4.second);
|
||||
auto curve_segment_4 = create_hcurve(file, pc2, angle_2, rc_2, lc_2);
|
||||
horizontal_curve_segments.push_back(curve_segment_4.first);
|
||||
horizontal_segments.push_back(curve_segment_4.second);
|
||||
|
||||
// PT2 to PC3
|
||||
auto curve_segment_5 = create_tangent(pt2, angle_3, run_3);
|
||||
horizontal_curve_segments->push(curve_segment_5.first);
|
||||
horizontal_segments->push(curve_segment_5.second);
|
||||
auto curve_segment_5 = create_tangent(file, pt2, angle_3, run_3);
|
||||
horizontal_curve_segments.push_back(curve_segment_5.first);
|
||||
horizontal_segments.push_back(curve_segment_5.second);
|
||||
|
||||
// Curve 3
|
||||
auto curve_segment_6 = create_hcurve(pc3, angle_3, rc_3, lc_3);
|
||||
horizontal_curve_segments->push(curve_segment_6.first);
|
||||
horizontal_segments->push(curve_segment_6.second);
|
||||
auto curve_segment_6 = create_hcurve(file, pc3, angle_3, rc_3, lc_3);
|
||||
horizontal_curve_segments.push_back(curve_segment_6.first);
|
||||
horizontal_segments.push_back(curve_segment_6.second);
|
||||
|
||||
// PT3 to POE
|
||||
auto curve_segment_7 = create_tangent(pt3, angle_4, run_4);
|
||||
horizontal_curve_segments->push(curve_segment_7.first);
|
||||
horizontal_segments->push(curve_segment_7.second);
|
||||
auto curve_segment_7 = create_tangent(file, pt3, angle_4, run_4);
|
||||
horizontal_curve_segments.push_back(curve_segment_7.first);
|
||||
horizontal_segments.push_back(curve_segment_7.second);
|
||||
|
||||
// Zero-length terminator segment
|
||||
auto terminator_segment = create_tangent(poe, angle_4, 0.0);
|
||||
terminator_segment.first->setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
horizontal_curve_segments->push(terminator_segment.first);
|
||||
horizontal_segments->push(terminator_segment.second);
|
||||
auto terminator_segment = create_tangent(file, poe, angle_4, 0.0);
|
||||
terminator_segment.first.setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
horizontal_curve_segments.push_back(terminator_segment.first);
|
||||
horizontal_segments.push_back(terminator_segment.second);
|
||||
|
||||
//
|
||||
// Create the horizontal alignment (IfcAlignmentHorizontal) and nest alignment segments
|
||||
//
|
||||
auto horizontal_alignment = new Schema::IfcAlignmentHorizontal(IfcParse::IfcGlobalId(), nullptr, std::string("Horizontal Alignment"), boost::none, boost::none, nullptr, nullptr);
|
||||
file.addEntity(horizontal_alignment);
|
||||
|
||||
auto nests_horizontal_segments = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests horizontal alignment segments with horizontal alignment"), horizontal_alignment, horizontal_segments);
|
||||
file.addEntity(nests_horizontal_segments);
|
||||
auto horizontal_alignment = file.create<Schema::IfcAlignmentHorizontal>();
|
||||
horizontal_alignment.setGlobalId(ifcopenshell::global_id());
|
||||
horizontal_alignment.setName("Example Alignment");
|
||||
|
||||
auto nests_horizontal_segments = file.create<Schema::IfcRelNests>();
|
||||
nests_horizontal_segments.setGlobalId(ifcopenshell::global_id());
|
||||
nests_horizontal_segments.setName("Nests horizontal alignment segments with horizontal alignment");
|
||||
nests_horizontal_segments.setRelatingObject(horizontal_alignment);
|
||||
nests_horizontal_segments.setRelatedObjects(horizontal_segments);
|
||||
|
||||
//
|
||||
// Create plan view footprint model representation for the horizontal alignment
|
||||
//
|
||||
|
||||
// start by defining a composite curve composed of the horizonal curve segments
|
||||
auto composite_curve = new Schema::IfcCompositeCurve(horizontal_curve_segments, false /*not self-intersecting*/);
|
||||
file.addEntity(composite_curve);
|
||||
|
||||
// the composite curve is a representation item
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr alignment_representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
alignment_representation_items->push(composite_curve);
|
||||
auto composite_curve = file.create<Schema::IfcCompositeCurve>();
|
||||
composite_curve.setSegments(horizontal_curve_segments);
|
||||
composite_curve.setSelfIntersect(false);
|
||||
|
||||
// create the footprint representation
|
||||
auto footprint_shape_representation = new Schema::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("FootPrint"), std::string("Curve2D"), alignment_representation_items);
|
||||
file.addEntity(footprint_shape_representation);
|
||||
|
||||
auto footprint_shape_representation = file.create<Schema::IfcShapeRepresentation>();
|
||||
footprint_shape_representation.setContextOfItems(axis_model_representation_subcontext);
|
||||
footprint_shape_representation.setRepresentationIdentifier("Footprint");
|
||||
footprint_shape_representation.setRepresentationType("Curve2D");
|
||||
// the composite curve is a representation item
|
||||
footprint_shape_representation.setItems({composite_curve});
|
||||
|
||||
//
|
||||
// Define vertical profile segments
|
||||
//
|
||||
|
||||
// create containers to store the curve segments
|
||||
typename aggregate_of<typename Schema::IfcSegment>::ptr vertical_curve_segments(new aggregate_of<typename Schema::IfcSegment>()); // geometry
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr vertical_segments(new aggregate_of<typename Schema::IfcObjectDefinition>()); // business logic
|
||||
std::vector<Schema::IfcSegment> vertical_curve_segments; // geometry
|
||||
std::vector<Schema::IfcObjectDefinition> vertical_segments; // business logic
|
||||
|
||||
// define key profile points
|
||||
auto vpob = file.addDoublet<Schema::IfcCartesianPoint>(0.0, 100.0); // beginning
|
||||
@@ -349,80 +429,86 @@ int main() {
|
||||
//
|
||||
|
||||
// Grade start to VPC1
|
||||
auto vertical_profile_segment_1 = create_gradient(vpob, 1.75 / 100, 1200);
|
||||
vertical_curve_segments->push(vertical_profile_segment_1.first);
|
||||
vertical_segments->push(vertical_profile_segment_1.second);
|
||||
auto vertical_profile_segment_1 = create_gradient(file, vpob, 1.75 / 100, 1200);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_1.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_1.second);
|
||||
|
||||
// Vertical Curve 1
|
||||
auto vertical_profile_segment_2 = create_vcurve(vpc1, 1.75 / 100, -1.0 / 100, 1600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_2.first);
|
||||
vertical_segments->push(vertical_profile_segment_2.second);
|
||||
auto vertical_profile_segment_2 = create_vcurve(file, vpc1, 1.75 / 100, -1.0 / 100, 1600);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_2.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_2.second);
|
||||
|
||||
// Grade VPT1 to VPC2
|
||||
auto vertical_profile_segment_3 = create_gradient(vpt1, -1.0 / 100, 1600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_3.first);
|
||||
vertical_segments->push(vertical_profile_segment_3.second);
|
||||
auto vertical_profile_segment_3 = create_gradient(file, vpt1, -1.0 / 100, 1600);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_3.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_3.second);
|
||||
|
||||
// Vertical Curve 2
|
||||
auto vertical_profile_segment_4 = create_vcurve(vpc2, -1.0 / 100, 2.0 / 100, 1200);
|
||||
vertical_curve_segments->push(vertical_profile_segment_4.first);
|
||||
vertical_segments->push(vertical_profile_segment_4.second);
|
||||
auto vertical_profile_segment_4 = create_vcurve(file, vpc2, -1.0 / 100, 2.0 / 100, 1200);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_4.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_4.second);
|
||||
|
||||
// Grade PVT2 to VPC3
|
||||
auto vertical_profile_segment_5 = create_gradient(vpt2, 2.0 / 100, 800);
|
||||
vertical_curve_segments->push(vertical_profile_segment_5.first);
|
||||
vertical_segments->push(vertical_profile_segment_5.second);
|
||||
auto vertical_profile_segment_5 = create_gradient(file, vpt2, 2.0 / 100, 800);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_5.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_5.second);
|
||||
|
||||
// Vertical Curve 3
|
||||
auto vertical_profile_segment_6 = create_vcurve(vpc3, 2.0 / 100, -2.0 / 100, 2000);
|
||||
vertical_curve_segments->push(vertical_profile_segment_6.first);
|
||||
vertical_segments->push(vertical_profile_segment_6.second);
|
||||
auto vertical_profile_segment_6 = create_vcurve(file, vpc3, 2.0 / 100, -2.0 / 100, 2000);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_6.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_6.second);
|
||||
|
||||
// Grade PVT3 to VPC4
|
||||
auto vertical_profile_segment_7 = create_gradient(vpt3, -2.0 / 100, 1000);
|
||||
vertical_curve_segments->push(vertical_profile_segment_7.first);
|
||||
vertical_segments->push(vertical_profile_segment_7.second);
|
||||
auto vertical_profile_segment_7 = create_gradient(file, vpt3, -2.0 / 100, 1000);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_7.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_7.second);
|
||||
|
||||
// Vertical Curve 4
|
||||
auto vertical_profile_segment_8 = create_vcurve(vpc4, -2.0 / 100, -0.5 / 100, 800);
|
||||
vertical_curve_segments->push(vertical_profile_segment_8.first);
|
||||
vertical_segments->push(vertical_profile_segment_8.second);
|
||||
auto vertical_profile_segment_8 = create_vcurve(file, vpc4, -2.0 / 100, -0.5 / 100, 800);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_8.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_8.second);
|
||||
|
||||
// Grade VPT4 to End
|
||||
auto vertical_profile_segment_9 = create_gradient(vpt4, -0.5 / 100, 2600);
|
||||
vertical_curve_segments->push(vertical_profile_segment_9.first);
|
||||
vertical_segments->push(vertical_profile_segment_9.second);
|
||||
auto vertical_profile_segment_9 = create_gradient(file, vpt4, -0.5 / 100, 2600);
|
||||
vertical_curve_segments.push_back(vertical_profile_segment_9.first);
|
||||
vertical_segments.push_back(vertical_profile_segment_9.second);
|
||||
|
||||
// Zero-length terminator
|
||||
auto vertical_terminator_segment = create_gradient(vpoe, -0.5 / 100, 0.0);
|
||||
vertical_terminator_segment.first->setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
vertical_curve_segments->push(vertical_terminator_segment.first);
|
||||
vertical_segments->push(vertical_terminator_segment.second);
|
||||
auto vertical_terminator_segment = create_gradient(file, vpoe, -0.5 / 100, 0.0);
|
||||
vertical_terminator_segment.first.setTransition(Schema::IfcTransitionCode::IfcTransitionCode_DISCONTINUOUS);
|
||||
vertical_curve_segments.push_back(vertical_terminator_segment.first);
|
||||
vertical_segments.push_back(vertical_terminator_segment.second);
|
||||
|
||||
//
|
||||
// Create the vertical alignment (IfcAlignmentVertical) and nest alignment segments
|
||||
//
|
||||
auto vertical_profile = new Schema::IfcAlignmentVertical(IfcParse::IfcGlobalId(), nullptr, std::string("Vertical Alignment"), boost::none, boost::none, nullptr, nullptr);
|
||||
file.addEntity(vertical_profile);
|
||||
auto vertical_profile = file.create<Schema::IfcAlignmentVertical>();
|
||||
vertical_profile.setGlobalId(ifcopenshell::global_id());
|
||||
vertical_profile.setName("Example Vertical Profile");
|
||||
|
||||
auto nests_vertical_segments = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, boost::none, std::string("Nests vertical alignment segments with vertical alignment"), vertical_profile, vertical_segments);
|
||||
file.addEntity(nests_vertical_segments);
|
||||
auto nests_vertical_segments = file.create<Schema::IfcRelNests>();
|
||||
nests_vertical_segments.setGlobalId(ifcopenshell::global_id());
|
||||
nests_vertical_segments.setName("Nests vertical alignment segments with vertical profile");
|
||||
nests_vertical_segments.setRelatingObject(vertical_profile);
|
||||
nests_vertical_segments.setRelatedObjects(vertical_segments);
|
||||
|
||||
//
|
||||
// Create profile view axis model representation for the vertical profile
|
||||
//
|
||||
|
||||
// start by defining a gradient curve composed of the vertical curve segments and associated with the horizontal composite curve
|
||||
auto gradient_curve = new Schema::IfcGradientCurve(vertical_curve_segments, false, composite_curve, nullptr);
|
||||
file.addEntity(gradient_curve);
|
||||
|
||||
// the gradient curve is a representation item
|
||||
typename aggregate_of<typename Schema::IfcRepresentationItem>::ptr profile_representation_items(new aggregate_of<typename Schema::IfcRepresentationItem>());
|
||||
profile_representation_items->push(gradient_curve);
|
||||
auto gradient_curve = file.create<Schema::IfcGradientCurve>();
|
||||
gradient_curve.setSegments(vertical_curve_segments);
|
||||
gradient_curve.setSelfIntersect(false);
|
||||
gradient_curve.setBaseCurve(composite_curve);
|
||||
|
||||
// create the axis representation
|
||||
auto axis3d_shape_representation = new Schema::IfcShapeRepresentation(axis_model_representation_subcontext, std::string("Axis"), std::string("Curve3D"), profile_representation_items);
|
||||
file.addEntity(axis3d_shape_representation);
|
||||
auto axis3d_shape_representation = file.create<Schema::IfcShapeRepresentation>();
|
||||
axis3d_shape_representation.setContextOfItems(axis_model_representation_subcontext);
|
||||
axis3d_shape_representation.setRepresentationIdentifier("Axis");
|
||||
axis3d_shape_representation.setRepresentationType("Curve3D");
|
||||
// the gradient curve is a representation item
|
||||
axis3d_shape_representation.setItems({gradient_curve});
|
||||
|
||||
// create axis representations for each segment
|
||||
create_segment_representations(file, global_placement, axis_model_representation_subcontext, horizontal_curve_segments, horizontal_segments);
|
||||
@@ -432,38 +518,39 @@ int main() {
|
||||
// Create the IfcAlignment
|
||||
//
|
||||
|
||||
// the alignment has two representations, a plan view footprint and a 3d curve
|
||||
typename aggregate_of<typename Schema::IfcRepresentation>::ptr alignment_representations(new aggregate_of<typename Schema::IfcRepresentation>());
|
||||
alignment_representations->push(footprint_shape_representation); // 2D footprint
|
||||
alignment_representations->push(axis3d_shape_representation); // 3D curve
|
||||
|
||||
// create the alignment product definition
|
||||
auto alignment_product = new Schema::IfcProductDefinitionShape(std::string("Alignment Product Definition Shape"), boost::none, alignment_representations);
|
||||
auto alignment_product = file.create<Schema::IfcProductDefinitionShape>();
|
||||
alignment_product.setName("Alignment Product Definition Shape");
|
||||
// the alignment has two representations, a plan view footprint and a 3d curve
|
||||
alignment_product.setRepresentations({footprint_shape_representation, axis3d_shape_representation});
|
||||
|
||||
// create the alignment
|
||||
auto alignment = new Schema::IfcAlignment(IfcParse::IfcGlobalId(), nullptr, std::string("Example Alignment"), boost::none, boost::none, global_placement, alignment_product, boost::none);
|
||||
file.addEntity(alignment);
|
||||
auto alignment = file.create<Schema::IfcAlignment>();
|
||||
alignment.setGlobalId(ifcopenshell::global_id());
|
||||
alignment.setName("Example Alignment");
|
||||
alignment.setObjectPlacement(global_placement);
|
||||
alignment.setRepresentation(alignment_product);
|
||||
|
||||
// Nest the IfcAlignmentHorizontal and IfcAlignmentVertical with the IfcAlignment to complete the business logic
|
||||
// 4.1.4.4.1 Alignments nest horizontal and vertical layouts
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Nesting/Alignment_Layouts/content.html
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr alignment_layout_list(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
alignment_layout_list->push(horizontal_alignment);
|
||||
alignment_layout_list->push(vertical_profile);
|
||||
|
||||
auto nests_alignment_layouts = new Schema::IfcRelNests(IfcParse::IfcGlobalId(), nullptr, std::string("Nest horizontal and vertical alignment layouts with the alignment"), boost::none, alignment, alignment_layout_list);
|
||||
file.addEntity(nests_alignment_layouts);
|
||||
|
||||
auto nests_alignment_layouts = file.create<Schema::IfcRelNests>();
|
||||
nests_alignment_layouts.setGlobalId(ifcopenshell::global_id());
|
||||
nests_alignment_layouts.setName("Nest horizontal and vertical alignment layouts with the alignment");
|
||||
nests_alignment_layouts.setRelatingObject(alignment);
|
||||
nests_alignment_layouts.setRelatedObjects({horizontal_alignment, vertical_profile});
|
||||
|
||||
// Define the relationship with the project
|
||||
|
||||
// IFC 4.1.4.1.1 "Every IfcAlignment must be related to IfcProject using the IfcRelAggregates relationship"
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Aggregation/Alignment_Aggregation_To_Project/content.html
|
||||
// IfcProject <-> IfcRelAggregates <-> IfcAlignment
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr list_of_alignments_in_project(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
list_of_alignments_in_project->push(alignment);
|
||||
auto aggregate_alignments_with_project = new Schema::IfcRelAggregates(IfcParse::IfcGlobalId(), nullptr, std::string("Alignments in project"), boost::none, project, list_of_alignments_in_project);
|
||||
file.addEntity(aggregate_alignments_with_project);
|
||||
|
||||
auto aggregate_alignments_with_project = file.create<Schema::IfcRelAggregates>();
|
||||
aggregate_alignments_with_project.setGlobalId(ifcopenshell::global_id());
|
||||
aggregate_alignments_with_project.setName("Alignments in project");
|
||||
aggregate_alignments_with_project.setRelatingObject(project);
|
||||
aggregate_alignments_with_project.setRelatedObjects({alignment});
|
||||
|
||||
// Define the spatial structure of the alignment with respect to the site
|
||||
|
||||
// IFC 4.1.5.1 alignment is referenced in spatial structure of an IfcSpatialElement. In this case IfcSite is the highest level IfcSpatialElement
|
||||
@@ -471,24 +558,26 @@ int main() {
|
||||
// IfcSite <-> IfcRelReferencedInSpatialStructure <-> IfcAlignment
|
||||
// This means IfcAlignment is not part of the IfcSite (it is not an aggregate component) but instead IfcAlignment is used within
|
||||
// the IfcSite by reference. This implies an IfcAlignment can traverse many IfcSite instances within an IfcProject
|
||||
typename Schema::IfcSpatialReferenceSelect::list::ptr list_alignments_referenced_in_site(new Schema::IfcSpatialReferenceSelect::list);
|
||||
list_alignments_referenced_in_site->push(alignment);
|
||||
std::vector<Schema::IfcSpatialReferenceSelect> list_alignments_referenced_in_site{alignment};
|
||||
|
||||
// this alignment traverse 3 bridge sites.
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
std::ostringstream os;
|
||||
os << "Site of Bridge " << i;
|
||||
auto site = file.addSite(project, nullptr);
|
||||
site->setName(os.str());
|
||||
auto site = file.addSite(project);
|
||||
site.setName(os.str());
|
||||
|
||||
std::ostringstream description;
|
||||
description << "Alignments referenced into the spatial structure of Bridge Site " << i;
|
||||
|
||||
auto rel_referenced_in_spatial_structure = new Schema::IfcRelReferencedInSpatialStructure(IfcParse::IfcGlobalId(), nullptr, boost::none, description.str(), list_alignments_referenced_in_site, site);
|
||||
file.addEntity(rel_referenced_in_spatial_structure);
|
||||
auto rel_referenced_in_spatial_structure = file.create<Schema::IfcRelReferencedInSpatialStructure>();
|
||||
rel_referenced_in_spatial_structure.setGlobalId(ifcopenshell::global_id());
|
||||
rel_referenced_in_spatial_structure.setDescription(description.str());
|
||||
rel_referenced_in_spatial_structure.setRelatedElements(list_alignments_referenced_in_site);
|
||||
rel_referenced_in_spatial_structure.setRelatingStructure(site);
|
||||
}
|
||||
|
||||
// That's it - save the model to a file
|
||||
std::ofstream ofs("IfcAlignment.ifc");
|
||||
std::ofstream ofs("FHWA_Bridge_Geometry_Alignment_Example.ifc");
|
||||
ofs << file;
|
||||
}
|
||||
|
||||
+374
-324
@@ -36,12 +36,11 @@
|
||||
#include <Precision.hxx>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/macros.h"
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcBaseClass.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/macros.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
#include "ifcgeom/Serialization/Serialization.h"
|
||||
#include "../ifcgeom/Serialization/Serialization.h"
|
||||
|
||||
#if USE_VLD
|
||||
#include <vld.h>
|
||||
@@ -50,7 +49,7 @@
|
||||
using namespace std::string_literals;
|
||||
|
||||
// Some convenience typedefs and definitions.
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
typedef std::pair<double, double> XY;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
@@ -59,235 +58,276 @@ void createGroundShape(TopoDS_Shape& shape);
|
||||
|
||||
int main() {
|
||||
|
||||
// The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several
|
||||
// convenience functions for working with geometry in IFC files.
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname("IfcOpenHouse.ifc");
|
||||
// The hierarchy_helper is a subclass of the regular file that provides several
|
||||
// convenience functions for working with geometry in IFC files.
|
||||
hierarchy_helper<IfcSchema> file;
|
||||
file.header().file_name().setname("IfcOpenHouse.ifc");
|
||||
|
||||
// Start by adding a wall to the file, initially leaving most attributes blank.
|
||||
IfcSchema::IfcWallStandardCase* south_wall = new IfcSchema::IfcWallStandardCase(
|
||||
guid(), // GlobalId
|
||||
0, // OwnerHistory
|
||||
"South wall"s, // Name
|
||||
null, // Description
|
||||
null, // ObjectType
|
||||
0, // ObjectPlacement
|
||||
0, // Representation
|
||||
null // Tag
|
||||
// Start by adding a wall to the file, initially leaving most attributes blank.
|
||||
auto south_wall = file.create<IfcSchema::IfcWallStandardCase>();
|
||||
south_wall.setGlobalId(guid());
|
||||
south_wall.setName("South wall");
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
south_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD);
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(south_wall);
|
||||
file.addBuildingProduct(south_wall);
|
||||
|
||||
// By adding a wall, a hierarchy has been automatically created that consists of the following
|
||||
// structure: IfcProject > IfcSite > IfcBuilding > IfcBuildingStorey > IfcWall
|
||||
// By adding a wall, a hierarchy has been automatically created that consists of the following
|
||||
// structure: IfcProject > IfcSite > IfcBuilding > IfcBuildingStorey > IfcWall
|
||||
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// project, which has been created automatically.
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("IfcOpenHouse"s);
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// project, which has been created automatically.
|
||||
file.getSingle<IfcSchema::IfcProject>().setName("IfcOpenHouse"s);
|
||||
|
||||
// An IfcOwnerHistory has been initialized as well, which should be assigned to the wall.
|
||||
south_wall->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
// An IfcOwnerHistory has been initialized as well, which should be assigned to the wall.
|
||||
south_wall.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
|
||||
// The wall will be shaped as a box, with the dimensions specified in millimeters. The resulting
|
||||
// product definition will consist of both a body representation as well as an axis representation
|
||||
// that runs over the centerline of the box in the X-axis.
|
||||
IfcSchema::IfcProductDefinitionShape* south_wall_shape = file.addAxisBox(10000, 360, 3000);
|
||||
// The wall will be shaped as a box, with the dimensions specified in millimeters. The resulting
|
||||
// product definition will consist of both a body representation as well as an axis representation
|
||||
// that runs over the centerline of the box in the X-axis.
|
||||
auto south_wall_shape = file.addAxisBox(10000, 360, 3000);
|
||||
|
||||
// Obtain a reference to the placement of the IfcBuildingStorey in order to create a hierarchy
|
||||
// of placements for the products
|
||||
IfcSchema::IfcObjectPlacement* storey_placement = file.getSingle<IfcSchema::IfcBuildingStorey>()->ObjectPlacement();
|
||||
// Obtain a reference to the placement of the IfcBuildingStorey in order to create a hierarchy
|
||||
// of placements for the products
|
||||
auto storey_placement = file.getSingle<IfcSchema::IfcBuildingStorey>().ObjectPlacement();
|
||||
|
||||
// The shape has to be assigned to the representation of the wall and is placed at the origin
|
||||
// of the coordinate system.
|
||||
south_wall->setRepresentation(south_wall_shape);
|
||||
south_wall->setObjectPlacement(file.addLocalPlacement(storey_placement));
|
||||
// The shape has to be assigned to the representation of the wall and is placed at the origin
|
||||
// of the coordinate system.
|
||||
south_wall.setRepresentation(south_wall_shape);
|
||||
south_wall.setObjectPlacement(file.addLocalPlacement(storey_placement));
|
||||
|
||||
// A pale white colour is assigned to the wall.
|
||||
IfcSchema::IfcPresentationStyleAssignment* wall_colour = setSurfaceColour(file, south_wall_shape, 0.75, 0.73, 0.68);
|
||||
// A pale white colour is assigned to the wall.
|
||||
auto wall_colour = setSurfaceColour(file, south_wall_shape, 0.75, 0.73, 0.68);
|
||||
|
||||
// Now create a footing for the wall to rest on.
|
||||
IfcSchema::IfcFooting* footing = new IfcSchema::IfcFooting(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
"Footing"s, null, null, 0, 0, null, IfcSchema::IfcFootingTypeEnum::IfcFootingType_STRIP_FOOTING);
|
||||
// Now create a footing for the wall to rest on.
|
||||
auto footing = file.create<IfcSchema::IfcFooting>();
|
||||
footing.setGlobalId(guid());
|
||||
footing.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
footing.setName("Footing");
|
||||
footing.setPredefinedType(IfcSchema::IfcFootingTypeEnum::IfcFootingType_STRIP_FOOTING);
|
||||
|
||||
file.addBuildingProduct(footing);
|
||||
file.addBuildingProduct(footing);
|
||||
|
||||
// The footing will span the entire floor plan of our building. The IfcRepresentationContext is
|
||||
// something that has been created automatically as well, but representations could have been
|
||||
// assigned to a specific context, for example to add a two dimensional plan representation as well.
|
||||
footing->setRepresentation(file.addBox(10100, 5460, 2000));
|
||||
footing->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000));
|
||||
// The footing will have a dark gray colour
|
||||
IfcSchema::IfcPresentationStyleAssignment* footing_colour = setSurfaceColour(file,footing->Representation(), 0.26, 0.22, 0.18);
|
||||
// The footing will span the entire floor plan of our building. The IfcRepresentationContext is
|
||||
// something that has been created automatically as well, but representations could have been
|
||||
// assigned to a specific context, for example to add a two dimensional plan representation as well.
|
||||
footing.setRepresentation(file.addBox(10100, 5460, 2000));
|
||||
footing.setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000));
|
||||
// The footing will have a dark gray colour
|
||||
auto footing_colour = setSurfaceColour(file, footing.Representation(), 0.26, 0.22, 0.18);
|
||||
|
||||
// IFC has two ways to apply boolean operations to geometry. IfcBooleanResults are commonly used
|
||||
// to clip geometry to a surface, for example to a slanted roof. For openings that are filled
|
||||
// with another element, for example a door or a window, an IfcOpeningElement is used instead.
|
||||
// IFC has two ways to apply boolean operations to geometry. IfcBooleanResults are commonly used
|
||||
// to clip geometry to a surface, for example to a slanted roof. For openings that are filled
|
||||
// with another element, for example a door or a window, an IfcOpeningElement is used instead.
|
||||
|
||||
// An opening element is created with rectangular geometry:
|
||||
IfcSchema::IfcOpeningElement* west_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(south_wall->ObjectPlacement(), -2500, 0, 400),
|
||||
file.addBox(6000, 3630, 1600), null
|
||||
// An opening element is created with rectangular geometry:
|
||||
auto west_opening = file.create<IfcSchema::IfcOpeningElement>();
|
||||
west_opening.setGlobalId(guid());
|
||||
west_opening.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
west_opening.setObjectPlacement(file.addLocalPlacement(south_wall.ObjectPlacement(), -2500, 0, 400));
|
||||
west_opening.setRepresentation(file.addBox(6000, 3630, 1600));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
|
||||
west_opening.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING);
|
||||
#endif
|
||||
);
|
||||
file.addEntity(west_opening);
|
||||
|
||||
// Relate the opening element to the wall.
|
||||
IfcSchema::IfcRelVoidsElement* void_element = new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, south_wall, west_opening);
|
||||
file.addEntity(void_element);
|
||||
// Relate the opening element to the wall.
|
||||
auto void_element = file.create<IfcSchema::IfcRelVoidsElement>();
|
||||
void_element.setGlobalId(guid());
|
||||
void_element.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
void_element.setRelatingBuildingElement(south_wall);
|
||||
void_element.setRelatedOpeningElement(west_opening);
|
||||
|
||||
// Now create an additional opening
|
||||
IfcSchema::IfcOpeningElement* south_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(storey_placement, 3000, 0, 400),
|
||||
file.addBox(1860, 3000, 1600), null
|
||||
// Now create an additional opening
|
||||
auto south_opening = file.create<IfcSchema::IfcOpeningElement>();
|
||||
south_opening.setGlobalId(guid());
|
||||
south_opening.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
south_opening.setObjectPlacement(file.addLocalPlacement(storey_placement, 3000, 0, 400));
|
||||
south_opening.setRepresentation(file.addBox(1860, 3000, 1600));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
|
||||
south_opening.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING);
|
||||
#endif
|
||||
);
|
||||
file.addEntity(south_opening);
|
||||
file.addEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, south_wall, south_opening));
|
||||
|
||||
// Create a roof element that will consist of two slabs:
|
||||
IfcSchema::IfcRoof* roof = new IfcSchema::IfcRoof(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), "Roof"s, null, null,
|
||||
file.addLocalPlacement(storey_placement), 0, null, IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
|
||||
|
||||
// The roof geometry is slanted 45 degrees by specifying a direction for the box extrusion
|
||||
IfcSchema::IfcShapeRepresentation* roof_rep = file.addEmptyRepresentation();
|
||||
file.addBox(roof_rep, 10200, 360, sqrt(2.0*2900*2900), 0, file.addPlacement3d(0, 0, 0, 0, 1, 0),
|
||||
file.addTriplet<IfcSchema::IfcDirection>(0, -sqrt(0.5), sqrt(0.5)));
|
||||
// Relate the opening element to the wall.
|
||||
auto void_element2 = file.create<IfcSchema::IfcRelVoidsElement>();
|
||||
void_element2.setGlobalId(guid());
|
||||
void_element2.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
void_element2.setRelatingBuildingElement(south_wall);
|
||||
void_element2.setRelatedOpeningElement(south_opening);
|
||||
|
||||
// CV-2x3-144: Roofs are aggregates and shall have at least one contained element and no own geometry
|
||||
IfcSchema::IfcSlab* south_roof_part = new IfcSchema::IfcSlab(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), "South roof"s,
|
||||
null, null, 0, 0, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
|
||||
// The geometry is instantiated by using IfcMappedItems. This way geometry definitions can
|
||||
// be reused while maintaining the cardinality constraint that the ShapeOfProduct relation
|
||||
// imposes on the IfcProductDefinitionShape. Note that this constrained is lifted in IFC4.
|
||||
south_roof_part->setRepresentation(file.addMappedItem(roof_rep));
|
||||
south_roof_part->setObjectPlacement(file.addLocalPlacement(roof->ObjectPlacement(), 0, -400, 2700));
|
||||
|
||||
// The same roof geometry is re-used on the north side of the roof, by inverting the X-axis of
|
||||
// the local placement the roof is rotated 180 degrees around the Z-axis
|
||||
IfcSchema::IfcSlab* north_roof_part = new IfcSchema::IfcSlab(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), "North roof"s,
|
||||
null, null, 0, 0, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
north_roof_part->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
north_roof_part->setRepresentation(file.addMappedItem(roof_rep));
|
||||
north_roof_part->setObjectPlacement(file.addLocalPlacement(roof->ObjectPlacement(), 0, 5400, 2700, 0, 0, 1, -1, 0, 0));
|
||||
|
||||
IfcSchema::IfcObjectDefinition::list::ptr roof_parts(new IfcSchema::IfcObjectDefinition::list);
|
||||
roof_parts->push(south_roof_part);
|
||||
roof_parts->push(north_roof_part);
|
||||
IfcSchema::IfcRelDecomposes* roof_decomposition = new IfcSchema::IfcRelAggregates(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, roof, roof_parts);
|
||||
file.addEntity(roof_decomposition);
|
||||
|
||||
file.addBuildingProduct(south_roof_part);
|
||||
file.addBuildingProduct(north_roof_part);
|
||||
file.addBuildingProduct(roof);
|
||||
|
||||
setSurfaceColour(file, roof_rep, 0.24, 0.08, 0.04);
|
||||
|
||||
// Copy the south wall to the north
|
||||
IfcSchema::IfcWallStandardCase* north_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), "North wall"s,
|
||||
null, null, file.addLocalPlacement(storey_placement, 0, 5000, 0), file.addAxisBox(10000, 360, 3000), null
|
||||
// Create a roof element that will consist of two slabs:
|
||||
auto roof = file.create<IfcSchema::IfcRoof>();
|
||||
roof.setGlobalId(guid());
|
||||
roof.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
roof.setName("Roof");
|
||||
roof.setObjectPlacement(file.addLocalPlacement(storey_placement));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(north_wall);
|
||||
setSurfaceColour(file,north_wall->Representation(), wall_colour);
|
||||
roof.setPredefinedType(IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
|
||||
#else
|
||||
roof.setShapeType(IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
|
||||
#endif
|
||||
|
||||
// Two identical representations are created for the two remaining walls. Mapped items
|
||||
// are not used, because it is not allowed by the standard for wall body representations.
|
||||
// MappedItems are not allowed for Axis representations as per CV-2x3-161
|
||||
IfcSchema::IfcProductDefinitionShape* clipped_wall_body_reps[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
IfcSchema::IfcShapeRepresentation* body = file.addEmptyRepresentation();
|
||||
file.addBox(body, 5000, 360, 6000);
|
||||
// The wall geometry is clipped using two IfcHalfSpaceSolids, created from an
|
||||
// 'axis 3d placement' that specifies the plane against which the geometry is clipped.
|
||||
file.clipRepresentation(body, file.addPlacement3d(-2500, 0, 3000, -1, 0, 1), false);
|
||||
file.clipRepresentation(body, file.addPlacement3d(2500, 0, 3000, 1, 0, 1), false);
|
||||
setSurfaceColour(file, body, wall_colour);
|
||||
// The roof geometry is slanted 45 degrees by specifying a direction for the box extrusion
|
||||
auto roof_rep = file.addEmptyRepresentation();
|
||||
file.addBox(roof_rep, 10200, 360, sqrt(2.0 * 2900 * 2900), IfcSchema::IfcAxis2Placement2D{}, file.addPlacement3d(0, 0, 0, 0, 1, 0), file.addTriplet<IfcSchema::IfcDirection>(0, -sqrt(0.5), sqrt(0.5)));
|
||||
|
||||
IfcSchema::IfcShapeRepresentation* axis = file.addEmptyRepresentation("Axis", "Curve2D");
|
||||
file.addAxis(axis, 5000);
|
||||
// CV-2x3-144: Roofs are aggregates and shall have at least one contained element and no own geometry
|
||||
auto south_roof_part = file.create<IfcSchema::IfcSlab>();
|
||||
south_roof_part.setGlobalId(guid());
|
||||
south_roof_part.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
south_roof_part.setName("South roof");
|
||||
south_roof_part.setPredefinedType(IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list);
|
||||
reps->push(body);
|
||||
reps->push(axis);
|
||||
clipped_wall_body_reps[i] = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
}
|
||||
// The geometry is instantiated by using IfcMappedItems. This way geometry definitions can
|
||||
// be reused while maintaining the cardinality constraint that the ShapeOfProduct relation
|
||||
// imposes on the IfcProductDefinitionShape. Note that this constrained is lifted in IFC4.
|
||||
south_roof_part.setRepresentation(file.addMappedItem(roof_rep));
|
||||
south_roof_part.setObjectPlacement(file.addLocalPlacement(roof.ObjectPlacement(), 0, -400, 2700));
|
||||
|
||||
// Now create a wall on the east of the building, again starting with just a box shape
|
||||
IfcSchema::IfcWallStandardCase* east_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
"East wall"s, null, null, file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0), clipped_wall_body_reps[0], null
|
||||
// The same roof geometry is re-used on the north side of the roof, by inverting the X-axis of
|
||||
// the local placement the roof is rotated 180 degrees around the Z-axis
|
||||
auto north_roof_part = file.create<IfcSchema::IfcSlab>();
|
||||
north_roof_part.setGlobalId(guid());
|
||||
north_roof_part.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
north_roof_part.setName("North roof");
|
||||
north_roof_part.setPredefinedType(IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF);
|
||||
|
||||
north_roof_part.setRepresentation(file.addMappedItem(roof_rep));
|
||||
north_roof_part.setObjectPlacement(file.addLocalPlacement(roof.ObjectPlacement(), 0, 5400, 2700, 0, 0, 1, -1, 0, 0));
|
||||
|
||||
{
|
||||
auto rel = file.create<IfcSchema::IfcRelAggregates>();
|
||||
rel.setGlobalId(guid());
|
||||
rel.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
rel.setRelatingObject(roof);
|
||||
rel.setRelatedObjects({south_roof_part, north_roof_part});
|
||||
}
|
||||
|
||||
file.addBuildingProduct(south_roof_part);
|
||||
file.addBuildingProduct(north_roof_part);
|
||||
file.addBuildingProduct(roof);
|
||||
|
||||
setSurfaceColour(file, roof_rep, 0.24, 0.08, 0.04);
|
||||
|
||||
// Copy the south wall to the north
|
||||
auto north_wall = file.create<IfcSchema::IfcWallStandardCase>();
|
||||
north_wall.setGlobalId(guid());
|
||||
north_wall.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
north_wall.setName("North wall");
|
||||
north_wall.setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 5000, 0));
|
||||
north_wall.setRepresentation(file.addAxisBox(10000, 360, 3000));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(east_wall);
|
||||
north_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD);
|
||||
#endif
|
||||
|
||||
// The east wall is copied to the west location of the house
|
||||
IfcSchema::IfcWallStandardCase* west_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
"West wall"s, null, null, file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0), clipped_wall_body_reps[1], null
|
||||
file.addBuildingProduct(north_wall);
|
||||
setSurfaceColour(file, north_wall.Representation(), wall_colour);
|
||||
|
||||
// Two identical representations are created for the two remaining walls. Mapped items
|
||||
// are not used, because it is not allowed by the standard for wall body representations.
|
||||
// MappedItems are not allowed for Axis representations as per CV-2x3-161
|
||||
IfcSchema::IfcProductDefinitionShape clipped_wall_body_reps[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto body = file.addEmptyRepresentation();
|
||||
file.addBox(body, 5000, 360, 6000);
|
||||
// The wall geometry is clipped using two IfcHalfSpaceSolids, created from an
|
||||
// 'axis 3d placement' that specifies the plane against which the geometry is clipped.
|
||||
file.clipRepresentation(body, file.addPlacement3d(-2500, 0, 3000, -1, 0, 1), false);
|
||||
file.clipRepresentation(body, file.addPlacement3d(2500, 0, 3000, 1, 0, 1), false);
|
||||
setSurfaceColour(file, body, wall_colour);
|
||||
|
||||
auto axis = file.addEmptyRepresentation("Axis", "Curve2D");
|
||||
file.addAxis(axis, 5000);
|
||||
|
||||
clipped_wall_body_reps[i] = file.create<IfcSchema::IfcProductDefinitionShape>();
|
||||
clipped_wall_body_reps[i].setRepresentations({body, axis});
|
||||
}
|
||||
|
||||
// Now create a wall on the east of the building, again starting with just a box shape
|
||||
auto east_wall = file.create<IfcSchema::IfcWallStandardCase>();
|
||||
east_wall.setGlobalId(guid());
|
||||
east_wall.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
east_wall.setName("East wall");
|
||||
east_wall.setObjectPlacement(file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0));
|
||||
east_wall.setRepresentation(clipped_wall_body_reps[0]);
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(west_wall);
|
||||
east_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD);
|
||||
#endif
|
||||
|
||||
// The west wall is assigned an opening element we created for the south wall, opening elements are
|
||||
// not shared across building elements, even if they share the same representation. Hence, the east
|
||||
// wall will not feature this opening.
|
||||
// NB: an Opening Element can only be used to create a single void within a single Element, as per:
|
||||
// http://www.buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcproductextension/lexical/ifcfeatureelementsubtraction.htm
|
||||
file.addBuildingProduct(east_wall);
|
||||
|
||||
// Not all viewers support opening elements with mapped representations, hence an exact copy of the
|
||||
// same subtraction box is instantiated for the otherwise identical opening element.
|
||||
IfcSchema::IfcOpeningElement* west_opening_copy = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(west_wall->ObjectPlacement(), 2500, -2500+4820, 400, 0, 0, 1, 0, 1, 0),
|
||||
file.addBox(6000, 3630, 1600), null
|
||||
// The east wall is copied to the west location of the house
|
||||
auto west_wall = file.create<IfcSchema::IfcWallStandardCase>();
|
||||
west_wall.setGlobalId(guid());
|
||||
west_wall.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
west_wall.setName("West wall");
|
||||
west_wall.setObjectPlacement(file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0));
|
||||
west_wall.setRepresentation(clipped_wall_body_reps[1]);
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
|
||||
#endif
|
||||
);
|
||||
file.addEntity(west_opening_copy);
|
||||
file.addEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, west_wall, west_opening_copy));
|
||||
|
||||
west_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD);
|
||||
#endif
|
||||
|
||||
file.addBuildingProduct(west_wall);
|
||||
|
||||
// The west wall is assigned an opening element we created for the south wall, opening elements are
|
||||
// not shared across building elements, even if they share the same representation. Hence, the east
|
||||
// wall will not feature this opening.
|
||||
// NB: an Opening Element can only be used to create a single void within a single Element, as per:
|
||||
// http://www.buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcproductextension/lexical/ifcfeatureelementsubtraction.htm
|
||||
|
||||
// Not all viewers support opening elements with mapped representations, hence an exact copy of the
|
||||
// same subtraction box is instantiated for the otherwise identical opening element.
|
||||
auto west_opening_copy = file.create<IfcSchema::IfcOpeningElement>();
|
||||
west_opening_copy.setGlobalId(guid());
|
||||
west_opening_copy.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
west_opening_copy.setObjectPlacement(file.addLocalPlacement(west_wall.ObjectPlacement(), 2500, -2500 + 4820, 400, 0, 0, 1, 0, 1, 0));
|
||||
west_opening_copy.setRepresentation(file.addBox(6000, 3630, 1600));
|
||||
#ifdef USE_IFC4
|
||||
west_opening_copy.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING);
|
||||
#endif
|
||||
|
||||
{
|
||||
auto rel = file.create<IfcSchema::IfcRelVoidsElement>();
|
||||
rel.setGlobalId(guid());
|
||||
rel.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
rel.setRelatingBuildingElement(west_wall);
|
||||
rel.setRelatedOpeningElement(west_opening_copy);
|
||||
}
|
||||
|
||||
// Up until now we have only used simple extrusions for the creation of the geometry. For the
|
||||
// ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. The surface
|
||||
// will be tessellated using the deflection specified.
|
||||
TopoDS_Shape shape;
|
||||
createGroundShape(shape);
|
||||
IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(STRINGIFY(IfcSchema), shape, 100.)->as<IfcSchema::IfcProductDefinitionShape>();
|
||||
file.getSingle<IfcSchema::IfcSite>()->setRepresentation(ground_representation);
|
||||
auto ground_representation = IfcGeom::tesselate(file, shape, 100.).as<IfcSchema::IfcProductDefinitionShape>();
|
||||
file.getSingle<IfcSchema::IfcSite>().setRepresentation(ground_representation);
|
||||
|
||||
GProp_GProps prop;
|
||||
BRepGProp::SurfaceProperties(shape, prop);
|
||||
const double site_area = prop.Mass() / 1000 / 1000;
|
||||
|
||||
IfcSchema::IfcProperty::list::ptr properties(new IfcSchema::IfcProperty::list);
|
||||
properties->push(new IfcSchema::IfcPropertySingleValue("TotalArea", null, new IfcSchema::IfcAreaMeasure(site_area), 0));
|
||||
IfcSchema::IfcPropertySet* pset = new IfcSchema::IfcPropertySet(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), "Pset_SiteCommon"s, null, properties);
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcObjectDefinition::list::ptr related_objs(new IfcSchema::IfcObjectDefinition::list);
|
||||
#else
|
||||
IfcSchema::IfcObject::list::ptr related_objs(new IfcSchema::IfcObject::list);
|
||||
#endif
|
||||
related_objs->push(file.getSingle<IfcSchema::IfcSite>());
|
||||
IfcSchema::IfcRelDefinesByProperties* site_prop = new IfcSchema::IfcRelDefinesByProperties(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, related_objs, pset);
|
||||
file.addEntity(site_prop);
|
||||
auto total_area = file.create<IfcSchema::IfcPropertySingleValue>();
|
||||
total_area.setName("TotalArea");
|
||||
auto area = file.create<IfcSchema::IfcAreaMeasure>();
|
||||
area.set_attribute_value(0, site_area);
|
||||
total_area.setNominalValue(area);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr ground_reps = file.getSingle<IfcSchema::IfcSite>()->Representation()->Representations();
|
||||
for (IfcSchema::IfcRepresentation::list::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
|
||||
(*it)->setContextOfItems(file.getRepresentationContext("Model"));
|
||||
auto pset = file.create<IfcSchema::IfcPropertySet>();
|
||||
pset.setGlobalId(guid());
|
||||
pset.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
pset.setName("Pset_SiteCommon");
|
||||
pset.setHasProperties({total_area});
|
||||
|
||||
auto site_prop = file.create<IfcSchema::IfcRelDefinesByProperties>();
|
||||
site_prop.setGlobalId(guid());
|
||||
site_prop.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
site_prop.setRelatedObjects({file.getSingle<IfcSchema::IfcSite>()});
|
||||
site_prop.setRelatingPropertyDefinition(pset);
|
||||
|
||||
auto ground_reps = file.getSingle<IfcSchema::IfcSite>().Representation().Representations();
|
||||
for (auto& rep : ground_reps) {
|
||||
rep.setContextOfItems(file.getRepresentationContext("Model"));
|
||||
}
|
||||
file.addEntity(ground_representation);
|
||||
file.add_entity(ground_representation);
|
||||
setSurfaceColour(file,ground_representation, 0.15, 0.25, 0.05);
|
||||
|
||||
// According to the Ifc2x3 schema an IfcWallStandardCase needs to have an IfcMaterialLayerSet
|
||||
@@ -297,58 +337,28 @@ int main() {
|
||||
// Some BIM authoring applications, such as Autodesk Revit, ignore the geometrical representation
|
||||
// by and large and construct native walls using the layer thickness and reference line offset
|
||||
// provided here.
|
||||
#ifdef USE_IFC4
|
||||
IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick", null, null);
|
||||
#else
|
||||
IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick");
|
||||
#endif
|
||||
IfcSchema::IfcMaterialLayer* layer = new IfcSchema::IfcMaterialLayer(
|
||||
material,
|
||||
360,
|
||||
null
|
||||
#ifdef USE_IFC4
|
||||
, null
|
||||
, null
|
||||
, null
|
||||
, null
|
||||
#endif
|
||||
);
|
||||
IfcSchema::IfcMaterialLayer::list::ptr layers (new aggregate_of<IfcSchema::IfcMaterialLayer>());
|
||||
layers->push(layer);
|
||||
IfcSchema::IfcMaterialLayerSet* layer_set = new IfcSchema::IfcMaterialLayerSet(
|
||||
layers,
|
||||
"Wall"s
|
||||
#ifdef USE_IFC4
|
||||
, null
|
||||
#endif
|
||||
);
|
||||
IfcSchema::IfcMaterialLayerSetUsage* layer_usage = new IfcSchema::IfcMaterialLayerSetUsage(
|
||||
layer_set,
|
||||
IfcSchema::IfcLayerSetDirectionEnum::IfcLayerSetDirection_AXIS2,
|
||||
IfcSchema::IfcDirectionSenseEnum::IfcDirectionSense_POSITIVE,
|
||||
-180
|
||||
#ifdef USE_IFC4
|
||||
, null
|
||||
#endif
|
||||
);
|
||||
auto material = file.create<IfcSchema::IfcMaterial>();
|
||||
material.setName("Brick");
|
||||
|
||||
IfcSchema::IfcRelAssociatesMaterial* associates_material = new IfcSchema::IfcRelAssociatesMaterial(
|
||||
guid(),
|
||||
file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null,
|
||||
null,
|
||||
#ifdef USE_IFC4
|
||||
file.instances_by_type<IfcSchema::IfcWallStandardCase>()->as<IfcSchema::IfcDefinitionSelect>(),
|
||||
#else
|
||||
file.instances_by_type<IfcSchema::IfcWallStandardCase>()->as<IfcSchema::IfcRoot>(),
|
||||
#endif
|
||||
layer_usage);
|
||||
auto layer = file.create<IfcSchema::IfcMaterialLayer>();
|
||||
layer.setMaterial(material);
|
||||
layer.setLayerThickness(360);
|
||||
|
||||
file.addEntity(material);
|
||||
file.addEntity(layer);
|
||||
file.addEntity(layer_set);
|
||||
file.addEntity(layer_usage);
|
||||
file.addEntity(associates_material);
|
||||
auto layer_set = file.create<IfcSchema::IfcMaterialLayerSet>();
|
||||
layer_set.setMaterialLayers({layer});
|
||||
layer_set.setLayerSetName("Wall");
|
||||
|
||||
auto layer_usage = file.create<IfcSchema::IfcMaterialLayerSetUsage>();
|
||||
layer_usage.setForLayerSet(layer_set);
|
||||
layer_usage.setDirectionSense(IfcSchema::IfcDirectionSenseEnum::IfcDirectionSense_POSITIVE);
|
||||
layer_usage.setLayerSetDirection(IfcSchema::IfcLayerSetDirectionEnum::IfcLayerSetDirection_AXIS1);
|
||||
layer_usage.setOffsetFromReferenceLine(-180);
|
||||
|
||||
auto associates_material = file.create<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
associates_material.setGlobalId(guid());
|
||||
associates_material.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
associates_material.setRelatedObjects({south_wall, north_wall, east_wall, west_wall});
|
||||
associates_material.setRelatingMaterial(layer_usage);
|
||||
|
||||
// In addition, another common way to represent geometry in IFC files is to use extrusions of
|
||||
// planar areas bounded by a polygon.
|
||||
@@ -359,55 +369,88 @@ int main() {
|
||||
stair_points.push_back(XY(500, 200));
|
||||
stair_points.push_back(XY(500, 400));
|
||||
stair_points.push_back(XY( 0, 400));
|
||||
IfcSchema::IfcStairFlight* stair = new IfcSchema::IfcStairFlight(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(storey_placement, 5050, 1000, 0, 0, 1, 0, 1, 0, 0),
|
||||
file.addExtrudedPolyline(stair_points, 1200), null, 2, 2, 0.2, 0.25
|
||||
auto stair = file.create<IfcSchema::IfcStairFlight>();
|
||||
stair.setGlobalId(guid());
|
||||
stair.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
stair.setObjectPlacement(file.addLocalPlacement(storey_placement, 5050, 1000, 0, 0, 1, 0, 1, 0, 0));
|
||||
stair.setRepresentation(file.addExtrudedPolyline(stair_points, 1200));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcStairFlightTypeEnum::IfcStairFlightType_STRAIGHT
|
||||
stair.setNumberOfRisers(2);
|
||||
#else
|
||||
stair.setNumberOfRiser(2);
|
||||
#endif
|
||||
stair.setNumberOfTreads(2);
|
||||
stair.setRiserHeight(0.2);
|
||||
stair.setTreadLength(0.25);
|
||||
#ifdef USE_IFC4
|
||||
stair.setPredefinedType(IfcSchema::IfcStairFlightTypeEnum::IfcStairFlightType_STRAIGHT);
|
||||
#endif
|
||||
);
|
||||
|
||||
file.addBuildingProduct(stair);
|
||||
setSurfaceColour(file, stair->Representation(), footing_colour);
|
||||
setSurfaceColour(file, stair.Representation(), footing_colour);
|
||||
|
||||
IfcSchema::IfcOpeningElement* door_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, file.addLocalPlacement(storey_placement, 5000-180, 2500-900, 0), file.addBox(1000, 1000, 2200), null
|
||||
auto door_opening = file.create<IfcSchema::IfcOpeningElement>();
|
||||
door_opening.setGlobalId(guid());
|
||||
door_opening.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
door_opening.setObjectPlacement(file.addLocalPlacement(storey_placement, 5000 - 180, 2500 - 900, 0));
|
||||
door_opening.setRepresentation(file.addBox(1000, 1000, 2200));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
|
||||
#endif
|
||||
);
|
||||
file.addEntity(door_opening);
|
||||
file.addEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, east_wall, door_opening));
|
||||
door_opening.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING);
|
||||
#endif
|
||||
|
||||
auto rel3 = file.create<IfcSchema::IfcRelVoidsElement>();
|
||||
rel3.setGlobalId(guid());
|
||||
rel3.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
rel3.setRelatingBuildingElement(east_wall);
|
||||
rel3.setRelatedOpeningElement(door_opening);
|
||||
|
||||
// A single shape representation can contain multiple representiation items. This way a product
|
||||
// can be a composition of multiple solids. The following door will be composed of four boxes
|
||||
// which constitute the door and its frame.
|
||||
IfcSchema::IfcDoor* door = new IfcSchema::IfcDoor(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, null,
|
||||
file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0), 0, null, 2200, 1000
|
||||
auto door = file.create<IfcSchema::IfcDoor>();
|
||||
door.setGlobalId(guid());
|
||||
door.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
door.setObjectPlacement(file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0));
|
||||
door.setOverallWidth(1000);
|
||||
door.setOverallHeight(2200);
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR
|
||||
, IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT
|
||||
, null
|
||||
door.setPredefinedType(IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR);
|
||||
door.setOperationType(IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT);
|
||||
#endif
|
||||
);
|
||||
door->setRepresentation(file.addBox(80, 80, 2120, 0, file.addPlacement3d(460, 0, 0)));
|
||||
IfcSchema::IfcRepresentation::list::ptr door_representations = door->Representation()->Representations();
|
||||
IfcSchema::IfcShapeRepresentation* door_body = 0;
|
||||
for (IfcSchema::IfcRepresentation::list::it i = door_representations->begin(); i != door_representations->end(); ++i) {
|
||||
IfcSchema::IfcRepresentation* rep = *i;
|
||||
if (rep->declaration().is(IfcSchema::IfcShapeRepresentation::Class()) && rep->RepresentationIdentifier().get_value_or("") == "Body") {
|
||||
door_body = (IfcSchema::IfcShapeRepresentation*) rep;
|
||||
|
||||
door.setRepresentation(file.addBox(80, 80, 2120, IfcSchema::IfcAxis2Placement2D{}, file.addPlacement3d(460, 0, 0)));
|
||||
|
||||
auto door_representations = door.Representation().Representations();
|
||||
IfcSchema::IfcShapeRepresentation door_body;
|
||||
for (auto& rep : door_representations) {
|
||||
if (rep.declaration().is(IfcSchema::IfcShapeRepresentation::Class()) && rep.RepresentationIdentifier().value_or("") == "Body") {
|
||||
door_body = rep.as<IfcSchema::IfcShapeRepresentation>();
|
||||
}
|
||||
}
|
||||
file.addBox(door_body, 80, 80, 2120, 0, file.addPlacement3d(-460, 0, 0));
|
||||
file.addBox(door_body, 1000, 80, 80, 0, file.addPlacement3d( 0, 0, 2120));
|
||||
|
||||
file.addBox(door_body, 80, 80, 2120, IfcSchema::IfcAxis2Placement2D{}, file.addPlacement3d(-460, 0, 0));
|
||||
file.addBox(door_body, 1000, 80, 80, IfcSchema::IfcAxis2Placement2D{}, file.addPlacement3d( 0, 0, 2120));
|
||||
file.addBox(door_body, 860, 30, 2120);
|
||||
file.addBuildingProduct(door);
|
||||
setSurfaceColour(file, door->Representation(), 0.9, 0.9, 0.9);
|
||||
file.addEntity(new IfcSchema::IfcRelFillsElement(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null, null, door_opening, door));
|
||||
|
||||
IfcSchema::IfcDoorStyle* door_style = new IfcSchema::IfcDoorStyle(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), "Door type"s, null, null, null, null, null,
|
||||
IfcSchema::IfcDoorStyleOperationEnum::IfcDoorStyleOperation_SINGLE_SWING_LEFT, IfcSchema::IfcDoorStyleConstructionEnum::IfcDoorStyleConstruction_WOOD, false, false);
|
||||
setSurfaceColour(file, door.Representation(), 0.9, 0.9, 0.9);
|
||||
{
|
||||
auto rel = file.create<IfcSchema::IfcRelFillsElement>();
|
||||
rel.setGlobalId(guid());
|
||||
rel.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
rel.setRelatingOpeningElement(door_opening);
|
||||
rel.setRelatedBuildingElement(door);
|
||||
}
|
||||
|
||||
auto door_style = file.create<IfcSchema::IfcDoorStyle>();
|
||||
door_style.setGlobalId(guid());
|
||||
door_style.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
door_style.setName("Door type");
|
||||
door_style.setOperationType(IfcSchema::IfcDoorStyleOperationEnum::IfcDoorStyleOperation_SINGLE_SWING_LEFT);
|
||||
door_style.setConstructionType(IfcSchema::IfcDoorStyleConstructionEnum::IfcDoorStyleConstruction_WOOD);
|
||||
door_style.setParameterTakesPrecedence(false);
|
||||
door_style.setSizeable(false);
|
||||
|
||||
// NOTE: typing by IfcDoorStyle will cause validation errors in IFC4+ but it's allowed for backwards compatibility
|
||||
// better to use IfcDoorType in the actual use case
|
||||
file.addRelatedObject<IfcSchema::IfcRelDefinesByType>(door_style, door);
|
||||
@@ -423,23 +466,23 @@ int main() {
|
||||
// Therefore the OverallWidth and OverallHeight of the window attributes will need to
|
||||
// match the bounding box of the representation. Furthermore, the window placement needs
|
||||
// to align with the lowerleft corner of the constituent parts.
|
||||
IfcSchema::IfcShapeRepresentation::list::ptr frame_representations(new IfcSchema::IfcShapeRepresentation::list);
|
||||
std::vector<IfcSchema::IfcShapeRepresentation> frame_representations;
|
||||
|
||||
IfcSchema::IfcShapeRepresentation* horizontal_bar = file.addEmptyRepresentation();
|
||||
IfcSchema::IfcShapeRepresentation* vertical_bar = file.addEmptyRepresentation();
|
||||
auto horizontal_bar = file.addEmptyRepresentation();
|
||||
auto vertical_bar = file.addEmptyRepresentation();
|
||||
file.addBox(horizontal_bar, 1860, 90, 90);
|
||||
file.addBox(vertical_bar, 90, 90, 1420);
|
||||
|
||||
frame_representations->push(horizontal_bar);
|
||||
frame_representations->push(horizontal_bar); // Add another reference to the horizontal bar created above
|
||||
frame_representations->push(vertical_bar);
|
||||
frame_representations->push(vertical_bar); // Add another reference to the vertical bar created above
|
||||
frame_representations.push_back(horizontal_bar);
|
||||
frame_representations.push_back(horizontal_bar); // Add another reference to the horizontal bar created above
|
||||
frame_representations.push_back(vertical_bar);
|
||||
frame_representations.push_back(vertical_bar); // Add another reference to the vertical bar created above
|
||||
|
||||
// The beams all have the same surface style assigned
|
||||
IfcSchema::IfcPresentationStyleAssignment* frame_style = 0;
|
||||
for (IfcSchema::IfcShapeRepresentation::list::it i = frame_representations->begin(); i != frame_representations->end(); i += 2) {
|
||||
IfcSchema::IfcPresentationStyleAssignment frame_style;
|
||||
for (auto i = frame_representations.begin(); i != frame_representations.end(); i += 2) {
|
||||
if (frame_style) {
|
||||
setSurfaceColour(file,*i, frame_style);
|
||||
setSurfaceColour(file, *i, frame_style);
|
||||
} else {
|
||||
frame_style = setSurfaceColour(file,*i, 0.5, 0.4, 0.3);
|
||||
}
|
||||
@@ -448,73 +491,80 @@ int main() {
|
||||
|
||||
// This window will be placed at five locations within the building. A list of placements is
|
||||
// created and is iterated over to create all window instances.
|
||||
IfcSchema::IfcLocalPlacement::list::ptr window_placements (new IfcSchema::IfcLocalPlacement::list);
|
||||
window_placements->push(file.addLocalPlacement(storey_placement, 2*-1770-430-930, -45, 400));
|
||||
window_placements->push(file.addLocalPlacement(storey_placement, -1770-430-930, -45, 400));
|
||||
window_placements->push(file.addLocalPlacement(storey_placement, -430-930, -45, 400));
|
||||
window_placements->push(file.addLocalPlacement(storey_placement, 3000-930, -45, 400));
|
||||
window_placements->push(file.addLocalPlacement(storey_placement, -4855+45, 885-930, 400, 0, 0, 1, 0, 1, 0));
|
||||
std::vector<IfcSchema::IfcLocalPlacement> window_placements;
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, 2*-1770-430-930, -45, 400));
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, -1770-430-930, -45, 400));
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, -430-930, -45, 400));
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, 3000-930, -45, 400));
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, -4855+45, 885-930, 400, 0, 0, 1, 0, 1, 0));
|
||||
|
||||
for (IfcSchema::IfcLocalPlacement::list::it it = window_placements->begin(); it != window_placements->end(); ++it) {
|
||||
|
||||
// Create the window at the current location
|
||||
IfcSchema::IfcLocalPlacement* place = *it;
|
||||
IfcSchema::IfcWindow* window = new IfcSchema::IfcWindow(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, place, 0, null, 1600, 1860
|
||||
for (auto& place : window_placements) {
|
||||
auto window = file.create<IfcSchema::IfcWindow>();
|
||||
window.setGlobalId(guid());
|
||||
window.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
window.setObjectPlacement(place);
|
||||
window.setOverallWidth(1860);
|
||||
window.setOverallHeight(1600);
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW
|
||||
, IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL
|
||||
, null
|
||||
window.setPredefinedType(IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW);
|
||||
window.setPartitioningType(IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL);
|
||||
#endif
|
||||
);
|
||||
file.addBuildingProduct(window);
|
||||
file.addBuildingProduct(window);
|
||||
|
||||
// Initialize a list of parts for the window to be composed of
|
||||
IfcSchema::IfcObjectDefinition::list::ptr window_parts(new aggregate_of<IfcSchema::IfcObjectDefinition>());
|
||||
std::vector<IfcSchema::IfcObjectDefinition> window_parts;
|
||||
|
||||
// The placements for the beams are not shared across the different windows because every
|
||||
// beam is placed relative to its parent window entity.
|
||||
IfcSchema::IfcLocalPlacement::list::ptr frame_placements (new aggregate_of<IfcSchema::IfcLocalPlacement>());
|
||||
frame_placements->push(file.addLocalPlacement(storey_placement, 930,45));
|
||||
frame_placements->push(file.addLocalPlacement(storey_placement, 930, 45, 1510));
|
||||
frame_placements->push(file.addLocalPlacement(storey_placement, -885+930, 45, 90));
|
||||
frame_placements->push(file.addLocalPlacement(storey_placement, 885+930, 45, 90));
|
||||
std::vector<IfcSchema::IfcLocalPlacement> frame_placements;
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, 930,45));
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, 930, 45, 1510));
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, -885+930, 45, 90));
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, 885+930, 45, 90));
|
||||
|
||||
// Now iterate over the placements and representations of the beam and add them to list of parts
|
||||
IfcSchema::IfcLocalPlacement::list::it frame_placement;
|
||||
IfcSchema::IfcShapeRepresentation::list::it frame_representation;
|
||||
for (frame_placement = frame_placements->begin(), frame_representation = frame_representations->begin();
|
||||
frame_placement != frame_placements->end() && frame_representation != frame_representations->end();
|
||||
std::vector<IfcSchema::IfcLocalPlacement>::const_iterator frame_placement;
|
||||
std::vector<IfcSchema::IfcShapeRepresentation>::const_iterator frame_representation;
|
||||
for (frame_placement = frame_placements.begin(), frame_representation = frame_representations.begin();
|
||||
frame_placement != frame_placements.end() && frame_representation != frame_representations.end();
|
||||
++frame_placement, ++frame_representation)
|
||||
{
|
||||
IfcSchema::IfcMember* frame_part = new IfcSchema::IfcMember(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, null, *frame_placement, file.addMappedItem(*frame_representation), null
|
||||
auto frame_part = file.create<IfcSchema::IfcMember>();
|
||||
frame_part.setGlobalId(guid());
|
||||
frame_part.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
frame_part.setObjectPlacement(*frame_placement);
|
||||
frame_part.setRepresentation(file.addMappedItem(*frame_representation));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcMemberTypeEnum::IfcMemberType_MULLION
|
||||
frame_part.setPredefinedType(IfcSchema::IfcMemberTypeEnum::IfcMemberType_MULLION);
|
||||
#endif
|
||||
);
|
||||
file.addEntity(frame_part);
|
||||
window_parts->push(frame_part);
|
||||
window_parts.push_back(frame_part);
|
||||
file.relatePlacements(window, frame_part);
|
||||
}
|
||||
|
||||
// Add the glass plate to the list of parts
|
||||
IfcSchema::IfcPlate* glass_part = new IfcSchema::IfcPlate(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(), null,
|
||||
null, null, file.addLocalPlacement(storey_placement, 930, 45, 90), file.addBox(1680, 10, 1420), null
|
||||
auto glass_part = file.create<IfcSchema::IfcPlate>();
|
||||
glass_part.setGlobalId(guid());
|
||||
glass_part.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
glass_part.setObjectPlacement(file.addLocalPlacement(storey_placement, 930, 45, 90));
|
||||
glass_part.setRepresentation(file.addBox(1860, 10, 1420));
|
||||
#ifdef USE_IFC4
|
||||
, IfcSchema::IfcPlateTypeEnum::IfcPlateType_SHEET
|
||||
glass_part.setPredefinedType(IfcSchema::IfcPlateTypeEnum::IfcPlateType_SHEET);
|
||||
#endif
|
||||
);
|
||||
file.addEntity(glass_part);
|
||||
window_parts->push(glass_part);
|
||||
|
||||
window_parts.push_back(glass_part);
|
||||
file.relatePlacements(window, glass_part);
|
||||
setSurfaceColour(file,glass_part->Representation(), 0.6, 0.7, 0.75, 0.1);
|
||||
setSurfaceColour(file, glass_part.Representation(), 0.6, 0.7, 0.75, 0.1);
|
||||
|
||||
// Now create a decomposition relation between the window and the parts. Most viewers and authoring
|
||||
// tools will consider the window a single entity that can be selected as a whole.
|
||||
IfcSchema::IfcRelDecomposes* decomposition = new IfcSchema::IfcRelAggregates(guid(), file.getSingle<IfcSchema::IfcOwnerHistory>(),
|
||||
null, null, window, window_parts);
|
||||
file.addEntity(decomposition);
|
||||
{
|
||||
|
||||
auto rel = file.create<IfcSchema::IfcRelAggregates>();
|
||||
rel.setGlobalId(guid());
|
||||
rel.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
rel.setRelatingObject(window);
|
||||
rel.setRelatedObjects(window_parts);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally create a file stream for our output and write the IFC file to it.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/********************************************************************************
|
||||
/********************************************************************************
|
||||
* *
|
||||
* This file is part of IfcOpenShell. *
|
||||
* *
|
||||
@@ -20,9 +20,9 @@
|
||||
// TODO: Multiple schemas
|
||||
#define IfcSchema Ifc2x3
|
||||
|
||||
#include "ifcparse/IfcFile.h"
|
||||
#include "ifcparse/IfcLogger.h"
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "../ifcparse/file.h"
|
||||
#include "../ifcparse/logger.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
@@ -47,7 +47,7 @@ static_assert(false, "A boost preprocessor sequence of schema identifiers is nee
|
||||
// for and then overflow into an existing empty include file.
|
||||
|
||||
#define INCLUDE_SCHEMA(n) \
|
||||
BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_SEQ_SIZE(SCHEMA_SEQ), n), BOOST_PP_STRINGIZE(../ifcparse/BOOST_PP_CAT(Ifc,BOOST_PP_SEQ_ELEM(BOOST_PP_MIN(n, BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_POP_BACK(SCHEMA_SEQ))),SCHEMA_SEQ)).h), "../ifcgeom/empty.h")
|
||||
BOOST_PP_IIF(BOOST_PP_GREATER(BOOST_PP_SEQ_SIZE(SCHEMA_SEQ), n), BOOST_PP_STRINGIZE(../ifcparse/schemas/BOOST_PP_CAT(Ifc,BOOST_PP_SEQ_ELEM(BOOST_PP_MIN(n, BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_POP_BACK(SCHEMA_SEQ))),SCHEMA_SEQ)).h), "../ifcgeom/empty.h")
|
||||
|
||||
#include INCLUDE_SCHEMA(0)
|
||||
#include INCLUDE_SCHEMA(1)
|
||||
@@ -80,7 +80,7 @@ struct is_ifc4_or_higher<T, std::void_t<decltype(T::IfcMaterialDefinition)>> : s
|
||||
|
||||
typedef std::map<std::string, std::map<std::string, std::string>> element_properties;
|
||||
|
||||
std::string format_string(const AttributeValue& argument) {
|
||||
std::string format_string(const attribute_value& argument) {
|
||||
// Argument is a runtime tagged variant for the various data types in a IFC model,
|
||||
// in this particular case we only care about flattening it to a string.
|
||||
// @todo mostly duplicated from XmlSerializer.cpp
|
||||
@@ -89,21 +89,21 @@ std::string format_string(const AttributeValue& argument) {
|
||||
}
|
||||
auto argument_type = argument.type();
|
||||
switch (argument_type) {
|
||||
case IfcUtil::Argument_BOOL: {
|
||||
case ifcopenshell::Argument_BOOL: {
|
||||
const bool b = argument;
|
||||
return b ? "true" : "false";
|
||||
}
|
||||
case IfcUtil::Argument_DOUBLE: {
|
||||
case ifcopenshell::Argument_DOUBLE: {
|
||||
const double d = argument;
|
||||
std::stringstream stream;
|
||||
stream << std::setprecision(std::numeric_limits< double >::max_digits10) << d;
|
||||
return stream.str();
|
||||
break; }
|
||||
case IfcUtil::Argument_STRING:
|
||||
case IfcUtil::Argument_ENUMERATION: {
|
||||
case ifcopenshell::Argument_STRING:
|
||||
case ifcopenshell::Argument_ENUMERATION: {
|
||||
return static_cast<std::string>(argument);
|
||||
break; }
|
||||
case IfcUtil::Argument_INT: {
|
||||
case ifcopenshell::Argument_INT: {
|
||||
const int v = argument;
|
||||
std::stringstream stream;
|
||||
stream << v;
|
||||
@@ -114,44 +114,42 @@ std::string format_string(const AttributeValue& argument) {
|
||||
}
|
||||
|
||||
template <typename Schema, typename T>
|
||||
void process_pset(element_properties& props, const T* inst) {
|
||||
void process_pset(element_properties& props, const T& inst) {
|
||||
// Process an individual Property or Quantity set.
|
||||
if (auto pset = inst->template as<typename Schema::IfcPropertySet>()) {
|
||||
if (!pset->Name()) {
|
||||
if (auto pset = inst.template as<typename Schema::IfcPropertySet>()) {
|
||||
if (!pset.Name()) {
|
||||
return;
|
||||
}
|
||||
auto ps = pset->HasProperties();
|
||||
for (auto it = ps->begin(); it != ps->end(); ++it) {
|
||||
auto& p = *it;
|
||||
if (auto singleval = p->template as<typename Schema::IfcPropertySingleValue>()) {
|
||||
auto ps = pset.HasProperties();
|
||||
for (auto& p : ps) {
|
||||
if (auto singleval = p.template as<typename Schema::IfcPropertySingleValue>()) {
|
||||
std::string propname, propvalue;
|
||||
if constexpr (is_ifc4_or_higher<Schema>::value) {
|
||||
if (!singleval->Name()) {
|
||||
if (!singleval.Name()) {
|
||||
continue;
|
||||
}
|
||||
propname = *singleval->Name();
|
||||
propname = *singleval.Name();
|
||||
}
|
||||
if constexpr (!is_ifc4_or_higher<Schema>::value) {
|
||||
propname = singleval->Name();
|
||||
propname = singleval.Name();
|
||||
}
|
||||
if (!singleval->NominalValue()) {
|
||||
if (!singleval.NominalValue()) {
|
||||
propvalue = "-";
|
||||
} else {
|
||||
props[*pset->Name()][propname] = format_string(singleval->NominalValue()->template as<IfcUtil::IfcBaseClass>()->get_attribute_value(0));
|
||||
props[*pset.Name()][propname] = format_string(singleval.NominalValue().concrete().get_attribute_value(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto qset = inst->template as<typename Schema::IfcElementQuantity>()) {
|
||||
if (!qset->Name()) {
|
||||
if (auto qset = inst.template as<typename Schema::IfcElementQuantity>()) {
|
||||
if (!qset.Name()) {
|
||||
return;
|
||||
}
|
||||
auto qs = qset->Quantities();
|
||||
for (auto it = qs->begin(); it != qs->end(); ++it) {
|
||||
auto& q = *it;
|
||||
if (q->template as<typename Schema::IfcPhysicalSimpleQuantity>() && q->get_attribute_value(3).type() == IfcUtil::Argument_DOUBLE) {
|
||||
double v = q->get_attribute_value(3);
|
||||
props[*qset->Name()][q->Name()] = std::to_string(v);
|
||||
auto qs = qset.Quantities();
|
||||
for (auto& q : qs) {
|
||||
if (q.template as<typename Schema::IfcPhysicalSimpleQuantity>() && q.get_attribute_value(3).type() == ifcopenshell::Argument_DOUBLE) {
|
||||
double v = q.get_attribute_value(3);
|
||||
props[*qset.Name()][q.Name()] = std::to_string(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,49 +161,44 @@ void process_pset(element_properties& props, const T* inst) {
|
||||
}
|
||||
|
||||
template <typename Schema>
|
||||
void get_psets_s(element_properties& props, const typename Schema::IfcObjectDefinition* inst) {
|
||||
void get_psets_s(element_properties& props, const typename Schema::IfcObjectDefinition& inst) {
|
||||
// Extracts the property definitions for an IFC instance.
|
||||
if (auto tyob = inst->template as<typename Schema::IfcTypeObject>()) {
|
||||
if (tyob->HasPropertySets()) {
|
||||
auto defs = *tyob->HasPropertySets();
|
||||
for (auto it = defs->begin(); it != defs->end(); ++it) {
|
||||
auto& def = *it;
|
||||
if (auto tyob = inst.template as<typename Schema::IfcTypeObject>()) {
|
||||
if (tyob.HasPropertySets()) {
|
||||
auto defs = *tyob.HasPropertySets();
|
||||
for (auto& def : defs) {
|
||||
process_pset<Schema>(props, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (is_ifc4_or_higher<Schema>::value) {
|
||||
if (auto mdef = inst->template as<typename Schema::IfcMaterialDefinition>()) {
|
||||
auto defs = mdef->HasProperties();
|
||||
for (auto it = defs->begin(); it != defs->end(); ++it) {
|
||||
auto& def = *it;
|
||||
if (auto mdef = inst.template as<typename Schema::IfcMaterialDefinition>()) {
|
||||
auto defs = mdef.HasProperties();
|
||||
for (auto& def : defs) {
|
||||
process_pset<Schema>(props, def);
|
||||
}
|
||||
}
|
||||
if (auto pdef = inst->template as<typename Schema::IfcProfileDef>()) {
|
||||
if (auto pdef = inst.template as<typename Schema::IfcProfileDef>()) {
|
||||
auto defs = pdef->HasProperties();
|
||||
for (auto it = defs->begin(); it != defs->end(); ++it) {
|
||||
auto& def = *it;
|
||||
for (auto& def : defs) {
|
||||
process_pset<Schema>(props, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto ob = inst->template as<typename Schema::IfcObject>()) {
|
||||
if (auto ob = inst.template as<typename Schema::IfcObject>()) {
|
||||
if constexpr (is_ifc4_or_higher<Schema>::value) {
|
||||
auto rels = ob->IsTypedBy();
|
||||
for (auto it = rels->begin(); it != rels->end(); ++it) {
|
||||
auto& rel = *it;
|
||||
auto rels = ob.IsTypedBy();
|
||||
for (auto& rel : rels) {
|
||||
get_psets_s<Schema>(props, rel->RelatingType());
|
||||
}
|
||||
}
|
||||
{
|
||||
auto rels = ob->IsDefinedBy();
|
||||
for (auto it = rels->begin(); it != rels->end(); ++it) {
|
||||
auto& rel = *it;
|
||||
if (auto bytype = rel->template as<typename Schema::IfcRelDefinesByType>()) {
|
||||
get_psets_s<Schema>(props, bytype->RelatingType());
|
||||
} else if (auto byprops = rel->template as<typename Schema::IfcRelDefinesByProperties>()) {
|
||||
process_pset<Schema>(props, byprops->RelatingPropertyDefinition());
|
||||
auto rels = ob.IsDefinedBy();
|
||||
for (auto& rel : rels) {
|
||||
if (auto bytype = rel.template as<typename Schema::IfcRelDefinesByType>()) {
|
||||
get_psets_s<Schema>(props, bytype.RelatingType());
|
||||
} else if (auto byprops = rel.template as<typename Schema::IfcRelDefinesByProperties>()) {
|
||||
process_pset<Schema>(props, byprops.RelatingPropertyDefinition());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,10 +213,10 @@ void get_psets_s(element_properties& props, const typename Schema::IfcObjectDefi
|
||||
#define GENERATE_LITERAL_STRING(elem) "Ifc" # elem
|
||||
|
||||
#define TEST_AND_DISPATCH(r, data, elem) \
|
||||
if (strcasecmp(schema_name, GENERATE_LITERAL_STRING(elem)) == 0) { get_psets_s<EXPAND_AND_CONCATENATE(elem)>(props, inst->as<EXPAND_AND_CONCATENATE(elem)::IfcObjectDefinition>()); }
|
||||
if (strcasecmp(schema_name, GENERATE_LITERAL_STRING(elem)) == 0) { get_psets_s<EXPAND_AND_CONCATENATE(elem)>(props, inst.as<EXPAND_AND_CONCATENATE(elem)::IfcObjectDefinition>()); }
|
||||
|
||||
void get_psets(element_properties& props, const IfcUtil::IfcBaseClass* inst) {
|
||||
auto schema_name = inst->declaration().schema()->name().c_str();
|
||||
void get_psets(element_properties& props, const express::Base& inst) {
|
||||
auto schema_name = inst.declaration().schema()->name().c_str();
|
||||
BOOST_PP_SEQ_FOR_EACH(TEST_AND_DISPATCH, , SCHEMA_SEQ)
|
||||
}
|
||||
|
||||
@@ -234,10 +227,10 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Redirect the output (both progress and log) to stdout
|
||||
Logger::SetOutput(&std::cout, &std::cout);
|
||||
logger::set_output(&std::cout, &std::cout);
|
||||
|
||||
// Parse the IFC file provided in argv[1]
|
||||
IfcParse::IfcFile file(argv[1]);
|
||||
ifcopenshell::file file(argv[1]);
|
||||
if (!file.good()) {
|
||||
std::cout << "Unable to parse .ifc file" << std::endl;
|
||||
return 1;
|
||||
@@ -259,19 +252,17 @@ int main(int argc, char** argv) {
|
||||
// we need to cast them to IfcWindows. Since these properties
|
||||
// are optional we need to make sure the properties are
|
||||
// defined for the window in question before accessing them.
|
||||
IfcSchema::IfcBuildingElement::list::ptr elements = file.instances_by_type<IfcSchema::IfcBuildingElement>();
|
||||
auto elements = file.instances_by_type<IfcSchema::IfcBuildingElement>();
|
||||
|
||||
std::cout << "Found " << elements->size() << " elements in " << argv[1] << ":" << std::endl;
|
||||
std::cout << "Found " << elements.size() << " elements in " << argv[1] << ":" << std::endl;
|
||||
|
||||
for (auto it = elements->begin(); it != elements->end(); ++it) {
|
||||
const auto* element = *it;
|
||||
element->toString(std::cout);
|
||||
for (auto& element : elements) {
|
||||
element.to_string(std::cout);
|
||||
std::cout << std::endl;
|
||||
|
||||
const IfcSchema::IfcWindow* window;
|
||||
if ((window = element->as<IfcSchema::IfcWindow>()) != 0) {
|
||||
if (window->OverallWidth() && window->OverallHeight()) {
|
||||
const double area = *window->OverallWidth() * *window->OverallHeight();
|
||||
if (auto window = element.as<IfcSchema::IfcWindow>()) {
|
||||
if (window.OverallWidth() && window.OverallHeight()) {
|
||||
const double area = *window.OverallWidth() * *window.OverallHeight();
|
||||
std::cout << "The area of this window is " << area << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#pragma warning(disable : 4018 4267 4250 4984 4985)
|
||||
|
||||
#include "ifcparse/Ifc4x3_add2.h"
|
||||
#include "ifcparse/IfcAlignmentHelper.h"
|
||||
#include "../ifcparse/Ifc4x3_add2.h"
|
||||
#include "../ifcparse/alignment_helper.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@@ -39,37 +39,49 @@
|
||||
|
||||
// performs basic project setup including created the IfcProject object
|
||||
// and initializing the project units to FEET
|
||||
Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
|
||||
Schema::IfcProject setup_project(hierarchy_helper<Schema>& file) {
|
||||
std::vector<std::string> file_description;
|
||||
file_description.push_back("ViewDefinition[Alignment-basedReferenceView]");
|
||||
file.header().file_description()->setdescription(file_description);
|
||||
file.header().file_description().setdescription(file_description);
|
||||
|
||||
auto project = file.addProject();
|
||||
project->setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
|
||||
project->setDescription(std::string("C++ Example - Simplified"));
|
||||
project.setName(std::string("FHWA Bridge Geometry Manual Example Alignment"));
|
||||
project.setDescription(std::string("C++ Example - Simplified"));
|
||||
|
||||
// set up project units for feet
|
||||
// the call to file.addProject() sets up length units as millimeter.
|
||||
auto units_in_context = project->UnitsInContext();
|
||||
auto units = units_in_context->Units();
|
||||
auto begin = units->begin();
|
||||
auto units_in_context = project.UnitsInContext();
|
||||
auto units = units_in_context.Units();
|
||||
auto begin = units.begin();
|
||||
auto iter = begin;
|
||||
auto end = units->end();
|
||||
auto end = units.end();
|
||||
for (; iter != end; iter++) {
|
||||
auto unit = *iter;
|
||||
if (unit->as<Schema::IfcSIUnit>() && unit->as<Schema::IfcSIUnit>()->UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
|
||||
auto dimensions = new Schema::IfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0);
|
||||
file.addEntity(dimensions);
|
||||
if (unit.as<Schema::IfcSIUnit>() && unit.as<Schema::IfcSIUnit>().UnitType() == Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT) {
|
||||
auto dimensions = file.create<Schema::IfcDimensionalExponents>();
|
||||
dimensions.setLengthExponent(1);
|
||||
dimensions.setMassExponent(0);
|
||||
dimensions.setTimeExponent(0);
|
||||
dimensions.setElectricCurrentExponent(0);
|
||||
dimensions.setThermodynamicTemperatureExponent(0);
|
||||
dimensions.setAmountOfSubstanceExponent(0);
|
||||
dimensions.setLuminousIntensityExponent(0);
|
||||
|
||||
auto conversion_factor = new Schema::IfcMeasureWithUnit(new Schema::IfcLengthMeasure(304.80), unit->as<Schema::IfcSIUnit>());
|
||||
file.addEntity(conversion_factor);
|
||||
auto conversion_factor = file.create<Schema::IfcMeasureWithUnit>();
|
||||
auto length = file.create<Schema::IfcLengthMeasure>();
|
||||
length.set_attribute_value(0, 304.80);
|
||||
conversion_factor.setValueComponent(length);
|
||||
conversion_factor.setUnitComponent(unit);
|
||||
|
||||
auto conversion_based_unit = new Schema::IfcConversionBasedUnit(dimensions, Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT, "FEET", conversion_factor);
|
||||
file.addEntity(conversion_based_unit);
|
||||
auto conversion_based_unit = file.create<Schema::IfcConversionBasedUnit>();
|
||||
conversion_based_unit.setDimensions(dimensions);
|
||||
conversion_based_unit.setUnitType(Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT);
|
||||
conversion_based_unit.setName("FEET");
|
||||
conversion_based_unit.setConversionFactor(conversion_factor);
|
||||
|
||||
units->remove(unit); // remove the millimeter unit
|
||||
units->push(conversion_based_unit); // add the feet unit
|
||||
units_in_context->setUnits(units); // update the UnitsInContext
|
||||
units.erase(std::remove(units.begin(), units.end(), unit)); // remove the millimeter unit
|
||||
units.push_back(conversion_based_unit); // add the feet unit
|
||||
units_in_context.setUnits(units); // update the UnitsInContext
|
||||
|
||||
break; // Done!, the length unit was found, so break out of the loop
|
||||
}
|
||||
@@ -79,7 +91,7 @@ Schema::IfcProject* setup_project(IfcHierarchyHelper<Schema>& file) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
IfcHierarchyHelper<Schema> file;
|
||||
hierarchy_helper<Schema> file;
|
||||
|
||||
auto project = setup_project(file);
|
||||
|
||||
@@ -129,10 +141,11 @@ int main() {
|
||||
// IFC 4.1.4.1.1 "Every IfcAlignment must be related to IfcProject using the IfcRelAggregates relationship"
|
||||
// https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/concepts/Object_Composition/Aggregation/Alignment_Aggregation_To_Project/content.html
|
||||
// IfcProject <-> IfcRelAggregates <-> IfcAlignment
|
||||
typename aggregate_of<typename Schema::IfcObjectDefinition>::ptr list_of_alignments_in_project(new aggregate_of<typename Schema::IfcObjectDefinition>());
|
||||
list_of_alignments_in_project->push(alignment);
|
||||
auto aggregate_alignments_with_project = new Schema::IfcRelAggregates(IfcParse::IfcGlobalId(), nullptr, std::string("Alignments in project"), boost::none, project, list_of_alignments_in_project);
|
||||
file.addEntity(aggregate_alignments_with_project);
|
||||
auto aggregate_alignments_with_project = file.create<Schema::IfcRelAggregates>();
|
||||
aggregate_alignments_with_project.setGlobalId(ifcopenshell::global_id());
|
||||
aggregate_alignments_with_project.setName("Alignments in project");
|
||||
aggregate_alignments_with_project.setRelatingObject(project);
|
||||
aggregate_alignments_with_project.setRelatedObjects({alignment});
|
||||
|
||||
// Define the spatial structure of the alignment with respect to the site
|
||||
|
||||
@@ -141,24 +154,24 @@ int main() {
|
||||
// IfcSite <-> IfcRelReferencedInSpatialStructure <-> IfcAlignment
|
||||
// This means IfcAlignment is not part of the IfcSite (it is not an aggregate component) but instead IfcAlignment is used within
|
||||
// the IfcSite by reference. This implies an IfcAlignment can traverse many IfcSite instances within an IfcProject
|
||||
typename Schema::IfcSpatialReferenceSelect::list::ptr list_alignments_referenced_in_site(new Schema::IfcSpatialReferenceSelect::list);
|
||||
list_alignments_referenced_in_site->push(alignment);
|
||||
|
||||
// Complete the model by creating sites for the 3 bridges
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
std::ostringstream os;
|
||||
os << "Site of Bridge " << i;
|
||||
auto site = file.addSite(project, nullptr);
|
||||
site->setName(os.str());
|
||||
auto site = file.addSite(project);
|
||||
site.setName(os.str());
|
||||
|
||||
std::ostringstream description;
|
||||
description << "Alignments referenced into the spatial structure of Bridge Site " << i;
|
||||
|
||||
auto rel_referenced_in_spatial_structure = new Schema::IfcRelReferencedInSpatialStructure(IfcParse::IfcGlobalId(), nullptr, boost::none, description.str(), list_alignments_referenced_in_site, site);
|
||||
file.addEntity(rel_referenced_in_spatial_structure);
|
||||
auto rel_referenced_in_spatial_structure = file.create<Schema::IfcRelReferencedInSpatialStructure>();
|
||||
rel_referenced_in_spatial_structure.setGlobalId(ifcopenshell::global_id());
|
||||
rel_referenced_in_spatial_structure.setDescription(description.str());
|
||||
rel_referenced_in_spatial_structure.setRelatedElements(std::vector<Schema::IfcSpatialReferenceSelect>{alignment});
|
||||
rel_referenced_in_spatial_structure.setRelatingStructure(site);
|
||||
}
|
||||
|
||||
// That's it - save the model to a file
|
||||
std::ofstream ofs("IfcSimplifiedAlignment.ifc");
|
||||
std::ofstream ofs("FHWA_Bridge_Geometry_Alignment_Example_Simplified.ifc");
|
||||
ofs << file;
|
||||
}
|
||||
|
||||
@@ -28,16 +28,15 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc4
|
||||
#include "ifcparse/Ifc4.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
boost::none_t const null = boost::none;
|
||||
static int i = 0;
|
||||
|
||||
void create_product_from_item(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::IfcRepresentationItem* item, const std::string& s) {
|
||||
void create_product_from_item(hierarchy_helper& file, IfcSchema::IfcRepresentationItem* item, const std::string& s) {
|
||||
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
|
||||
guid(), 0, S("product"), null, null, 0, 0, null, null);
|
||||
file.addBuildingProduct(product);
|
||||
@@ -50,8 +49,8 @@ void create_product_from_item(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::If
|
||||
items->push(item);
|
||||
|
||||
if (s == "GeometricSet") {
|
||||
IfcSchema::IfcGeometricSet* set = new IfcSchema::IfcGeometricSet(items->as<IfcSchema::IfcGeometricSetSelect>());
|
||||
file.addEntity(set);
|
||||
IfcSchema::IfcGeometricSet* set = new IfcSchema::IfcGeometricSet(items->generalize());
|
||||
file.add_entity(set);
|
||||
items = IfcSchema::IfcRepresentationItem::list::ptr(new IfcSchema::IfcRepresentationItem::list());
|
||||
items->push(set);
|
||||
}
|
||||
@@ -61,55 +60,55 @@ void create_product_from_item(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::If
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
file.add_entity(rep);
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
}
|
||||
|
||||
void create_surfaces_from_profile(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::IfcProfileDef* profile) {
|
||||
void create_surfaces_from_profile(hierarchy_helper& file, IfcSchema::IfcProfileDef* profile) {
|
||||
IfcSchema::IfcSurfaceOfLinearExtrusion* extrusion = new IfcSchema::IfcSurfaceOfLinearExtrusion(profile, file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 100.);
|
||||
file.addEntity(extrusion);
|
||||
file.add_entity(extrusion);
|
||||
|
||||
IfcSchema::IfcAxis1Placement* ax1 = new IfcSchema::IfcAxis1Placement(file.addTriplet<IfcSchema::IfcCartesianPoint>(0,100,0), file.addTriplet<IfcSchema::IfcDirection>(1,0,0));
|
||||
IfcSchema::IfcSurfaceOfRevolution* revolution = new IfcSchema::IfcSurfaceOfRevolution(profile, file.addPlacement3d(), ax1);
|
||||
file.addEntity(ax1);
|
||||
file.addEntity(revolution);
|
||||
file.add_entity(ax1);
|
||||
file.add_entity(revolution);
|
||||
|
||||
create_product_from_item(file, extrusion, "GeometricSet");
|
||||
create_product_from_item(file, revolution, "GeometricSet");
|
||||
}
|
||||
|
||||
void create_solids_from_profile(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::IfcProfileDef* profile) {
|
||||
void create_solids_from_profile(hierarchy_helper& file, IfcSchema::IfcProfileDef* profile) {
|
||||
IfcSchema::IfcExtrudedAreaSolid* extrusion = new IfcSchema::IfcExtrudedAreaSolid(profile, file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 100.);
|
||||
file.addEntity(extrusion);
|
||||
file.add_entity(extrusion);
|
||||
|
||||
IfcSchema::IfcAxis1Placement* ax1 = new IfcSchema::IfcAxis1Placement(file.addTriplet<IfcSchema::IfcCartesianPoint>(0,100,0), file.addTriplet<IfcSchema::IfcDirection>(1,0,0));
|
||||
IfcSchema::IfcRevolvedAreaSolid* revolution1 = new IfcSchema::IfcRevolvedAreaSolid(profile, file.addPlacement3d(), ax1, 360.);
|
||||
IfcSchema::IfcRevolvedAreaSolid* revolution2 = new IfcSchema::IfcRevolvedAreaSolid(profile, file.addPlacement3d(), ax1, 90.);
|
||||
file.addEntity(ax1);
|
||||
file.addEntity(revolution1);
|
||||
file.addEntity(revolution2);
|
||||
file.add_entity(ax1);
|
||||
file.add_entity(revolution1);
|
||||
file.add_entity(revolution2);
|
||||
|
||||
create_product_from_item(file, extrusion, "SweptSolid");
|
||||
create_product_from_item(file, revolution1, "SweptSolid");
|
||||
create_product_from_item(file, revolution2, "SweptSolid");
|
||||
}
|
||||
|
||||
void create_products_from_curve(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::IfcBoundedCurve* curve) {
|
||||
void create_products_from_curve(hierarchy_helper& file, IfcSchema::IfcBoundedCurve* curve) {
|
||||
IfcSchema::IfcArbitraryOpenProfileDef* open = new IfcSchema::IfcArbitraryOpenProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_CURVE, null, curve);
|
||||
IfcSchema::IfcCenterLineProfileDef* center_line = new IfcSchema::IfcCenterLineProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, curve, 10.);
|
||||
file.addEntity(open);
|
||||
file.addEntity(center_line);
|
||||
file.add_entity(open);
|
||||
file.add_entity(center_line);
|
||||
|
||||
create_surfaces_from_profile(file, open);
|
||||
create_solids_from_profile(file, center_line);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char filename[] = "arbitrary_open_profile_def.ifc";
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname(filename);
|
||||
const char filename[] = "IfcArbitraryOpenProfileDef.ifc";
|
||||
hierarchy_helper file;
|
||||
file.header().file_name().name(filename);
|
||||
|
||||
double coords1[] = {-50.0, 0.0};
|
||||
double coords2[] = { 50.0, 0.0};
|
||||
@@ -118,24 +117,23 @@ int main(int argc, char** argv) {
|
||||
points->push(new IfcSchema::IfcCartesianPoint(std::vector<double>(coords2, coords2+2)));
|
||||
file.addEntities(points->generalize());
|
||||
IfcSchema::IfcPolyline* poly = new IfcSchema::IfcPolyline(points);
|
||||
file.addEntity(poly);
|
||||
file.add_entity(poly);
|
||||
|
||||
create_products_from_curve(file, poly);
|
||||
|
||||
IfcSchema::IfcEllipse* ellipse = new IfcSchema::IfcEllipse(file.addPlacement2d(), 50., 25.);
|
||||
file.addEntity(ellipse);
|
||||
aggregate_of_instance::ptr trim1(new aggregate_of_instance);
|
||||
aggregate_of_instance::ptr trim2(new aggregate_of_instance);
|
||||
file.add_entity(ellipse);
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
trim1->push(new IfcSchema::IfcParameterValue( 0.));
|
||||
trim2->push(new IfcSchema::IfcParameterValue(180.));
|
||||
IfcSchema::IfcTrimmedCurve* trim = new IfcSchema::IfcTrimmedCurve(ellipse, trim1->as<IfcSchema::IfcTrimmingSelect>(), trim2->as<IfcSchema::IfcTrimmingSelect>(), true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
file.addEntity(trim);
|
||||
IfcSchema::IfcTrimmedCurve* trim = new IfcSchema::IfcTrimmedCurve(ellipse, trim1, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
file.add_entity(trim);
|
||||
|
||||
create_products_from_curve(file, trim);
|
||||
|
||||
using namespace std::string_literals;
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("arbitrary_open_profile_def"s);
|
||||
file.getSingle<Ifc2x3::IfcProject>()->setName("IfcArbitraryOpenProfileDef");
|
||||
|
||||
std::ofstream f(filename);
|
||||
f << file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,18 +27,18 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char filename[] = "composite_profile_def.ifc";
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname(filename);
|
||||
const char filename[] = "IfcCompositeProfileDef.ifc";
|
||||
hierarchy_helper file;
|
||||
file.header().file_name().name(filename);
|
||||
|
||||
double coords1[] = {100.0, 0.0};
|
||||
double coords2[] = {200.0, 0.0};
|
||||
@@ -65,11 +65,11 @@ int main(int argc, char** argv) {
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, file.addPlacement2d(80.), 50.0, 25.0, 5.0, 10.0, 2.0, null);
|
||||
|
||||
file.addEntity(p2);
|
||||
file.addEntity(p3);
|
||||
file.add_entity(p2);
|
||||
file.add_entity(p3);
|
||||
|
||||
file.addEntity(transform1);
|
||||
file.addEntity(transform2);
|
||||
file.add_entity(transform1);
|
||||
file.add_entity(transform2);
|
||||
|
||||
IfcSchema::IfcDerivedProfileDef* p5 = new IfcSchema::IfcDerivedProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, p2, transform1, null);
|
||||
IfcSchema::IfcDerivedProfileDef* p6 = new IfcSchema::IfcDerivedProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, p3, transform2, null);
|
||||
@@ -95,8 +95,8 @@ int main(int argc, char** argv) {
|
||||
IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(composite,
|
||||
file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);
|
||||
|
||||
file.addEntity(composite);
|
||||
file.addEntity(solid);
|
||||
file.add_entity(composite);
|
||||
file.add_entity(solid);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
|
||||
@@ -107,13 +107,12 @@ int main(int argc, char** argv) {
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
file.add_entity(rep);
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
|
||||
using namespace std::string_literals;
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("composite_profile_def"s);
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("IfcCompositeProfileDef");
|
||||
|
||||
std::ofstream f(filename);
|
||||
f << file;
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
class Node {
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
return operate(OP_INTERSECT, p);
|
||||
}
|
||||
|
||||
IfcSchema::IfcRepresentationItem* serialize(IfcHierarchyHelper<IfcSchema>& file) const {
|
||||
IfcSchema::IfcRepresentationItem* serialize(hierarchy_helper& file) const {
|
||||
IfcSchema::IfcRepresentationItem* my;
|
||||
if (op == OP_TERMINAL) {
|
||||
IfcSchema::IfcAxis2Placement3D* place = file.addPlacement3d(x,y,z,zx,zy,zz,xx,xy,xz);
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
my = new IfcSchema::IfcRightCircularCone(place, b, a);
|
||||
}
|
||||
} else {
|
||||
IfcSchema::IfcBooleanOperator o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION;
|
||||
IfcSchema::IfcBooleanOperator::IfcBooleanOperator o;
|
||||
if (op == OP_ADD) {
|
||||
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION;
|
||||
} else if (op == OP_SUBTRACT) {
|
||||
@@ -125,17 +125,17 @@ public:
|
||||
} else if (op == OP_INTERSECT) {
|
||||
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION;
|
||||
}
|
||||
my = new IfcSchema::IfcBooleanResult(o, left->serialize(file)->as<IfcSchema::IfcBooleanOperand>(), right->serialize(file)->as<IfcSchema::IfcBooleanOperand>());
|
||||
my = new IfcSchema::IfcBooleanResult(o, left->serialize(file), right->serialize(file));
|
||||
}
|
||||
file.addEntity(my);
|
||||
file.add_entity(my);
|
||||
return my;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char filename[] = "csg_primitive.ifc";
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname(filename);
|
||||
const char filename[] = "IfcCsgPrimitive.ifc";
|
||||
hierarchy_helper file;
|
||||
file.header().file_name().name(filename);
|
||||
|
||||
IfcSchema::IfcRepresentationItem* csg1 = Node::Box(8000.,6000.,3000.).subtract(
|
||||
Node::Box(7600.,5600.,2800.).move(200.,200.,200.)
|
||||
@@ -181,13 +181,12 @@ int main(int argc, char** argv) {
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
file.add_entity(rep);
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
|
||||
using namespace std::string_literals;
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("csg_primitive"s);
|
||||
file.getSingle<IfcSchema::IfcProject>()->setName("IfcCompositeProfileDef");
|
||||
|
||||
std::ofstream f(filename);
|
||||
f << file;
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
typedef struct {
|
||||
@@ -44,7 +44,7 @@ typedef struct {
|
||||
|
||||
static int i = 0;
|
||||
|
||||
void create_testcase_for(IfcHierarchyHelper<IfcSchema>& file, const EllipsePie& pie, IfcSchema::IfcTrimmingPreference pref) {
|
||||
void create_testcase_for(hierarchy_helper& file, const EllipsePie& pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference pref) {
|
||||
const double deg = 1. / 180. * 3.141592653;
|
||||
double flt1[] = {0. , 0. };
|
||||
double flt2[] = {pie.r1 * cos(pie.t1*deg), pie.r2 * sin(pie.t1*deg)};
|
||||
@@ -66,24 +66,24 @@ void create_testcase_for(IfcHierarchyHelper<IfcSchema>& file, const EllipsePie&
|
||||
|
||||
|
||||
Ifc2x3::IfcEllipse* ellipse = new Ifc2x3::IfcEllipse(file.addPlacement2d(), pie.r1, pie.r2);
|
||||
file.addEntity(ellipse);
|
||||
aggregate_of_instance::ptr trim1(new aggregate_of_instance);
|
||||
aggregate_of_instance::ptr trim2(new aggregate_of_instance);
|
||||
if (pref == IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER) {
|
||||
file.add_entity(ellipse);
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
if (pref == Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER) {
|
||||
trim1->push(new Ifc2x3::IfcParameterValue(pie.t1));
|
||||
trim2->push(new Ifc2x3::IfcParameterValue(pie.t2));
|
||||
} else {
|
||||
trim1->push(p2);
|
||||
trim2->push(p3);
|
||||
}
|
||||
Ifc2x3::IfcTrimmedCurve* trim = new Ifc2x3::IfcTrimmedCurve(ellipse, trim1->as<IfcSchema::IfcTrimmingSelect>(), trim2->as<IfcSchema::IfcTrimmingSelect>(), true, pref);
|
||||
file.addEntity(trim);
|
||||
Ifc2x3::IfcTrimmedCurve* trim = new Ifc2x3::IfcTrimmedCurve(ellipse, trim1, trim2, true, pref);
|
||||
file.add_entity(trim);
|
||||
|
||||
Ifc2x3::IfcCompositeCurveSegment::list::ptr segments(new Ifc2x3::IfcCompositeCurveSegment::list());
|
||||
Ifc2x3::IfcCompositeCurveSegment* s2 = new Ifc2x3::IfcCompositeCurveSegment(Ifc2x3::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, trim);
|
||||
|
||||
Ifc2x3::IfcPolyline* poly = new Ifc2x3::IfcPolyline(points);
|
||||
file.addEntity(poly);
|
||||
file.add_entity(poly);
|
||||
Ifc2x3::IfcCompositeCurveSegment* s1 = new Ifc2x3::IfcCompositeCurveSegment(Ifc2x3::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly);
|
||||
segments->push(s1);
|
||||
|
||||
@@ -92,8 +92,8 @@ void create_testcase_for(IfcHierarchyHelper<IfcSchema>& file, const EllipsePie&
|
||||
|
||||
Ifc2x3::IfcCompositeCurve* ccurve = new Ifc2x3::IfcCompositeCurve(segments, false);
|
||||
Ifc2x3::IfcArbitraryClosedProfileDef* profile = new Ifc2x3::IfcArbitraryClosedProfileDef(Ifc2x3::IfcProfileTypeEnum::IfcProfileType_AREA, null, ccurve);
|
||||
file.addEntity(ccurve);
|
||||
file.addEntity(profile);
|
||||
file.add_entity(ccurve);
|
||||
file.add_entity(profile);
|
||||
|
||||
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
|
||||
guid(), 0, S("profile"), null, null, 0, 0, null, null);
|
||||
@@ -105,7 +105,7 @@ void create_testcase_for(IfcHierarchyHelper<IfcSchema>& file, const EllipsePie&
|
||||
IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(profile,
|
||||
file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);
|
||||
|
||||
file.addEntity(solid);
|
||||
file.add_entity(solid);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
|
||||
@@ -116,33 +116,33 @@ void create_testcase_for(IfcHierarchyHelper<IfcSchema>& file, const EllipsePie&
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
file.add_entity(rep);
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const std::string filename = "ellipse_pies.ifc";
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
hierarchy_helper file;
|
||||
{ EllipsePie pie = {80., 50., 0., 150.};
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
{ EllipsePie pie = {80, 50., 30., 300.};
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
{ EllipsePie pie = {80, 50., 300., 30.};
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
{ EllipsePie pie = {50., 80., 0., 150.};
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
{ EllipsePie pie = {50, 80., 30., 300.};
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
{ EllipsePie pie = {50, 80., 300., 30.};
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
|
||||
std::ofstream f(filename.c_str());
|
||||
f << file;
|
||||
}
|
||||
|
||||
+14
-15
@@ -22,19 +22,18 @@
|
||||
* Example that generates various forms of IfcFace *
|
||||
* *
|
||||
********************************************************************************/
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
|
||||
static int x = 0;
|
||||
|
||||
void create_testcase(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::IfcFace* face, const std::string& name) {
|
||||
void create_testcase(hierarchy_helper& file, IfcSchema::IfcFace* face, const std::string& name) {
|
||||
IfcSchema::IfcFace::list::ptr faces(new IfcSchema::IfcFace::list);
|
||||
faces->push(face);
|
||||
IfcSchema::IfcOpenShell* shell = new IfcSchema::IfcOpenShell(faces);
|
||||
@@ -58,14 +57,14 @@ void create_testcase(IfcHierarchyHelper<IfcSchema>& file, IfcSchema::IfcFace* fa
|
||||
file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(shape);
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
hierarchy_helper file;
|
||||
{
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
|
||||
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
|
||||
@@ -193,7 +192,7 @@ int main(int argc, char** argv) {
|
||||
IfcSchema::IfcCartesianPoint::list::ptr trim2(new IfcSchema::IfcCartesianPoint::list);
|
||||
trim1->push(point1);
|
||||
trim2->push(point2);
|
||||
IfcSchema::IfcTrimmedCurve* trimmed_curve = new IfcSchema::IfcTrimmedCurve(circle, trim1->generalize()->as<IfcSchema::IfcTrimmingSelect>(), trim2->generalize()->as<IfcSchema::IfcTrimmingSelect>(), true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);
|
||||
IfcSchema::IfcTrimmedCurve* trimmed_curve = new IfcSchema::IfcTrimmedCurve(circle, trim1->generalize(), trim2->generalize(), true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);
|
||||
IfcSchema::IfcArbitraryOpenProfileDef* profile = new IfcSchema::IfcArbitraryOpenProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_CURVE, boost::none, trimmed_curve);
|
||||
IfcSchema::IfcAxis1Placement* place = new IfcSchema::IfcAxis1Placement(file.addTriplet<IfcSchema::IfcCartesianPoint>(0., 0., 0.), file.addTriplet<IfcSchema::IfcDirection>(1., 0., 0.));
|
||||
IfcSchema::IfcSurfaceOfRevolution* surface = new IfcSchema::IfcSurfaceOfRevolution(profile, file.addPlacement3d(), place);
|
||||
@@ -201,8 +200,8 @@ int main(int argc, char** argv) {
|
||||
IfcSchema::IfcFace* face = new IfcSchema::IfcFaceSurface(bounds, surface, true);
|
||||
create_testcase(file, face, "face surface");
|
||||
}
|
||||
const char filename[] = "faces.ifc";
|
||||
file.header().file_name()->setname(filename);
|
||||
std::ofstream f(filename);
|
||||
const std::string filename = "faces.ifc";
|
||||
file.header().file_name().name(filename);
|
||||
std::ofstream f(filename.c_str());
|
||||
f << file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,24 +27,22 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
const static double PI = boost::math::constants::pi<double>();
|
||||
#include "ifcparse\Ifc2x3.h"
|
||||
#include "ifcparse\IfcUtil.h"
|
||||
#include "ifcparse\hierarchy_helper.h"
|
||||
#include "ifcgeom\IfcGeom.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
boost::none_t const null = boost::none;
|
||||
|
||||
void create_curve_rebar(IfcHierarchyHelper<IfcSchema>& file)
|
||||
void create_curve_rebar(hierarchy_helper& file)
|
||||
{
|
||||
int dia = 24;
|
||||
int R = 3 * dia;
|
||||
int length = 12 * dia;
|
||||
|
||||
double crossSectionarea = PI * dia * dia / 4;
|
||||
double crossSectionarea = M_PI * (dia / 2) * 2;
|
||||
IfcSchema::IfcReinforcingBar* rebar = new IfcSchema::IfcReinforcingBar(
|
||||
guid(), 0, S("test"), null,
|
||||
null, 0, 0,
|
||||
@@ -73,32 +71,31 @@ void create_curve_rebar(IfcHierarchyHelper<IfcSchema>& file)
|
||||
points1->push(p2);
|
||||
file.addEntities(points1->generalize());
|
||||
IfcSchema::IfcPolyline* poly1 = new IfcSchema::IfcPolyline(points1);
|
||||
file.addEntity(poly1);
|
||||
file.add_entity(poly1);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment1 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly1);
|
||||
file.addEntity(segment1);
|
||||
file.add_entity(segment1);
|
||||
segments->push(segment1);
|
||||
|
||||
/*second segment - arc */
|
||||
IfcSchema::IfcAxis2Placement3D* axis1 = new IfcSchema::IfcAxis2Placement3D(p3, file.addTriplet<IfcSchema::IfcDirection>(1, 0, 0), file.addTriplet<IfcSchema::IfcDirection>(0, 1, 0));
|
||||
file.addEntity(axis1);
|
||||
file.add_entity(axis1);
|
||||
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(axis1, R);
|
||||
file.addEntity(circle);
|
||||
file.add_entity(circle);
|
||||
|
||||
IfcEntityList::ptr trim1(new IfcEntityList);
|
||||
IfcEntityList::ptr trim2(new IfcEntityList);
|
||||
|
||||
IfcSchema::IfcTrimmingSelect::list::ptr trim1(new IfcSchema::IfcTrimmingSelect::list);
|
||||
IfcSchema::IfcTrimmingSelect::list::ptr trim2(new IfcSchema::IfcTrimmingSelect::list);
|
||||
|
||||
trim1->push(new IfcSchema::IfcParameterValue(180));
|
||||
trim1->push(p2);
|
||||
|
||||
trim2->push(new IfcSchema::IfcParameterValue(270));
|
||||
trim2->push(p4);
|
||||
|
||||
IfcSchema::IfcTrimmedCurve* trimmed_curve = new IfcSchema::IfcTrimmedCurve(circle, trim1, trim2, false, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
|
||||
file.addEntity(trimmed_curve);
|
||||
file.add_entity(trimmed_curve);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment2 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, false, trimmed_curve);
|
||||
file.addEntity(segment2);
|
||||
file.add_entity(segment2);
|
||||
segments->push(segment2);
|
||||
|
||||
/*third segment - line */
|
||||
@@ -107,14 +104,14 @@ void create_curve_rebar(IfcHierarchyHelper<IfcSchema>& file)
|
||||
points2->push(p5);
|
||||
file.addEntities(points2->generalize());
|
||||
IfcSchema::IfcPolyline* poly2 = new IfcSchema::IfcPolyline(points2);
|
||||
file.addEntity(poly2);
|
||||
file.add_entity(poly2);
|
||||
|
||||
IfcSchema::IfcCompositeCurveSegment* segment3 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly2);
|
||||
file.addEntity(segment3);
|
||||
file.add_entity(segment3);
|
||||
segments->push(segment3);
|
||||
|
||||
IfcSchema::IfcCompositeCurve* curve = new IfcSchema::IfcCompositeCurve(segments, false);
|
||||
file.addEntity(curve);
|
||||
file.add_entity(curve);
|
||||
|
||||
IfcSchema::IfcSweptDiskSolid* solid = new IfcSchema::IfcSweptDiskSolid(curve, dia / 2, null, 0, 1);
|
||||
|
||||
@@ -126,7 +123,7 @@ void create_curve_rebar(IfcHierarchyHelper<IfcSchema>& file)
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
file.addEntity(shape);
|
||||
file.add_entity(shape);
|
||||
|
||||
rebar->setRepresentation(shape);
|
||||
|
||||
@@ -136,8 +133,8 @@ void create_curve_rebar(IfcHierarchyHelper<IfcSchema>& file)
|
||||
|
||||
int main()
|
||||
{
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname("ifc_curve_rebar.ifc");
|
||||
hierarchy_helper file;
|
||||
file.header().file_name().name("ifc_curve_rebar.ifc");
|
||||
create_curve_rebar(file);
|
||||
std::ofstream f("ifc_curve_rebar.ifc");
|
||||
f << file;
|
||||
|
||||
+44
-45
@@ -27,21 +27,21 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define IfcSchema Ifc2x3
|
||||
#include "ifcparse/Ifc2x3.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc2x3.h"
|
||||
#include "../ifcparse/IfcUtil.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
typedef IfcWrite::IfcGuidHelper guid;
|
||||
boost::none_t const null = (static_cast<boost::none_t>(0));
|
||||
|
||||
void create_testcase_for(IfcSchema::IfcProfileDef::list::ptr profiles) {
|
||||
IfcSchema::IfcProfileDef* profile = *profiles->begin();
|
||||
const std::string& profile_type = profile->declaration().name();
|
||||
const std::string profile_type = IfcSchema::Type::ToString(profile->type());
|
||||
const std::string filename = profile_type + ".ifc";
|
||||
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
file.header().file_name()->setname(filename);
|
||||
hierarchy_helper file;
|
||||
file.filename(filename);
|
||||
|
||||
int i = 0;
|
||||
for (IfcSchema::IfcProfileDef::list::it it = profiles->begin(); it != profiles->end(); ++it, ++i) {
|
||||
@@ -54,15 +54,15 @@ void create_testcase_for(IfcSchema::IfcProfileDef::list::ptr profiles) {
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement(0, 100. * i));
|
||||
|
||||
if (profile->declaration().is(IfcSchema::IfcParameterizedProfileDef::Class())) {
|
||||
if (profile->is(IfcSchema::Type::IfcParameterizedProfileDef)) {
|
||||
((IfcSchema::IfcParameterizedProfileDef*) profile)->setPosition(file.addPlacement2d());
|
||||
}
|
||||
|
||||
IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(profile,
|
||||
file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);
|
||||
|
||||
file.addEntity(profile);
|
||||
file.addEntity(solid);
|
||||
file.add_entity(profile);
|
||||
file.add_entity(solid);
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);
|
||||
@@ -72,9 +72,9 @@ void create_testcase_for(IfcSchema::IfcProfileDef::list::ptr profiles) {
|
||||
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("SweptSolid"), items);
|
||||
reps->push(rep);
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
|
||||
file.addEntity(rep);
|
||||
file.addEntity(shape);
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
|
||||
file.add_entity(rep);
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
}
|
||||
@@ -84,123 +84,122 @@ void create_testcase_for(IfcSchema::IfcProfileDef::list::ptr profiles) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
IfcSchema::get_schema(); // Ensure schema is initialized
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcUShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcUShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, null));
|
||||
profiles->push(new IfcSchema::IfcUShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcUShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, null, null));
|
||||
profiles->push(new IfcSchema::IfcUShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcUShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, 4.0, null));
|
||||
profiles->push(new IfcSchema::IfcUShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcUShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, 1.0, 3.0, 6.0, null));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcTShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, null, null, null));
|
||||
profiles->push(new IfcSchema::IfcTShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, 2.0, null, null, null));
|
||||
profiles->push(new IfcSchema::IfcTShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, 2.0, 2.0, null));
|
||||
profiles->push(new IfcSchema::IfcTShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, 3.0, 2.0, 1.0, 2.0, 2.0, null));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcZShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcZShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, null, null));
|
||||
profiles->push(new IfcSchema::IfcZShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcZShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcEllipseProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcEllipseProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 25.0, 15.0));
|
||||
profiles->push(new IfcSchema::IfcEllipseProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcEllipseProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 15.0, 25.0));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcIShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcIShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 25.0, 50.0, 5.0, 5.0, null));
|
||||
profiles->push(new IfcSchema::IfcIShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcIShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 25.0, 50.0, 5.0, 5.0, 2.0));
|
||||
profiles->push(new IfcSchema::IfcAsymmetricIShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcAsymmetricIShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 25.0, 50.0, 5.0, 5.0, 2.0, 20.0, 10.0, 5.0, null));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcLShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcLShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, null, null, null, null, null));
|
||||
profiles->push(new IfcSchema::IfcLShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcLShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 2.0, 2.0, null, null, null));
|
||||
profiles->push(new IfcSchema::IfcLShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcLShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, null, null, 2.0, null, null));
|
||||
profiles->push(new IfcSchema::IfcLShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcLShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 1.0, 2.0, 2.0, null, null));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcCShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcCShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 10.0, null, null));
|
||||
profiles->push(new IfcSchema::IfcCShapeProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcCShapeProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 10.0, 2.0, null));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcCircleProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcCircleProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 25.0));
|
||||
profiles->push(new IfcSchema::IfcCircleHollowProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcCircleHollowProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 25.0, 5.0));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcRectangleProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcRectangleProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0));
|
||||
profiles->push(new IfcSchema::IfcRoundedRectangleProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcRoundedRectangleProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0));
|
||||
profiles->push(new IfcSchema::IfcRectangleHollowProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcRectangleHollowProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, null, null));
|
||||
profiles->push(new IfcSchema::IfcRectangleHollowProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcRectangleHollowProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 25.0, 5.0, 2.0, 4.0));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
|
||||
profiles->push(new IfcSchema::IfcTrapeziumProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTrapeziumProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 30.0, 25.0, 0.0));
|
||||
profiles->push(new IfcSchema::IfcTrapeziumProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTrapeziumProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 60.0, 25.0, -20.0));
|
||||
profiles->push(new IfcSchema::IfcTrapeziumProfileDef(
|
||||
profiles->push(new Ifc2x3::IfcTrapeziumProfileDef(
|
||||
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
|
||||
null, 0, 50.0, 10.0, 25.0, 30.0));
|
||||
create_testcase_for(profiles); }
|
||||
|
||||
@@ -23,19 +23,15 @@
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
|
||||
#define IfcSchema Ifc4
|
||||
#include "ifcparse/Ifc4.h"
|
||||
#include "ifcparse/IfcHierarchyHelper.h"
|
||||
#include "../ifcparse/schemas/Ifc4.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
|
||||
#include "suzanne_geometry.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
typedef std::string S;
|
||||
typedef IfcParse::IfcGlobalId guid;
|
||||
boost::none_t const null = boost::none;
|
||||
typedef ifcopenshell::global_id guid;
|
||||
|
||||
template <typename T>
|
||||
std::vector< std::vector<T> > create_vector_from_array(const T* arr, unsigned size) {
|
||||
@@ -54,36 +50,31 @@ std::vector< std::vector<T> > create_vector_from_array(const T* arr, unsigned si
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
IfcHierarchyHelper<IfcSchema> file;
|
||||
hierarchy_helper<Ifc4> file;
|
||||
|
||||
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
|
||||
guid(), 0, S("Blender's Suzanne"), null, null, 0, 0, null, null);
|
||||
Ifc4::IfcBuildingElementProxy product = file.create<Ifc4::IfcBuildingElementProxy>().initialize(
|
||||
guid(), std::nullopt, S("Blender's Suzanne"), std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);
|
||||
file.addBuildingProduct(product);
|
||||
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
product.setOwnerHistory(file.getSingle<Ifc4::IfcOwnerHistory>());
|
||||
|
||||
product->setObjectPlacement(file.addLocalPlacement());
|
||||
|
||||
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
|
||||
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);
|
||||
product.setObjectPlacement(file.addLocalPlacement());
|
||||
|
||||
std::vector< std::vector< double > > vertices_vector = create_vector_from_array(vertices, sizeof(vertices) / sizeof(vertices[0]));
|
||||
std::vector< std::vector< int > > indices_vector = create_vector_from_array(indices, sizeof(indices) / sizeof(indices[0]));
|
||||
|
||||
IfcSchema::IfcCartesianPointList3D* coordinates = new IfcSchema::IfcCartesianPointList3D(vertices_vector);
|
||||
IfcSchema::IfcTriangulatedFaceSet* faceset = new IfcSchema::IfcTriangulatedFaceSet(coordinates, null, null, indices_vector, null);
|
||||
Ifc4::IfcCartesianPointList3D coordinates = file.create<Ifc4::IfcCartesianPointList3D>().initialize(vertices_vector);
|
||||
Ifc4::IfcTriangulatedFaceSet faceset = file.create<Ifc4::IfcTriangulatedFaceSet>().initialize(coordinates, std::nullopt, std::nullopt, indices_vector, std::nullopt);
|
||||
|
||||
items->push(faceset);
|
||||
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
|
||||
file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items);
|
||||
reps->push(rep);
|
||||
Ifc4::IfcShapeRepresentation rep = file.create<Ifc4::IfcShapeRepresentation>().initialize(
|
||||
file.getRepresentationContext("Model"), "Body", "SurfaceModel", {faceset});
|
||||
|
||||
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
|
||||
file.addEntity(shape);
|
||||
Ifc4::IfcProductDefinitionShape shape = file.create<Ifc4::IfcProductDefinitionShape>().initialize(std::nullopt, std::nullopt, {rep});
|
||||
file.add_entity(shape);
|
||||
|
||||
product->setRepresentation(shape);
|
||||
product.setRepresentation(shape);
|
||||
|
||||
const std::string filename = "triangulated_faceset.ifc";
|
||||
file.header().file_name()->setname(filename);
|
||||
std::ofstream f(filename);
|
||||
const std::string filename = "tesselated_faceset.ifc";
|
||||
file.header().file_name().setname(filename);
|
||||
std::ofstream f(filename.c_str());
|
||||
f << file;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user