Compare commits

..

1 Commits

Author SHA1 Message Date
Andrej730 54df93c001 build_pyodide: debug 2026-08-07 12:12:36 +05:00
593 changed files with 17257 additions and 13523 deletions
+1
View File
@@ -1,4 +1,5 @@
Checks: 'bugprone-*,cert-*,clang-analyzer-*,readability-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: none
+4 -7
View File
@@ -35,9 +35,6 @@ jobs:
lfs: true
token: ${{ secrets.BUILD_REPO_TOKEN }}
- name: Set up uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install Dependencies
run: |
brew update
@@ -64,7 +61,7 @@ jobs:
- name: Unpack Dependencies
run: |
cd build
uv run ../nix/cache_dependencies.py unpack
python ../nix/cache_dependencies.py unpack
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.23
@@ -105,7 +102,7 @@ jobs:
# INSTALL_RPATH to "@loader_path" on Apple.
CXXFLAGS="-O3" CFLAGS="-O3 ${DARWIN_C_SOURCE}" ADD_COMMIT_SHA=1 BUILD_CFG=Release \
BUILD_BONSAIVIEWER=ON QT_DIR="${QT_DIR}" \
uv run ./nix/build-all.py -v --diskcleanup --ifcopenshell-shared ${MAC_INTEL} \
python3 ./nix/build-all.py -v --diskcleanup --ifcopenshell-shared ${MAC_INTEL} \
| tee build.log
- name: Upload Build Logs
@@ -122,7 +119,7 @@ jobs:
- name: Pack Dependencies
run: |
cd build
uv run ../nix/cache_dependencies.py pack
python ../nix/cache_dependencies.py pack
- name: Commit and Push Changes to Build Repository
run: |
@@ -139,7 +136,7 @@ jobs:
# packaging/build.py stages the connector binary + connector.json
# into dist/autodesk/; the .app loop below copies that folder into
# the bundle. Same on-disk shape as the Linux and Windows builds.
uv run src/bonsaiviewer-autodesk/packaging/build.py
python3 src/bonsaiviewer-autodesk/packaging/build.py
autodesk_connector_dir="$PWD/src/bonsaiviewer-autodesk/dist/autodesk"
test -d "$autodesk_connector_dir"
+4
View File
@@ -92,6 +92,10 @@ jobs:
./run_pytest.py setup
./run_pytest.py run
- name: Setup tmate session
if: failure()
uses: mxschmitt/action-tmate@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
+1 -1
View File
@@ -72,7 +72,7 @@ jobs:
python-version: '3.11'
- name: Get current version
id: version
run: echo "version=$(sed -E 's/[[:alpha:]]+[0-9]+$//' VERSION)" >> $GITHUB_OUTPUT
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Compile
run: |
cd src/bonsai && make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
@@ -27,9 +27,7 @@ jobs:
- name: Get current version
id: version
# Strip any trailing prerelease label and number; the dated alpha
# suffix is added below.
run: echo "version=$(sed -E 's/[[:alpha:]]+[0-9]+$//' VERSION)" >> $GITHUB_OUTPUT
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Get current date
id: date
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
python-version: '3.11'
- name: Get current version
id: version
run: echo "version=$(sed -E 's/[[:alpha:]]+[0-9]+$//' VERSION)" >> $GITHUB_OUTPUT
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
- name: Get current date
id: date
run: echo "date=$(date +'%y%m%d')" >> $GITHUB_OUTPUT
+2 -13
View File
@@ -43,7 +43,6 @@ jobs:
sudo apt update
sudo apt-get install --no-install-recommends -y \
cmake \
bison \
gcc \
g++ \
libboost-date-time-dev \
@@ -62,23 +61,13 @@ jobs:
libocct-ocaf-dev \
libocct-visualization-dev \
libpcre3-dev \
libpcre2-dev \
libtbb-dev \
libxml2-dev \
libxi-dev \
occt-misc \
tcl-dev \
tk-dev
- name: Build SWIG
# IfcOpenShell requires SWIG 4.1+, ubuntu-22.04 ships 4.0.2.
run: |
sudo apt-get remove --purge -y swig swig4.0
git clone https://github.com/swig/swig --branch v4.2.1 --depth 1
cmake -S swig -B swig/build -DCMAKE_BUILD_TYPE=Release
cmake --build swig/build -j "$(nproc)"
sudo cmake --install swig/build
swig -version
tk-dev \
swig
- name: Configure minimal IfcOpenShell
run: |
+1 -1
View File
@@ -121,7 +121,7 @@ jobs:
cd OpenCOLLADA
git checkout v1.6.68
patch -p1 --batch --forward -i ../nix/patches/opencollada/pr622_and_disable_subdirs.patch
patch -p1 --batch --forward -i ../nix/patches/opencollada/config_select_libs_by_use_shared.patch
patch -p1 --batch --forward -i ../nix/patches/opencollada/allow_static_libraries_config_on_unix.patch
mkdir build && cd build
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
@@ -1,87 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
name: Publish C++ API documentation
on:
push:
branches:
- v0.9.0
paths:
- '.github/workflows/publish-cpp-api-docs.yml'
- 'docs/cpp-api/**'
- 'src/ifcgeom/**'
- 'src/ifcparse/**'
- 'src/serializers/**'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: publish-cpp-api-docs
cancel-in-progress: false
jobs:
publish:
if: github.repository == 'IfcOpenShell/IfcOpenShell'
runs-on: ubuntu-24.04
steps:
- name: Checkout IfcOpenShell
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.10'
- name: Install documentation dependencies
run: |
sudo apt-get update
sudo apt-get install --yes doxygen graphviz
python -m pip install --requirement docs/cpp-api/requirements.txt
- name: Build C++ API documentation
working-directory: docs/cpp-api
run: |
export PROJECT_NUMBER="$(git rev-parse --short HEAD)"
python -m sphinx -M html . output -W --keep-going
- name: Checkout documentation repository
uses: actions/checkout@v7
with:
repository: IfcOpenShell/cpp_docs
ref: master
path: published-docs
token: ${{ secrets.BUILD_REPO_TOKEN }}
- name: Replace published documentation
run: |
publish_tree="${RUNNER_TEMP}/published-docs-tree"
mkdir -p "${publish_tree}/v0.9.0-latest"
rsync --archive docs/cpp-api/output/html/ "${publish_tree}/v0.9.0-latest/"
touch "${publish_tree}/.nojekyll"
if [[ -f published-docs/CNAME ]]; then
cp published-docs/CNAME "${publish_tree}/CNAME"
fi
rsync --archive --delete --exclude='.git/' "${publish_tree}/" published-docs/
- name: Commit and push if changed
working-directory: published-docs
env:
SOURCE_SHA: ${{ github.sha }}
run: |
git config user.name 'IfcOpenBot'
git config user.email 'IfcOpenBot@users.noreply.github.com'
git add --all
if git diff --cached --quiet; then
echo "No changes to commit"
exit 0
fi
git commit -m "Update C++ API docs from ${SOURCE_SHA:0:7}"
git push origin master
+3 -7
View File
@@ -111,11 +111,10 @@ src/ifcopenshell-python/ifcopenshell/_ifcopenshell_wrapper*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.py
# plugins
src/ifcopenshell-python/ifcopenshell/ifcopenshell_document_*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_geometry_*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell_parse_schema*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell.document.*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell.geometry.*.so
src/ifcopenshell-python/ifcopenshell/ifcopenshell.parse.schema*.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.geometry.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.geometry.writer.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.parse.so
src/ifcopenshell-python/ifcopenshell/libifcopenshell.plugin.so
@@ -126,9 +125,6 @@ src/ifcopenshell-python/ifcopenshell/libifcopenshell.plugin.so
.clangd
# clangd cache
.cache
# Useful for symlinking json compilation database from cmake,
# allowing clang commands without `-p path/to/build`.
/compile_commands.json
# Brickschema
src/bonsai/bonsai/bim/schema/Brick.ttl
+3
View File
@@ -8,6 +8,9 @@
[submodule "src/ifcopenshell-python/test/Sample-BIM-Files"]
path = src/ifcopenshell-python/test/Sample-BIM-Files
url = https://github.com/IfcOpenShell/ids-test-files
[submodule "docs/cpp-api/assets/doxygen-awesome-css"]
path = docs/cpp-api/assets/doxygen-awesome-css
url = https://github.com/jothepro/doxygen-awesome-css.git
[submodule "src/ifcopenshell-python/ifcopenshell/simple_spf"]
path = src/ifcopenshell-python/ifcopenshell/simple_spf
url = https://github.com/IfcOpenShell/step-file-parser
+1 -1
View File
@@ -1 +1 @@
0.9.0alpha0
0.8.6
+14 -19
View File
@@ -36,14 +36,6 @@ file(READ "../VERSION" "RELEASE_VERSION_")
string(STRIP "${RELEASE_VERSION_}" RELEASE_VERSION)
message(STATUS "Detected version '${RELEASE_VERSION}'")
# CMake's project(VERSION) only accepts numeric components. Keep the complete
# release identifier for build information, but use its numeric release part
# for PROJECT_VERSION, SOVERSION, and generated CMake package metadata.
string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" PROJECT_VERSION_NUMERIC "${RELEASE_VERSION}")
if(NOT PROJECT_VERSION_NUMERIC)
message(FATAL_ERROR "VERSION must start with a numeric major.minor.patch version: '${RELEASE_VERSION}'")
endif()
add_definitions(-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
if(POLICY CMP0141) # 3.25+
@@ -63,15 +55,9 @@ endif()
# Include utility macros and functions
include(utilities.cmake)
# Use a SemVer-compatible spelling for CPack artifact names. A trailing
# alphabetic label and number is separated from the numeric version by a
# hyphen: for example, 0.9.0alpha0 becomes 0.9.0-alpha0.
# use extra version to make pre-release using eg semver
if(NOT DEFINED EXTRA_VERSION)
if(RELEASE_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+([A-Za-z]+)([0-9]+)$")
set(EXTRA_VERSION "-${CMAKE_MATCH_1}${CMAKE_MATCH_2}")
else()
set(EXTRA_VERSION "")
endif()
set(EXTRA_VERSION "-alpha.3")
endif()
option(MINIMAL_BUILD "The build is to make a minimal version of IFC converter from OCCT into IFC." OFF)
@@ -133,7 +119,7 @@ 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 "Use VERSION as the branch label when commit information is embedded" OFF)
option(VERSION_OVERRIDE "Override the version defined in buildinfo.cpp with the file VERSION in the repository root" OFF)
set(
PYTHON_MODULE_INSTALL_DIR
@@ -141,7 +127,15 @@ set(
"Directory to install IfcPython package to. By default package is installed in found Python's site-packages."
)
project(IfcOpenShell VERSION ${PROJECT_VERSION_NUMERIC})
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})
@@ -699,7 +693,8 @@ endif()
# Documentation
if(BUILD_DOCUMENTATION)
add_subdirectory(../docs/cpp-api docs/cpp-api)
set(CMAKE_MODULE_PATH "../docs/cmake")
add_subdirectory(../docs docs)
endif()
if(BUILD_EXAMPLES)
-1
View File
@@ -52,7 +52,6 @@ macro(SET_INSTALL_SELF_RPATH _target)
endmacro()
function(ifcopenshell_plugin_target TARGET)
# Plug-ins are loaded by exact filename and should not receive a platform library prefix.
set_target_properties(${TARGET} PROPERTIES PREFIX "")
if((NOT WIN32) AND BUILD_SHARED_LIBS AND NOT WASM_BUILD AND NOT CREATE_BUNDLE AND NOT CMAKE_INSTALL_RPATH AND COMMAND SET_INSTALL_SELF_RPATH)
SET_INSTALL_SELF_RPATH(${TARGET})
+34 -14
View File
@@ -1,15 +1,35 @@
find_package(Doxygen REQUIRED)
find_program(
SPHINX_EXECUTABLE
NAMES sphinx-build
REQUIRED
DOC "Path to the sphinx-build executable"
)
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE NAMES sphinx-build DOC "Path to sphinx-build executable")
add_custom_target(
cpp_api_docs
COMMAND ${SPHINX_EXECUTABLE} -M html . output -W --keep-going
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating the IfcOpenShell C++ API documentation"
VERBATIM
)
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx "Failed to find sphinx-build executable" SPHINX_EXECUTABLE)
find_package(Doxygen REQUIRED)
#find_package(Sphinx REQUIRED)
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
message(STATUS "SPHINX BUILD ${CMAKE_CURRENT_BINARY_DIR}")
file(MAKE_DIRECTORY ./output/doxygen)
if(DOXYGEN_FOUND)
add_custom_target(
Sphinx
ALL
COMMAND ${SPHINX_EXECUTABLE} -v -T -b html ${SPHINX_SOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/output
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output
COMMENT "Generating documentation with Sphinx"
)
# add_custom_target(ifcopenshell_python_docs ALL
# COMMAND make html
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# COMMENT "Generating documentation with Sphinx")
else(DOXYGEN_FOUND)
message("Doxygen need to be installed to generate the doxygen documentation")
endif(DOXYGEN_FOUND)
+22 -63
View File
@@ -68,7 +68,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = ./output/doxygen
OUTPUT_DIRECTORY = ./output
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
@@ -852,7 +852,7 @@ WARNINGS = YES
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = NO
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as documenting some parameters in
@@ -901,7 +901,7 @@ WARN_IF_UNDOC_ENUM_VAL = NO
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO.
WARN_AS_ERROR = FAIL_ON_WARNINGS
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
@@ -944,6 +944,7 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = ../../src/ifcgeom \
../../src/ifcgeom_schema_agnostic \
../../src/ifcparse \
../../src/serializers \
@@ -1000,7 +1001,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = ../../src/ifcparse/schemas
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -1024,33 +1025,7 @@ EXCLUDE_PATTERNS =
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
EXCLUDE_SYMBOLS = "ifcopenshell::geom::opaque_number::*" \
ifcopenshell::entity::attribute_by_name_cmp \
ifcopenshell::impl::rocks_db_file_storage::rocksdb_types_iterator \
ifcopenshell::impl::in_memory_file_storage::type_iterator \
"util::string_buffer::*_item" \
util::string_buffer::item \
ifcopenshell::geom::layer_filter::wildcards_match \
ifcopenshell::paged_file_impl::entry \
ifcopenshell::token \
attribute_value::pointer_type \
INCLUDE_PARENT_PARENT_DIR \
POSTFIX_SCHEMA_ \
POSTFIX_SCHEMA__ \
STRINGIFY_ \
MAKE_INIT_FN_ \
MAKE_INIT_FN__ \
key_from_string \
add_ \
subtract_ \
multiply_ \
divide_ \
equals_ \
less_than_ \
negate_ \
ifcopenshell::geom::utils::create_cube \
ifcopenshell::geom::utils::create_polyhedron \
ifcopenshell::geom::utils::create_nef_polyhedron
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
@@ -1261,7 +1236,7 @@ IGNORE_PREFIX =
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = NO
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1336,7 +1311,7 @@ HTML_STYLESHEET =
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_STYLESHEET = assets/doxygen-awesome-css/doxygen-awesome.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -2191,7 +2166,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = YES
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -2328,7 +2303,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -2336,7 +2311,7 @@ MACRO_EXPANSION = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = YES
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
@@ -2369,17 +2344,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = IFC_PARSE_API= \
IFC_SCHEMA_API= \
IFC_GEOM_API= \
IFC_GEOMLIBRARY_API= \
IFC_GEOMSERIALIZATION_API= \
SERIALIZERS_API= \
"POSTFIX_SCHEMA(name)=name##_Schema" \
"Handle(name):=opencascade::handle<name>" \
kernel_=kernel \
Simplekernel_=Simplekernel \
inline=
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -2388,22 +2353,7 @@ PREDEFINED = IFC_PARSE_API= \
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED = kernel_ \
cgal_shape \
cgal_kernel \
cgal_placement \
cgal_point \
cgal_direction \
cgal_vector \
cgal_plane \
cgal_curve \
cgal_wire \
cgal_face \
cgal_polyhedron \
cgal_vertex_descriptor \
cgal_face_descriptor \
create_cube \
create_polyhedron
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
@@ -2781,6 +2731,15 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
+18 -41
View File
@@ -1,56 +1,33 @@
# IfcOpenShell C++ API documentation
This directory contains the Sphinx, Doxygen, Breathe, and Exhale configuration
for the IfcOpenShell C++ API reference. During a Sphinx build, Exhale runs
Doxygen, Breathe consumes the generated XML, and Exhale creates the API pages.
## Prerequisites
- Python 3.10 or newer
- [Doxygen](https://www.doxygen.nl/)
- [Graphviz](https://graphviz.org/)
Install the Python dependencies from this directory:
```shell
python -m pip install -r requirements.txt
```
Both `doxygen` and `dot` must be available on `PATH`. For the standard Windows
install locations, this can be done for the current PowerShell session with:
```powershell
$env:Path = "C:\Program Files\doxygen\bin;C:\Program Files\Graphviz\bin;$env:Path"
```
This folder contains the setup to build the IfcOpenShell C++ API documentation from the source code.
## Generating the documentation
From this directory, run:
> Prerequisites:
>
> Make sure to have [Doxygen](https://www.doxygen.nl) and [Graphviz](https://graphviz.org) installed into your `$PATH` variable.
>
> The documentation also use the [doxygen-awesome](https://jothepro.github.io/doxygen-awesome-css) theme as a git submodule.
Build with the command (from within the `/docs/cpp-api` folder):
```shell
python -m sphinx -M html . output -W --keep-going
$ doxygen
```
To include the current Git commit in Doxygen's project metadata, set
`PROJECT_NUMBER` before building. For example, in PowerShell:
```powershell
$env:PROJECT_NUMBER = git rev-parse --short HEAD
python -m sphinx -M html . output -W --keep-going
```
Or in a POSIX shell:
To include the current git commit hash into the build documentation, use the following command:
```shell
PROJECT_NUMBER=$(git rev-parse --short HEAD) python -m sphinx -M html . output -W --keep-going
$ PROJECT_NUMBER=$(git rev-parse --short HEAD) doxygen
```
Alternatively, configure the main CMake project with
`-DBUILD_DOCUMENTATION=ON` and build the `cpp_api_docs` target.
This will extract the current commit hash in short version and sets the propper ENV variable used by doxygen.
The generated documentation is written to `output/html/index.html`. The
generated Doxygen XML and Exhale sources are kept under `output/` as build
artifacts.
The generation of the documentation might take a while depending on your systems hardware, as it is configured to generate the Class graphs using .
The generated headers under `src/ifcparse/schemas` are intentionally excluded
from this documentation build.
The resulting documentation is located unter `/cpp-api/output/html` and can be directly accessed with your browser:
```shell
$ open ./output/html/index.html
```
-59
View File
@@ -1,59 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
import warnings
from pathlib import Path
from shutil import rmtree
from sphinx.deprecation import RemovedInSphinx90Warning
warnings.filterwarnings("ignore", category=RemovedInSphinx90Warning, module=r"exhale\.configs")
generated_directories = (
Path(__file__).parent / "output" / "api",
Path(__file__).parent / "output" / "doxygen",
)
for generated_directory in generated_directories:
if generated_directory.is_dir():
rmtree(generated_directory)
project = "IfcOpenShell"
copyright = "2020, IfcOpenShell"
extensions = [
"breathe",
"exhale",
]
primary_domain = "cpp"
highlight_language = "cpp"
html_theme = "alabaster"
breathe_projects = {
"IfcOpenShell": "./output/doxygen/xml",
}
breathe_default_project = "IfcOpenShell"
exhale_args = {
"containmentFolder": "./output/api",
"rootFileName": "library_root.rst",
"rootFileTitle": "IfcOpenShell C++ API",
"doxygenStripFromPath": "../..",
"createTreeView": False,
"exhaleExecutesDoxygen": True,
"exhaleUseDoxyfile": True,
}
cpp_id_attributes = [
"IFC_PARSE_API",
"IFC_SCHEMA_API",
"IFC_GEOM_API",
"IFC_GEOMLIBRARY_API",
"IFC_GEOMSERIALIZATION_API",
"SERIALIZERS_API",
]
exclude_patterns = [
"output/doctrees",
"output/doxygen",
"output/html",
]
-9
View File
@@ -1,9 +0,0 @@
.. This file was generated with the assistance of an AI coding tool.
IfcOpenShell C++ API
====================
.. toctree::
:maxdepth: 2
output/api/library_root
-5
View File
@@ -1,5 +0,0 @@
# This file was generated with the assistance of an AI coding tool.
Sphinx==8.1.3
breathe==4.36.0
exhale==0.3.7
+37 -54
View File
@@ -1,8 +1,5 @@
#!/usr/bin/python
# /// script
# dependencies = [
# "typing_extensions",
# ]
# ///
###############################################################################
# #
@@ -35,7 +32,6 @@ Example usage:
Available arguments:
``-py-313`` - build for specific Python version
(building for all supported Python version by default).
``-occt-xxx`` - use a specific OCCT version (e.g. ``-occt-7.8.1``) instead of the default
``-wasm`` - compile for wasm
``-without-xxx`` - do not build dependency ``xxx`` (e.g. ``--without-swig``)
``-mac-cross-compile-intel`` - cross compile for Intel Mac on Apple Silicon host
@@ -43,8 +39,6 @@ Available arguments:
``-ifcopenshell-shared`` - build only IfcOpenShell's own libraries as shared
(dependencies stay static). Redundant if ``-shared`` is also passed.
``-diskcleanup`` - clean up build directories after finishing building dependencies
``-build-examples`` - build IfcOpenShell examples
``-lto`` - enable link-time optimization (adds ``-flto`` to compiler flags)
``-v`` - enable verbose logs
@@ -137,8 +131,6 @@ from pathlib import Path
from typing import Literal
from urllib.request import urlretrieve
from typing_extensions import assert_never
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
@@ -225,11 +217,6 @@ def cecho(message, color=NO_COLOR):
# Flags.
BUILD_EXAMPLES = "build-examples" in flags
DISK_CLEANUP = "diskcleanup" in flags
LTO = "lto" in flags
VERBOSE = "v" in flags
APPLE = platform.system() == "Darwin"
MAC_CROSS_COMPILE_INTEL = "mac-cross-compile-intel" in flags
assert platform.system() == "Darwin" or not MAC_CROSS_COMPILE_INTEL
@@ -385,7 +372,7 @@ def gather_dependencies(dep: str) -> Generator[str]:
yield x
if VERBOSE:
if "v" in flags:
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
@@ -445,6 +432,7 @@ if WASM:
SKIP_TARGETS_FOR_WASM = {
"rocksdb",
"opencollada",
"swig",
"pcre",
"IfcGeom",
"IfcConvert",
@@ -466,8 +454,12 @@ bison = "bison"
missing_commands: list[str] = []
required_commands = [git, bunzip2, tar, cc, cplusplus, autoconf, automake, make, "patch", "cmake", yacc, xz, bison]
if WASM:
if "wasm" in flags:
# Skip swig build for WASM.
required_commands.append("swig")
required_commands.append("pyodide")
required_commands.remove(yacc)
required_commands.remove(bison)
if platform.system() == "Linux" and "BonsaiViewer" in targets:
required_commands.append("patchelf")
@@ -584,7 +576,7 @@ def run_autoconf(dependency_name: str, configure_args: list[str], cwd: str) -> N
prefix = os.path.realpath(f"{DEPS_DIR}/install/{dependency_name}")
wasm = []
if WASM:
if "wasm" in flags:
wasm.append("emconfigure")
run(
@@ -592,7 +584,7 @@ def run_autoconf(dependency_name: str, configure_args: list[str], cwd: str) -> N
*wasm,
"/bin/sh",
"../configure",
*(["--host=wasm32"] if WASM and not any(s.startswith("--host") for s in configure_args) else []),
*(["--host=wasm32"] if "wasm" in flags and not any(s.startswith("--host") for s in configure_args) else []),
*configure_args,
f"--prefix={prefix}",
],
@@ -600,20 +592,18 @@ def run_autoconf(dependency_name: str, configure_args: list[str], cwd: str) -> N
)
def run_cmake(
name, cmake_args: list[str], cmake_dir: str | None = None, cwd: str | None = None, native: bool = False
) -> None:
def run_cmake(arg1, cmake_args: list[str], cmake_dir: str | None = None, cwd: str | None = None):
if cmake_dir is None:
P = ".."
else:
P = cmake_dir
wasm = []
if WASM and not native:
if "wasm" in flags:
wasm.append("emcmake")
cmake_flags: list[str] = []
if not native and (not WASM or not WASM_CMAKE_IS_USING_INIT_VARS):
if not WASM or not WASM_CMAKE_IS_USING_INIT_VARS:
# For WASM we provide flags using just environment variables.
# If we provide them using cmake vars, it will override emscripten toolchain flags.
# Unsure if we need this in general even for non-WASM builds.
@@ -629,10 +619,6 @@ def run_cmake(
f"-DBUILD_SHARED_LIBS={OFF_ON[not BUILD_STATIC]}",
)
if WASM and native:
# Override emscripten cmake toolchain coming from environment variable.
cmake_flags.append("-DCMAKE_TOOLCHAIN_FILE=")
run(
[
*wasm,
@@ -641,7 +627,7 @@ def run_cmake(
*cmake_flags,
*cmake_args,
f"-DCMAKE_BUILD_TYPE={BUILD_CFG}",
*([] if native else [f"-DCMAKE_SHARED_LINKER_FLAGS={os.environ['LDFLAGS']}"]),
f"-DCMAKE_SHARED_LINKER_FLAGS={os.environ['LDFLAGS']}",
],
cwd=cwd,
)
@@ -689,7 +675,7 @@ def build_dependency(
additional_files: dict[str, str] | None = None,
no_append_name=False,
cmake_dir=None,
cmake_native: bool = False,
**kwargs,
) -> None:
"""Handles building of dependencies with different tools (which are
distinguished with the `mode` argument. `build_tool_args` is expected to be
@@ -698,8 +684,7 @@ def build_dependency(
:param pre_compile_subs: A sequence of ``(fn, before, after)``
:param additional_files: Mapping path->url.
:param cmake_native: For ``mode="cmake"``, force a native (host) build
even when building for WASM. Needed for build-time tools like swig.
:param kwargs: Additional ``mode`` related kwargs.
"""
check_dir = os.path.join(DEPS_DIR, "install", name)
if os.path.exists(check_dir):
@@ -735,7 +720,7 @@ def build_dependency(
logger.info(f"\rChecking {name}... ")
git_clone_or_pull_repository(download_url, target_dir=os.path.join(build_dir, download_name), revision=revision)
else:
assert_never(download_tool)
raise ValueError(f"download tool '{download_tool}' is not supported")
download_dir = os.path.join(build_dir, download_name)
if os.path.isdir(download_dir):
@@ -796,9 +781,9 @@ def build_dependency(
if mode == "autoconf":
run_autoconf(name, build_tool_args, cwd=extract_build_dir)
elif mode == "cmake":
run_cmake(name, build_tool_args, cwd=extract_build_dir, native=cmake_native)
run_cmake(name, build_tool_args, cwd=extract_build_dir)
else:
assert_never(mode)
raise ValueError()
for fn, before, after in pre_compile_subs:
with open(os.path.join(extract_dir, fn), "r") as f:
s = f.read()
@@ -814,14 +799,14 @@ def build_dependency(
logger.info(f"\rConfiguring {name}...")
run([bash, "./bootstrap.sh"], cwd=extract_dir)
logger.info(f"\rBuilding {name}... ")
run(["./b2", f"-j{IFCOS_NUM_BUILD_PROCS}"] + build_tool_args, cwd=extract_dir, can_fail=WASM)
run(["./b2", f"-j{IFCOS_NUM_BUILD_PROCS}"] + build_tool_args, cwd=extract_dir, can_fail="wasm" in flags)
logger.info(f"\rInstalling {name}... ")
shutil.copytree(
os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", f"boost-{BOOST_VERSION}", "boost")
)
logger.info(f"\rInstalled {name} \n")
if DISK_CLEANUP:
if "diskcleanup" in flags:
shutil.rmtree(build_dir, ignore_errors=True)
@@ -927,7 +912,7 @@ ADDITIONAL_ARGS_STR = " ".join(ADDITIONAL_ARGS)
CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
if WASM:
if "wasm" in flags:
# WASM `SIDE_MODULE_` are absorbed by `emcmake` automatically.
CXXFLAGS = CXXFLAGS_MINIMAL
CFLAGS = CFLAGS_MINIMAL
@@ -948,7 +933,7 @@ else:
CFLAGS = CFLAGS_MINIMAL
LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}"
if LTO:
if "lto" in flags:
for f in compiler_flags:
locals()[f] += f" -flto={IFCOS_NUM_BUILD_PROCS}"
@@ -1025,7 +1010,6 @@ if "swig" in targets:
download_name="swig",
download_tool=download_tool_git,
revision=f"v{SWIG_VERSION}",
cmake_native=WASM,
)
if USE_OCCT and "occ" in targets:
@@ -1036,13 +1020,13 @@ if USE_OCCT and "occ" in targets:
# Skip ExpToCasExe as we don't need it and it requires additional dependencies.
# Before 7.7.2 ExpToCasExe is part of DataExchange, DETools doesn't exist yet.
# Since we do need DataExchange (used for iges_serializer), we use a patch to skip only ExpToCasExe.
# Since we do need DataExchange (used for IgesSerializer), we use a patch to skip only ExpToCasExe.
if "7.7.2" > OCCT_VERSION >= "7.7":
patches.append("./patches/occt/no_ExpToCasExe.patch")
elif OCCT_VERSION >= "7.7.2":
occt_args.append("-DBUILD_MODULE_DETools=OFF")
if WASM:
if "wasm" in flags:
patches.append("./patches/occt/no_em_js.patch")
build_dependency(
@@ -1126,7 +1110,7 @@ if "libxml2" in targets:
"--without-iconv",
"--without-lzma",
]
if WASM:
if "wasm" in flags:
build_tool_args.append("--without-threads")
build_dependency(
f"libxml2-{LIBXML2_VERSION}",
@@ -1148,7 +1132,7 @@ if "OpenCOLLADA" in targets:
# whether shared libs were actually built. We make it follow `USE_SHARED` instead.
patches.append("./patches/opencollada/config_select_libs_by_use_shared.patch")
if WASM:
if "wasm" in flags:
# This is necessary for the WASM build, because recent versions of
# clang don't have the tr1:: namespace anymore. However, it breaks
# some versions of gcc (9.4.0 at least) due to specializing std::hash
@@ -1178,7 +1162,7 @@ if "OpenCOLLADA" in targets:
revision=OPENCOLLADA_VERSION,
)
if "python" in targets and not USE_CURRENT_PYTHON_VERSION and not WASM:
if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flags:
# Python should not be built with -fvisibility=hidden, from experience that introduces segfaults
OLD_CPP_FLAGS = os.environ["CPPFLAGS"]
OLD_CXX_FLAGS = os.environ["CXXFLAGS"]
@@ -1239,7 +1223,7 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and not WASM:
if "boost" in targets:
str_concat = lambda prefix: lambda postfix: "" if postfix.strip() == "" else "=".join((prefix, postfix.strip()))
toolset = []
if WASM:
if "wasm" in flags:
toolset.append("toolset=emscripten")
build_dependency(
f"boost-{BOOST_VERSION}",
@@ -1267,7 +1251,7 @@ if "boost" in targets:
# patch="./patches/boost/boostorg_regex_62.patch",
download_name=f"boost-{BOOST_VERSION}-b2-nodocs.tar.gz",
)
if WASM:
if "wasm" in flags:
# only supported on nix for now
run(
("find", ".", "-name", "*.bc", "-exec", "bash", "-c", "emar q ${1%.bc}.a $1", "bash", "{}", ";"),
@@ -1309,7 +1293,9 @@ if "cgal" in targets:
name=f"gmp-{GMP_VERSION}",
mode="autoconf",
build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, "--with-pic", *gmp_args],
pre_compile_subs=([("build/config.h", "HAVE_OBSTACK_VPRINTF 1", "HAVE_OBSTACK_VPRINTF 0")] if WASM else []),
pre_compile_subs=(
[("build/config.h", "HAVE_OBSTACK_VPRINTF 1", "HAVE_OBSTACK_VPRINTF 0")] if "wasm" in flags else []
),
patch=gmp_patches,
# Sometimes ftp.gnu.org is very slow, use ftpmirror.gnu.org as a workaround.
download_url="https://ftpmirror.gnu.org/gnu/gmp/",
@@ -1441,7 +1427,7 @@ os.makedirs(ifcos_build_dir, exist_ok=True)
cmake_args = [
"-DUSE_MMAP=OFF",
f"-DBUILD_EXAMPLES={OFF_ON[BUILD_EXAMPLES]}",
"-DBUILD_EXAMPLES=OFF",
"-DBUILD_SHARED_LIBS=" + OFF_ON[not IFCOPENSHELL_STATIC],
"-DGLTF_SUPPORT=ON",
"-DBoost_NO_BOOST_CMAKE=On",
@@ -1471,7 +1457,7 @@ def get_cmake_args_prefix_path(additional_paths: Sequence[str] = ()) -> list[str
return [f"-DCMAKE_PREFIX_PATH={prefix_path}"]
if WASM:
if "wasm" in flags:
# Boost is built by the build script so should not be found
# inside of the sysroot set by the emscriptem toolchain
cmake_args.append("-DWASM_BUILD=On")
@@ -1537,10 +1523,7 @@ if "rocksdb" in targets:
)
if "swig" in targets:
# `cmake_args_prefix_path` won't work on wasm
# because `find_program` in emscripten toolchain don't use `find_root_path`.
# As a workaround we provide executable path directly on all platforms.
cmake_args.append(f"-DSWIG_EXECUTABLE={DEPS_DIR}/install/swig-{SWIG_VERSION}/bin/swig")
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/swig-{SWIG_VERSION}")
if os.environ.get("QT_DIR"):
cmake_args_prefix_path.append(os.environ["QT_DIR"])
@@ -1652,7 +1635,7 @@ if "IfcOpenShell-Python" in targets:
if platform.system() != "Darwin":
if BUILD_CFG == "Release":
for so in glob.glob(os.path.join(module_dir, "*.so")):
if WASM:
if "wasm" in flags:
run(["wasm-strip", so, "-k", "dylink.0"])
elif os.path.basename(so).startswith("_ifcopenshell_wrapper"):
# TODO: This symbol name depends on the Python version?
@@ -1662,7 +1645,7 @@ if "IfcOpenShell-Python" in targets:
return module_dir
if WASM:
if "wasm" in flags:
compile_python_wrapper(
run(["pyodide", "config", "get", "python_version"]),
run(["pyodide", "config", "get", "python_include_dir"]),
+2 -5
View File
@@ -28,11 +28,8 @@ since it's pure cmake without any additional moving parts.
- clone IfcOpenShell repo next to it to `IfcOpenShell` folder
- run `python nix/build-all.py -wasm -py-313` in `IfcOpenShell`
- it will produce Python package in `IfcOpenShell/ifcopenshell`
- run `python pyodide/build-all-pack-wheel-local.py`, it will
- clean up previous wheels
- run `pyodide build`
- prepare standalone and modular wheels
- produce final wheels in `IfcOpenShell/dist` and `IfcOpenshell/dist-modular`
- run `pyodide build`
- it will produce a wheel in `IfcOpenShell/dist`
- testing:
- ensure you're in pyodide environment
- `cd IfcOpenshell/pyodide`
-38
View File
@@ -1,38 +0,0 @@
#!/usr/bin/env python3
"""Intended to be run after nix/build-all.py has finished the wasm build."""
import shutil
import subprocess
from pathlib import Path
def get_repo_root() -> Path:
output = subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True)
return Path(output.strip())
def run(cmd: list[str], **kwargs) -> None:
print("$", " ".join(cmd))
subprocess.check_call(cmd, **kwargs)
def main() -> None:
repo_root = get_repo_root()
shutil.rmtree(repo_root / "dist", ignore_errors=True)
shutil.rmtree(repo_root / "dist_modular", ignore_errors=True)
run(["pyodide", "build"], cwd=repo_root)
shutil.rmtree(repo_root / "ifcopenshell", ignore_errors=True)
(repo_root / "setup.py").unlink(missing_ok=True)
run(["git", "restore", "pyproject.toml"], cwd=repo_root)
wheel = next((repo_root / "dist").glob("ifcopenshell-*.whl"))
run(["uv", "run", "pyodide/order_pyodide_wheel_shared_objects.py", str(wheel)], cwd=repo_root)
run(
["uv", "run", "pyodide/split_pyodide_ifcopenshell_wheel.py", str(wheel), "dist-modular/"],
cwd=repo_root,
)
if __name__ == "__main__":
main()
+7 -9
View File
@@ -1,8 +1,10 @@
#!/usr/bin/bash
set -ex
PYODIDE_VERSION=0.29.4
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PYODIDE_VERSION=0.29.3
PYODIDE_BUILD_VERSION=0.33.0
PYODIDE_XBUILDENV_ROOT="${HOME}/.cache/.pyodide-xbuildenv-${PYODIDE_BUILD_VERSION}"
PYODIDE_XBUILDENV="${PYODIDE_XBUILDENV_ROOT}/${PYODIDE_VERSION}"
# Script is assuming that it will be possible to execute it multiple times
# therefore we're clearing venv each time and ignoring existing 'emsdk' folder.
@@ -14,14 +16,12 @@ source .venv/bin/activate
# Install pyodide cross build environment.
# Instructions: https://pyodide.org/en/stable/development/building-packages.html
uv pip install -r "${SCRIPT_DIR}/requirements.txt"
uv pip install "pyodide-build==${PYODIDE_BUILD_VERSION}"
# `uv run` is required, so xbuildenv would skip using `pip`.
uv run pyodide xbuildenv install "${PYODIDE_VERSION}"
uv run pyodide xbuildenv install-emscripten
# Cache path includes a hash segment that varies by pyodide-build version,
# so query it instead of constructing it manually.
EMSDK_ROOT=$(uv run pyodide config get emsdk_dir)
EMSDK_ROOT="${PYODIDE_XBUILDENV}/emsdk"
[ -f "${EMSDK_ROOT}/emsdk_env.sh" ] && source "${EMSDK_ROOT}/emsdk_env.sh"
[ -f "${EMSDK_ROOT}/../../emsdk_env.sh" ] && source "${EMSDK_ROOT}/../../emsdk_env.sh"
which emcc
@@ -29,10 +29,8 @@ emcc --version
mkdir -p packages/ifcopenshell
VERSION=`cat IfcOpenShell/VERSION`
# Normalize to the canonical PEP 440 form (e.g. 0.9.0alpha0 -> 0.9.0a0).
VERSION=`python3 -c "from packaging.version import Version; print(Version('$VERSION'))"`
cp IfcOpenShell/pyodide/meta.yaml packages/ifcopenshell
sed -i s/9.9.9/$VERSION/g packages/ifcopenshell/meta.yaml
sed -i s/0.8.0/$VERSION/g packages/ifcopenshell/meta.yaml
# Use custom build ifcopenshell directory in build-all to make caching simpler
# Otherwise pyodide build path typically includes package version, so cached cmake configs might break.
+1 -2
View File
@@ -1,7 +1,6 @@
package:
name: ifcopenshell
# Placeholder, replaced by build_pyodide.sh with the actual version from VERSION file.
version: 9.9.9
version: 0.8.0
source:
# meta.yaml is placed as `packages/ifcopenshell/meta.yaml`.
@@ -34,10 +34,10 @@ SCHEMA_ORDER = {
}
MAIN_SHARED_OBJECT_RE = re.compile(r"^_ifcopenshell_wrapper(?:\.|$)")
SCHEMA_PLUGIN_RE = re.compile(r"^ifcopenshell_parse_schema_(.+)\.so$")
MAPPING_PLUGIN_RE = re.compile(r"^ifcopenshell_geometry_mapping_(.+)\.so$")
DOCUMENT_PLUGIN_RE = re.compile(r"^ifcopenshell_document_[a-z0-9]+(?:_(.+))?\.so$")
GEOMETRY_SERIALIZATION_PLUGIN_RE = re.compile(r"^ifcopenshell_geometry_writer_(.+)\.so$")
SCHEMA_PLUGIN_RE = re.compile(r"^ifcopenshell\.parse\.schema\.([^.]+)\.so$")
MAPPING_PLUGIN_RE = re.compile(r"^ifcopenshell\.geometry\.mapping\.([^.]+)\.so$")
DOCUMENT_PLUGIN_RE = re.compile(r"^ifcopenshell\.document\.[^.]+\.([^.]+)\.so$")
GEOMETRY_SERIALIZATION_PLUGIN_RE = re.compile(r"^ifcopenshell\.geometry\.serialization\.([^.]+)\.so$")
def schema_key(schema: str) -> tuple[int, str]:
-1
View File
@@ -1 +0,0 @@
pyodide-build==0.39.0
+10 -20
View File
@@ -63,30 +63,20 @@ select = [
#
"FA", # future annotations
"UP", # pyupgrade
"unnecessary-iterable-allocation-for-first-element",
"unsorted-dunder-all",
"RUF015", # next() > list_comprehension[0]
"RUF022", # sort __all__
"I", # import sorting
"unused-noqa",
"rule-codes-in-selectors",
"noqa-comments",
"rule-codes-in-suppression-comments",
# General util rules.
"invalid-rule-code",
"redirected-noqa",
"invalid-pyproject-toml",
"invalid-suppression-comment",
]
ignore = [
# Conflicts with Blender using annotations for props definitions.
"future-rewritable-type-annotation",
"FA100", # Conflicts with Blender using annotations for props definitions.
# Maybe will enable later:
"non-pep604-annotation-union", # Union[X,Y] to X | Y
"non-pep604-annotation-optional", # Optional to X | None
"redundant-open-modes", # Unnecessary mode argument
"yield-in-for-loop", # yield for -> yield from
"format-literals", # implicit references for positional format fields
"printf-string-formatting", # Replace % with .format
"f-string", # Replace .format with f-string
"UP007", # Union[X,Y] to X | Y
"UP045", # Optional to X | None
"UP015", # Unnecessary mode argument
"UP028", # yield for -> yield from
"UP030", # implicit references for positional format fields
"UP031", # Replace % with .format
"UP032", # Replace .format with f-string
]
[tool.ty.rules]
+3 -3
View File
@@ -10,7 +10,7 @@ name = "bcf-client"
# author = "IfcOpenShell"
description = "BCF-XML file handler."
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.8"
keywords = ["IFC", "BCF", "BIM"]
dependencies = [
"xsdata>=24.4",
@@ -65,6 +65,6 @@ commands = pytest --cov --cov-report=term tests
[tool.ruff]
extend = "../../pyproject.toml"
lint.extend-select = [
"unused-import", # unused imports
lint.select = [
"F401", # unused imports
]
+11 -13
View File
@@ -42,12 +42,10 @@ endif
IS_STABLE:=FALSE
VERSION:=$(shell cat ../../VERSION)
VERSION_BASE:=$(shell sed -E 's/[[:alpha:]]+[0-9]+$$//' ../../VERSION)
VERSION_PYTHON:=$(shell sed 's/alpha/a/' ../../VERSION)
VERSION_SEMVER:=$(shell sed -E 's/([[:alpha:]]+)([0-9]+)$$/-\\1\\2/' ../../VERSION)
VERSION_MAJOR:=$(shell cat '../../VERSION' | cut -d '.' -f 1)
VERSION_MINOR:=$(shell cat '../../VERSION' | cut -d '.' -f 2)
VERSION_PATCH:=$(shell cat '../../VERSION' | cut -d '.' -f 3)
VERSION_DATE:=$(shell date '+%y%m%d')
VERSION_DAILY:=$(VERSION_BASE)a$(VERSION_DATE)
VERSION_SEMVER_DAILY:=$(VERSION_BASE)-alpha$(VERSION_DATE)
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)
@@ -262,14 +260,14 @@ endif
cp pyproject.toml build/
ifeq ($(IS_STABLE), TRUE)
$(SED) "s/0.0.0/$(VERSION_SEMVER)/" build/bonsai/blender_manifest.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/pyproject.toml
$(SED) "s/0.0.0/$(VERSION)/" build/bonsai/blender_manifest.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
else
$(SED) "s/0.0.0/$(VERSION_SEMVER_DAILY)/" build/bonsai/blender_manifest.toml
$(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_DAILY)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/pyproject.toml
endif
# Blender 5.1+ requires Python 3.13.
@@ -281,9 +279,9 @@ endif
# Provides bonsai Add-on functionality
ifeq ($(IS_STABLE), TRUE)
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
else
$(SED) 's/version = "0.0.0"/version = "$(VERSION_DAILY)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)a$(VERSION_DATE)"/' build/pyproject.toml
endif
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
cp build/dist/*.whl build/wheels/
@@ -317,9 +315,9 @@ endif
rm -rf build/bonsai/libs/
ifeq ($(IS_STABLE), TRUE)
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION_SEMVER)-$(BLENDER_PLATFORM).zip ./bonsai
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION)-$(BLENDER_PLATFORM).zip ./bonsai
else
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION_SEMVER_DAILY)-$(BLENDER_PLATFORM).zip ./bonsai
cd build && zip -r bonsai_$(PYVERSION)-$(VERSION)-alpha$(VERSION_DATE)-$(BLENDER_PLATFORM).zip ./bonsai
endif
mv build/bonsai*.zip dist/
+8 -3
View File
@@ -185,10 +185,13 @@ class IfcStore:
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
IfcStore.cache_path = cache_path
cache_path = Path(IfcStore.cache_path)
settings = ifcopenshell.geom.settings()
cache_settings = ifcopenshell.geom.settings()
serializer_settings = ifcopenshell.geom.serializer_settings()
cache_preexists = cache_path.exists()
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, settings)
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(
IfcStore.cache_path, cache_settings, serializer_settings
)
if cache_preexists:
print(f"Successfully loaded existing cache: {cache_path.name}.")
else:
@@ -203,7 +206,9 @@ class IfcStore:
os.remove(IfcStore.cache_path)
try:
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, settings)
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(
IfcStore.cache_path, cache_settings, serializer_settings
)
print("New cache was created.")
except Exception as e:
print(f"Failed to create a cache: {str(e)}.")
+5 -5
View File
@@ -740,7 +740,7 @@ class IfcImporter:
self.update_progress((percent_average / 100 * progress_range) + start_progress)
shape = iterator.get()
if shape:
assert isinstance(shape, W.triangulation_element)
assert isinstance(shape, W.TriangulationElement)
product = self.file.by_id(shape.id)
self.create_product(product, shape)
results.add(product)
@@ -1079,9 +1079,9 @@ class IfcImporter:
def create_curve(
self,
element: ifcopenshell.entity_instance,
shape: Union[W.triangulation, W.triangulation_element],
shape: Union[W.Triangulation, W.TriangulationElement],
) -> bpy.types.Curve:
if isinstance(shape, W.triangulation_element):
if isinstance(shape, W.TriangulationElement):
geometry = shape.geometry
else:
geometry = shape
@@ -1112,11 +1112,11 @@ class IfcImporter:
def create_mesh(
self,
element: ifcopenshell.entity_instance,
shape: Union[W.triangulation, W.triangulation_element],
shape: Union[W.Triangulation, W.TriangulationElement],
cartesian_point_offset: Union[npt.NDArray[np.float64], Literal[False]] = None,
) -> Union[bpy.types.Mesh, None]:
try:
if isinstance(shape, W.triangulation_element):
if isinstance(shape, W.TriangulationElement):
# shape is ShapeElementType
geometry = shape.geometry
else:
@@ -678,7 +678,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
# Identify all potential building elements
# TODO: don't select everything, use AABB culling in Blender
building_elements = list(
building_elements = (
tool.Ifc.get().by_type("IfcWall")
+ tool.Ifc.get().by_type("IfcSlab")
+ tool.Ifc.get().by_type("IfcVirtualElement")
@@ -708,7 +708,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
while True:
tree.add_element(iterator.get_native())
shape = iterator.get()
assert isinstance(shape, W.triangulation_element)
assert isinstance(shape, W.TriangulationElement)
shapes[shape.id] = {
"verts": ifcopenshell.util.shape.get_vertices(shape.geometry),
"faces": ifcopenshell.util.shape.get_faces(shape.geometry),
@@ -348,7 +348,10 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
if self.obj_type == "Object":
objects = [o.name for o in tool.Blender.get_selected_objects()]
if context.selected_objects:
objects = [o.name for o in context.selected_objects]
else:
objects = [context.active_object.name]
else:
objects = [self.obj]
props = tool.Classification.get_classification_props()
@@ -516,7 +516,7 @@ def _world_segment_to_screen_pixels(
# ---------------------------------------------------------------------------
class BIM_GT_box_face_quad(bpy.types.Gizmo):
class BIM_GT_box_face_quad(bpy.types.Gizmo): # noqa: N801 — Blender bl_idname convention
"""Near-invisible face-quad click target with drag-to-resize modal.
Geometry: a unit quad in the local XY plane at z=0. The adapter
@@ -620,7 +620,7 @@ class BIM_GT_box_face_quad(bpy.types.Gizmo):
return {"RUNNING_MODAL"}
class BIM_GT_box_face_outline(bpy.types.Gizmo):
class BIM_GT_box_face_outline(bpy.types.Gizmo): # noqa: N801 — Blender bl_idname convention
"""Thin non-interactive colored edge outline for one face.
Drawn as 4 line segments in the face plane. The layout helper
@@ -160,7 +160,7 @@ def _make_face_set_cb(gz: Any, group: Any, axis: int, is_max: bool):
return setter
class OBJECT_GGT_bim_clip_box(bpy.types.GizmoGroup):
class OBJECT_GGT_bim_clip_box(bpy.types.GizmoGroup): # noqa: N801 — Blender bl_idname convention
"""Face-quad resize handles on the active clip box.
Renders six near-invisible click-target quads and six colored edge
@@ -987,7 +987,7 @@ class ExportCostSchedulesToPDF(bpy.types.Operator, ExportHelper):
@classmethod
def poll(cls, context):
try:
import typst # ruff: ignore[unused-import]
import typst # noqa: F401
return True
except ModuleNotFoundError:
@@ -313,7 +313,7 @@ class CreateAllShapes(bpy.types.Operator):
failures.append(element)
print("***** FAILURE *****")
if shape:
assert isinstance(shape, W.triangulation_element)
assert isinstance(shape, W.TriangulationElement)
geom = shape.geometry
print(
f"Success {time.time() - start:.3f}s "
@@ -28,7 +28,7 @@ operators via ``target_set_operator``; drag handles inherit modal state
from ``GizmoMovable``.
"""
__all__ = [ # ruff: ignore[unsorted-dunder-all]
__all__ = [ # noqa: RUF022 (unsorted `__all__`)
"GizmoColor",
"GizmoAxis",
"TextAlignment",
@@ -5660,7 +5660,7 @@ class BaseParametricGizmoGroup:
"""
return 0.0
def _update_view_dependent_dimensions(self, context: bpy.types.Context, mw: Matrix, props) -> None:
def _update_view_dependent_dimensions(self, context: bpy.types.Context, mw: Matrix, props) -> None: # noqa: ARG002
"""Update overall_width, overall_height, and lining_offset based on view direction.
This base implementation handles the common pattern for door/window gizmos.
@@ -5837,7 +5837,7 @@ class BaseParametricGizmoGroup:
self.update_dimension_gizmos(mw, props)
self._refresh_element_specific(context, mw, props)
def _refresh_element_specific(self, context: bpy.types.Context, mw: "Matrix", props) -> None:
def _refresh_element_specific(self, context: bpy.types.Context, mw: "Matrix", props) -> None: # noqa: ARG002
"""Override for element-specific refresh logic.
Called from both refresh() (on state change) and draw_prepare() (per frame),
@@ -6344,7 +6344,7 @@ class BaseParametricGizmoGroup:
"""
return (0.0, 0.0)
def get_icon_y_offset(self, context: bpy.types.Context, mw: Matrix) -> float:
def get_icon_y_offset(self, context: bpy.types.Context, mw: Matrix) -> float: # noqa: ARG002
"""Get Y offset for icons based on view direction.
Uses get_icon_y_extent() to determine how far to offset icons based on
@@ -6546,7 +6546,9 @@ class BaseParametricGizmoGroup:
self._refresh_element_specific(context, mw, props)
def _update_dimension_gizmo_positions(self, context: bpy.types.Context, mw: "Matrix", props) -> None:
def _update_dimension_gizmo_positions(
self, context: bpy.types.Context, mw: "Matrix", props # noqa: ARG002
) -> None:
"""Update dimension gizmo positions based on view direction.
Override this method in subclasses to implement view-dependent
@@ -1406,28 +1406,31 @@ class CreateDrawing(bpy.types.Operator):
# Backwards compatibility with older ifcopenshell builds that don't expose these keys.
pass
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
self.svg_settings.set("svg-without-storeys", True)
self.svg_settings.set("svg-write-poly", True)
self.svg_settings.set("svg-poly", True)
self.serialiser_settings = ifcopenshell.geom.serializer_settings()
self.serialiser_settings.set("svg-without-storeys", True)
self.serialiser_settings.set("svg-write-poly", True)
self.serialiser_settings.set("svg-poly", True)
# Objects with more than these edges are rendered as wireframe instead of HLR for optimisation
self.svg_settings.set("profile-threshold", 10000)
self.svg_settings.set("svg-xmlns", True)
self.svg_settings.set("svg-project", True)
self.svg_settings.set("auto-elevation", False)
self.svg_settings.set("auto-section", False)
self.svg_settings.set("print-space-names", False)
self.svg_settings.set("print-space-areas", False)
self.svg_settings.set("door-arcs", False)
self.svg_settings.set("svg-no-css", True)
self.svg_settings.set("elevation-ref-guid", self.camera_element.GlobalId)
self.svg_settings.set("scale", str(self.scale))
self.svg_settings.set("svg-subtract-before", "always")
self.svg_settings.set("svg-prefilter", True) # See #3359
self.svg_settings.set("svg-unify-inputs", True)
self.svg_settings.set("svg-segment-projection", True)
self.serialiser_settings.set("profile-threshold", 10000)
self.serialiser_settings.set("svg-xmlns", True)
self.serialiser_settings.set("svg-project", True)
self.serialiser_settings.set("auto-elevation", False)
self.serialiser_settings.set("auto-section", False)
self.serialiser_settings.set("print-space-names", False)
self.serialiser_settings.set("print-space-areas", False)
self.serialiser_settings.set("door-arcs", False)
self.serialiser_settings.set("svg-no-css", True)
self.serialiser_settings.set("elevation-ref-guid", self.camera_element.GlobalId)
self.serialiser_settings.set("scale", str(self.scale))
self.serialiser_settings.set("svg-subtract-before", "always")
self.serialiser_settings.set("svg-prefilter", True) # See #3359
self.serialiser_settings.set("svg-unify-inputs", True)
self.serialiser_settings.set("svg-segment-projection", True)
if target_view == "REFLECTED_PLAN_VIEW":
self.svg_settings.set("svg-mirror-y", True)
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
self.serialiser_settings.set("svg-mirror-y", True)
self.serialiser = ifcopenshell.geom.serializers.svg(
self.svg_buffer, self.svg_settings, self.serialiser_settings
)
# tree = ifcopenshell.geom.tree()
# This instructs the tree to explode BReps into faces and return
# the style of the face when running tree.select_ray()
@@ -72,10 +72,11 @@ class ExportOBJ(bpy.types.Operator):
# Conversion from IFC to OBJ
# Settings for obj
settings = ifcopenshell.geom.settings()
serializer_settings = ifcopenshell.geom.serializer_settings()
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.SURFACES_AND_SOLIDS)
settings.set("apply-default-materials", True)
settings.set("use-element-guids", True)
serializer_settings.set("use-element-guids", True)
settings.set("use-world-coords", True)
ifc_file: ifcopenshell.file
@@ -89,7 +90,7 @@ class ExportOBJ(bpy.types.Operator):
obj_file_path = os.path.join(output_dir, "model.obj")
mtl_file_path = os.path.join(output_dir, "model.mtl")
serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings)
serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings, serializer_settings)
serialiser.setFile(ifc_file)
serialiser.setUnitNameAndMagnitude("METER", 1.0)
serialiser.writeHeader()
@@ -106,7 +107,7 @@ class ExportOBJ(bpy.types.Operator):
if iterator.initialize():
while True:
shape = iterator.get()
assert isinstance(shape, W.triangulation_element)
assert isinstance(shape, W.TriangulationElement)
materials = shape.geometry.materials
for material in materials:
+1 -1
View File
@@ -430,7 +430,7 @@ class SverchokData:
@classmethod
def has_sverchok(cls) -> bool:
try:
import sverchok # ruff: ignore[unused-import]
import sverchok # noqa: F401
return True
except ModuleNotFoundError:
+6 -4
View File
@@ -560,7 +560,7 @@ class AddDoor(bpy.types.Operator, tool.Ifc.Operator):
)
update_door_modifier_representation(obj)
def _execute(self, context: bpy.types.Context) -> set[str]:
def _execute(self, context: bpy.types.Context) -> set[str]: # noqa: ARG002
for obj in tool.Blender.get_selected_objects():
if not tool.Blender.Modifier.is_eligible_for_door_modifier(obj):
continue
@@ -638,7 +638,7 @@ class RemoveDoor(bpy.types.Operator, tool.Ifc.Operator):
pset = tool.Pset.get_element_pset(element, "BBIM_Door")
ifcopenshell.api.pset.remove_pset(tool.Ifc.get(), product=element, pset=pset)
def _execute(self, context: bpy.types.Context) -> set[str]:
def _execute(self, context: bpy.types.Context) -> set[str]: # noqa: ARG002
for obj in tool.Blender.get_selected_objects():
self.remove_door_on_object(obj)
return {"FINISHED"}
@@ -683,7 +683,7 @@ class ToggleDoorSwing(bpy.types.Operator, tool.Ifc.Operator):
return True
return False
def _execute(self, context: bpy.types.Context) -> set[str]:
def _execute(self, context: bpy.types.Context) -> set[str]: # noqa: ARG002
obj = tool.Blender.get_active_object()
if not obj:
return {"CANCELLED"}
@@ -909,7 +909,9 @@ class GizmoDoorEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
setattr(self, f"gizmo_swing_arc_{cfg.name}", main)
setattr(self, f"gizmo_swing_arc_{cfg.name}_flip", flip)
def _refresh_element_specific(self, context: bpy.types.Context, mw: Matrix, props: "BIMDoorProperties") -> None:
def _refresh_element_specific(
self, context: bpy.types.Context, mw: Matrix, props: "BIMDoorProperties" # noqa: ARG002
) -> None:
"""Update door-specific swing arc gizmos."""
self.update_swing_gizmos(mw, props)
+2 -2
View File
@@ -765,7 +765,7 @@ class GizmoRoofEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
def is_element_type(cls, element: ifcopenshell.entity_instance) -> bool:
return tool.Parametric.is_roof(element)
def _update_dimension_gizmo_positions(self, context: bpy.types.Context, mw, props) -> None:
def _update_dimension_gizmo_positions(self, context: bpy.types.Context, mw, props) -> None: # noqa: ARG002
"""Anchor every dimension gizmo at the object origin. Each gizmo's
declared axis (height/slope along +Z, thickness along -Z) separates
them in 3D so they don't visually collide despite sharing a
@@ -776,7 +776,7 @@ class GizmoRoofEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
self.set_dimension_gizmo_position("angle", mw, origin, (0, 0, 1))
self.set_dimension_gizmo_position("roof_thickness", mw, origin, (0, 0, -1))
def get_element_height(self, props) -> float:
def get_element_height(self, props) -> float: # noqa: ARG002
"""Object-local Z of the mesh's topmost vertex, so the pen / validate /
cancel / cycle row anchors visibly above sloped or stepped roof
bodies rather than at the parametric ``props.height`` which may not
+5 -3
View File
@@ -405,7 +405,7 @@ class SetStairTreads(bpy.types.Operator):
bl_label = "Set Number of Treads"
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set[str]:
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set[str]: # noqa: ARG002
obj = context.active_object
if not obj:
return {"CANCELLED"}
@@ -658,7 +658,9 @@ class GizmoStairEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
self.tread_count_label_gizmo.alpha = 0.8
self.tread_count_label_gizmo.target_set_operator("bim.input_stair_treads")
def _refresh_element_specific(self, context: bpy.types.Context, mw: Matrix, props: "BIMStairProperties") -> None:
def _refresh_element_specific(
self, context: bpy.types.Context, mw: Matrix, props: "BIMStairProperties" # noqa: ARG002
) -> None:
"""Update stair-specific lock and tread count gizmos. Lock positioning is
handled per-frame in the dimension-positioning hook."""
self.update_lock_gizmo(props)
@@ -705,7 +707,7 @@ class GizmoStairEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
self.update_gizmo_visibility(self.tread_count_label_gizmo, props.is_editing)
def _update_dimension_gizmo_positions(
self, context: bpy.types.Context, mw: Matrix, props: "BIMStairProperties"
self, context: bpy.types.Context, mw: Matrix, props: "BIMStairProperties" # noqa: ARG002
) -> None:
"""Update dimension gizmo positions based on camera view direction."""
viewing_from_negative_y, viewing_from_negative_x = self._frame_view_dir
+2 -2
View File
@@ -2174,7 +2174,7 @@ class GizmoWallEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
return (far, near)
def _update_dimension_gizmo_positions(
self, context: bpy.types.Context, mw: Matrix, props: "BIMWallProperties"
self, context: bpy.types.Context, mw: Matrix, props: "BIMWallProperties" # noqa: ARG002
) -> None:
"""Re-position length / height / height_end dimensions to the camera-facing
Y-side of the wall every frame. Mirrors the door & stair pattern: when the
@@ -2530,7 +2530,7 @@ def _perpendicular_wall_params(
return clamped_x, abs(cursor_local_y), side_sign
def _commit_pending_wall_edits_for_selection(context: bpy.types.Context) -> None:
def _commit_pending_wall_edits_for_selection(context: bpy.types.Context) -> None: # noqa: ARG001
"""Thin wall-scoped alias for ``tool.Parametric.commit_pending_edits_for_selection``.
Encapsulates the ``names=("wall",)`` filter so the registry name is
+1 -1
View File
@@ -538,7 +538,7 @@ class RemoveWindow(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Remove Window"
bl_options = {"REGISTER"}
def _execute(self, context: bpy.types.Context) -> set[str]:
def _execute(self, context: bpy.types.Context) -> set[str]: # noqa: ARG002
obj = context.active_object
assert obj
element = tool.Ifc.get_entity(obj)
@@ -2442,7 +2442,7 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
if iterator.initialize():
while True: # Main loop.
shape = iterator.get()
assert isinstance(shape, W.triangulation_element)
assert isinstance(shape, W.TriangulationElement)
results.add(self.file.by_id(shape.id))
geometry = shape.geometry
@@ -2518,7 +2518,7 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
print("Finished", time.time() - start)
return {"FINISHED"}
def process_occurrence(self, shape: W.triangulation_element) -> None:
def process_occurrence(self, shape: W.TriangulationElement) -> None:
element = self.file.by_id(shape.id)
mat = ifcopenshell.util.shape.get_shape_matrix(shape)
@@ -558,7 +558,7 @@ class IntegerInputDialogMixin:
return None
return props
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set[str]:
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set[str]: # noqa: ARG002
props = self._resolve_props(context)
if props is None:
return {"CANCELLED"}
+2 -29
View File
@@ -142,35 +142,8 @@ def assign_material(
else:
element_material_type = material_type
# TODO: this whole dance is a stopgap and wants rewriting.
#
# material.assign_material creates material sets with no items in them,
# ignoring the material it was handed -- an IfcMaterialLayerSet with no
# MaterialLayers is not valid IFC, since the list is mandatory and
# [1:?]. So we repair it below, after the fact. Worse, the API rejects a
# plain IfcMaterial outright when asked for a usage, which is exactly
# what the Object Materials dropdown gives us, so we cannot even pass it
# on and have to let the API invent an empty set and then fill it in.
#
# The fix is for assign_material to build the set around the material it
# is given, rather than leaving an invalid one behind for its callers to
# patch up. That is a wider change than it looks: add_material_set has
# the same behaviour, and the create-empty-then-add-items idiom is
# spread through the API's own docstrings, examples and tests. Until
# that is untangled, keep the repair here where it is at least visible.
# Only a usage refuses a plain IfcMaterial; every other type still wants
# it, and IfcMaterial and IfcMaterialList cannot be created without it.
pass_material = material_tool.is_a_material_set(material) or not element_material_type.endswith("Usage")
ifc.run(
"material.assign_material",
products=[element],
type=element_material_type,
material=material if pass_material else None,
)
# A usage points at the set rather than being one, and it is the set
# that needs an item adding to it below.
assigned_material = material_tool.get_material(element, should_skip_usage=True)
ifc.run("material.assign_material", products=[element], type=element_material_type, material=material)
assigned_material = material_tool.get_material(element)
assert assigned_material # Type checker.
if material_tool.is_a_material_set(material):
+1 -1
View File
@@ -651,7 +651,7 @@ class Material:
def get_default_material(cls): pass
def get_elements_by_material(cls, material): pass
def get_material_attributes(cls): pass
def get_material(cls, element, should_inherit: bool = False, should_skip_usage: bool = False): pass
def get_material(cls, element, should_inherit: bool = False): pass
def get_object_ui_active_material(cls): pass
def get_object_ui_material_type(cls): pass
def get_style(cls, material): pass
+1 -1
View File
@@ -17,7 +17,7 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
#
# Ignore unused imports.
# ruff: file-ignore[unused-import]
# ruff: noqa: F401
from bonsai.tool.aggregate import Aggregate
from bonsai.tool.array import Array
-2
View File
@@ -25,7 +25,6 @@ import importlib
import math
import os
import platform
import re
import subprocess
import sys
import tempfile
@@ -1757,7 +1756,6 @@ class Blender(bonsai.core.tool.Blender):
repo_path = repo.working_tree_dir
assert repo_path
version_ = (Path(repo_path) / "VERSION").read_text().strip()
version_ = re.sub(r"[A-Za-z]+\d+$", "", version_)
commit_date = bonsai.get_last_commit_date()
assert commit_date
commit_date = datetime.fromisoformat(commit_date)
+2 -2
View File
@@ -1187,7 +1187,7 @@ class Geometry(bonsai.core.tool.Geometry):
if iterator and iterator.initialize():
while True:
shape = iterator.get()
assert isinstance(shape, W.triangulation_element)
assert isinstance(shape, W.TriangulationElement)
element = tool.Ifc.get().by_id(shape.id)
if obj := tool.Ifc.get_object(element):
# It's possible that there will be multiple shapes for the same context,
@@ -2179,7 +2179,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.declaration.as_entity().all_attributes()
for a in item.declaration().as_entity.all_attributes()
if a.type_of_attribute()._is("IfcLengthMeasure")
]
+2 -2
View File
@@ -872,7 +872,7 @@ class Loader(bonsai.core.tool.Loader):
cls,
element: ifcopenshell.entity_instance,
representation: ifcopenshell.entity_instance,
shape: W.triangulation_element,
shape: W.TriangulationElement,
) -> bpy.types.Camera:
"""Create camera data.
@@ -1026,7 +1026,7 @@ class Loader(bonsai.core.tool.Loader):
@classmethod
def convert_geometry_to_mesh(
cls,
geometry: W.triangulation,
geometry: W.Triangulation,
mesh: bpy.types.Mesh,
verts: Optional[npt.NDArray[np.float64]] = None,
*,
+2 -7
View File
@@ -220,14 +220,9 @@ class Material(bonsai.core.tool.Material):
@classmethod
def get_material(
cls,
element: ifcopenshell.entity_instance,
should_inherit: bool = False,
should_skip_usage: bool = False,
cls, element: ifcopenshell.entity_instance, should_inherit: bool = False
) -> Union[ifcopenshell.entity_instance, None]:
return ifcopenshell.util.element.get_material(
element, should_inherit=should_inherit, should_skip_usage=should_skip_usage
)
return ifcopenshell.util.element.get_material(element, should_inherit=should_inherit)
@classmethod
def is_a_material_set(cls, material: ifcopenshell.entity_instance) -> bool:
+1 -1
View File
@@ -2459,7 +2459,7 @@ class Model(bonsai.core.tool.Model):
polygons = {}
for curve in curves:
geometry = ifcopenshell.geom.create_shape(settings, curve)
assert isinstance(geometry, W.triangulation)
assert isinstance(geometry, W.Triangulation)
v = ifcopenshell.util.shape.get_vertices(geometry, is_2d=True)
v = np.round(v, 4) # Round to nearest 0.1mm, otherwise things like circles don't polygonise reliably
edges = ifcopenshell.util.shape.get_edges(geometry)
+1 -1
View File
@@ -53,7 +53,7 @@ class Profile(bonsai.core.tool.Profile):
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
shape = ifcopenshell.geom.create_shape(settings, profile)
assert isinstance(shape, W.triangulation)
assert isinstance(shape, W.Triangulation)
verts = ifcopenshell.util.shape.get_vertices(shape)
if verts.size == 0:
raise RuntimeError(f"Profile shape has no vertices, it probably is invalid: '{profile}'.")
@@ -76,10 +76,6 @@ Release
Notes:
- Typically all packages are released at once using the same version schema
- ``VERSION`` uses Python/PEP 440-compatible spelling. For example, an alpha
release may be ``0.9.0alpha0`` (canonicalized to ``0.9.0a0``); build scripts
derive numeric-only and SemVer forms such as ``0.9.0`` and
``0.9.0-alpha0`` where required.
- The ``README.md`` badges can serve as a visual reference for what versions have been released
- Corrective Release (if needed after a standard release):
+1 -1
View File
@@ -33,7 +33,7 @@ exclude = ["test*"]
[tool.ruff]
extend = "../../pyproject.toml"
lint.extend-select = [
"unused-import", # unused imports
"F401", # unused imports
]
[tool.ruff.lint.isort]
-1
View File
@@ -42,7 +42,6 @@ markers =
type
unit
void
wall
web
# Provide plugins explicitly, so it will be possible run tests with PYTEST_DISABLE_PLUGIN_AUTOLOAD.
+4 -4
View File
@@ -45,10 +45,10 @@ for dep in dependencies:
subprocess.check_call(command + [dep])
try:
import pygments # ruff: ignore[unused-import]
import pytest # ruff: ignore[unused-import]
import pytest_bdd # ruff: ignore[unused-import]
import pytest_blender # ruff: ignore[unused-import]
import pygments # noqa: F401
import pytest # noqa: F401
import pytest_bdd # noqa: F401
import pytest_blender # noqa: F401
print("Test dependency installation was successful!")
except Exception as e:
+24 -21
View File
@@ -163,29 +163,32 @@ class Drawer:
# self.svg_settings.set_deflection_tolerance(0.0001)
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
self.svg_settings.set("svg-without-storeys", True)
self.svg_settings.set("svg-write-poly", True)
self.svg_settings.set("svg-poly", True)
self.serialiser_settings = ifcopenshell.geom.serializer_settings()
self.serialiser_settings.set("svg-without-storeys", True)
self.serialiser_settings.set("svg-write-poly", True)
self.serialiser_settings.set("svg-poly", True)
# Objects with more than these edges are rendered as wireframe instead of HLR for optimisation
self.svg_settings.set("profile-threshold", 10000)
self.svg_settings.set("svg-xmlns", True)
self.svg_settings.set("svg-project", True)
self.svg_settings.set("auto-elevation", False)
self.svg_settings.set("auto-section", False)
self.svg_settings.set("print-space-names", False)
self.svg_settings.set("print-space-areas", False)
self.svg_settings.set("door-arcs", False)
self.svg_settings.set("svg-no-css", True)
self.svg_settings.set("elevation-ref-guid", self.camera_element.GlobalId)
self.svg_settings.set("scale", "1/50")
self.svg_settings.set("svg-subtract-before", "always")
self.svg_settings.set("svg-prefilter", True) # See #3359
# self.svg_settings.set("svg-prefilter", False) # See #3359
self.svg_settings.set("svg-unify-inputs", True)
self.svg_settings.set("svg-segment-projection", True)
self.serialiser_settings.set("profile-threshold", 10000)
self.serialiser_settings.set("svg-xmlns", True)
self.serialiser_settings.set("svg-project", True)
self.serialiser_settings.set("auto-elevation", False)
self.serialiser_settings.set("auto-section", False)
self.serialiser_settings.set("print-space-names", False)
self.serialiser_settings.set("print-space-areas", False)
self.serialiser_settings.set("door-arcs", False)
self.serialiser_settings.set("svg-no-css", True)
self.serialiser_settings.set("elevation-ref-guid", self.camera_element.GlobalId)
self.serialiser_settings.set("scale", "1/50")
self.serialiser_settings.set("svg-subtract-before", "always")
self.serialiser_settings.set("svg-prefilter", True) # See #3359
# self.serialiser_settings.set("svg-prefilter", False) # See #3359
self.serialiser_settings.set("svg-unify-inputs", True)
self.serialiser_settings.set("svg-segment-projection", True)
if target_view == "REFLECTED_PLAN_VIEW":
self.svg_settings.set("svg-mirror-y", True)
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
self.serialiser_settings.set("svg-mirror-y", True)
self.serialiser = ifcopenshell.geom.serializers.svg(
self.svg_buffer, self.svg_settings, self.serialiser_settings
)
self.serialiser.setFile(ifc)
@@ -72,12 +72,12 @@ Scenario: Add classification reference - object
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
When I press "bim.add_classification_reference(reference={reference}, obj='IfcWall/Cube', obj_type='Object')"
When I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
Then nothing happens
Scenario: Change classification level
@@ -88,8 +88,8 @@ Scenario: Change classification level
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
@@ -104,8 +104,8 @@ Scenario: Disable editing classification references
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
When I press "bim.disable_editing_classification_references"
@@ -119,12 +119,12 @@ Scenario: Enable editing classification reference
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWall/Cube', obj_type='Object')"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
When I press "bim.enable_editing_classification_reference(reference={reference})"
Then nothing happens
@@ -137,12 +137,12 @@ Scenario: Disable editing classification reference
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWall/Cube', obj_type='Object')"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.enable_editing_classification_reference(reference={reference})"
When I press "bim.disable_editing_classification_reference"
@@ -156,15 +156,15 @@ Scenario: Remove classification reference - object
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWall/Cube', obj_type='Object')"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.enable_editing_classification_reference(reference={reference})"
When I press "bim.remove_classification_reference(reference={reference}, obj='IfcWall/Cube', obj_type='Object')"
When I press "bim.remove_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
Then nothing happens
Scenario: Edit classification reference
@@ -175,12 +175,12 @@ Scenario: Edit classification reference
And I press "bim.add_classification"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.change_classification_level(parent_id={classification})"
And the variable "reference" is "{classification_ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWall/Cube', obj_type='Object')"
And I press "bim.add_classification_reference(reference={reference}, obj='IfcWallType/Cube', obj_type='Object')"
And the variable "reference" is "{ifc}.by_type('IfcClassificationReference')[0].id()"
And I press "bim.enable_editing_classification_reference(reference={reference})"
When I press "bim.edit_classification_reference"
@@ -185,7 +185,6 @@ Scenario: Update representation - updating a layered extrusion
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -214,7 +213,6 @@ Scenario: Update representation - updating a profiled extrusion
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -418,7 +416,6 @@ Scenario: Override duplicate move - copying a layered extrusion
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -450,7 +447,6 @@ Scenario: Override duplicate move - copying a profiled extrusion
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -121,7 +121,6 @@ Scenario: Assign material - material layer set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
When I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
Then the object "IfcWallType/Empty" does not have the material "Default"
@@ -135,7 +134,6 @@ Scenario: Unassign material - material layer set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
When I press "bim.unassign_material"
@@ -157,7 +155,6 @@ Scenario: Unassign material - removing inherited material
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
@@ -184,7 +181,6 @@ Scenario: Enable editing assigned material - material layer set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
When I press "bim.enable_editing_assigned_material"
@@ -204,7 +200,6 @@ Scenario: Disable editing assigned material - material layer set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -225,7 +220,6 @@ Scenario: Edit assigned material - material layer set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -241,7 +235,6 @@ Scenario: Assign material - material profile set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
When I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
Then the object "IfcWallType/Empty" does not have the material "Default"
@@ -255,7 +248,6 @@ Scenario: Unassign material - material profile set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
When I press "bim.unassign_material"
@@ -275,7 +267,6 @@ Scenario: Enable editing assigned material - material profile set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
When I press "bim.enable_editing_assigned_material"
@@ -295,7 +286,6 @@ Scenario: Disable editing assigned material - material profile set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -316,7 +306,6 @@ Scenario: Edit assigned material - material profile set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -465,7 +454,6 @@ Scenario: Add material set layer
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -489,7 +477,6 @@ Scenario: Remove material set layer
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
+97 -139
View File
@@ -314,12 +314,6 @@ Scenario: Load project elements - auto offset of cartesian points
Then the object "IfcBuildingElementProxy/NAME" is at "0,0,0"
Scenario: Load project elements - all georeferencing coordinate situations - disabled false origin mode
# D, G and J have their geometry far from their placement, so each is
# shifted onto one of its own verts to keep its precision. Which vert that
# is comes from the geometry kernel and has changed before, so these assert
# that the origin is on a vert rather than which one, and name verts rather
# than origins. In automatic mode the model origin is picked the same way
# and everything moves with it, so there they are relative to it.
Given an empty Blender session
And I press "bim.load_project(filepath='{cwd}/test/files/geolocation.ifc', is_advanced=True)"
When I set "scene.BIMProjectProperties.false_origin_mode" to "DISABLED"
@@ -332,19 +326,13 @@ Scenario: Load project elements - all georeferencing coordinate situations - dis
And the object "IfcActuator/A" is at "7,3,0"
And the object "IfcActuator/B" is at "6,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "13,4,-1" at map coordinates "13000,4000,-1000"
And the object "IfcActuator/D" has a vert at "15,2,1" at map coordinates "15000,2000,1000"
And the object "IfcActuator/D" is at "13,4,-1"
And the object "IfcActuator/E" is at "6,3,0"
And the object "IfcActuator/F" is at "3,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "15,6,-1" at map coordinates "15000,6000,-1000"
And the object "IfcActuator/G" has a vert at "17,4,1" at map coordinates "17000,4000,1000"
And the object "IfcActuator/G" is at "15,6,-1"
And the object "IfcActuator/H" is at "9,2,0"
And the object "IfcActuator/I" is at "3,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "11,3,-1" at map coordinates "11000,3000,-1000"
And the object "IfcActuator/J" has a vert at "13,1,1" at map coordinates "13000,1000,1000"
And the object "IfcActuator/J" is at "11,3,-1"
And the object "IfcActuator/K" is at "10,0,0"
Scenario: Load project elements - all georeferencing coordinate situations - automatic false origin mode
@@ -354,27 +342,24 @@ Scenario: Load project elements - all georeferencing coordinate situations - aut
When I set "scene.BIMProjectProperties.distance_limit" to "5"
And I press "bim.load_project_elements"
Then "scene.BIMGeoreferenceProperties.has_blender_offset" is "True"
And the model origin is on an object vertex
And "scene.BIMGeoreferenceProperties.model_origin" is "13000.0,4000.0,-1000.0"
And "scene.BIMGeoreferenceProperties.blender_offset_x" is "13000.0"
And "scene.BIMGeoreferenceProperties.blender_offset_y" is "4000.0"
And "scene.BIMGeoreferenceProperties.blender_offset_z" is "-1000.0"
And the object "IfcSite/My Site" is at "0,0,0"
And the object "IfcBuilding/My Building" is at "0,0,0"
And the object "IfcBuildingStorey/My Storey" is at "0,0,0"
And the object "IfcActuator/A" is at "7,3,0" relative to the model origin at map coordinates "7000,3000,0"
And the object "IfcActuator/B" is at "6,1,0" relative to the model origin at map coordinates "6000,1000,0"
And the object "IfcActuator/A" is at "-6,-1,1"
And the object "IfcActuator/B" is at "-7,-3,1"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "13,4,-1" relative to the model origin at map coordinates "13000,4000,-1000"
And the object "IfcActuator/D" has a vert at "15,2,1" relative to the model origin at map coordinates "15000,2000,1000"
And the object "IfcActuator/E" is at "6,3,0" relative to the model origin at map coordinates "6000,3000,0"
And the object "IfcActuator/F" is at "3,3,0" relative to the model origin at map coordinates "3000,3000,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "15,6,-1" relative to the model origin at map coordinates "15000,6000,-1000"
And the object "IfcActuator/G" has a vert at "17,4,1" relative to the model origin at map coordinates "17000,4000,1000"
And the object "IfcActuator/H" is at "9,2,0" relative to the model origin at map coordinates "9000,2000,0"
And the object "IfcActuator/I" is at "3,3,0" relative to the model origin at map coordinates "3000,3000,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "11,3,-1" relative to the model origin at map coordinates "11000,3000,-1000"
And the object "IfcActuator/J" has a vert at "13,1,1" relative to the model origin at map coordinates "13000,1000,1000"
And the object "IfcActuator/K" is at "10,0,0" relative to the model origin at map coordinates "10000,0,0"
And the object "IfcActuator/D" is at "0,0,0"
And the object "IfcActuator/E" is at "-7,-1,1"
And the object "IfcActuator/F" is at "-10,-1,1"
And the object "IfcActuator/G" is at "2,2,0"
And the object "IfcActuator/H" is at "-4,-2,1"
And the object "IfcActuator/I" is at "-10,-1,1"
And the object "IfcActuator/J" is at "-2,-1,0"
And the object "IfcActuator/K" is at "-3,-4,1"
Scenario: Load project elements - all georeferencing coordinate situations - manual false origin mode
Given an empty Blender session
@@ -394,20 +379,23 @@ Scenario: Load project elements - all georeferencing coordinate situations - man
And the object "IfcActuator/A" is at "-3,3,0"
And the object "IfcActuator/B" is at "-4,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "3,4,-1" at map coordinates "13000,4000,-1000"
And the object "IfcActuator/D" has a vert at "5,2,1" at map coordinates "15000,2000,1000"
And the object "IfcActuator/D" is at "3,4,-1"
And the object "IfcActuator/E" is at "-4,3,0"
And the object "IfcActuator/F" is at "-7,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "5,6,-1" at map coordinates "15000,6000,-1000"
And the object "IfcActuator/G" has a vert at "7,4,1" at map coordinates "17000,4000,1000"
And the object "IfcActuator/G" is at "5,6,-1"
And the object "IfcActuator/H" is at "-1,2,0"
And the object "IfcActuator/I" is at "-7,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "1,3,-1" at map coordinates "11000,3000,-1000"
And the object "IfcActuator/J" has a vert at "3,1,1" at map coordinates "13000,1000,1000"
And the object "IfcActuator/J" is at "1,3,-1"
And the object "IfcActuator/K" is at "0,0,0"
And the object "IfcActuator/D" has a cartesian point offset of "31,4,-1"
And the object "IfcActuator/G" has a cartesian point offset of "-25,6,-1"
And the object "IfcActuator/J" has a cartesian point offset of "11,3,-1"
And the object "IfcActuator/D" has a vertex at "3,2,-1"
And the object "IfcActuator/D" has a vertex at "5,2,-1"
And the object "IfcActuator/G" has a vertex at "5,4,-1"
And the object "IfcActuator/G" has a vertex at "7,4,-1"
And the object "IfcActuator/J" has a vertex at "1,1,-1"
And the object "IfcActuator/J" has a vertex at "3,1,-1"
Scenario: Load project elements - all georeferencing coordinate situations with an offset site - disabled false origin mode
Given an empty Blender session
@@ -422,19 +410,13 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "5.985,14.71,0"
And the object "IfcActuator/B" is at "5.5367,12.519,0"
And the object "IfcActuator/C" is at "0,10,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "11.5218,17.2284,-1" at map coordinates "11521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "13.9712,15.8141,1" at map coordinates "13971.246,15814.136,1000"
And the object "IfcActuator/D" is at "11.522,17.228,-1"
And the object "IfcActuator/E" is at "5.0191,14.451,0"
And the object "IfcActuator/F" is at "2.1213,13.674,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "12.936,19.6778,-1" at map coordinates "12935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "15.3855,18.2636,1" at map coordinates "15385.465,18263.627,1000"
And the object "IfcActuator/G" is at "12.936,19.678,-1"
And the object "IfcActuator/H" is at "8.1757,14.261,0"
And the object "IfcActuator/I" is at "2.1213,13.674,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "9.8487,15.7448,-1" at map coordinates "9848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "12.2982,14.3306,1" at map coordinates "12298.216,14330.573,1000"
And the object "IfcActuator/J" is at "9.8487,15.745,-1"
And the object "IfcActuator/K" is at "9.6593,12.588,0"
Scenario: Load project elements - all georeferencing coordinate situations with an offset site - automatic false origin mode
@@ -454,19 +436,13 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "7,3,0"
And the object "IfcActuator/B" is at "6,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "13,4,-1" at map coordinates "11521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "15,2,1" at map coordinates "13971.246,15814.136,1000"
And the object "IfcActuator/D" is at "13,4,-1"
And the object "IfcActuator/E" is at "6,3,0"
And the object "IfcActuator/F" is at "3,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "15,6,-1" at map coordinates "12935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "17,4,1" at map coordinates "15385.465,18263.627,1000"
And the object "IfcActuator/G" is at "15,6,-1"
And the object "IfcActuator/H" is at "9,2,0"
And the object "IfcActuator/I" is at "3,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "11,3,-1" at map coordinates "9848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "13,1,1" at map coordinates "12298.216,14330.573,1000"
And the object "IfcActuator/J" is at "11,3,-1"
And the object "IfcActuator/K" is at "10,0,0"
Scenario: Load project elements - all georeferencing coordinate situations with an offset site - manual false origin mode
@@ -487,20 +463,23 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "5.985,4.71,0"
And the object "IfcActuator/B" is at "5.5367,2.519,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "11.5218,7.2284,-1" at map coordinates "11521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "13.9712,5.8141,1" at map coordinates "13971.246,15814.136,1000"
And the object "IfcActuator/D" is at "11.522,7.228,-1"
And the object "IfcActuator/E" is at "5.0191,4.451,0"
And the object "IfcActuator/F" is at "2.1213,3.674,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "12.936,9.6778,-1" at map coordinates "12935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "15.3855,8.2636,1" at map coordinates "15385.465,18263.627,1000"
And the object "IfcActuator/G" is at "12.936,9.678,-1"
And the object "IfcActuator/H" is at "8.1757,4.261,0"
And the object "IfcActuator/I" is at "2.1213,3.674,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "9.8487,5.7448,-1" at map coordinates "9848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "12.2982,4.3306,1" at map coordinates "12298.216,14330.573,1000"
And the object "IfcActuator/J" is at "9.8487,5.745,-1"
And the object "IfcActuator/K" is at "9.6593,2.588,0"
And the object "IfcActuator/D" has a cartesian point offset of "31,4,-1"
And the object "IfcActuator/G" has a cartesian point offset of "-25,6,-1"
And the object "IfcActuator/J" has a cartesian point offset of "11,3,-1"
And the object "IfcActuator/D" has a vertex at "12.039,5.296,-1"
And the object "IfcActuator/D" has a vertex at "13.971,5.814,-1"
And the object "IfcActuator/G" has a vertex at "13.454,7.746,-1"
And the object "IfcActuator/G" has a vertex at "15.385,8.264,-1"
And the object "IfcActuator/J" has a vertex at "10.366,3.813,-1"
And the object "IfcActuator/J" has a vertex at "12.298,4.331,-1"
Scenario: Load project elements - all georeferencing coordinate situations with an offset site - manual false origin mode - with custom project north
Given an empty Blender session
@@ -521,19 +500,13 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "7,3,0"
And the object "IfcActuator/B" is at "6,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "13,4,-1" at map coordinates "11521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "15,2,1" at map coordinates "13971.246,15814.136,1000"
And the object "IfcActuator/D" is at "13,4,-1"
And the object "IfcActuator/E" is at "6,3,0"
And the object "IfcActuator/F" is at "3,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "15,6,-1" at map coordinates "12935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "17,4,1" at map coordinates "15385.465,18263.627,1000"
And the object "IfcActuator/G" is at "15,6,-1"
And the object "IfcActuator/H" is at "9,2,0"
And the object "IfcActuator/I" is at "3,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "11,3,-1" at map coordinates "9848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "13,1,1" at map coordinates "12298.216,14330.573,1000"
And the object "IfcActuator/J" is at "11,3,-1"
And the object "IfcActuator/K" is at "10,0,0"
Scenario: Load project elements - all georeferencing coordinate situations with a map conversion - disabled false origin mode (this should be identical to the situation with no map conversion)
@@ -549,19 +522,13 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "7,3,0"
And the object "IfcActuator/B" is at "6,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "13,4,-1" at map coordinates "28000,4000,-1000"
And the object "IfcActuator/D" has a vert at "15,2,1" at map coordinates "30000,2000,1000"
And the object "IfcActuator/D" is at "13,4,-1"
And the object "IfcActuator/E" is at "6,3,0"
And the object "IfcActuator/F" is at "3,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "15,6,-1" at map coordinates "30000,6000,-1000"
And the object "IfcActuator/G" has a vert at "17,4,1" at map coordinates "32000,4000,1000"
And the object "IfcActuator/G" is at "15,6,-1"
And the object "IfcActuator/H" is at "9,2,0"
And the object "IfcActuator/I" is at "3,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "11,3,-1" at map coordinates "26000,3000,-1000"
And the object "IfcActuator/J" has a vert at "13,1,1" at map coordinates "28000,1000,1000"
And the object "IfcActuator/J" is at "11,3,-1"
And the object "IfcActuator/K" is at "10,0,0"
Scenario: Load project elements - all georeferencing coordinate situations with a map conversion - automatic false origin mode (this should affect the Blender eastings and northings, which is now different to the Blender offset XYZ, but is otherwise identical to the non-map conversion variant)
@@ -571,27 +538,24 @@ Scenario: Load project elements - all georeferencing coordinate situations with
When I set "scene.BIMProjectProperties.distance_limit" to "5"
And I press "bim.load_project_elements"
Then "scene.BIMGeoreferenceProperties.has_blender_offset" is "True"
And the model origin is on an object vertex
And "scene.BIMGeoreferenceProperties.model_origin" is "28000.0,4000.0,-1000.0"
And "scene.BIMGeoreferenceProperties.blender_offset_x" is "13000.0"
And "scene.BIMGeoreferenceProperties.blender_offset_y" is "4000.0"
And "scene.BIMGeoreferenceProperties.blender_offset_z" is "-1000.0"
And the object "IfcSite/My Site" is at "0,0,0"
And the object "IfcBuilding/My Building" is at "0,0,0"
And the object "IfcBuildingStorey/My Storey" is at "0,0,0"
And the object "IfcActuator/A" is at "22,3,0" relative to the model origin at map coordinates "22000,3000,0"
And the object "IfcActuator/B" is at "21,1,0" relative to the model origin at map coordinates "21000,1000,0"
And the object "IfcActuator/A" is at "-6,-1,1"
And the object "IfcActuator/B" is at "-7,-3,1"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "28,4,-1" relative to the model origin at map coordinates "28000,4000,-1000"
And the object "IfcActuator/D" has a vert at "30,2,1" relative to the model origin at map coordinates "30000,2000,1000"
And the object "IfcActuator/E" is at "21,3,0" relative to the model origin at map coordinates "21000,3000,0"
And the object "IfcActuator/F" is at "18,3,0" relative to the model origin at map coordinates "18000,3000,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "30,6,-1" relative to the model origin at map coordinates "30000,6000,-1000"
And the object "IfcActuator/G" has a vert at "32,4,1" relative to the model origin at map coordinates "32000,4000,1000"
And the object "IfcActuator/H" is at "24,2,0" relative to the model origin at map coordinates "24000,2000,0"
And the object "IfcActuator/I" is at "18,3,0" relative to the model origin at map coordinates "18000,3000,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "26,3,-1" relative to the model origin at map coordinates "26000,3000,-1000"
And the object "IfcActuator/J" has a vert at "28,1,1" relative to the model origin at map coordinates "28000,1000,1000"
And the object "IfcActuator/K" is at "25,0,0" relative to the model origin at map coordinates "25000,0,0"
And the object "IfcActuator/D" is at "0,0,0"
And the object "IfcActuator/E" is at "-7,-1,1"
And the object "IfcActuator/F" is at "-10,-1,1"
And the object "IfcActuator/G" is at "2,2,0"
And the object "IfcActuator/H" is at "-4,-2,1"
And the object "IfcActuator/I" is at "-10,-1,1"
And the object "IfcActuator/J" is at "-2,-1,0"
And the object "IfcActuator/K" is at "-3,-4,1"
Scenario: Load project elements - all georeferencing coordinate situations with a map conversion - manual false origin mode (this should affect the Blender eastings and northings, which is now different to the Blender offset XYZ, but is otherwise identical to the non-map conversion variant)
Given an empty Blender session
@@ -611,20 +575,23 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "-3,3,0"
And the object "IfcActuator/B" is at "-4,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "3,4,-1" at map coordinates "28000,4000,-1000"
And the object "IfcActuator/D" has a vert at "5,2,1" at map coordinates "30000,2000,1000"
And the object "IfcActuator/D" is at "3,4,-1"
And the object "IfcActuator/E" is at "-4,3,0"
And the object "IfcActuator/F" is at "-7,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "5,6,-1" at map coordinates "30000,6000,-1000"
And the object "IfcActuator/G" has a vert at "7,4,1" at map coordinates "32000,4000,1000"
And the object "IfcActuator/G" is at "5,6,-1"
And the object "IfcActuator/H" is at "-1,2,0"
And the object "IfcActuator/I" is at "-7,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "1,3,-1" at map coordinates "26000,3000,-1000"
And the object "IfcActuator/J" has a vert at "3,1,1" at map coordinates "28000,1000,1000"
And the object "IfcActuator/J" is at "1,3,-1"
And the object "IfcActuator/K" is at "0,0,0"
And the object "IfcActuator/D" has a cartesian point offset of "31,4,-1"
And the object "IfcActuator/G" has a cartesian point offset of "-25,6,-1"
And the object "IfcActuator/J" has a cartesian point offset of "11,3,-1"
And the object "IfcActuator/D" has a vertex at "3,2,-1"
And the object "IfcActuator/D" has a vertex at "5,2,-1"
And the object "IfcActuator/G" has a vertex at "5,4,-1"
And the object "IfcActuator/G" has a vertex at "7,4,-1"
And the object "IfcActuator/J" has a vertex at "1,1,-1"
And the object "IfcActuator/J" has a vertex at "3,1,-1"
Scenario: Load project elements - all georeferencing coordinate situations with map conversion and an offset site - disabled false origin mode
Given an empty Blender session
@@ -639,19 +606,13 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "5.985,14.71,0"
And the object "IfcActuator/B" is at "5.5367,12.519,0"
And the object "IfcActuator/C" is at "0,10,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "11.5218,17.2284,-1" at map coordinates "26521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "13.9712,15.8141,1" at map coordinates "28971.246,15814.136,1000"
And the object "IfcActuator/D" is at "11.522,17.228,-1"
And the object "IfcActuator/E" is at "5.0191,14.451,0"
And the object "IfcActuator/F" is at "2.1213,13.674,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "12.936,19.6778,-1" at map coordinates "27935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "15.3855,18.2636,1" at map coordinates "30385.465,18263.627,1000"
And the object "IfcActuator/G" is at "12.936,19.678,-1"
And the object "IfcActuator/H" is at "8.1757,14.261,0"
And the object "IfcActuator/I" is at "2.1213,13.674,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "9.8487,15.7448,-1" at map coordinates "24848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "12.2982,14.3306,1" at map coordinates "27298.216,14330.573,1000"
And the object "IfcActuator/J" is at "9.8487,15.745,-1"
And the object "IfcActuator/K" is at "9.6593,12.588,0"
Scenario: Load project elements - all georeferencing coordinate situations with map conversion and an offset site - automatic false origin mode
@@ -671,19 +632,13 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "7,3,0"
And the object "IfcActuator/B" is at "6,1,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "13,4,-1" at map coordinates "26521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "15,2,1" at map coordinates "28971.246,15814.136,1000"
And the object "IfcActuator/D" is at "13,4,-1"
And the object "IfcActuator/E" is at "6,3,0"
And the object "IfcActuator/F" is at "3,3,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "15,6,-1" at map coordinates "27935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "17,4,1" at map coordinates "30385.465,18263.627,1000"
And the object "IfcActuator/G" is at "15,6,-1"
And the object "IfcActuator/H" is at "9,2,0"
And the object "IfcActuator/I" is at "3,3,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "11,3,-1" at map coordinates "24848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "13,1,1" at map coordinates "27298.216,14330.573,1000"
And the object "IfcActuator/J" is at "11,3,-1"
And the object "IfcActuator/K" is at "10,0,0"
Scenario: Load project elements - all georeferencing coordinate situations with map conversion and an offset site - manual false origin mode
@@ -704,20 +659,23 @@ Scenario: Load project elements - all georeferencing coordinate situations with
And the object "IfcActuator/A" is at "5.985,4.71,0"
And the object "IfcActuator/B" is at "5.5367,2.519,0"
And the object "IfcActuator/C" is at "0,0,0"
And the object "IfcActuator/D" has its origin on a vertex
And the object "IfcActuator/D" has a vert at "11.5218,7.2284,-1" at map coordinates "26521.758,17228.35,-1000"
And the object "IfcActuator/D" has a vert at "13.9712,5.8141,1" at map coordinates "28971.246,15814.136,1000"
And the object "IfcActuator/D" is at "11.522,7.228,-1"
And the object "IfcActuator/E" is at "5.0191,4.451,0"
And the object "IfcActuator/F" is at "2.1213,3.674,0"
And the object "IfcActuator/G" has its origin on a vertex
And the object "IfcActuator/G" has a vert at "12.936,9.6778,-1" at map coordinates "27935.975,19677.841,-1000"
And the object "IfcActuator/G" has a vert at "15.3855,8.2636,1" at map coordinates "30385.465,18263.627,1000"
And the object "IfcActuator/G" is at "12.936,9.678,-1"
And the object "IfcActuator/H" is at "8.1757,4.261,0"
And the object "IfcActuator/I" is at "2.1213,3.674,0"
And the object "IfcActuator/J" has its origin on a vertex
And the object "IfcActuator/J" has a vert at "9.8487,5.7448,-1" at map coordinates "24848.726,15744.786,-1000"
And the object "IfcActuator/J" has a vert at "12.2982,4.3306,1" at map coordinates "27298.216,14330.573,1000"
And the object "IfcActuator/J" is at "9.8487,5.745,-1"
And the object "IfcActuator/K" is at "9.6593,2.588,0"
And the object "IfcActuator/D" has a cartesian point offset of "31,4,-1"
And the object "IfcActuator/G" has a cartesian point offset of "-25,6,-1"
And the object "IfcActuator/J" has a cartesian point offset of "11,3,-1"
And the object "IfcActuator/D" has a vertex at "12.039,5.296,-1"
And the object "IfcActuator/D" has a vertex at "13.971,5.814,-1"
And the object "IfcActuator/G" has a vertex at "13.454,7.746,-1"
And the object "IfcActuator/G" has a vertex at "15.385,8.264,-1"
And the object "IfcActuator/J" has a vertex at "10.366,3.813,-1"
And the object "IfcActuator/J" has a vertex at "12.298,4.331,-1"
Scenario: Link IFC - from an empty IFC project
Given an empty IFC project
-4
View File
@@ -81,7 +81,6 @@ Scenario: Assign type - assign to a type with a material layer set, which automa
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
When the variable "type" is "{ifc}.by_type('IfcWallType')[0].id()"
@@ -103,7 +102,6 @@ Scenario: Assign type - assign to a type with a material layer set, which automa
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
When the variable "type" is "{ifc}.by_type('IfcWallType')[0].id()"
@@ -127,7 +125,6 @@ Scenario: Assign type - assign to a different type with a LAYER2 material layer
And I press "bim.assign_class"
And the variable "type" is "{ifc}.by_type('IfcWallType')[-1].id()"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialLayerSet"
And I press "bim.assign_material"
And I add an empty
@@ -183,7 +180,6 @@ Scenario: Assign type - assign to a type with a material profile set
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
And I press "bim.assign_class"
And I press "bim.add_material()"
And the object "IfcWallType/Empty" is selected
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
And I press "bim.assign_material"
And I press "bim.enable_editing_assigned_material"
@@ -45,14 +45,14 @@ def test_text_formatter_defaults_to_none():
def test_text_formatter_field_stores_callable():
formatter = lambda props, value: f"{value:.2f}m"
formatter = lambda props, value: f"{value:.2f}m" # noqa: E731
config = DimensionGizmoConfig(attr_name="length", axis=(1, 0, 0), text_formatter=formatter)
assert config.text_formatter is not None
assert callable(config.text_formatter)
def test_text_formatter_receives_props_and_value():
formatter = lambda props, value: f"{props.label}={value}"
formatter = lambda props, value: f"{props.label}={value}" # noqa: E731
config = DimensionGizmoConfig(attr_name="length", axis=(1, 0, 0), text_formatter=formatter)
props = SimpleNamespace(label="L")
assert config.text_formatter(props, 3.14) == "L=3.14"
@@ -104,7 +104,7 @@ class TestParametricGizmoPollsHideDuringTransformModal:
continue
try:
result = poll(bpy.context)
except Exception as exc:
except Exception as exc: # noqa: BLE001
offenders.append((name, f"poll raised: {type(exc).__name__}: {exc}"))
continue
if result:
@@ -98,7 +98,7 @@ class TestWallGizmoGroupsHideDuringPreview:
continue
try:
result = poll(bpy.context)
except Exception as exc:
except Exception as exc: # noqa: BLE001
offenders.append((name, f"poll raised: {type(exc).__name__}: {exc}"))
continue
if result:
@@ -119,7 +119,7 @@ class TestWallGizmoGroupsHideOnArrayChildSelection:
for name, cls in groups:
try:
result = cls.poll(bpy.context)
except Exception as exc:
except Exception as exc: # noqa: BLE001
offenders.append((name, f"poll raised: {type(exc).__name__}: {exc}"))
continue
if result:
@@ -159,7 +159,7 @@ class TestWallOperatorsRejectArrayChildSelection:
for name, cls in ops:
try:
result = cls.poll(bpy.context)
except Exception as exc:
except Exception as exc: # noqa: BLE001
offenders.append((name, f"poll raised: {type(exc).__name__}: {exc}"))
continue
if result:
-107
View File
@@ -36,7 +36,6 @@ import bpy
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.unit
import numpy as np
import pytest
from mathutils import Vector
@@ -1001,7 +1000,6 @@ def i_click_button_and_expect_error_error_msg(button, error_msg):
@given(parsers.parse('I evaluate expression "{expression}"'))
@when(parsers.parse('I evaluate expression "{expression}"'))
@then(parsers.parse('I evaluate expression "{expression}"'))
def i_evaluate_expression(expression):
expression = replace_variables(expression)
exec(expression)
@@ -1682,111 +1680,6 @@ def the_object_name_has_a_vertex_at_location(name, location):
assert is_pass, f"No verts found at {location}: {verts}"
def get_model_origin() -> Vector:
"""Where the model was shifted to, in Blender units.
Geometry far from the origin is moved next to it so it keeps its precision,
and the shift is recorded as the model origin. Which vert of which object it
lands on is not something to depend on, so anything measured from it stays
put even when that choice changes.
"""
props = bpy.context.scene.BIMGeoreferenceProperties
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(an_ifc_file_exists())
return Vector([float(co) for co in props.model_origin.split(",")]) * unit_scale
def get_world_verts(obj: bpy.types.Object) -> list[Vector]:
mesh = obj.data
assert isinstance(mesh, bpy.types.Mesh) and len(mesh.vertices), f"Object {obj.name} has no mesh"
return [obj.matrix_world @ v.co for v in mesh.vertices]
def assert_vert_at_map_coordinates(obj: bpy.types.Object, vert: Vector, coordinates: str) -> None:
# Same conversion as the georeferencing calculator, which works in project
# units rather than Blender ones.
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(an_ifc_file_exists())
enh = Vector(tool.Georeference.xyz2enh(tuple(co / unit_scale for co in vert)))
expected = Vector([float(co) for co in coordinates.split(",")])
assert (enh - expected).length < 0.05, f"Vert {vert} is at map coordinates {enh[:]} instead of {coordinates}"
@then(
parsers.parse(
'the object "{name}" is at "{location}" relative to the model origin at map coordinates "{coordinates}"'
)
)
def the_object_name_is_at_location_relative_to_the_model_origin_at_map_coordinates(name, location, coordinates):
"""For objects with no geometry to name a vert on.
The Blender location is only meaningful next to the origin everything was
shifted by, since the two move together, but the map coordinates hold still
either way.
"""
obj = the_object_name_exists(name)
obj_location = obj.location + get_model_origin()
assert (
obj_location - Vector([float(co) for co in location.split(",")])
).length < 0.05, f"Object is at {obj_location} relative to the model origin instead of {location}"
assert_vert_at_map_coordinates(obj, obj.matrix_world.translation, coordinates)
@then(parsers.parse('the object "{name}" has a vert at "{location}" at map coordinates "{coordinates}"'))
def the_object_name_has_a_vert_at_location_at_map_coordinates(name, location, coordinates):
"""Check where a vert sits in Blender and where it is in the world.
Both matter: the Blender location is what the user sees, and checking only
the map coordinates would pass just as happily if the georeferencing maths
or the offsets it reads were wrong, since the same maths produces both.
"""
obj = the_object_name_exists(name)
target = Vector([float(co) for co in location.split(",")])
verts = get_world_verts(obj)
vert = next((v for v in verts if (v - target).length < 0.001), None)
assert vert is not None, f"No vert found at {location}: {verts}"
assert_vert_at_map_coordinates(obj, vert, coordinates)
@then(
parsers.parse(
'the object "{name}" has a vert at "{location}" relative to the model origin at map coordinates "{coordinates}"'
)
)
def the_object_name_has_a_vert_at_location_relative_to_the_model_origin_at_map_coordinates(name, location, coordinates):
"""As above, for when the whole model has been shifted onto the origin.
Blender locations are then only meaningful relative to that origin, since
everything moves together with it.
"""
obj = the_object_name_exists(name)
target = Vector([float(co) for co in location.split(",")]) - get_model_origin()
verts = get_world_verts(obj)
vert = next((v for v in verts if (v - target).length < 0.001), None)
assert vert is not None, f"No vert found at {location} relative to the model origin: {verts}"
assert_vert_at_map_coordinates(obj, vert, coordinates)
@then(parsers.parse('the object "{name}" has its origin on a vertex'))
def the_object_name_has_its_origin_on_a_vertex(name):
"""Far away geometry is shifted onto one of its own verts, which keeps the
origin on the geometry and the local coordinates small enough to keep their
precision. Which vert that is does not matter."""
obj = the_object_name_exists(name)
mesh = obj.data
assert isinstance(mesh, bpy.types.Mesh) and len(mesh.vertices), f"Object {obj.name} has no mesh"
nearest = min(v.co.length for v in mesh.vertices)
assert nearest < 0.001, f"Object origin is {nearest} away from its nearest vert"
@then("the model origin is on an object vertex")
def the_model_origin_is_on_an_object_vertex():
for obj in bpy.data.objects:
if not isinstance(obj.data, bpy.types.Mesh):
continue
if any(v.length < 0.001 for v in get_world_verts(obj)):
return
assert False, "No object has a vert at the model origin"
@then(parsers.parse('the object "{name}" has no scale'))
def the_object_name_has_no_scale(name):
assert the_object_name_exists(name).scale == Vector(
+1 -1
View File
@@ -1,5 +1,5 @@
[tool.ruff]
extend = "../pyproject.toml"
lint.ignore = [
"unused-import", # unused imports
"F401", # unused imports
]
+3 -3
View File
@@ -168,7 +168,7 @@ ifcopenshell_deploy_qt_runtime(BonsaiViewer)
# them explicitly. (In a static build these are absent from lib/
# and the glob just no-ops, so this rule is safe in both modes.)
#
# 2. Plug-ins (ifcopenshell_*.dylib, no `lib` prefix) dlopen-only
# 2. Plug-ins (ifcopenshell.*.dylib, no `lib` prefix) dlopen-only
# deps the plug-in loader resolves at runtime. macdeployqt has
# no way to know about these.
#
@@ -177,7 +177,7 @@ ifcopenshell_deploy_qt_runtime(BonsaiViewer)
# inside the bundle), so plug-ins and core libs both find each other
# on the first probe.
#
# The geometry-writer filter drops ifcopenshell_geometry_writer_*.dylib
# The geometry-writer filter drops ifcopenshell.geometry.writer.*.dylib
# (the per-schema OBJ / glTF / DAE / STP / IGS / SVG / TTL export
# converters heavy, viewer-irrelevant). Mirrors the Rocky workflow's
# filter in `stage_runtime_payload` (see 27249770e).
@@ -195,7 +195,7 @@ if(APPLE)
install(CODE [[
set(_fw "${CMAKE_INSTALL_PREFIX}/BonsaiViewer.app/Contents/Frameworks")
file(GLOB _ifc_dylibs "${CMAKE_INSTALL_PREFIX}/lib/*.dylib")
list(FILTER _ifc_dylibs EXCLUDE REGEX "ifcopenshell_geometry_writer_")
list(FILTER _ifc_dylibs EXCLUDE REGEX "ifcopenshell\\.geometry\\.writer\\.")
if(_ifc_dylibs)
message(STATUS "Staging IfcOpenShell dylibs (linked core + plug-ins) into BonsaiViewer.app/Contents/Frameworks")
file(COPY ${_ifc_dylibs} DESTINATION "${_fw}")
+1 -1
View File
@@ -70,7 +70,7 @@ std::optional<BasicElementInfo> ElementRegistry::findBasicElementInfo(uint32_t o
return it->second;
}
std::optional<express::base> ElementRegistry::findEntity(uint32_t object_id) const {
std::optional<express::Base> ElementRegistry::findEntity(uint32_t object_id) const {
if (!loader_) return std::nullopt;
auto info = findBasicElementInfo(object_id);
+1 -1
View File
@@ -54,7 +54,7 @@ public:
void removeModel(uint32_t session_model_id);
std::vector<BasicElementInfo> basicElementInfoForModel(uint32_t session_model_id) const;
std::optional<BasicElementInfo> findBasicElementInfo(uint32_t object_id) const;
std::optional<express::base> findEntity(uint32_t object_id) const;
std::optional<express::Base> findEntity(uint32_t object_id) const;
private:
void onSidecarElementsReady(uint32_t session_model_id,
@@ -44,7 +44,7 @@ Main components
code.
``GeometryStreamer``
Runs ``ifcopenshell::geom::iterator`` on a worker thread for raw IFC loads. It emits a
Runs ``IfcGeom::Iterator`` on a worker thread for raw IFC loads. It emits a
``StreamedMesh`` once for each unique representation mesh and a
``StreamedInstance`` for each placed occurrence.
+3 -3
View File
@@ -32,7 +32,7 @@
#include "../../../ifcviewer/SceneLoader.h"
#include "../../../ifcviewer/SidecarBuilder.h"
#include "../../../ifcviewer/ViewportWindow.h"
#include "../../../ifcgeom/serializer.h"
#include "../../../ifcgeom/Serializer.h"
#include "../../../serializers/document_serializer_plugin.h"
#include <QDebug>
@@ -685,7 +685,7 @@ void convertIfcToDatabase(SessionState& session, QWidget& host) {
throw ifcopenshell::exception("RDB serializer does not support streaming from an input filename");
}
std::shared_ptr<ifcopenshell::geom::serializer> serializer = registry.create("rdb", context);
boost::shared_ptr<Serializer> serializer = registry.create("rdb", context);
serializer->finalize();
} catch (const std::exception& e) {
*error_message = QString::fromUtf8(e.what());
@@ -791,7 +791,7 @@ void exportGeometryDatabase(SessionState& session, QWidget& host) {
throw ifcopenshell::exception("RDB serializer does not support streaming from an input filename");
}
std::shared_ptr<ifcopenshell::geom::serializer> serializer = registry.create("rdb", context);
boost::shared_ptr<Serializer> serializer = registry.create("rdb", context);
serializer->finalize();
serializer.reset();
@@ -75,16 +75,16 @@ QString formatCachedUnitScale(double meters_per_unit) {
return QString("Cached scale: 1 unit = %1 m").arg(formatNumber(meters_per_unit));
}
std::string enumString(const ifcopenshell::attribute_value& av) {
std::string enumString(const attribute_value& av) {
if (av.isNull()) return {};
if (av.type() != ifcopenshell::Argument_ENUMERATION) return {};
ifcopenshell::enumeration_reference enumeration = av;
enumeration_reference enumeration = av;
return std::string(enumeration.value() ? enumeration.value() : "");
}
QString formatNamedUnit(const express::base& unit) {
QString formatNamedUnit(const express::Base& unit) {
if (!unit) return "";
auto entity = unit.as<express::entity>();
auto entity = unit.as<express::Entity>();
if (unit.declaration().is("IfcSIUnit")) {
const std::string prefix = enumString(entity.get("Prefix"));
const std::string name = enumString(entity.get("Name"));
+4 -4
View File
@@ -109,7 +109,7 @@ void PropertiesPanelView::refresh(uint32_t object_id) {
state.entity = {"No item selected", ""};
auto entity = registry ? registry->findEntity(object_id)
: std::optional<express::base>{};
: std::optional<express::Base>{};
if (entity) {
state.entity.entity_class = QString::fromStdString(entity->declaration().name());
if (auto predefined_type = get_predefined_type(*entity)) {
@@ -121,16 +121,16 @@ void PropertiesPanelView::refresh(uint32_t object_id) {
}
// Relationships: the construction type and the spatial container, shown
// by name (falling back to the entity class when unnamed).
auto display_name = [](const express::base& related) -> QString {
auto display_name = [](const express::Base& related) -> QString {
if (auto name = get_string_attribute(related, "Name"); name && !name->empty()) {
return QString::fromStdString(*name);
}
return QString::fromStdString(related.declaration().name());
};
if (express::base type = get_type(*entity)) {
if (express::Base type = get_type(*entity)) {
state.relationships.append({"Type", display_name(type)});
}
if (express::base container = get_container(*entity)) {
if (express::Base container = get_container(*entity)) {
state.relationships.append({"Container", display_name(container)});
}
// Property sets (Pset_*) and quantity sets (Qto_* / BaseQuantities),
@@ -60,7 +60,7 @@ TreeNode* findNodeRecursive(QList<TreeNode>& nodes, const NodePath& path, int de
return nullptr;
}
ItemKind kindOf(const express::base& element) {
ItemKind kindOf(const express::Base& element) {
const auto& declaration = element.declaration();
if (declaration.is("IfcSite")) return ItemKind::Site;
if (declaration.is("IfcBuilding")) return ItemKind::Building;
@@ -68,14 +68,14 @@ ItemKind kindOf(const express::base& element) {
return ItemKind::Space; // IfcSpace, IfcSpatialZone, …
}
QString displayName(const express::base& element) {
QString displayName(const express::Base& element) {
if (auto name = get_string_attribute(element, "Name"); name && !name->empty()) {
return QString::fromStdString(*name);
}
return QString::fromStdString(element.declaration().name());
}
TreeNode buildNode(const express::base& element) {
TreeNode buildNode(const express::Base& element) {
TreeNode node;
node.name = displayName(element);
node.kind = kindOf(element);
+2 -2
View File
@@ -46,6 +46,6 @@ py-modules = ["bsdd","bsdd_json","type_hints"]
[tool.ruff]
extend = "../../pyproject.toml"
lint.extend-select = [
"unused-import", # unused imports
lint.select = [
"F401", # unused imports
]
+6 -9
View File
@@ -3,10 +3,7 @@ IS_STABLE:=FALSE
PYTHON:=python3
PIP:=pip3
VERSION:=$(shell cat ../../VERSION)
VERSION_BASE:=$(shell sed -E 's/[[:alpha:]]+[0-9]+$$//' ../../VERSION)
VERSION_PYTHON:=$(shell sed 's/alpha/a/' ../../VERSION)
VERSION_DATE:=$(shell date '+%y%m%d')
VERSION_DAILY:=$(VERSION_BASE)a$(VERSION_DATE)
SED:=sed -i
VENV_BIN:=bin
@@ -33,18 +30,18 @@ dist:
cp pyproject.toml build/
if [ -f README.md ]; then cp README.md build/; fi
ifeq ($(IS_STABLE), TRUE)
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
ifdef IS_MODULE
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/$(PACKAGE_NAME)
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/$(PACKAGE_NAME)
else
$(SED) 's/version = "0.0.0"/version = "$(VERSION_PYTHON)"/' build/$(PACKAGE_NAME)/__init__.py
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/$(PACKAGE_NAME)/__init__.py
endif
else
$(SED) 's/version = "0.0.0"/version = "$(VERSION_DAILY)"/' build/pyproject.toml
$(SED) 's/version = "0.0.0"/version = "$(VERSION)a$(VERSION_DATE)"/' build/pyproject.toml
ifdef IS_MODULE
$(SED) 's/version = "0.0.0"/version = "$(VERSION_DAILY)"/' build/$(PACKAGE_NAME)
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/$(PACKAGE_NAME)
else
$(SED) 's/version = "0.0.0"/version = "$(VERSION_DAILY)"/' build/$(PACKAGE_NAME)/__init__.py
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/$(PACKAGE_NAME)/__init__.py
endif
endif
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
+8 -8
View File
@@ -50,7 +50,7 @@
#include "ifcparse/hierarchy_helper.h"
#include "plugin/plugin.h"
#include "../ifcgeom/serialization/serialization.h"
#include "../ifcgeom/Serialization/Serialization.h"
#if USE_VLD
#include <vld.h>
@@ -101,7 +101,7 @@ 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.
auto building_shape = ifcopenshell::geom::serialise(file, building_shell, false).as<IfcSchema::IfcProductDefinitionShape>();
auto building_shape = IfcGeom::serialise(file, building_shell, false).as<IfcSchema::IfcProductDefinitionShape>();
file.add_entity(building_shape);
auto building_representations = building_shape.Representations();
@@ -117,9 +117,9 @@ int main() {
TopoDS_Shape shape;
createGroundShape(shape);
auto ground_representation = ifcopenshell::geom::serialise(file, shape, true);
auto ground_representation = IfcGeom::serialise(file, shape, true);
if (!ground_representation) {
ground_representation = ifcopenshell::geom::tesselate(file, shape, 100.);
ground_representation = IfcGeom::tesselate(file, shape, 100.);
}
file.getSingle<IfcSchema::IfcSite>().setRepresentation(ground_representation.as<IfcSchema::IfcProductDefinitionShape>());
@@ -131,13 +131,13 @@ int main() {
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.
// BRepBuilderAPI_NurbsConvert can be used to serialize such surfaces as nurbs surfaces.
// Note that IFC lacks elementary surfaces that STEP does have, such as spherical_surface.
// BRepBuilderAPI_NurbsConvert can be used to serialize such surfaces as nurbs surfaces.
TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(gp_Pnt(), 1000.).Shape();
auto sphere_representation = ifcopenshell::geom::serialise(file, sphere, true);
IfcSchema::IfcProductDefinitionShape* sphere_representation = IfcGeom::serialise(sphere, true);
if (S(IfcSchema::Identifier) == "IFC4") {
sphere = BRepBuilderAPI_NurbsConvert(sphere, true).Shape();
sphere_representation = ifcopenshell::geom::serialise(file, sphere, true);
sphere_representation = IfcGeom::serialise(sphere, true);
}
*/
+2 -2
View File
@@ -47,7 +47,7 @@
#include "ifcparse/hierarchy_helper.h"
#include "plugin/plugin.h"
#include "../ifcgeom/serialization/serialization.h"
#include "../ifcgeom/Serialization/Serialization.h"
#if USE_VLD
#include <vld.h>
@@ -301,7 +301,7 @@ int main() {
// will be tessellated using the deflection specified.
TopoDS_Shape shape;
createGroundShape(shape);
auto ground_representation = ifcopenshell::geom::tesselate(file, shape, 100.).as<IfcSchema::IfcProductDefinitionShape>();
auto ground_representation = IfcGeom::tesselate(file, shape, 100.).as<IfcSchema::IfcProductDefinitionShape>();
file.getSingle<IfcSchema::IfcSite>().setRepresentation(ground_representation);
GProp_GProps prop;
+4 -4
View File
@@ -70,10 +70,10 @@ typedef IfcSchema::IfcBuildingElement element_t;
typedef IfcSchema::IfcBuiltElement element_t;
#endif
std::string format_string(const ifcopenshell::attribute_value& 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 xml_serializer.cpp
// @todo mostly duplicated from XmlSerializer.cpp
if (argument.isNull()) {
return "-";
}
@@ -194,7 +194,7 @@ void get_psets_s(element_properties& props, const typename Schema::IfcObjectDefi
}
}
element_properties get_psets(const express::base& inst) {
element_properties get_psets(const express::Base& inst) {
element_properties props;
if (auto object_definition = inst.as<IfcSchema::IfcObjectDefinition>()) {
get_psets_s<IfcSchema>(props, object_definition);
@@ -213,7 +213,7 @@ int main(int argc, char** argv) {
#endif
// Redirect the output (both progress and log) to stdout
ifcopenshell::logger::root().set_output(&std::cout, &std::cout);
::logger::root().set_output(&std::cout, &std::cout);
// Parse the IFC file provided in argv[1]
ifcopenshell::file file(argv[1]);
+97
View File
@@ -0,0 +1,97 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('IfcParseExamples_test.ifc','2025-12-12T13:49:19+05:00',(''),(''),'IfcOpenShell 0.0.0','Bonsai 0.8.5-alpha251212-a47cbc3','Nobody');
FILE_SCHEMA(('IFC2X3'));
ENDSEC;
DATA;
#1=IFCPERSON('HSeldon','Seldon','Hari',$,$,$,$,$);
#2=IFCORGANIZATION('APTR','Aperture Science',$,$,$);
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
#4=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$);
#5=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#4),(#6));
#6=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org');
#7=IFCAPPLICATION(#5,'0.8.5','Bonsai','Bonsai-0.8.5');
#8=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1765520605,#3,#7,1765520605);
#9=IFCPROJECT('3GSSvm5SP5U96O5iar8uYb',#8,'My Project',$,$,$,$,(#22,#34),#17);
#10=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#11=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#12=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#13=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
#14=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#15=IFCMEASUREWITHUNIT(IFCREAL(0.0174532925199433),#14);
#16=IFCCONVERSIONBASEDUNIT(#13,.PLANEANGLEUNIT.,'degree',#15);
#17=IFCUNITASSIGNMENT((#12,#10,#16,#11));
#18=IFCCARTESIANPOINT((0.,0.,0.));
#19=IFCDIRECTION((0.,0.,1.));
#20=IFCDIRECTION((1.,0.,0.));
#21=IFCAXIS2PLACEMENT3D(#18,#19,#20);
#22=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#21,$);
#23=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#22,$,.MODEL_VIEW.,$);
#24=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Model',*,*,*,*,#22,$,.GRAPH_VIEW.,$);
#25=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Box','Model',*,*,*,*,#22,$,.MODEL_VIEW.,$);
#26=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#22,$,.SECTION_VIEW.,$);
#27=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#22,$,.ELEVATION_VIEW.,$);
#28=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#22,$,.MODEL_VIEW.,$);
#29=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Model',*,*,*,*,#22,$,.PLAN_VIEW.,$);
#30=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Profile','Model',*,*,*,*,#22,$,.ELEVATION_VIEW.,$);
#31=IFCCARTESIANPOINT((0.,0.));
#32=IFCDIRECTION((1.,0.));
#33=IFCAXIS2PLACEMENT2D(#31,#32);
#34=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Plan',2,1.E-05,#33,$);
#35=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Plan',*,*,*,*,#34,$,.GRAPH_VIEW.,$);
#36=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Plan',*,*,*,*,#34,$,.PLAN_VIEW.,$);
#37=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#34,$,.PLAN_VIEW.,$);
#38=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Annotation','Plan',*,*,*,*,#34,$,.REFLECTED_PLAN_VIEW.,$);
#39=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765520605,#3,#7,1765520605);
#40=IFCSITE('2Ys4j4OOf3x9UYeALDJ_g3',#39,'My Site',$,$,#66,$,$,.ELEMENT.,$,$,$,$,$);
#46=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765520605,#3,#7,1765520605);
#47=IFCBUILDING('3e580MQFTCD9tCSaZWRPi9',#46,'My Building',$,$,#73,$,$,.ELEMENT.,$,$,$);
#53=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765520605,#3,#7,1765520605);
#54=IFCBUILDINGSTOREY('3XFkAr6KbAsP5wR3gxi3my',#53,'My Storey',$,$,#80,$,$,.ELEMENT.,$);
#60=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1765520605,#3,#7,1765520605);
#61=IFCRELAGGREGATES('2OWfFyDQn0qP0Vd6NWGX7k',#60,$,$,#9,(#40));
#62=IFCCARTESIANPOINT((0.,0.,0.));
#63=IFCDIRECTION((0.,0.,1.));
#64=IFCDIRECTION((1.,0.,0.));
#65=IFCAXIS2PLACEMENT3D(#62,#63,#64);
#66=IFCLOCALPLACEMENT($,#65);
#67=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1765520605,#3,#7,1765520605);
#68=IFCRELAGGREGATES('3_X_vfPpXDaO3NODBJgmSp',#67,$,$,#40,(#47));
#69=IFCCARTESIANPOINT((0.,0.,0.));
#70=IFCDIRECTION((0.,0.,1.));
#71=IFCDIRECTION((1.,0.,0.));
#72=IFCAXIS2PLACEMENT3D(#69,#70,#71);
#73=IFCLOCALPLACEMENT(#66,#72);
#74=IFCOWNERHISTORY(#3,#7,.READWRITE.,.ADDED.,1765520605,#3,#7,1765520605);
#75=IFCRELAGGREGATES('3ScF98CZD3pAa0SW$mhBHF',#74,$,$,#47,(#54));
#76=IFCCARTESIANPOINT((0.,0.,0.));
#77=IFCDIRECTION((0.,0.,1.));
#78=IFCDIRECTION((1.,0.,0.));
#79=IFCAXIS2PLACEMENT3D(#76,#77,#78);
#80=IFCLOCALPLACEMENT(#73,#79);
#110=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765529359,#3,#7,1765520632);
#111=IFCWALL('1lfiJzSCH1a8vjDuSLL4Ii',#110,'Cube',$,$,#203,$,$);
#112=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765520818,#3,#7,1765520632);
#113=IFCRELCONTAINEDINSPATIALSTRUCTURE('2Gqx6vP7jB4x_h65Tawn0f',#112,$,$,(#111,#182,#150),#54);
#149=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765529359,#3,#7,1765520810);
#150=IFCWINDOW('1eE0btB_54tBL59wLXKO1B',#149,'Cube',$,$,#208,$,$,$,$);
#181=IFCOWNERHISTORY(#3,#7,.READWRITE.,.MODIFIED.,1765529359,#3,#7,1765520818);
#182=IFCBUILDINGELEMENTPART('0fmaoBJgz0kPBavO$EJkPn',#181,'Cube',$,$,#198,$,$);
#194=IFCCARTESIANPOINT((-3.82147288322449,-0.987768530845642,1.78445267677307));
#195=IFCDIRECTION((0.,0.,1.));
#196=IFCDIRECTION((1.,0.,0.));
#197=IFCAXIS2PLACEMENT3D(#194,#195,#196);
#198=IFCLOCALPLACEMENT(#80,#197);
#199=IFCCARTESIANPOINT((0.,0.,0.));
#200=IFCDIRECTION((0.,0.,1.));
#201=IFCDIRECTION((1.,0.,0.));
#202=IFCAXIS2PLACEMENT3D(#199,#200,#201);
#203=IFCLOCALPLACEMENT(#80,#202);
#204=IFCCARTESIANPOINT((0.,4.25822639465332,0.));
#205=IFCDIRECTION((0.,0.,1.));
#206=IFCDIRECTION((1.,0.,0.));
#207=IFCAXIS2PLACEMENT3D(#204,#205,#206);
#208=IFCLOCALPLACEMENT(#80,#207);
ENDSEC;
END-ISO-10303-21;
+147
View File
@@ -0,0 +1,147 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates various representations from *
* IfcArbitraryOpenProfileDefs and its subclass IfcCenterLineProfileDef *
* *
********************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc4
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
typedef std::string S;
typedef ifcopenshell::global_id guid;
boost::none_t const null = boost::none;
static int i = 0;
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);
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
product->setObjectPlacement(file.addLocalPlacement(0, 120 * i++));
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
items->push(item);
if (s == "GeometricSet") {
IfcSchema::IfcGeometricSet* set = new IfcSchema::IfcGeometricSet(items->generalize());
file.add_entity(set);
items = IfcSchema::IfcRepresentationItem::list::ptr(new IfcSchema::IfcRepresentationItem::list());
items->push(set);
}
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), s, items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
file.add_entity(rep);
file.add_entity(shape);
product->setRepresentation(shape);
}
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.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.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(hierarchy_helper& file, IfcSchema::IfcProfileDef* profile) {
IfcSchema::IfcExtrudedAreaSolid* extrusion = new IfcSchema::IfcExtrudedAreaSolid(profile, file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 100.);
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.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(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.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[] = "IfcArbitraryOpenProfileDef.ifc";
hierarchy_helper file;
file.header().file_name().name(filename);
double coords1[] = {-50.0, 0.0};
double coords2[] = { 50.0, 0.0};
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list());
points->push(new IfcSchema::IfcCartesianPoint(std::vector<double>(coords1, coords1+2)));
points->push(new IfcSchema::IfcCartesianPoint(std::vector<double>(coords2, coords2+2)));
file.addEntities(points->generalize());
IfcSchema::IfcPolyline* poly = new IfcSchema::IfcPolyline(points);
file.add_entity(poly);
create_products_from_curve(file, poly);
IfcSchema::IfcEllipse* ellipse = new IfcSchema::IfcEllipse(file.addPlacement2d(), 50., 25.);
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, trim2, true, IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
file.add_entity(trim);
create_products_from_curve(file, trim);
file.getSingle<Ifc2x3::IfcProject>()->setName("IfcArbitraryOpenProfileDef");
std::ofstream f(filename);
f << file;
}
+150
View File
@@ -0,0 +1,150 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates extrusions of parameterized profiles. *
* *
********************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc2x3
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
typedef std::string S;
typedef ifcopenshell::global_id guid;
boost::none_t const null = boost::none;
#ifdef SCHEMA_IfcIShapeProfileDef_HAS_FlangeEdgeRadius
#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null
#else
#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcLShapeProfileDef_HAS_CentreOfGravityInX
#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null
#else
#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcTShapeProfileDef_HAS_CentreOfGravityInY
#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS , null
#else
#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcCShapeProfileDef_HAS_CentreOfGravityInX
#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS , null
#else
#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
int main(int argc, char** argv) {
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};
double coords3[] = {300.0, 0.0};
IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list());
IfcSchema::IfcCartesianTransformationOperator2D* transform1 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet<IfcSchema::IfcDirection>(1, 0), file.addDoublet<IfcSchema::IfcDirection>(0, -1), file.addDoublet<IfcSchema::IfcCartesianPoint>(40, 0), null);
IfcSchema::IfcCartesianTransformationOperator2D* transform2 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet<IfcSchema::IfcDirection>(0, -1), file.addDoublet<IfcSchema::IfcDirection>(1, 0), file.addDoublet<IfcSchema::IfcCartesianPoint>(40, 0), 0.3);
IfcSchema::IfcProfileDef* p1 = new IfcSchema::IfcIShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, file.addPlacement2d(), 25.0, 50.0, 5.0, 5.0, 2.0 IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS);
IfcSchema::IfcProfileDef* p2 = new IfcSchema::IfcLShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, file.addPlacement2d(), 50.0, 25.0, 5.0, 1.0, 2.0, 2.0 IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS);
IfcSchema::IfcProfileDef* p3 = new IfcSchema::IfcTShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, file.addPlacement2d(), 50.0, 40.0, 10.0, 10.0, 3.0, 2.0, 1.0, 2.0, 2.0 IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS);
IfcSchema::IfcProfileDef* p4 = new IfcSchema::IfcCShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, file.addPlacement2d(80.), 50.0, 25.0, 5.0, 10.0, 2.0 IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS);
file.add_entity(p2);
file.add_entity(p3);
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);
profiles->push(p1);
profiles->push(p5);
profiles->push(p6);
profiles->push(p4);
file.addEntities(profiles->generalize());
IfcSchema::IfcCompositeProfileDef* composite = new IfcSchema::IfcCompositeProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, S("IFC"), profiles, null);
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
guid(), 0, S("profile"), null, null, 0, 0, null, null);
file.addBuildingProduct(product);
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
product->setObjectPlacement(file.addLocalPlacement());
IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(composite,
file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);
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());
items->push(solid);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("SweptSolid"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
file.add_entity(rep);
file.add_entity(shape);
product->setRepresentation(shape);
file.getSingle<IfcSchema::IfcProject>()->setName("IfcCompositeProfileDef");
std::ofstream f(filename);
f << file;
}
+200
View File
@@ -0,0 +1,200 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates a Constructive Solid Geometry example *
* *
********************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc2x3
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
typedef std::string S;
typedef ifcopenshell::global_id guid;
boost::none_t const null = boost::none;
class Node {
private:
typedef enum {
OP_ADD, OP_SUBTRACT, OP_INTERSECT, OP_TERMINAL
} Op;
typedef enum {
PRIM_BOX, PRIM_CONE, PRIM_CYLINDER, PRIM_PYRAMID, PRIM_SPHERE
} Prim;
double x,y,z, zx,zy,zz, xx,xy,xz, a,b,c;
const Node *left, *right;
Op op;
Prim prim;
Node& operate(Op op, const Node& p) {
left = new Node(*this);
right = new Node(p);
this->op = op;
return *this;
}
Node(Prim p, double la, double lb=0., double lc=0.)
: prim(p), op(OP_TERMINAL),
x(0.), y(0.), z(0.),
zx(0.), zy(0.), zz(1.),
xx(1.), xy(0.), xz(0.),
a(la), b(lb), c(lc) {}
public:
static Node Sphere(double r) {
return Node(PRIM_SPHERE, r);
}
static Node Box(double dx, double dy, double dz) {
return Node(PRIM_BOX, dx, dy, dz);
}
static Node Pyramid(double dx, double dy, double dz) {
return Node(PRIM_PYRAMID, dx, dy, dz);
}
static Node Cylinder(double r, double h) {
return Node(PRIM_CYLINDER, r, h);
}
static Node Cone(double r, double h) {
return Node(PRIM_CONE, r, h);
}
Node& move(
double px = 0., double py = 0., double pz = 0.,
double zx = 0., double zy = 0., double zz = 1.,
double xx = 1., double xy = 0., double xz = 0.)
{
this->x = px; this->y = py; this->z = pz;
this->zx = zx; this->zy = zy; this->zz = zz;
this->xx = xx; this->xy = xy; this->xz = xz;
return *this;
}
Node& add(const Node& p) {
return operate(OP_ADD, p);
}
Node& subtract(const Node& p) {
return operate(OP_SUBTRACT, p);
}
Node& intersect(const Node& p) {
return operate(OP_INTERSECT, p);
}
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);
if (prim == PRIM_SPHERE) {
my = new IfcSchema::IfcSphere(place, a);
} else if (prim == PRIM_BOX) {
my = new IfcSchema::IfcBlock(place, a, b, c);
} else if (prim == PRIM_PYRAMID) {
my = new IfcSchema::IfcRectangularPyramid(place, a, b, c);
} else if (prim == PRIM_CYLINDER) {
my = new IfcSchema::IfcRightCircularCylinder(place, b, a);
} else if (prim == PRIM_CONE) {
my = new IfcSchema::IfcRightCircularCone(place, b, a);
}
} else {
IfcSchema::IfcBooleanOperator::IfcBooleanOperator o;
if (op == OP_ADD) {
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION;
} else if (op == OP_SUBTRACT) {
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE;
} else if (op == OP_INTERSECT) {
o = IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION;
}
my = new IfcSchema::IfcBooleanResult(o, left->serialize(file), right->serialize(file));
}
file.add_entity(my);
return my;
}
};
int main(int argc, char** argv) {
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.)
).add(
Node::Pyramid(8000.,6000.,3000.).move(0,0,3000.).add(
Node::Cylinder(1000.,4000.).move(4000.,1000.,4000., 0.,1.,0.)
).subtract(
Node::Pyramid(7600.,5600.,2800.).move(200.,200.,3000.)
).subtract(
Node::Cylinder(900.,4000.).move(4000.,1000.,4000., 0.,1.,0.).intersect(
Node::Box(2000.,4000.,1000.).move(3000.,1000.,4000.)
)
)
).serialize(file);
const double x = 1000.; const double y = -4000.;
IfcSchema::IfcRepresentationItem* csg2 = Node::Sphere(5000.).move(x,y,-4500.).intersect(
Node::Box(6000., 6000., 6000.).move(x-3000., y-3000., 0.)
).add(
Node::Cone(500., 3000.).move(x,y).add(
Node::Cone(1500., 1000.).move(x,y, 900.).add(
Node::Cone(1100., 1000.).move(x,y, 1800.).add(
Node::Cone(750., 600.).move(x,y, 2700.)
)))).serialize(file);
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
guid(), 0, S("IfcCsgPrimitive"), null, null, 0, 0, null, null);
file.addBuildingProduct(product);
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
product->setObjectPlacement(file.addLocalPlacement());
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
items->push(csg1);
items->push(csg2);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("CSG"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
file.add_entity(rep);
file.add_entity(shape);
product->setRepresentation(shape);
file.getSingle<IfcSchema::IfcProject>()->setName("IfcCompositeProfileDef");
std::ofstream f(filename);
f << file;
}
+161
View File
@@ -0,0 +1,161 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates profiles of trimmed ellipses. *
* *
********************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc2x3
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
typedef std::string S;
typedef ifcopenshell::global_id guid;
boost::none_t const null = boost::none;
#ifdef SCHEMA_HAS_IfcSegment
typedef IfcSchema::IfcSegment curve_segment_tt;
#else
typedef IfcSchema::IfcCompositeCurveSegment curve_segment_tt;
#endif
typedef struct {
double r1;
double r2;
double t1;
double t2;
} EllipsePie;
static int i = 0;
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)};
double flt3[] = {pie.r1 * cos(pie.t2*deg), pie.r2 * sin(pie.t2*deg)};
std::vector<double> coords1(flt1, flt1 + 2);
std::vector<double> coords2(flt2, flt2 + 2);
std::vector<double> coords3(flt3, flt3 + 2);
IfcSchema::IfcCartesianPoint* p1 = new IfcSchema::IfcCartesianPoint(coords1);
IfcSchema::IfcCartesianPoint* p2 = new IfcSchema::IfcCartesianPoint(coords2);
IfcSchema::IfcCartesianPoint* p3 = new IfcSchema::IfcCartesianPoint(coords3);
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list());
points->push(p3);
points->push(p1);
points->push(p2);
file.addEntities(points->generalize());
IfcSchema::IfcEllipse* ellipse = new IfcSchema::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) {
trim1->push(new IfcSchema::IfcParameterValue(pie.t1));
trim2->push(new IfcSchema::IfcParameterValue(pie.t2));
} else {
trim1->push(p2);
trim2->push(p3);
}
IfcSchema::IfcTrimmedCurve* trim = new IfcSchema::IfcTrimmedCurve(ellipse, trim1->as<IfcSchema::IfcTrimmingSelect>(), trim2->as<IfcSchema::IfcTrimmingSelect>(), true, pref);
file.addEntity(trim);
curve_segment_tt::list::ptr segments(new curve_segment_tt::list());
IfcSchema::IfcCompositeCurveSegment* s2 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, trim);
IfcSchema::IfcPolyline* poly = new IfcSchema::IfcPolyline(points);
file.addEntity(poly);
IfcSchema::IfcCompositeCurveSegment* s1 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly);
segments->push(s1);
segments->push(s2);
file.addEntities(segments->generalize());
IfcSchema::IfcCompositeCurve* ccurve = new IfcSchema::IfcCompositeCurve(segments, false);
IfcSchema::IfcArbitraryClosedProfileDef* profile = new IfcSchema::IfcArbitraryClosedProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, ccurve);
file.addEntity(ccurve);
file.addEntity(profile);
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
guid(), 0, S("profile"), null, null, 0, 0, null, null);
file.addBuildingProduct(product);
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
product->setObjectPlacement(file.addLocalPlacement(0, 200 * i++));
IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(profile,
file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);
file.add_entity(solid);
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
items->push(solid);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("SweptSolid"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
file.add_entity(rep);
file.add_entity(shape);
product->setRepresentation(shape);
}
int main(int argc, char** argv) {
const std::string filename = "ellipse_pies.ifc";
hierarchy_helper file;
{ EllipsePie pie = {80., 50., 0., 150.};
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, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
{ EllipsePie pie = {80, 50., 300., 30.};
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, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
{ EllipsePie pie = {50, 80., 30., 300.};
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, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER);
create_testcase_for(file, pie, Ifc2x3::IfcTrimmingPreference::IfcTrimmingPreference_CARTESIAN);}
std::ofstream f(filename.c_str());
f << file;
}
+214
View File
@@ -0,0 +1,214 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates various forms of IfcFace *
* *
********************************************************************************/
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc2x3
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
typedef std::string S;
typedef ifcopenshell::global_id guid;
boost::none_t const null = (static_cast<boost::none_t>(0));
static int x = 0;
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);
IfcSchema::IfcConnectedFaceSet::list::ptr shells(new IfcSchema::IfcConnectedFaceSet::list);
shells->push(shell);
IfcSchema::IfcFaceBasedSurfaceModel* model = new IfcSchema::IfcFaceBasedSurfaceModel(shells);
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
guid(), 0, name, null, null, 0, 0, null, null);
file.addBuildingProduct(product);
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
product->setObjectPlacement(file.addLocalPlacement(0, 1000 * x++, 0));
IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);
items->push(model);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
file.add_entity(shape);
product->setRepresentation(shape);
}
int main(int argc, char** argv) {
hierarchy_helper file;
{
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
bounds->push(bound);
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
create_testcase(file, face, "polyloop");
}
{
IfcSchema::IfcCartesianPoint* point1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, 0., 0.);
IfcSchema::IfcCartesianPoint* point2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, 0., 0.);
IfcSchema::IfcVertexPoint* vertex1 = new IfcSchema::IfcVertexPoint(point1);
IfcSchema::IfcVertexPoint* vertex2 = new IfcSchema::IfcVertexPoint(point2);
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(file.addPlacement2d(), 400.);
IfcSchema::IfcEdgeCurve* edge1 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, circle, true);
IfcSchema::IfcEdgeCurve* edge2 = new IfcSchema::IfcEdgeCurve(vertex2, vertex1, circle, true);
IfcSchema::IfcOrientedEdge* oriented_edge1 = new IfcSchema::IfcOrientedEdge(edge1, true);
IfcSchema::IfcOrientedEdge* oriented_edge2 = new IfcSchema::IfcOrientedEdge(edge2, true);
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
edges->push(oriented_edge1);
edges->push(oriented_edge2);
IfcSchema::IfcEdgeLoop* loop = new IfcSchema::IfcEdgeLoop(edges);
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
bounds->push(bound);
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
create_testcase(file, face, "circle");
}
{
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
IfcSchema::IfcFaceOuterBound* outer_bound = new IfcSchema::IfcFaceOuterBound(loop, true);
IfcSchema::IfcCartesianPoint::list::ptr points2 (new IfcSchema::IfcCartesianPoint::list);
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-300, -300, 0));
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, -300, 0));
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, +300, 0));
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-300, +300, 0));
IfcSchema::IfcPolyLoop* loop2 = new IfcSchema::IfcPolyLoop(points2);
IfcSchema::IfcFaceBound* inner_bound1 = new IfcSchema::IfcFaceBound(loop2, false);
IfcSchema::IfcCartesianPoint::list::ptr points3 (new IfcSchema::IfcCartesianPoint::list);
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, +300, 0));
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+300, +300, 0));
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+300, -300, 0));
points3->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, -300, 0));
IfcSchema::IfcPolyLoop* loop3 = new IfcSchema::IfcPolyLoop(points3);
IfcSchema::IfcFaceBound* inner_bound2 = new IfcSchema::IfcFaceBound(loop3, true);
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
bounds->push(inner_bound1);
bounds->push(outer_bound);
bounds->push(inner_bound2);
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
create_testcase(file, face, "polyloop with holes");
}
{
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-100, +400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
IfcSchema::IfcFaceOuterBound* bound1 = new IfcSchema::IfcFaceOuterBound(loop, true);
IfcSchema::IfcCartesianPoint::list::ptr points2 (new IfcSchema::IfcCartesianPoint::list);
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, +400, 0));
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
points2->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+100, -400, 0));
IfcSchema::IfcPolyLoop* loop2 = new IfcSchema::IfcPolyLoop(points2);
IfcSchema::IfcFaceOuterBound* bound2 = new IfcSchema::IfcFaceOuterBound(loop2, false);
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
bounds->push(bound1);
bounds->push(bound2);
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
create_testcase(file, face, "multiple outer boundaries (invalid)");
}
{
IfcSchema::IfcCartesianPoint::list::ptr points (new IfcSchema::IfcCartesianPoint::list);
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, -400, 1e-6));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, -400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, +400, 0));
points->push(file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, +400, 0));
IfcSchema::IfcPolyLoop* loop = new IfcSchema::IfcPolyLoop(points);
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
bounds->push(bound);
IfcSchema::IfcFace* face = new IfcSchema::IfcFace(bounds);
create_testcase(file, face, "imprecise polyloop");
}
{
IfcSchema::IfcCartesianPoint* point1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(+400, 0., 0.);
IfcSchema::IfcCartesianPoint* point2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(-400, 0., 0.);
IfcSchema::IfcVertexPoint* vertex1 = new IfcSchema::IfcVertexPoint(point1);
IfcSchema::IfcVertexPoint* vertex2 = new IfcSchema::IfcVertexPoint(point2);
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(file.addPlacement2d(), 400.);
IfcSchema::IfcEdgeCurve* edge1 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, circle, true);
IfcSchema::IfcEdgeCurve* edge2 = new IfcSchema::IfcEdgeCurve(vertex2, vertex1, circle, true);
IfcSchema::IfcOrientedEdge* oriented_edge1 = new IfcSchema::IfcOrientedEdge(edge1, true);
IfcSchema::IfcOrientedEdge* oriented_edge2 = new IfcSchema::IfcOrientedEdge(edge2, true);
IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
edges->push(oriented_edge1);
edges->push(oriented_edge2);
IfcSchema::IfcEdgeLoop* loop = new IfcSchema::IfcEdgeLoop(edges);
IfcSchema::IfcFaceOuterBound* bound = new IfcSchema::IfcFaceOuterBound(loop, true);
IfcSchema::IfcFaceBound::list::ptr bounds (new IfcSchema::IfcFaceBound::list);
bounds->push(bound);
IfcSchema::IfcCartesianPoint::list::ptr trim1(new IfcSchema::IfcCartesianPoint::list);
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(), 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);
IfcSchema::IfcFace* face = new IfcSchema::IfcFaceSurface(bounds, surface, true);
create_testcase(file, face, "face surface");
}
const std::string filename = "faces.ifc";
file.header().file_name().name(filename);
std::ofstream f(filename.c_str());
f << file;
}
+164
View File
@@ -0,0 +1,164 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example of curve rebar. *
* *
********************************************************************************/
#include <iostream>
#include <string>
#include <fstream>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc2x3
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
#include <boost/math/constants/constants.hpp>
const static double PI = boost::math::constants::pi<double>();
typedef std::string S;
typedef ifcopenshell::global_id guid;
boost::none_t const null = boost::none;
#ifdef SCHEMA_HAS_IfcSegment
typedef IfcSchema::IfcSegment curve_segment_t;
#else
typedef IfcSchema::IfcCompositeCurveSegment curve_segment_t;
#endif
#ifdef SCHEMA_IfcReinforcingBar_HAS_PredefinedType
#define IFC_REINFORCING_BAR_TYPE IfcSchema::IfcReinforcingBarTypeEnum::IfcReinforcingBarType_LIGATURE
#else
#define IFC_REINFORCING_BAR_TYPE IfcSchema::IfcReinforcingBarRoleEnum::IfcReinforcingBarRole_LIGATURE
#endif
void create_curve_rebar(hierarchy_helper<IfcSchema>& file)
{
int dia = 24;
int R = 3 * dia;
int length = 12 * dia;
double crossSectionarea = M_PI * (dia / 2) * 2;
IfcSchema::IfcReinforcingBar* rebar = new IfcSchema::IfcReinforcingBar(
guid(), 0, S("test"), null,
null, 0, 0,
null, S("SR24"), //SteelGrade
dia, //diameter
crossSectionarea, //crossSectionarea = math.pi*(12.0/2)**2
0,
IFC_REINFORCING_BAR_TYPE,
IfcSchema::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurfaceEnum::IfcReinforcingBarSurface_PLAIN //PLAIN or TEXTURED
);
file.addBuildingProduct(rebar);
rebar->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
curve_segment_t::list::ptr segments(new curve_segment_t::list());
IfcSchema::IfcCartesianPoint* p1 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, 0, 1000.);
IfcSchema::IfcCartesianPoint* p2 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, 0, 0);
IfcSchema::IfcCartesianPoint* p3 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R, 0);
IfcSchema::IfcCartesianPoint* p4 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R, -R);
IfcSchema::IfcCartesianPoint* p5 = file.addTriplet<IfcSchema::IfcCartesianPoint>(0, R + length, -R);
/*first segment - line */
IfcSchema::IfcCartesianPoint::list::ptr points1(new IfcSchema::IfcCartesianPoint::list());
points1->push(p1);
points1->push(p2);
file.addEntities(points1->generalize());
IfcSchema::IfcPolyline* poly1 = new IfcSchema::IfcPolyline(points1);
file.add_entity(poly1);
IfcSchema::IfcCompositeCurveSegment* segment1 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly1);
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.add_entity(axis1);
IfcSchema::IfcCircle* circle = new IfcSchema::IfcCircle(axis1, R);
file.add_entity(circle);
IfcEntityList::ptr trim1(new IfcEntityList);
IfcEntityList::ptr trim2(new IfcEntityList);
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.add_entity(trimmed_curve);
IfcSchema::IfcCompositeCurveSegment* segment2 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTSAMEGRADIENT, false, trimmed_curve);
file.add_entity(segment2);
segments->push(segment2);
/*third segment - line */
IfcSchema::IfcCartesianPoint::list::ptr points2(new IfcSchema::IfcCartesianPoint::list());
points2->push(p4);
points2->push(p5);
file.addEntities(points2->generalize());
IfcSchema::IfcPolyline* poly2 = new IfcSchema::IfcPolyline(points2);
file.add_entity(poly2);
IfcSchema::IfcCompositeCurveSegment* segment3 = new IfcSchema::IfcCompositeCurveSegment(IfcSchema::IfcTransitionCode::IfcTransitionCode_CONTINUOUS, true, poly2);
file.add_entity(segment3);
segments->push(segment3);
IfcSchema::IfcCompositeCurve* curve = new IfcSchema::IfcCompositeCurve(segments, false);
file.add_entity(curve);
IfcSchema::IfcSweptDiskSolid* solid = new IfcSchema::IfcSweptDiskSolid(curve, dia / 2, null, 0, 1);
IfcSchema::IfcRepresentation::list::ptr reps(new IfcSchema::IfcRepresentation::list());
IfcSchema::IfcRepresentationItem::list::ptr items(new IfcSchema::IfcRepresentationItem::list());
items->push(solid);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("AdvancedSweptSolid"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(null, null, reps);
file.add_entity(shape);
rebar->setRepresentation(shape);
IfcSchema::IfcObjectPlacement* storey_placement = file.getSingle<IfcSchema::IfcBuildingStorey>()->ObjectPlacement();
rebar->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 0, 0));
}
int main()
{
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;
return 0;
}
+249
View File
@@ -0,0 +1,249 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates extrusions of parameterized profiles. *
* *
********************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc2x3
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
typedef std::string S;
typedef IfcWrite::IfcGuidHelper guid;
boost::none_t const null = (static_cast<boost::none_t>(0));
#ifdef SCHEMA_IfcUShapeProfileDef_HAS_CentreOfGravityInX
#define IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS , null
#else
#define IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcTShapeProfileDef_HAS_CentreOfGravityInY
#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS , null
#else
#define IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcIShapeProfileDef_HAS_FlangeEdgeRadius
#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null
#else
#define IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcAsymmetricIShapeProfileDef_HAS_BottomFlangeSlope
#define IFC_ASYMMETRIC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null, null
#else
#define IFC_ASYMMETRIC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcLShapeProfileDef_HAS_CentreOfGravityInX
#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS , null, null
#else
#define IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
#ifdef SCHEMA_IfcCShapeProfileDef_HAS_CentreOfGravityInX
#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS , null
#else
#define IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS
#endif
void create_testcase_for(IfcSchema::IfcProfileDef::list::ptr profiles) {
IfcSchema::IfcProfileDef* profile = *profiles->begin();
const std::string profile_type = IfcSchema::Type::ToString(profile->type());
const std::string filename = profile_type + ".ifc";
hierarchy_helper file;
file.filename(filename);
int i = 0;
for (IfcSchema::IfcProfileDef::list::it it = profiles->begin(); it != profiles->end(); ++it, ++i) {
IfcSchema::IfcProfileDef* profile = *it;
IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
guid(), 0, S("profile"), null, null, 0, 0, null, null);
file.addBuildingProduct(product);
file.getSingle<IfcSchema::IfcProject>()->setName(profile_type);
product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
product->setObjectPlacement(file.addLocalPlacement(0, 100. * i));
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.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);
items->push(solid);
IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("SweptSolid"), items);
reps->push(rep);
IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
file.add_entity(rep);
file.add_entity(shape);
product->setRepresentation(shape);
}
std::ofstream f(filename.c_str());
f << file;
}
int main(int argc, char** argv) {
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
profiles->push(new Ifc2x3::IfcUShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcUShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, null IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcUShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, 4.0 IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcUShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, 1.0, 3.0, 6.0 IFC_U_SHAPE_PROFILE_DEF_EXTRA_ARGS));
create_testcase_for(profiles); }
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
profiles->push(new Ifc2x3::IfcTShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, null, null IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcTShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, 2.0, 2.0, 2.0, null, null IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcTShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, null, null, null, 2.0, 2.0 IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::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 IFC_T_SHAPE_PROFILE_DEF_EXTRA_ARGS));
create_testcase_for(profiles); }
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
profiles->push(new Ifc2x3::IfcZShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 5.0, null, null));
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 Ifc2x3::IfcEllipseProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 25.0, 15.0));
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(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 25.0, 50.0, 5.0, 5.0, null IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcIShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 25.0, 50.0, 5.0, 5.0, 2.0 IFC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcAsymmetricIShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 25.0, 50.0, 5.0, 5.0, 2.0, 20.0, 10.0, 5.0, null IFC_ASYMMETRIC_I_SHAPE_PROFILE_DEF_EXTRA_ARGS));
create_testcase_for(profiles); }
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
profiles->push(new Ifc2x3::IfcLShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, null, null, null IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcLShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 2.0, 2.0, null IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcLShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, null, null, 2.0 IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcLShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 1.0, 2.0, 2.0 IFC_L_SHAPE_PROFILE_DEF_EXTRA_ARGS));
create_testcase_for(profiles); }
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
profiles->push(new Ifc2x3::IfcCShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 10.0, null IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS));
profiles->push(new IfcSchema::IfcCShapeProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, 10.0, 2.0 IFC_C_SHAPE_PROFILE_DEF_EXTRA_ARGS));
create_testcase_for(profiles); }
{ IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list);
profiles->push(new Ifc2x3::IfcCircleProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 25.0));
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 Ifc2x3::IfcRectangleProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0));
profiles->push(new Ifc2x3::IfcRoundedRectangleProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0));
profiles->push(new Ifc2x3::IfcRectangleHollowProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 25.0, 5.0, null, null));
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 Ifc2x3::IfcTrapeziumProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 30.0, 25.0, 0.0));
profiles->push(new Ifc2x3::IfcTrapeziumProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 60.0, 25.0, -20.0));
profiles->push(new Ifc2x3::IfcTrapeziumProfileDef(
IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
null, 0, 50.0, 10.0, 25.0, 30.0));
create_testcase_for(profiles); }
}
File diff suppressed because it is too large Load Diff
+96
View File
@@ -0,0 +1,96 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Example that generates an IfcTriangulatedFaceSet *
* *
********************************************************************************/
#include <iostream>
#include <fstream>
#include <optional>
#include "ifcparse/macros.h"
#ifndef IfcSchema
#define IfcSchema Ifc4
#endif
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
#include "ifcparse/hierarchy_helper.h"
#include "suzanne_geometry.h"
#include <fstream>
typedef std::string S;
typedef ifcopenshell::global_id guid;
template <typename T>
std::vector< std::vector<T> > create_vector_from_array(const T* arr, unsigned size) {
std::vector< std::vector<T> > result;
result.reserve(size);
for (unsigned i = 0; i < size; ) {
std::vector<T> ts; ts.reserve(3);
for (unsigned j = 0; j < 3; ++i, ++j) {
ts.push_back(arr[i]);
}
result.push_back(ts);
}
return result;
}
int main(int argc, char** argv) {
hierarchy_helper<Ifc4> file;
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<Ifc4::IfcOwnerHistory>());
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
#ifdef SCHEMA_IfcCartesianPointList3D_HAS_TagList
, boost::none
#endif
);
IfcSchema::IfcTriangulatedFaceSet* faceset = new IfcSchema::IfcTriangulatedFaceSet(coordinates, null, null, indices_vector, null);
Ifc4::IfcShapeRepresentation rep = file.create<Ifc4::IfcShapeRepresentation>().initialize(
file.getRepresentationContext("Model"), "Body", "SurfaceModel", {faceset});
Ifc4::IfcProductDefinitionShape shape = file.create<Ifc4::IfcProductDefinitionShape>().initialize(std::nullopt, std::nullopt, {rep});
file.add_entity(shape);
product.setRepresentation(shape);
const std::string filename = "tesselated_faceset.ifc";
file.header().file_name().setname(filename);
std::ofstream f(filename.c_str());
f << file;
}
+2 -2
View File
@@ -206,7 +206,7 @@ class context:
Args:
file (ifcopenshell.file): file containing elem
elem (triangulation_element): triangulated geometry
elem (TriangulationElement): triangulated geometry
min_thickness (float, optional): minimal thickness of the oriented bounding box to create around elem
Returns:
@@ -889,7 +889,7 @@ class context:
def write_obj(ofn, *, elem=None, item=None):
s = ifcopenshell.geom.settings(USE_WORLD_COORDS=True, WELD_VERTICES=False)
if item:
geom = item.triangulate(s)
geom = item.Triangulate(s)
else:
geom = elem.geometry
-27
View File
@@ -36,34 +36,8 @@ message("Running CMakeLists.txt in /src/helpers")
find_package(Eigen3 REQUIRED)
# helpers_math: the schema-free subset unit conversion tables and Helmert
# matrix math over plain numbers. No IfcParse, no Qt, no schema dispatch, so it
# builds under Emscripten where the rest of this directory cannot.
#
# It exists as its own target rather than being compiled into both `helpers` and
# IfcViewerCore so the objects are built exactly once and both libraries agree
# on them. IfcViewerCore links it directly (see src/ifcviewer/CMakeLists.txt);
# `helpers` re-exports it PUBLIC below so existing consumers keep working
# through the unchanged unit.h / geolocation.h includes.
set(HELPER_MATH_CPP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/unit_convert.cpp
${CMAKE_CURRENT_SOURCE_DIR}/geolocation_transform.cpp
)
add_library(helpers_math STATIC ${HELPER_MATH_CPP_FILES})
target_include_directories(helpers_math PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(helpers_math PUBLIC Eigen3::Eigen)
set_target_properties(helpers_math PROPERTIES POSITION_INDEPENDENT_CODE ON)
install(TARGETS helpers_math EXPORT ${IFCOPENSHELL_EXPORT_TARGETS})
# The rest of this directory needs IfcParse and the generated schema bindings,
# so it is desktop-only.
if(EMSCRIPTEN)
return()
endif()
file(GLOB HELPER_CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB HELPER_H_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
list(REMOVE_ITEM HELPER_CPP_FILES ${HELPER_MATH_CPP_FILES})
add_library(helpers STATIC ${HELPER_CPP_FILES} ${HELPER_H_FILES})
@@ -90,7 +64,6 @@ target_include_directories(helpers PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(helpers PUBLIC
IfcParse
Eigen3::Eigen
helpers_math
PRIVATE
${HELPER_SCHEMA_LIBRARIES}
)
+19 -19
View File
@@ -41,7 +41,7 @@ struct is_ifc4_or_higher : std::false_type {};
template <typename Schema>
struct is_ifc4_or_higher<Schema, std::void_t<typename Schema::IfcMaterialDefinition>> : std::true_type {};
std::string schema_name(const express::base& instance) {
std::string schema_name(const express::Base& instance) {
return instance.declaration().schema()->name();
}
@@ -52,7 +52,7 @@ std::string schema_name(const express::base& instance) {
// A primitive scalar attribute value formatted for display, or std::nullopt for
// IFC null and for non-primitive values (entity references, aggregates/lists,
// binary) — which the properties UI omits.
std::optional<std::string> format_scalar(const ifcopenshell::attribute_value& value) {
std::optional<std::string> format_scalar(const attribute_value& value) {
if (value.isNull()) {
return std::nullopt;
}
@@ -60,7 +60,7 @@ std::optional<std::string> format_scalar(const ifcopenshell::attribute_value& va
case ifcopenshell::Argument_STRING:
return static_cast<std::string>(value);
case ifcopenshell::Argument_ENUMERATION: {
const ifcopenshell::enumeration_reference enumeration = value;
const enumeration_reference enumeration = value;
return enumeration.value() ? std::string(enumeration.value()) : std::string();
}
case ifcopenshell::Argument_INT:
@@ -87,7 +87,7 @@ std::optional<std::string> format_scalar(const ifcopenshell::attribute_value& va
// ifcopenshell.util.element.get_type: the construction type of an occurrence
// (get_type(type_element) == type_element).
template <typename Schema>
express::base get_type_s(const express::base& element) {
express::Base get_type_s(const express::Base& element) {
if (element.template as<typename Schema::IfcTypeObject>()) {
return element;
}
@@ -111,9 +111,9 @@ express::base get_type_s(const express::base& element) {
}
template <typename Schema>
std::optional<std::string> get_predefined_type_s(const express::base& element) {
std::optional<std::string> get_predefined_type_s(const express::Base& element) {
// Prefer the associated type element's predefined type.
if (const express::base type = get_type_s<Schema>(element)) {
if (const express::Base type = get_type_s<Schema>(element)) {
std::optional<std::string> predefined_type = get_string_attribute(type, "PredefinedType");
if (!predefined_type || *predefined_type == "USERDEFINED") {
// ElementType (IfcElementType) or ProcessType (IfcTypeProcess) — the
@@ -140,7 +140,7 @@ std::optional<std::string> get_predefined_type_s(const express::base& element) {
// ifcopenshell.util.element.get_aggregate: the aggregate parent, via the
// Decomposes inverse (IfcRelAggregates.RelatingObject).
template <typename Schema>
express::base get_aggregate_s(const express::base& element) {
express::Base get_aggregate_s(const express::Base& element) {
const auto object = element.template as<typename Schema::IfcObjectDefinition>();
if (!object) {
return {};
@@ -162,8 +162,8 @@ express::base get_aggregate_s(const express::base& element) {
// The spatial-structure children aggregated under this element (IsDecomposedBy →
// RelatedObjects, filtered to spatial elements).
template <typename Schema>
std::vector<express::base> get_spatial_children_s(const express::base& element) {
std::vector<express::base> children;
std::vector<express::Base> get_spatial_children_s(const express::Base& element) {
std::vector<express::Base> children;
const auto object = element.template as<typename Schema::IfcObjectDefinition>();
if (!object) {
return children;
@@ -182,14 +182,14 @@ std::vector<express::base> get_spatial_children_s(const express::base& element)
// ifc_class): the directly containing spatial element, or the container of the
// aggregate parent for an aggregated part.
template <typename Schema>
express::base get_container_s(const express::base& element) {
express::Base get_container_s(const express::Base& element) {
if (const auto product = element.template as<typename Schema::IfcElement>()) {
const auto relationships = product.ContainedInStructure();
if (!relationships.empty()) {
return relationships.front().RelatingStructure();
}
}
if (const express::base aggregate = get_aggregate_s<Schema>(element)) {
if (const express::Base aggregate = get_aggregate_s<Schema>(element)) {
return get_container_s<Schema>(aggregate);
}
return {};
@@ -197,7 +197,7 @@ express::base get_container_s(const express::base& element) {
} // namespace
std::optional<std::string> get_predefined_type(const express::base& element) {
std::optional<std::string> get_predefined_type(const express::Base& element) {
if (!element) {
return std::nullopt;
}
@@ -211,7 +211,7 @@ std::optional<std::string> get_predefined_type(const express::base& element) {
unsupported_schema(name);
}
std::vector<std::pair<std::string, std::string>> get_scalar_attributes(const express::base& element) {
std::vector<std::pair<std::string, std::string>> get_scalar_attributes(const express::Base& element) {
std::vector<std::pair<std::string, std::string>> result;
if (!element) {
return result;
@@ -230,7 +230,7 @@ std::vector<std::pair<std::string, std::string>> get_scalar_attributes(const exp
return result;
}
express::base get_type(const express::base& element) {
express::Base get_type(const express::Base& element) {
if (!element) {
return {};
}
@@ -244,7 +244,7 @@ express::base get_type(const express::base& element) {
unsupported_schema(name);
}
express::base get_container(const express::base& element) {
express::Base get_container(const express::Base& element) {
if (!element) {
return {};
}
@@ -258,7 +258,7 @@ express::base get_container(const express::base& element) {
unsupported_schema(name);
}
std::vector<express::base> get_spatial_children(const express::base& element) {
std::vector<express::Base> get_spatial_children(const express::Base& element) {
if (!element) {
return {};
}
@@ -275,7 +275,7 @@ std::vector<express::base> get_spatial_children(const express::base& element) {
// getattr(element, name) for a string- or enum-valued attribute. Reads by name,
// so it works uniformly across the various subtypes that carry a given
// attribute (PredefinedType, Name, ...).
std::optional<std::string> get_string_attribute(const express::base& element,
std::optional<std::string> get_string_attribute(const express::Base& element,
const std::string& name) {
if (!element) {
return std::nullopt;
@@ -288,13 +288,13 @@ std::optional<std::string> get_string_attribute(const express::base& element,
if (index < 0) {
return std::nullopt;
}
const ifcopenshell::attribute_value value = element.get_attribute_value(static_cast<std::size_t>(index));
const attribute_value value = element.get_attribute_value(static_cast<std::size_t>(index));
if (value.isNull()) {
return std::nullopt;
}
switch (value.type()) {
case ifcopenshell::Argument_ENUMERATION: {
const ifcopenshell::enumeration_reference enumeration = value;
const enumeration_reference enumeration = value;
return enumeration.value() ? std::string(enumeration.value()) : std::string();
}
case ifcopenshell::Argument_STRING:

Some files were not shown because too many files have changed in this diff Show More