Merge branch 'v0.8.0' into ifcmax/initial-refresh

This commit is contained in:
Josef Wienerroither
2026-03-14 07:15:11 +01:00
88 changed files with 28670 additions and 370 deletions
+27 -9
View File
@@ -5,11 +5,26 @@ on:
jobs:
build_ifcopenshell:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
arch: ['x64']
include:
- arch: x64
runs_on: windows-2022
deps_dir: _deps-vs2022-x64-installed
vcvars: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"'
build_branch: windows-x64
zip_suffix: win64
- arch: ARM64
runs_on: windows-11-arm
deps_dir: _deps-vs2022-ARM64-installed
vcvars: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsarm64.bat"'
build_branch: windows-arm64
zip_suffix: win-arm64
runs-on: ${{ matrix.runs_on }}
steps:
- name: Checkout Repository
uses: actions/checkout@v6
@@ -20,7 +35,7 @@ jobs:
uses: actions/checkout@v6
with:
repository: IfcOpenShell/build-outputs
path: _deps-vs2022-x64-installed
path: ${{ matrix.deps_dir }}
ref: windows-${{ matrix.arch }}
lfs: true
token: ${{ secrets.BUILD_REPO_TOKEN }}
@@ -31,7 +46,7 @@ jobs:
- name: Unpack Dependencies
run: |
cd _deps-vs2022-x64-installed
cd ${{ matrix.deps_dir }}
Get-ChildItem -Path . -Filter 'cache-*.zip' | ForEach-Object {
7z x $_.FullName
}
@@ -46,14 +61,16 @@ jobs:
- name: Run Build Script And Pack .zip Archives
shell: cmd
env:
TARGET_ARCH: ${{ matrix.arch }} # lets the Python script know which arch to target (optional override)
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
call ${{ matrix.vcvars }}
cd win
python build-all-win.py
- name: Pack Dependencies
run: |
cd _deps-vs2022-x64-installed
cd ${{ matrix.deps_dir }}
Get-ChildItem -Path . -Directory | ForEach-Object {
$cacheFile = "cache-$($_.Name).zip"
echo $cacheFile
@@ -64,12 +81,13 @@ jobs:
- name: Commit and Push Changes to Build Repository
run: |
cd _deps-vs2022-x64-installed
cd ${{ matrix.deps_dir }}
git config user.name "IfcOpenBot"
git config user.email "ifcopenbot@ifcopenshell.org"
git checkout -B ${{ matrix.build_branch }}
git add *.zip
git commit -m "Update build artifacts [skip ci]" || echo "No changes to commit"
git push || echo "Push failed"
git push --set-upstream origin ${{ matrix.build_branch }} || echo "Push failed"
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v6
@@ -88,4 +106,4 @@ jobs:
foreach ($zip in Get-ChildItem -Path "$env:USERPROFILE\output" -Filter *.zip) {
aws s3 cp "$($zip.FullName)" s3://ifcopenshell-builds/ --debug
Start-Sleep -Seconds 5
}
}
@@ -33,6 +33,8 @@ jobs:
id: syntax-errors
run: |
ERROR=0
# Using 2 Python versions - one minimum required for IfcOpenShell
# and other that's used by Blender currently.
python3.10 -W error -m compileall -q src/ifcopenshell-python || ERROR=1
python3.11 -W error -m compileall -q src/bonsai || ERROR=1
exit $ERROR
+1
View File
@@ -181,6 +181,7 @@ jobs:
"-DSCHEMA_VERSIONS=2x3;4;4x3_add2" \
-DGLTF_SUPPORT=On \
-DWITH_ROCKSDB=On \
-DBUILD_EXAMPLES=ON \
../cmake
sudo make -j $(nproc)
sudo make install
+7 -1
View File
@@ -2,7 +2,7 @@
name = "IfcOpenShell"
version = "0.0.0"
dependencies = [
"black==26.3.0",
"black==26.3.1",
"ruff==0.15.5",
"poethepoet",
"gersemi==0.26.0",
@@ -28,6 +28,12 @@ extend-exclude = '''
reportInvalidTypeForm = false
disableBytesTypePromotions = true
reportUnnecessaryTypeIgnoreComment = true
# Pylance doesn't respect gitignore, so we have to exclude files manually here
# to avoid VS Code slowing down.
# https://github.com/microsoft/pylance-release/issues/5169
exclude = [
"_deps",
]
# Define here general ruff settings,
# then they will be inherited by projects' .toml files.
+4 -23
View File
@@ -223,19 +223,8 @@ endif
cd build/bonsai/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
# Provides IFCJSON functionality
cd build && wget -O ifc2json.zip https://github.com/IFCJSON-Team/IFC2JSON_python/archive/refs/heads/master.zip
cd build && unzip ifc2json.zip && rm ifc2json.zip
# IFCJSON doesn't have pyproject.toml, so we use python command.
cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-*/file_converters && \
$(PYTHON) -c "from setuptools import setup; \
setup( \
name='ifcjson', \
version='0.0.1', \
author='Jan Brouwer', \
author_email='jan@brewsky.nl', \
packages=['ifcjson'], \
)" bdist_wheel
cp -r build/IFC2JSON_python-*/file_converters/dist/*.whl build/wheels/
# TODO: Use official repo, once https://github.com/IFCJSON-Team/IFC2JSON_python/pull/8 is merged.
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m pip wheel "git+https://github.com/Andrej730/IFC2JSON_python.git@pyproject_toml" --no-deps -w wheels/
# Brickschema requires pkg_resources which is provided by Blender.
# Provides Brickschema functionality
@@ -243,16 +232,8 @@ endif
cd build/bonsai/bim/data/brick/ && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
# Required for hipped roof generation
cd build && wget https://github.com/prochitecture/bpypolyskel/archive/refs/heads/master.zip
cd build && unzip master.zip && rm master.zip
cd build && . env/$(VENV_ACTIVATE) && cd bpypolyskel-master && \
$(PYTHON) -c "from setuptools import setup; \
setup( \
name='bpypolyskel', \
version='0.0.0', \
packages=['bpypolyskel'], \
)" bdist_wheel
cp -r build/bpypolyskel-master/dist/*.whl build/wheels/
# TODO: Use official repo once https://github.com/prochitecture/bpypolyskel/pull/22 is merged.
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m pip wheel "git+https://github.com/Andrej730/bpypolyskel.git@pyproject_toml" --no-deps -w wheels/
# folder for executable files
mkdir -p build/bonsai/libs/bin
+38 -36
View File
@@ -35,12 +35,11 @@ IN_PACKAGE = __package__ == "bonsai"
import platform
import re
import traceback
import uuid
import webbrowser
from collections import deque
from collections.abc import Generator
from pathlib import Path
from typing import Any, Union
from typing import TYPE_CHECKING, Any, Union
last_commit_hash = "8888888"
last_commit_date = "9999999"
@@ -72,6 +71,21 @@ REINSTALLED_BBIM_VERSION: Union[str, None] = None
REGISTERED_BBIM_PACKAGE: str
def is_registering() -> bool:
"""
During addon registration ``bpy.context`` and ``bpy.data`` are restricted
and you can't access their properties.
"""
import bpy
if TYPE_CHECKING or bpy.app.version >= (5, 0, 0):
import _bpy_restrict_state as bpy_restrict_state
else:
import bpy_restrict_state
return isinstance(bpy.context, bpy_restrict_state._RestrictContext)
def initialize_bbim_semver():
"""Initialize `bbim_semver` dictionary.
@@ -93,9 +107,13 @@ def initialize_bbim_semver():
bbim_semver["version"] = version_str
def get_debug_info():
def get_debug_info(*, bonsai_failed_to_load: bool = False) -> dict[str, Any]:
import bpy
bbim_version = bbim_semver["version"]
# All data here should be gettable even in case of `bpy.context` and `bpy.data` being inaccessible
# and Bonsai completely failed to load.
debug_info = {
"os": platform.system(),
"os_version": platform.version(),
@@ -111,6 +129,14 @@ def get_debug_info():
"last_error": last_error,
}
# Can't access blend data or context during registration.
# If Bonsai failed to load we cannot safely access any of its properties or its tools
# as they may not be registered yet and acessing them will break Bonsai Fatal Error UI.
if is_registering() or bonsai_failed_to_load:
return debug_info
import bonsai.tool as tool
# Add .blend file save information
if bpy.data.is_saved:
debug_info["blend_file_path"] = bpy.data.filepath
@@ -131,7 +157,7 @@ def get_debug_info():
return debug_info
def format_debug_info(info: dict):
def format_debug_info(info: dict[str, Any]) -> str:
last_actions = ""
for action in info["last_actions"]:
last_actions += f"\n# {action['type']}: {action['name']}"
@@ -149,33 +175,10 @@ def get_binaries(path: Path) -> Generator[Path, None, None]:
yield from path.glob("**/*.so")
def safe_link_dlls() -> None:
# Blender 4.2+ has a problem on Windows for disabling/enabling/reinstalling extensions
# with loaded binary dependencies (on Windows you can't remove a binary if it's loaded by some program).
# To avoid this issue we temporary hard link dlls to our temp directory on unregister()
# (unregister is executed before Blender will try to uninstall dependencies and the issue will arise).
# Then, Blender won't have a problem unlinking unloaded dlls as they are still linked somewhere.
# On register() we clean up our temp directory with binaries.
#
# TODO: If user uninstalls Bonsai to never use it again, temporary directory won't be cleared.
#
# See: https://projects.blender.org/blender/blender/issues/125049
import bpy
ext_path = Path(bpy.utils.user_resource("EXTENSIONS"))
local_path = ext_path / ".local"
# We use random hash subfolder as user may try to enable/disable addon multiple times.
random_hash = uuid.uuid4().hex[:8]
temp_local = ext_path / ".local_temp" / random_hash
temp_local.mkdir(parents=True)
for filepath in get_binaries(local_path):
dest_path = temp_local / filepath.relative_to(local_path)
dest_path.parent.mkdir(exist_ok=True, parents=True)
os.link(filepath, dest_path)
# TODO: remove before 0.8.6 release.
# On Windows issues with removing extensions were resolved in Blender 4.3,
# but we removed our workaround that was producing some junk only in 0.8.5 release.
# So we're temporarily keeping the part that's cleaning up outputs from previous releases.
def clean_up_dlls_safe_links() -> None:
import bpy
@@ -205,6 +208,8 @@ def clean_up_dlls_safe_links() -> None:
if IN_BLENDER:
import bpy
initialize_bbim_semver()
def get_binary_info() -> dict[str, Any]:
@@ -296,9 +301,6 @@ if IN_BLENDER:
purge_cache()
def unregister():
if platform.system() == "Windows":
safe_link_dlls()
import bonsai.bim
bonsai.bim.unregister()
@@ -333,7 +335,7 @@ if IN_BLENDER:
bl_context = "scene"
def draw(self, context):
info = get_debug_info()
info = get_debug_info(bonsai_failed_to_load=True)
layout = self.layout
layout.alert = True
@@ -409,7 +411,7 @@ if IN_BLENDER:
bl_description = "Copies debugging information to your clipboard for use in bugreports"
def execute(self, context):
info = get_debug_info()
info = get_debug_info(bonsai_failed_to_load=True)
info.update(get_binary_info())
info = format_debug_info(info)
context.window_manager.clipboard = info
+3 -1
View File
@@ -747,6 +747,7 @@ class IfcImporter:
self.update_progress((percent_average / 100 * progress_range) + start_progress)
shape = iterator.get()
if shape:
assert isinstance(shape, W.TriangulationElement)
product = self.file.by_id(shape.id)
self.create_product(product, shape)
results.add(product)
@@ -1020,7 +1021,8 @@ class IfcImporter:
obj.hide_select = True
obj.hide_viewport = True
self.project["blender"].objects.link(obj)
self.project["blender"].BIMCollectionProperties.obj = obj
collection_props = tool.Blender.get_collection_props(self.project["blender"])
collection_props.obj = obj
props = tool.Blender.get_object_bim_props(obj)
props.collection = self.collections[project.GlobalId] = self.project["blender"]
+11 -2
View File
@@ -16,8 +16,9 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy.props
import bpy.types
from typing import TYPE_CHECKING
import bpy
class AuginProperties(bpy.types.PropertyGroup):
@@ -27,3 +28,11 @@ class AuginProperties(bpy.types.PropertyGroup):
project_name: bpy.props.StringProperty(name="Project Name")
project_filename: bpy.props.StringProperty(name="IFC Filename")
is_success: bpy.props.BoolProperty(name="Is Successful Upload", default=False)
if TYPE_CHECKING:
username: str
password: str
token: str
project_name: str
project_filename: str
is_success: bool
+2 -2
View File
@@ -1253,8 +1253,8 @@ class ActivateBcfViewpoint(bpy.types.Operator):
else:
obj.data.show_background_images = False
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
assert (space := tool.Blender.get_view3d_space())
space.region_3d.view_perspective = "CAMERA"
if self.file:
self.set_viewpoint_components(viewpoint, context)
@@ -27,6 +27,7 @@ import ifcopenshell.api
import ifcopenshell.api.boundary
import ifcopenshell.api.root
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.element
import ifcopenshell.util.placement
import ifcopenshell.util.shape
@@ -701,6 +702,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
while True:
tree.add_element(iterator.get_native())
shape = iterator.get()
assert isinstance(shape, W.TriangulationElement)
shapes[shape.id] = {
"verts": ifcopenshell.util.shape.get_vertices(shape.geometry),
"faces": ifcopenshell.util.shape.get_faces(shape.geometry),
+12 -1
View File
@@ -16,9 +16,18 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import TYPE_CHECKING
import bpy
from bpy.types import Panel, UIList
import bonsai.tool as tool
if TYPE_CHECKING:
from bonsai.bim.module.brick.prop import Brick
from bonsai.bim.helper import prop_with_search
from bonsai.bim.module.brick.data import BrickschemaData, BrickschemaReferencesData
from bonsai.tool.brick import BrickStore
@@ -274,7 +283,9 @@ class BIM_PT_brickschema_viewport(Panel):
class BIM_UL_bricks(UIList):
split_screen = False
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: Brick, icon, active_data, active_propname
) -> None:
if item:
split = layout.split(factor=0.85, align=True)
row = split.row()
@@ -457,9 +457,7 @@ class HideClash(bpy.types.Operator):
def execute(self, context):
ClashDecorator.uninstall()
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
tool.Blender.update_all_viewports(context)
return {"FINISHED"}
+44 -15
View File
@@ -26,10 +26,11 @@ from bpy.types import Panel, UIList
import bonsai.bim.helper
import bonsai.bim.module.cost.prop as CostProp
import bonsai.tool as tool
from bonsai.bim.module.cost.data import CostSchedulesData
from bonsai.bim.module.cost.data import CostItem, CostSchedulesData
if TYPE_CHECKING:
from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
from bonsai.bim.prop import StrProperty
class BIM_PT_cost_schedules(Panel):
@@ -398,8 +399,8 @@ class BIM_PT_cost_item_types(Panel):
op = row2.operator("bim.calculate_cost_item_resource_value", text="", icon="DISC")
op.cost_item = cost_item.ifc_definition_id
rtprops = context.scene.BIMResourceTreeProperties
rprops = tool.Resource.get_resource_props()
rtprops = rprops.tree
if rtprops.resources and rprops.active_resource_index < len(rtprops.resources):
if has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
@@ -661,9 +662,18 @@ class BIM_UL_cost_items_trait:
split2.alignment = "LEFT"
split2.label(text="Rate")
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMCostProperties,
item: CostProp.CostItem,
icon,
active_data,
active_propname,
) -> None:
if item:
self.props = tool.Cost.get_cost_props()
self.props = data
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
row = layout.row(align=True)
@@ -694,7 +704,7 @@ class BIM_UL_cost_items_trait:
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
def draw_parent_operator(self, row, cost_item_id):
def draw_parent_operator(self, row: bpy.types.UILayout, cost_item_id: int) -> None:
if self.props.active_cost_item_id:
if self.props.active_cost_item_id != cost_item_id:
op = row.operator("bim.change_parent_cost_item", text="", icon="LINKED", emboss=False).new_parent = (
@@ -703,7 +713,7 @@ class BIM_UL_cost_items_trait:
else:
row.label(text="", icon="BLANK1")
def draw_hierarchy(self, row, item):
def draw_hierarchy(self, row: bpy.types.UILayout, item: CostProp.CostItem) -> None:
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
@@ -749,7 +759,7 @@ class BIM_UL_cost_items_trait:
else:
row.label(text="-")
def draw_value_column(self, layout, cost_item):
def draw_value_column(self, layout: bpy.types.UILayout, cost_item: CostItem) -> None:
if cost_item["TotalAppliedValue"]:
text = "{0:,.2f}".format(cost_item["TotalAppliedValue"]).replace(",", " ")
if cost_item["UnitBasisValueComponent"] not in [None, 1]:
@@ -760,13 +770,13 @@ class BIM_UL_cost_items_trait:
else:
layout.label(text="-")
def draw_total_cost_column(self, layout, cost_item):
def draw_total_cost_column(self, layout: bpy.types.UILayout, cost_item: CostItem) -> None:
format_numbers = "{0:,.2f}".format(cost_item["TotalCost"]).replace(",", " ")
currency = CostSchedulesData.data["currency"]
text = "{} {}".format(format_numbers, currency["name"]) if currency else format_numbers
layout.label(text=text)
def draw_order_operator(self, row, ifc_definition_id, cost_item):
def draw_order_operator(self, row: bpy.types.UILayout, ifc_definition_id: int, cost_item: CostItem) -> None:
if cost_item["NestingIndex"] is not None:
if cost_item["NestingIndex"] == 0:
op = row.operator("bim.reorder_cost_item_nesting", icon="TRIA_DOWN", text="")
@@ -793,12 +803,14 @@ class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
def draw_quantity_column(self, layout, cost_item):
self.draw_uom_column(layout, cost_item)
def draw_total_cost_column(self, layout, cost_item):
def draw_total_cost_column(self, layout: bpy.types.UILayout, cost_item: CostItem) -> None:
pass # No such thing as a total cost in a schedule of rates
class BIM_UL_cost_columns(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: StrProperty, icon, active_data, active_propname
) -> None:
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
@@ -806,9 +818,17 @@ class BIM_UL_cost_columns(UIList):
class BIM_UL_cost_item_types(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = tool.Cost.get_cost_props()
cost_item = props.cost_items[props.active_cost_item_index]
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMCostProperties,
item: CostProp.CostItemType,
icon,
active_data,
active_propname,
) -> None:
cost_item = data.cost_items[data.active_cost_item_index]
if item:
row = layout.row(align=True)
@@ -844,7 +864,16 @@ class BIM_UL_cost_item_quantities(UIList):
class BIM_UL_product_cost_items(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data,
item: CostItemQuantity,
icon,
active_data,
active_propname,
) -> None:
if item:
row = layout.row(align=True)
op = row.operator("bim.highlight_product_cost_item", text="", icon="STYLUS_PRESSURE")
@@ -98,8 +98,8 @@ class VisualiseDiff(bpy.types.Operator):
obj.color = (0.0, 1.0, 0.0, 1.0)
elif global_id in diff["changed"]:
obj.color = (0.0, 0.0, 1.0, 1.0)
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
assert (space := tool.Blender.get_view3d_space())
space.shading.color_type = "OBJECT"
return {"FINISHED"}
@@ -19,6 +19,7 @@
import json
import bpy
import ifcopenshell.util.element
import bonsai.core.document as core
import bonsai.tool as tool
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Union
from typing import TYPE_CHECKING, Literal, Union
import bpy
from bpy.props import (
@@ -66,7 +66,7 @@ class Document(PropertyGroup):
tree_depth: int
has_children: bool
is_expanded: bool
document_type: str
document_type: Literal["PROJECT", "INFORMATION", "REFERENCE"]
class DocumentObject(PropertyGroup):
+34 -6
View File
@@ -16,9 +16,22 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import TYPE_CHECKING
import bpy
from bpy.types import Panel, UIList
import bonsai.tool as tool
if TYPE_CHECKING:
from bonsai.bim.module.document.prop import (
BIMDocumentProperties,
Document,
DocumentObject,
)
from bonsai.bim.helper import draw_attributes
from bonsai.bim.module.document.data import DocumentData, ObjectDocumentData
@@ -207,7 +220,16 @@ class BIM_PT_object_documents(Panel):
class BIM_UL_documents(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMDocumentProperties,
item: Document,
icon,
active_data,
active_propname,
) -> None:
if item:
row = layout.row(align=True)
indent_depth = 0
@@ -252,16 +274,22 @@ class BIM_UL_documents(UIList):
class BIM_UL_document_objects(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMDocumentProperties,
item: DocumentObject,
icon,
active_data,
active_propname,
) -> None:
if item:
row = layout.row(align=True)
row.prop(item, "name", text="", emboss=False, icon="OBJECT_DATA")
row.operator("bim.select_object", text="", icon="RESTRICT_SELECT_OFF").obj_name = item.name
props = tool.Document.get_document_props()
if props.active_document:
document = props.active_document
if document := data.active_document:
op = row.operator("bim.unassign_document", text="", icon="X")
op.document = document.ifc_definition_id
op.obj = item.name
@@ -22,7 +22,9 @@ from pathlib import Path
from typing import Any, Union
import bpy
import ifcopenshell.util.classification
import ifcopenshell.util.element
import ifcopenshell.util.placement
import ifcopenshell.util.unit
from natsort import natsorted
@@ -70,9 +70,9 @@ class profile_consequential:
cls.start_time = None
lines = "\n".join(cls.lines)
print(lines)
import pyperclip
pyperclip.copy(lines)
assert (wm := bpy.context.window_manager)
wm.clipboard = lines
cls.lines = []
@@ -151,7 +151,7 @@ from bpy_extras.view3d_utils import (
)
from gpu_extras.batch import batch_for_shader
from ifcopenshell.util.unit import si_conversions
from mathutils import Matrix, Vector
from mathutils import Matrix, Vector, geometry
from mathutils.geometry import intersect_line_line
from mathutils.kdtree import KDTree
@@ -1243,9 +1243,7 @@ class SnapManager:
@staticmethod
def _redraw_viewport() -> None:
"""Force 3D viewport redraw."""
for area in bpy.context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
tool.Blender.update_all_viewports()
def build_snap_cache(
self, context: bpy.types.Context, active_obj: bpy.types.Object, include_active: bool = False
@@ -42,6 +42,7 @@ import bmesh
import bpy
import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.api.geometry
import ifcopenshell.api.pset
import ifcopenshell.api.style
import ifcopenshell.geom
+3 -3
View File
@@ -515,6 +515,8 @@ class BIM_PT_derived_coordinates(Panel):
return context.active_object is not None
def draw(self, context):
assert context.active_object
props = tool.Model.get_model_props()
if not DerivedCoordinatesData.is_loaded:
DerivedCoordinatesData.load()
@@ -529,10 +531,8 @@ class BIM_PT_derived_coordinates(Panel):
row = self.layout.row(align=True)
row.enabled = False
area_3d = next((area for area in context.screen.areas if area.type == "VIEW_3D"), None)
space_3d = next((space for space in area_3d.spaces if space.type == "VIEW_3D"), None)
if bpy.context.scene.BIMModelProperties.show_bounding_box:
if props.show_bounding_box:
for axis, icon, idx in [("X", "STRIP_COLOR_01", 0), ("Y", "STRIP_COLOR_04", 1), ("Z", "STRIP_COLOR_05", 2)]:
row.label(text="", icon=icon)
row.prop(context.active_object, "dimensions", text=axis, index=idx)
+13
View File
@@ -16,6 +16,9 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from typing import TYPE_CHECKING
import bpy
from bpy.props import BoolProperty, CollectionProperty, EnumProperty, StringProperty
from bpy.types import PropertyGroup
@@ -37,3 +40,13 @@ class BIMCityJsonProperties(PropertyGroup):
lod: EnumProperty(name="LOD", description="", items=get_lods, options={"ANIMATABLE"}, default=None)
is_lod_found: BoolProperty(name="Is LOD Found", default=False)
load_after_convert: BoolProperty(name="Load After Converting", default=True)
if TYPE_CHECKING:
input: str
output: str
name: str
split_lod: bool
lods: bpy.types.bpy_prop_collection_idprop[StrProperty]
lod: str
is_lod_found: bool
load_after_convert: bool
@@ -30,6 +30,7 @@ from typing import TYPE_CHECKING, Union
import bpy
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.geolocation
import pyradiance as pr
import requests
@@ -106,9 +107,8 @@ class ExportOBJ(bpy.types.Operator):
if iterator.initialize():
while True:
shape = iterator.get()
assert isinstance(shape, W.TriangulationElement)
materials = shape.geometry.materials
material_ids = shape.geometry.material_ids
# material_names = shape.geometry.material_names
for material in materials:
ifc_materials.append(material.name)
@@ -21,6 +21,11 @@ import bpy
from . import operator, prop, ui
classes = (
operator.ImportQuickFavorites,
operator.RemoveQuickFavoritesItem,
operator.MoveQuickFavoritesItem,
operator.AddQuickFavoritesItem,
operator.ConfirmQuickFavoriteOperator,
operator.DrawSystemArrows,
operator.GetConnectedSystemElements,
operator.IfcSverchokUseBonsaiFile,
@@ -28,8 +33,12 @@ classes = (
operator.SetOverrideColour,
operator.SnapSpacesTogether,
operator.SplitAlongEdge,
prop.QuickFavoriteEnumItem,
prop.QuickFavoriteProperty,
prop.QuickFavoritesItem,
prop.BIMMiscProperties,
ui.BIM_PT_misc_utilities,
ui.BIM_PT_quick_favorites_manager,
)
+48
View File
@@ -0,0 +1,48 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from typing import Any
import bpy
def refresh() -> None:
QuickFavoritesData.is_loaded = False
class QuickFavoritesData:
data: dict[str, Any] = {}
is_loaded = False
@classmethod
def load(cls) -> None:
cls.data = {
"operators": cls.operators(),
}
cls.is_loaded = True
@classmethod
def operators(cls) -> list[str]:
items: list[str] = []
for module_name in dir(bpy.ops):
module = getattr(bpy.ops, module_name)
for op_name in dir(module):
op = getattr(module, op_name)
bl_label = op.get_rna_type().name
items.append(f"{module_name}.{op_name} - {bl_label}")
return items
+152 -3
View File
@@ -30,6 +30,9 @@ import bonsai.core.misc as core
import bonsai.core.root
import bonsai.tool as tool
if TYPE_CHECKING:
from bpy.stub_internal import rna_enums
class SetOverrideColour(bpy.types.Operator):
bl_idname = "bim.set_override_colour"
@@ -41,10 +44,11 @@ class SetOverrideColour(bpy.types.Operator):
return context.selected_objects
def execute(self, context):
props = tool.Misc.get_misc_props()
for obj in context.selected_objects:
obj.color = context.scene.BIMMiscProperties.override_colour
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
obj.color = props.override_colour
assert (space := tool.Blender.get_view3d_space())
space.shading.color_type = "OBJECT"
return {"FINISHED"}
@@ -351,6 +355,151 @@ class DrawSystemArrows(bpy.types.Operator, tool.Ifc.Operator):
return matrix
class ConfirmQuickFavoriteOperator(bpy.types.Operator):
bl_idname = "bim.confirm_quick_favorite_operator"
bl_label = "Confirm Operator"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
index: int
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Misc.get_misc_props()
fav = props.quick_favorites[self.index]
rna = fav.get_searched_operator()
if rna is None:
self.report({"INFO"}, "No operator entered for search.")
return {"CANCELLED"}
fav.operator_id = tool.Blender.operator_idname_to_py(rna.identifier)
fav.label = rna.name
fav.properties.clear()
has_skipped = False
for p in rna.properties:
# skip silently, e.g. `rna_type` is a PointerProperty
if isinstance(p, bpy.types.PointerProperty):
continue
if isinstance(p, (bpy.types.FloatProperty, bpy.types.BoolProperty, bpy.types.IntProperty)) and p.is_array:
print(f"Array property '{p.identifier}' is not supported, skipping.")
has_skipped = True
continue
item = fav.properties.add()
item.name = p.identifier
item.display_name = p.name
if isinstance(p, bpy.types.FloatProperty):
item.value_prop = "float_value"
item.float_value = p.default
elif isinstance(p, bpy.types.BoolProperty):
item.value_prop = "bool_value"
item.bool_value = p.default
elif isinstance(p, bpy.types.IntProperty):
item.value_prop = "int_value"
item.int_value = p.default
elif isinstance(p, bpy.types.EnumProperty):
item.value_prop = "enum_value"
item.set_enum_items([(e.identifier, e.name, e.description) for e in p.enum_items])
item.enum_value = p.default
elif isinstance(p, bpy.types.StringProperty):
item.value_prop = "string_value"
item.string_value = p.default
else:
print(f"Unhandled property type {type(p).__name__} for '{p.identifier}', skipping.")
has_skipped = True
if has_skipped:
self.report({"WARNING"}, "Some properties were skipped, see the system console for details.")
return {"FINISHED"}
class ImportQuickFavorites(bpy.types.Operator):
bl_idname = "bim.import_quick_favorites"
bl_label = "Import Quick Favorites"
bl_description = "Import operators from Blender's Quick Favorites menu, including their configured properties"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Misc.get_misc_props()
props.quick_favorites.clear()
has_missing_props = False
for i, qf in enumerate(tool.Misc.QuickFavorites.get_quick_favorites()):
fav = props.quick_favorites.add()
fav.label = qf.ui_name
fav.search = qf.op_idname_py
bpy.ops.bim.confirm_quick_favorite_operator(index=i)
fav.label = qf.ui_name or fav.label
for prop in fav.properties:
prop.is_active = prop.name in qf.props
for key, value in qf.props.items():
if key not in fav.properties:
print(f"Property '{key}' not found in operator '{qf.op_idname_py}'.")
has_missing_props = True
continue
item = fav.properties[key]
item.set_value(value)
if has_missing_props:
self.report(
{"WARNING"}, "Some properties were not found during import, see the system console for details."
)
return {"FINISHED"}
class MoveQuickFavoritesItem(bpy.types.Operator):
bl_idname = "bim.move_quick_favorites_item"
bl_label = "Move Quick Favorites Item"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
direction: bpy.props.EnumProperty( # pyright: ignore[reportRedeclaration]
items=[("UP", "Up", ""), ("DOWN", "Down", "")]
)
if TYPE_CHECKING:
index: int
direction: Literal["UP", "DOWN"]
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Misc.get_misc_props()
total = len(props.quick_favorites)
new_index = self.index - 1 if self.direction == "UP" else self.index + 1
if 0 <= new_index < total:
props.quick_favorites.move(self.index, new_index)
return {"FINISHED"}
class RemoveQuickFavoritesItem(bpy.types.Operator):
bl_idname = "bim.remove_quick_favorites_item"
bl_label = "Remove Quick Favorites Item"
bl_options = {"REGISTER", "UNDO"}
index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
index: int
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Misc.get_misc_props()
props.quick_favorites.remove(self.index)
return {"FINISHED"}
class AddQuickFavoritesItem(bpy.types.Operator):
bl_idname = "bim.add_quick_favorites_item"
bl_label = "Add Quick Favorites Item"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Misc.get_misc_props()
fav = props.quick_favorites.add()
fav.search = "bim.select_query_elements"
index = len(props.quick_favorites) - 1
bpy.ops.bim.confirm_quick_favorite_operator(index=index)
fav.properties["query"].string_value = "IfcWall"
return {"FINISHED"}
class IfcSverchokUseBonsaiFile(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.ifcsverchok_use_bonsai_file"
bl_label = "Use Bonsai IFC File"
+123 -2
View File
@@ -16,19 +16,140 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from typing import TYPE_CHECKING, Any, Literal, cast, get_args
import bpy
from bpy.props import (
BoolProperty,
CollectionProperty,
EnumProperty,
FloatProperty,
FloatVectorProperty,
IntProperty,
StringProperty,
)
from bpy.types import PropertyGroup
from bonsai.bim.module.misc.data import QuickFavoritesData
QuickFavoriteValueType = Literal["float_value", "bool_value", "int_value", "string_value", "enum_value"]
class QuickFavoriteEnumItem(PropertyGroup):
name: StringProperty(name="Name", default="") # pyright: ignore[reportRedeclaration]
display_name: StringProperty(name="Display Name", default="") # pyright: ignore[reportRedeclaration]
description: StringProperty(name="Description", default="") # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
name: str
display_name: str
description: str
def get_enum_items(self: "QuickFavoriteProperty", context: bpy.types.Context | None) -> list[tuple[str, str, str]]:
return [(item.name, item.display_name, item.description) for item in self.enum_items]
class QuickFavoriteProperty(PropertyGroup):
name: StringProperty(name="Name", default="") # pyright: ignore[reportRedeclaration]
display_name: StringProperty(name="Display Name", default="") # pyright: ignore[reportRedeclaration]
value_prop: EnumProperty( # pyright: ignore[reportRedeclaration]
name="Value Prop",
items=tuple((v, v, "") for v in get_args(QuickFavoriteValueType)),
)
string_value: StringProperty(name="String Value", default="") # pyright: ignore[reportRedeclaration]
float_value: FloatProperty(name="Float Value", default=0.0) # pyright: ignore[reportRedeclaration]
int_value: IntProperty(name="Int Value", default=0) # pyright: ignore[reportRedeclaration]
bool_value: BoolProperty(name="Bool Value", default=False) # pyright: ignore[reportRedeclaration]
enum_value: EnumProperty(name="Enum Value", items=get_enum_items) # pyright: ignore[reportRedeclaration]
enum_items: CollectionProperty(type=QuickFavoriteEnumItem) # pyright: ignore[reportRedeclaration]
is_active: BoolProperty( # pyright: ignore[reportRedeclaration]
name="Is Active",
description="Only active properties will be added to the operator when invoked from Quick Favorites",
default=False,
)
def set_value(self, value: Any) -> None:
setattr(self, self.value_prop, value)
def set_enum_items(self, items: list[tuple[str, str, str]]) -> None:
self.enum_items.clear()
for identifier, name, description in items:
item = self.enum_items.add()
item.name = identifier
item.display_name = name
item.description = description
if TYPE_CHECKING:
name: str
display_name: str
value_prop: QuickFavoriteValueType
string_value: str
float_value: float
int_value: int
bool_value: bool
enum_value: str
enum_items: bpy.types.bpy_prop_collection_idprop[QuickFavoriteEnumItem]
is_active: bool
def get_operator_suggestions(self: "QuickFavoritesItem", context: bpy.types.Context, edit_text: str) -> list[str]:
if not QuickFavoritesData.is_loaded:
QuickFavoritesData.load()
return QuickFavoritesData.data["operators"]
class QuickFavoritesItem(PropertyGroup):
is_expanded: BoolProperty(name="Is Expanded", default=False) # pyright: ignore[reportRedeclaration]
search: StringProperty( # pyright: ignore[reportRedeclaration]
name="Search",
default="",
search=get_operator_suggestions,
# Resetting `search_options`, allowing users only to use suggestions.
search_options=set(),
)
properties: CollectionProperty(type=QuickFavoriteProperty) # pyright: ignore[reportRedeclaration]
operator_id: StringProperty( # pyright: ignore[reportRedeclaration]
name="Operator ID",
default="",
)
label: StringProperty( # pyright: ignore[reportRedeclaration]
name="Label",
description="Label that will be used in Quick Favorites for this operator",
default="",
)
def get_searched_operator(self) -> bpy.types.Struct | None:
if not self.search:
return None
search_label = self.search
name = search_label.split(" - ", 1)[0]
module, func = name.split(".", 1)
op = getattr(getattr(bpy.ops, module), func)
rna = cast(bpy.types.Struct, op.get_rna_type())
return rna
if TYPE_CHECKING:
is_expanded: bool
search: str
"""Internal property set when confirming results of the search field"""
properties: bpy.types.bpy_prop_collection_idprop[QuickFavoriteProperty]
operator_id: str
label: str
class BIMMiscProperties(PropertyGroup):
total_storeys: IntProperty(
total_storeys: IntProperty( # pyright: ignore[reportRedeclaration]
name="Total Storeys",
description="Number of storeys above object's storey to take into account for resizing",
default=1,
)
override_colour: FloatVectorProperty(
override_colour: FloatVectorProperty( # pyright: ignore[reportRedeclaration]
name="Override Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4
)
quick_favorites: CollectionProperty(type=QuickFavoritesItem) # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
total_storeys: int
override_colour: tuple[float, float, float, float]
quick_favorites: bpy.types.bpy_prop_collection_idprop[QuickFavoritesItem]
+73 -1
View File
@@ -18,6 +18,8 @@
import bpy
import bonsai.tool as tool
class BIM_PT_misc_utilities(bpy.types.Panel):
bl_idname = "BIM_PT_misc_utilities"
@@ -30,7 +32,8 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
def draw(self, context):
layout = self.layout
props = context.scene.BIMMiscProperties
assert layout
props = tool.Misc.get_misc_props()
row = layout.split(factor=0.2, align=True)
row.prop(props, "override_colour", text="")
row.operator("bim.set_override_colour")
@@ -56,3 +59,72 @@ class BIM_PT_misc_utilities(bpy.types.Panel):
row.operator("bim.disable_editing_sketch_extrusion_profile", text="", icon="CANCEL")
row = layout.row()
row.operator("bim.import_plot", text="Import Plot Coordinates", icon="FILE_FOLDER")
class BIM_PT_quick_favorites_manager(bpy.types.Panel):
bl_idname = "BIM_PT_quick_favorites_manager"
bl_label = "Quick Favorites Manager"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "output"
bl_options = {"DEFAULT_CLOSED"}
bl_parent_id = "BIM_PT_tab_sandbox"
def draw(self, context):
layout = self.layout
assert layout
props = tool.Misc.get_misc_props()
row = layout.row(align=True)
row.label(text="Quick Favorites:")
row.operator("bim.add_quick_favorites_item", text="", icon="ADD")
row.operator("bim.import_quick_favorites", text="", icon="BLENDER")
op = row.operator("bim.show_description", text="", icon="INFO")
op.attr_name = "Quick Favorites Manager"
op.description = (
"Blender does not support editing Quick Favorites natively. "
"This manager allows you to load existing Quick Favorites operators, "
"configure their properties and labels, and re-add them to the menu with customized settings."
)
for fav in props.quick_favorites:
if fav.operator_id:
row = layout.row()
op = row.operator(fav.operator_id, text=fav.label)
for item in fav.properties:
if item.is_active:
setattr(op, item.name, getattr(item, item.value_prop))
layout.separator()
for i, fav in enumerate(props.quick_favorites):
box = layout.box()
row = box.row(align=True)
row.prop(fav, "is_expanded", text="", icon="TRIA_DOWN" if fav.is_expanded else "TRIA_RIGHT", emboss=False)
row.prop(fav, "label", text="")
if i > 0:
up = row.operator("bim.move_quick_favorites_item", text="", icon="TRIA_UP")
up.index = i
up.direction = "UP"
if i < len(props.quick_favorites) - 1:
down = row.operator("bim.move_quick_favorites_item", text="", icon="TRIA_DOWN")
down.index = i
down.direction = "DOWN"
row.operator("bim.remove_quick_favorites_item", text="", icon="X").index = i
if not fav.is_expanded:
continue
row = box.row(align=True)
row.prop(fav, "search", text="")
row.operator("bim.confirm_quick_favorite_operator", text="", icon="VIEWZOOM").index = i
if not fav.operator_id:
continue
layout.separator()
if fav.properties:
box.label(text="Properties:")
prop_box = box.box()
for item in fav.properties:
row = prop_box.row(align=True)
row.prop(item, item.value_prop, text=item.display_name)
row.prop(item, "is_active", text="", icon="RADIOBUT_ON" if item.is_active else "RADIOBUT_OFF")
else:
box.label(text="No Properties.")
@@ -19,7 +19,7 @@
from __future__ import annotations
import math
from math import cos, radians, sin, tan
from math import cos, pi, radians, sin, tan
from typing import Any, Literal
import blf
@@ -27,6 +27,10 @@ import bmesh
import bpy
import gpu
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.unit
import mathutils
from bpy.types import SpaceView3D
from bpy_extras import view3d_utils
@@ -35,6 +39,7 @@ from gpu_extras.batch import batch_for_shader
from gpu_extras.presets import draw_circle_2d
from mathutils import Matrix, Quaternion, Vector
import bonsai.core.geometry
import bonsai.tool as tool
from bonsai.bim.module.drawing.helper import format_distance
@@ -1566,7 +1571,7 @@ class ProductDecorator:
obj_type,
representation,
)
context.view_layer.update()
bpy.context.view_layer.update()
break
translate_mouse = Matrix.Translation(mouse_point)
@@ -733,6 +733,9 @@ class BIMStairProperties(PropertyGroup):
class BIMSverchokProperties(PropertyGroup):
node_group: bpy.props.PointerProperty(name="Node Group", type=NodeTree)
if TYPE_CHECKING:
node_group: bpy.types.NodeTree | None
def window_type_prop_update(self, context):
number_of_panels, panels_data = self.window_types_panels[self.window_type]
@@ -31,7 +31,7 @@ import bonsai.tool as tool
def update_sverchok_modifier(context):
obj = context.active_object
props = obj.BIMSverchokProperties
props = tool.Model.get_sverchok_props(obj)
element = tool.Ifc.get_entity(obj)
psets = ifcopenshell.util.element.get_psets(element)
pset = psets.get("BBIM_Sverchok", None)
@@ -69,10 +69,10 @@ class CreateNewSverchokGraph(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER"}
def _execute(self, context):
import sverchok
import sverchok.ui.sv_temporal_viewers
obj = context.active_object
props = obj.BIMSverchokProperties
props = tool.Model.get_sverchok_props(obj)
node_group = bpy.data.node_groups.new("IfcNodeTree", type="SverchCustomTreeType")
plane = node_group.nodes.new(type="SvPlaneNodeMk3")
@@ -96,7 +96,7 @@ class DeleteSverchokGraph(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMSverchokProperties
props = tool.Model.get_sverchok_props(obj)
bpy.data.node_groups.remove(props.node_group)
return {"FINISHED"}
@@ -113,7 +113,8 @@ class UpdateDataFromSverchok(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER"}
def invoke(self, context, event):
if not context.active_object.BIMSverchokProperties.node_group:
props = tool.Model.get_sverchok_props(context.active_object)
if not props.node_group:
return context.window_manager.invoke_props_dialog(self)
return self._execute(context)
@@ -124,7 +125,7 @@ class UpdateDataFromSverchok(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMSverchokProperties
props = tool.Model.get_sverchok_props(obj)
node_group = props.node_group
if node_group:
@@ -192,11 +193,11 @@ class ImportSverchokGraph(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
filename_ext = ".json"
def _execute(self, context):
import sverchok
import sverchok.utils.sv_json_import
importer = sverchok.utils.sv_json_import.JSONImporter.init_from_path(self.filepath)
obj = context.active_object
props = obj.BIMSverchokProperties
props = tool.Model.get_sverchok_props(obj)
node_group = context.scene.io_panel_properties.import_tree
if not node_group:
@@ -231,10 +232,10 @@ class ExportSverchokGraph(bpy.types.Operator, tool.Ifc.Operator, ExportHelper):
compress: bpy.props.BoolProperty()
def _execute(self, context):
import sverchok
import sverchok.utils.sv_json_export
obj = context.active_object
props = obj.BIMSverchokProperties
props = tool.Model.get_sverchok_props(obj)
ng = props.node_group
destination_path = self.filepath
if not destination_path.lower().endswith(".json"):
@@ -273,7 +274,8 @@ class ExportSverchokGraph(bpy.types.Operator, tool.Ifc.Operator, ExportHelper):
return {"FINISHED"}
def draw(self, context):
graph_name = context.active_object.BIMSverchokProperties.node_group.name
props = tool.Model.get_sverchok_props(context.active_object)
graph_name = props.node_group.name
self.layout.label(text=f'Save node tree "{graph_name}" into json:')
col = self.layout.column(heading="Options") # new syntax in >= 2.90
+1 -1
View File
@@ -360,7 +360,7 @@ class BIM_PT_sverchok(bpy.types.Panel):
self.layout.label(text="Requires Sverchok Add-on", icon="ERROR")
return
props = context.active_object.BIMSverchokProperties
props = tool.Model.get_sverchok_props(context.active_object)
self.layout.prop_search(props, "node_group", bpy.data, "node_groups")
self.layout.operator("bim.create_new_sverchok_graph", icon="ADD")
@@ -215,8 +215,6 @@ class NestDecorator:
self.draw_batch("LINES", line_z, color, [(0, 1)])
else:
self.draw_batch("POINTS", [location], color)
# if context.scene.BIMNestProperties.in_aggregate_mode:
# return
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(nest))
components_objs = [tool.Ifc.get_object(p) for p in components]
components_objs.append(nest)
@@ -2142,6 +2142,7 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
if iterator.initialize():
while True: # Main loop.
shape = iterator.get()
assert isinstance(shape, W.TriangulationElement)
results.add(self.file.by_id(shape.id))
geometry = shape.geometry
@@ -2372,18 +2373,23 @@ class HideQueriedLinkedElement(bpy.types.Operator):
bl_label = "Hide Queried Linked Element"
bl_description = (
"Hide geometry for currently queried linked element.\n\n"
"ALT+Click (or ALT+H in Explore Tool) to unhide all geometry for currently selected linked model.\n"
"(Not Yet Implemented) SHIFT+Click to hide everything but currently queried element."
"SHIFT+Click (or SHIFT+H in Explore Tool) to hide everything "
"in the currently selected model, but the queried element.\n"
"ALT+Click (or ALT+H in Explore Tool) to unhide all geometry for currently selected linked model.\n\n"
"Known limitation: doesn't work with UNDO."
)
bl_options = {"REGISTER", "UNDO"}
unhide_all: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
hide_all_except: bpy.props.BoolProperty(options={"SKIP_SAVE"}) # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
unhide_all: bool
hide_all_except: bool
def invoke(self, context, event):
self.unhide_all = event.alt
self.hide_all_except = event.shift
return self.execute(context)
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
@@ -2392,6 +2398,9 @@ class HideQueriedLinkedElement(bpy.types.Operator):
if self.unhide_all:
return self.run_unhide_all()
if self.hide_all_except:
return self.run_hide_all_except()
obj = props.queried_obj
if not obj:
self.report({"INFO"}, "No object is queried to hide.")
@@ -2413,6 +2422,21 @@ class HideQueriedLinkedElement(bpy.types.Operator):
self.report({"INFO"}, "All linked model geometry is unhidden.")
return {"FINISHED"}
def run_hide_all_except(self) -> set["rna_enums.OperatorReturnItems"]:
props = tool.Project.get_project_props()
obj = props.queried_obj
if not obj:
self.report({"INFO"}, "No object is queried.")
return {"FINISHED"}
link = props.active_link
if not link:
self.report({"INFO"}, "No linked model is currently selected.")
return {"FINISHED"}
guid = props.queried_guid
tool.Project.Link.hide_all_elements_except(link, obj, guid)
self.report({"INFO"}, "All other linked model geometry is now hidden.")
return {"FINISHED"}
class AppendInspectedLinkedElement(AppendLibraryElement):
bl_idname = "bim.append_inspected_linked_element"
@@ -2698,10 +2722,7 @@ class CreateClippingPlane(bpy.types.Operator):
self.report({"INFO"}, "Maximum of six clipping planes allowed.")
return {"FINISHED"}
assert context.screen
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
tool.Blender.update_all_viewports(context)
assert context.region and context.region_data
region = context.region
+4 -4
View File
@@ -230,11 +230,11 @@ class Link(PropertyGroup):
)
georeferenced: EnumProperty(
name="Georeferenced",
description="Georeferencing status: compatibility between host and linked model",
description="Georeferencing status, compatibility between host and linked model",
items=[
("NONE", "No Georef", "Linked model has no georeferencing"),
("NOT_COMPATIBLE", "Not Compatible", "Has geo data but CRS differ from host"),
("FULL_COMPATIBLE", "Full Compatible", "Both CRS name and vertical datum match host"),
("NONE", "No Georef", "Linked model has no georeferencing", "QUESTION", 0),
("NOT_COMPATIBLE", "Not Compatible", "Has geo data but CRS differ from host", "ERROR", 1),
("FULL_COMPATIBLE", "Full Compatible", "Both CRS name and vertical datum match host", "WORLD", 2),
],
default="NONE",
)
+9 -7
View File
@@ -255,7 +255,7 @@ class BIM_PT_project(Panel):
row = self.layout.row(align=True)
row.operator("bim.load_project_elements")
def draw_editing_buttons(self, context, row):
def draw_editing_buttons(self, context: object, row: bpy.types.UILayout) -> None:
pprops = self.props
if tool.Ifc.get():
if pprops.is_editing:
@@ -619,12 +619,14 @@ class BIM_UL_links(UIList):
):
row = layout.row(align=True)
if item.is_loaded:
if item.georeferenced == "NONE":
row.label(text="", icon="QUESTION")
elif item.georeferenced == "NOT_COMPATIBLE":
row.label(text="", icon="ERROR")
elif item.georeferenced == "FULL_COMPATIBLE":
row.label(text="", icon="WORLD")
from bonsai.bim.module.project.prop import Link
s = Link.bl_rna
geo_prop = s.properties["georeferenced"]
assert isinstance(geo_prop, bpy.types.EnumProperty)
enum_item = geo_prop.enum_items[item.georeferenced]
op = row.operator("bim.show_description", text="", icon=enum_item.icon, emboss=False)
op.description = f"{geo_prop.description}\n{enum_item.name}: {enum_item.description}"
if item.has_transformation:
row.label(text="", icon="OBJECT_ORIGIN")
@@ -41,6 +41,7 @@ class ExploreTool(bpy.types.WorkSpaceTool):
("bim.explore_hotkey", {"type": "M", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_M")]}),
("bim.explore_hotkey", {"type": "S", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_S")]}),
("bim.explore_hotkey", {"type": "H", "value": "PRESS"}, {"properties": [("hotkey", "H")]}),
("bim.explore_hotkey", {"type": "H", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_H")]}),
("bim.explore_hotkey", {"type": "H", "value": "PRESS", "alt": True}, {"properties": [("hotkey", "A_H")]}),
)
@@ -147,5 +148,8 @@ class ExploreHotkey(bpy.types.Operator):
def hotkey_H(self) -> None:
bpy.ops.bim.hide_queried_linked_element()
def hotkey_S_H(self) -> None:
bpy.ops.bim.hide_queried_linked_element(hide_all_except=True)
def hotkey_A_H(self) -> None:
bpy.ops.bim.hide_queried_linked_element(unhide_all=True)
@@ -188,7 +188,7 @@ class BIMResourceProperties(PropertyGroup):
@property
def productivity(self) -> "BIMResourceProductivity":
assert bpy.context.scene
productivity = bpy.context.scene.BIMResourceProductivity
productivity = bpy.context.scene.BIMResourceProductivity # pyright: ignore[reportAttributeAccessIssue]
assert isinstance(productivity, BIMResourceProductivity)
return productivity
@@ -41,6 +41,7 @@ classes = (
operator.SelectByProperty,
operator.SelectFilterElements,
operator.SelectGlobalId,
operator.SelectQueryElements,
operator.SelectIfcClass,
operator.SelectSimilar,
operator.ShowAllElements,
@@ -42,6 +42,8 @@ from bonsai.bim.ifc import IfcStore
from bonsai.bim.prop import StrProperty
if TYPE_CHECKING:
from bpy.stub_internal import rna_enums
from bonsai.bim.prop import BIMFacet
@@ -791,6 +793,27 @@ class Search(Operator):
return {"FINISHED"}
class SelectQueryElements(Operator):
bl_idname = "bim.select_query_elements"
bl_label = "Select Query Elements"
bl_description = "Select elements matching an provided selector query"
bl_options = {"REGISTER", "UNDO"}
query: StringProperty(name="Query") # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
query: str
def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), self.query)
objs = [obj for e in results if isinstance(obj := tool.Ifc.get_object(e), bpy.types.Object)]
active_object = context.active_object or next(iter(objs), None)
selection = tool.Blender.validate_object_selection(context, active_object, objs)
tool.Blender.set_objects_selection(*selection, clear_previous_selection=False)
self.report({"INFO"}, f"{len(results)} Results, {len(selection.selected_objects)} Objects Selected")
return {"FINISHED"}
class SaveSearch(Operator, tool.Ifc.Operator):
bl_idname = "bim.save_search"
bl_label = "Save Search"
@@ -1053,8 +1076,8 @@ class ColourByProperty(Operator):
colourscheme[str(values[index])]["total"] += 1
obj.color = (*tool.Search.get_quantitative_palette(palette, value, min_value, max_value), 1)
if areas := [a for a in context.screen.areas if a.type == "VIEW_3D"]:
areas[0].spaces[0].shading.color_type = "OBJECT"
assert (space := tool.Blender.get_view3d_space())
space.shading.color_type = "OBJECT"
props.colourscheme.clear()
@@ -1078,16 +1101,18 @@ class ColourByProperty(Operator):
return (1, value)
def store_state(self, context):
if areas := [a for a in context.screen.areas if a.type == "VIEW_3D"]:
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
if space := tool.Blender.get_view3d_space():
self.transaction_data = {"color_type": space.shading.color_type}
def rollback(self, data):
if data:
data["area"].spaces[0].shading.color_type = data["color_type"]
assert (space := tool.Blender.get_view3d_space())
space.shading.color_type = data["color_type"]
def commit(self, data):
if data:
data["area"].spaces[0].shading.color_type = "OBJECT"
assert (space := tool.Blender.get_view3d_space())
space.shading.color_type = "OBJECT"
class SelectByProperty(Operator):
+28 -12
View File
@@ -16,6 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional
import bpy
@@ -37,8 +39,11 @@ from bonsai.bim.module.sequence.data import (
if TYPE_CHECKING:
from bonsai.bim.module.sequence.prop import (
BIMTaskTreeProperties,
BIMTaskTypeColor,
BIMWorkScheduleProperties,
Task,
TaskProduct,
TaskResource,
)
from bonsai.bim.prop import Attribute
@@ -799,23 +804,24 @@ class BIM_UL_task_columns(UIList):
self,
context,
layout: bpy.types.UILayout,
data: "BIMWorkScheduleProperties",
item: "Attribute",
data: BIMWorkScheduleProperties,
item: Attribute,
icon,
active_data,
active_propname,
):
props = tool.Sequence.get_work_schedule_props()
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
if props.sort_column == item.name:
if data.sort_column == item.name:
row.label(text="", icon="SORTALPHA")
row.operator("bim.remove_task_column", text="", icon="X").name = item.name
class BIM_UL_task_inputs(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: TaskProduct, icon, active_data, active_propname
) -> None:
if item:
row = layout.row(align=True)
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
@@ -825,7 +831,9 @@ class BIM_UL_task_inputs(UIList):
class BIM_UL_task_resources(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: TaskResource, icon, active_data, active_propname
) -> None:
if item:
row = layout.row(align=True)
row.operator("bim.go_to_resource", text="", icon="STYLUS_PRESSURE").resource = item.ifc_definition_id
@@ -834,7 +842,9 @@ class BIM_UL_task_resources(UIList):
class BIM_UL_animation_colors(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: BIMTaskTypeColor, icon, active_data, active_propname
) -> None:
if item:
row = layout.row()
row.prop(item, "color", text="")
@@ -842,7 +852,9 @@ class BIM_UL_animation_colors(UIList):
class BIM_UL_task_outputs(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: TaskProduct, icon, active_data, active_propname
) -> None:
if item:
row = layout.row(align=True)
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
@@ -851,7 +863,9 @@ class BIM_UL_task_outputs(UIList):
class BIM_UL_product_input_tasks(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: TaskProduct, icon, active_data, active_propname
) -> None:
if item:
row = layout.row(align=True)
op = row.operator("bim.go_to_task", text="", icon="STYLUS_PRESSURE")
@@ -861,7 +875,9 @@ class BIM_UL_product_input_tasks(UIList):
class BIM_UL_product_output_tasks(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: TaskProduct, icon, active_data, active_propname
) -> None:
if item:
row = layout.row(align=True)
op = row.operator("bim.go_to_task", text="", icon="STYLUS_PRESSURE")
@@ -886,8 +902,8 @@ class BIM_UL_tasks(UIList):
self,
context,
layout: bpy.types.UILayout,
data: "BIMTaskTreeProperties",
item: "Task",
data: BIMTaskTreeProperties,
item: Task,
icon,
active_data,
active_propname,
@@ -512,7 +512,7 @@ class SetContainerVisibility(bpy.types.Operator):
containers -= set(tool.Ifc.get().by_type("IfcSpatialZone"))
for container in containers:
if obj := tool.Ifc.get_object(container):
if collection := obj.BIMObjectProperties.collection:
if collection := tool.Blender.get_object_bim_props(obj).collection:
collection.hide_viewport = True
should_hide = False
else:
@@ -523,7 +523,7 @@ class SetContainerVisibility(bpy.types.Operator):
while queue:
container = queue.pop()
if obj := tool.Ifc.get_object(container):
if collection := obj.BIMObjectProperties.collection:
if collection := tool.Blender.get_object_bim_props(obj).collection:
collection.hide_viewport = should_hide
if self.should_include_children:
queue.extend(ifcopenshell.util.element.get_parts(container))
+1 -1
View File
@@ -357,7 +357,7 @@ class BIM_UL_elements(UIList):
super().__init__(*args, **kwargs)
self.use_filter_show = True
def draw_toggle(self, row: bpy.types.UILayout, is_expanded: bool, index: int):
def draw_toggle(self, row: bpy.types.UILayout, is_expanded: bool, index: int) -> None:
icon_id = "DISCLOSURE_TRI_DOWN" if is_expanded else "DISCLOSURE_TRI_RIGHT"
row.operator("bim.toggle_container_element", text="", emboss=False, icon=icon_id).element_index = index
@@ -42,14 +42,10 @@ class ShowLoads(bpy.types.Operator):
assert context.screen
if event.type == "F5":
LoadsDecorator.update()
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
tool.Blender.update_all_viewports(context)
if event.type == "ESC":
LoadsDecorator.uninstall()
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
tool.Blender.update_all_viewports(context)
return {"FINISHED"}
return {"PASS_THROUGH"}
@@ -69,9 +65,7 @@ class ShowLoads(bpy.types.Operator):
raise exc
context.window.cursor_modal_restore()
context.window_manager.modal_handler_add(self)
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
tool.Blender.update_all_viewports(context)
return {"RUNNING_MODAL"}
+4 -1
View File
@@ -102,7 +102,10 @@ def update_shading_styles(self: "BIMStylesProperties", context: bpy.types.Contex
def update_shader_graph(self: Union["Texture", "BIMStylesProperties"], context: bpy.types.Context) -> None:
props = self.id_data.BIMStylesProperties if isinstance(self, Texture) else self
if isinstance(self, Texture):
props = tool.Style.get_style_props()
else:
props = self
if not props.update_graph:
return
+2 -3
View File
@@ -123,7 +123,6 @@ class BIM_UL_units(UIList):
active_data,
active_propname,
) -> None:
props = tool.Unit.get_unit_props()
if item:
icon = tool.Unit.get_icon_for_unit_class(item.ifc_class)
row = layout.row(align=True)
@@ -137,10 +136,10 @@ class BIM_UL_units(UIList):
op = row.operator("bim.assign_unit", text="", icon="KEYFRAME", emboss=False)
op.unit = item.ifc_definition_id
if props.active_unit_id == item.ifc_definition_id:
if data.active_unit_id == item.ifc_definition_id:
row.operator("bim.edit_unit", text="", icon="CHECKMARK").unit = item.ifc_definition_id
row.operator("bim.disable_editing_unit", text="", icon="CANCEL")
elif props.active_unit_id:
elif data.active_unit_id:
row.operator("bim.remove_unit", text="", icon="X").unit = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_unit", text="", icon="GREASEPENCIL")
+5 -1
View File
@@ -29,6 +29,8 @@ from bonsai.bim.module.void.data import BooleansData, VoidsData
if TYPE_CHECKING:
import bpy.stub_internal.rna_enums as rna_enums
from bonsai.bim.module.void.prop import Boolean
OPENING_ICON = "SELECT_SUBTRACT"
FILLING_ICON = "SELECT_INTERSECT"
@@ -175,7 +177,9 @@ class BIM_PT_booleans(Panel):
class BIM_UL_booleans(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: Boolean, icon, active_data, active_propname
) -> None:
if item:
if item.operator == "DIFFERENCE":
icon = "SELECT_DIFFERENCE"
+2 -2
View File
@@ -70,7 +70,7 @@ class SetTab(bpy.types.Operator):
if context.area.spaces.active.search_filter:
return {"FINISHED"}
tool.Blender.setup_tabs()
aprops = tool.Blender.get_area_props(context)
aprops = tool.Blender.get_active_area_props(context)
aprops.tab = self.tab
return {"FINISHED"}
@@ -85,7 +85,7 @@ class SwitchTab(bpy.types.Operator):
if context.area.spaces.active.search_filter:
return {"FINISHED"}
tool.Blender.setup_tabs()
aprops = tool.Blender.get_area_props(context)
aprops = tool.Blender.get_active_area_props(context)
aprops.tab = aprops.alt_tab
return {"FINISHED"}
+2 -1
View File
@@ -59,7 +59,8 @@ def update_is_visible(self: "BIMTabVisibility", context: bpy.types.Context) -> N
def update_global_tab(self: "BIMTabProperties", context: bpy.types.Context) -> None:
tool.Blender.setup_tabs()
screen = context.id_data
aprops = screen.BIMAreaProperties[screen.areas[:].index(context.area)]
assert isinstance(screen, bpy.types.Screen)
aprops = tool.Blender.get_area_props(screen)[screen.areas[:].index(context.area)]
aprops.tab = self.tab
+2 -9
View File
@@ -16,19 +16,12 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from typing import TYPE_CHECKING
import bpy
import ifcopenshell
import ifcopenshell.util.pset
import bonsai
import bonsai.tool as tool
if TYPE_CHECKING or bpy.app.version >= (5, 0, 0):
import _bpy_restrict_state as bpy_restrict_state
else:
import bpy_restrict_state
class IfcSchema:
data_dir: str
@@ -62,7 +55,7 @@ class IfcSchema:
self.psetqto.get_by_name.cache_clear()
# During register we cannot access the context either way.
if isinstance(bpy.context, bpy_restrict_state._RestrictContext):
if bonsai.is_registering():
return
for path in tool.Blender.get_data_dir_paths("pset", "*.ifc"):
self.psetqto.templates.append(ifcopenshell.open(path))
+4 -3
View File
@@ -1031,8 +1031,7 @@ class BIM_PT_tabs(Panel):
def draw(self, context):
if not UIData.is_loaded:
UIData.load()
aprops = tool.Blender.get_area_props(context)
addon_prefs = tool.Blender.get_addon_preferences()
aprops = tool.Blender.get_active_area_props(context)
row = self.layout.row()
row.alignment = "CENTER"
@@ -1119,7 +1118,9 @@ class BIM_PT_tabs(Panel):
op = row.operator("bim.open_uri", text="", icon="QUESTION")
op.uri = "https://docs.bonsaibim.org/guides/troubleshooting.html#incompatible-blender-features"
def draw_tab_entry(self, row, icon, tab_name, enabled=True, highlight=True):
def draw_tab_entry(
self, row: bpy.types.UILayout, icon: int | str, tab_name: str, enabled: bool = True, highlight: bool = True
) -> None:
tab_entry = row.row(align=True)
if isinstance(icon, int):
tab_entry.operator("bim.set_tab", text="", emboss=highlight, icon_value=icon).tab = tab_name
+1
View File
@@ -99,6 +99,7 @@ class Blender:
def get_object_bounding_box(cls, obj): pass
def get_selected_objects(cls, include_active=False): pass
def get_viewport_context(cls): pass
def operator_idname_to_py(cls, idname): pass
def is_ifc_class_active(cls, ifc_class): pass
def is_ifc_object(cls, obj): pass
def remove_object(cls, obj): pass
+64 -19
View File
@@ -74,7 +74,13 @@ if TYPE_CHECKING:
BIMSolarProperties,
RadianceExporterProperties,
)
from bonsai.bim.prop import BIMObjectProperties, BIMProperties
from bonsai.bim.prop import (
BIMAreaProperties,
BIMCollectionProperties,
BIMObjectProperties,
BIMProperties,
BIMTabProperties,
)
T = TypeVar("T")
@@ -138,17 +144,20 @@ class Blender(bonsai.core.tool.Blender):
space.region_3d.view_perspective = "CAMERA"
@classmethod
def get_area_props(cls, context: bpy.types.Context) -> bpy.types.PropertyGroup:
def get_active_area_props(cls, context: bpy.types.Context) -> BIMAreaProperties | BIMTabProperties:
FULLSCREEN_SUFFIX = "-nonnormal" # Ctrl-space temporary fullscreen
assert (screen := context.screen)
try:
if context.screen.name.endswith("-nonnormal"): # Ctrl-space temporary fullscreen
screen = bpy.data.screens[context.screen.name.removesuffix("-nonnormal")]
if screen.name.endswith(FULLSCREEN_SUFFIX):
screen = bpy.data.screens[screen.name.removesuffix(FULLSCREEN_SUFFIX)]
# The original area object has its type changed to "EMPTY" apparently
index = [a.type for a in screen.areas].index("EMPTY")
return screen.BIMAreaProperties[index]
return context.screen.BIMAreaProperties[context.screen.areas[:].index(context.area)]
return cls.get_area_props(screen)[index]
assert (area := context.area)
return cls.get_area_props(screen)[screen.areas[:].index(area)]
except IndexError:
# Fallback in case areas aren't setup yet.
return context.screen.BIMTabProperties
return cls.get_tab_props(screen)
@classmethod
def set_active_object(cls, obj: bpy.types.Object) -> None:
@@ -165,15 +174,16 @@ class Blender(bonsai.core.tool.Blender):
def setup_tabs(cls) -> None:
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
for screen in bpy.data.screens:
if len(screen.BIMAreaProperties) == 20:
area_props = cls.get_area_props(screen)
if len(area_props) == 20:
continue
screen.BIMAreaProperties.clear()
area_props.clear()
for i in range(20): # 20 is an arbitrary value of split areas
screen.BIMAreaProperties.add()
area_props.add()
@classmethod
def should_show_panel(cls, context: bpy.types.Context, tab: str, panel: str) -> bool:
aprops = cls.get_area_props(context)
aprops = cls.get_active_area_props(context)
if aprops.path_from_id() == "BIMAreaProperties" and context.area.spaces.active.search_filter:
return True
if (is_bookmark_tab := aprops.tab == "BOOKMARK") or aprops.tab == tab:
@@ -185,6 +195,7 @@ class Blender(bonsai.core.tool.Blender):
return True
elif panel_visibility.is_visible:
return True
return False
@classmethod
def is_default_scene(cls) -> bool:
@@ -330,15 +341,27 @@ class Blender(bonsai.core.tool.Blender):
@classmethod
def get_view3d_area(cls) -> Union[bpy.types.Area, None]:
for window in bpy.context.window_manager.windows:
assert (wm := bpy.context.window_manager)
for window in wm.windows:
for area in window.screen.areas:
if area.type == "VIEW_3D":
return area
@classmethod
def operator_idname_to_py(cls, idname: str) -> str:
"""Convert a Blender internal operator idname to its Python equivalent.
Example: ``MESH_OT_primitive_cube_add`` -> ``mesh.primitive_cube_add``
"""
module, func = idname.split("_OT_", 1)
return f"{module.lower()}.{func}"
@classmethod
def get_view3d_space(cls) -> Union[bpy.types.SpaceView3D, None]:
if area := cls.get_view3d_area():
return area.spaces.active
space = area.spaces.active
assert isinstance(space, bpy.types.SpaceView3D)
return space
@classmethod
def get_blender_prop_default_value(cls, props: bpy.types.bpy_struct, prop_name: str) -> Any:
@@ -459,6 +482,14 @@ class Blender(bonsai.core.tool.Blender):
def update_viewport(cls) -> None:
cls.get_viewport_context()["area"].tag_redraw()
@classmethod
def update_all_viewports(cls, context: bpy.types.Context | None = None) -> None:
context = context or bpy.context
assert context.screen
for area in context.screen.areas:
if area.type == "VIEW_3D":
area.tag_redraw()
@classmethod
def force_depsgraph_update(cls) -> None:
"""useful if you need to trigger callbacks like `depsgraph_update_pre`"""
@@ -1489,7 +1520,7 @@ class Blender(bonsai.core.tool.Blender):
def override_scene_panel(cls, original_panel: bpy.types.Panel) -> None:
@classmethod
def poll_check_blender_tab(cls, context):
aprops = tool.Blender.get_area_props(context)
aprops = tool.Blender.get_active_area_props(context)
if aprops.path_from_id() == "BIMAreaProperties" and context.area.spaces.active.search_filter:
return True
return aprops.tab == "BLENDER"
@@ -1829,6 +1860,18 @@ class Blender(bonsai.core.tool.Blender):
assert (scene := bpy.context.scene)
return scene.BIMProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_area_props(cls, screen: bpy.types.Screen) -> bpy.types.bpy_prop_collection_idprop[BIMAreaProperties]:
return screen.BIMAreaProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_tab_props(cls, screen: bpy.types.Screen) -> BIMTabProperties:
return screen.BIMTabProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_collection_props(cls, collection: bpy.types.Collection) -> BIMCollectionProperties:
return collection.BIMCollectionProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_object_bim_props(cls, obj: bpy.types.Object) -> BIMObjectProperties:
return obj.BIMObjectProperties # pyright: ignore[reportAttributeAccessIssue]
@@ -1882,19 +1925,21 @@ class Blender(bonsai.core.tool.Blender):
@classmethod
def clear_undo_history(cls) -> None:
"""Clears the Blender history, Bonsai history, and IfcOpenShell history"""
old_undo_steps = bpy.context.preferences.edit.undo_steps
bpy.context.preferences.edit.undo_steps = 2
assert (preferences := bpy.context.preferences)
old_undo_steps = preferences.edit.undo_steps
preferences.edit.undo_steps = 2
for i in range(3):
bpy.ops.ed.undo_push(message="Undo history cleared")
bpy.context.preferences.edit.undo_steps = old_undo_steps
preferences.edit.undo_steps = old_undo_steps
tool.Ifc.clear_history()
old_history_size = tool.Ifc.get().history_size
tool.Ifc.get().set_history_size(0)
tool.Ifc.get().set_history_size(old_history_size)
@classmethod
def get_unit_scale(cls):
unit_length = bpy.context.scene.unit_settings.length_unit
def get_unit_scale(cls) -> float:
assert (scene := bpy.context.scene)
unit_length = scene.unit_settings.length_unit
unit_scale = 1.0
if unit_length == "CENTIMETERS":
unit_scale = 0.01
+4 -4
View File
@@ -841,7 +841,7 @@ class Cad:
return new_verts
@classmethod
def region_2d_to_vector_3d_np(cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3d, coord: Vector) -> Vector:
def region_2d_to_vector_3d_np(cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3D, coord: Vector) -> Vector:
"""
Numpy version of view3d_utils.region_2d_to_vector_3d
Return a direction vector from the viewport at the specific 2d region
@@ -880,7 +880,7 @@ class Cad:
@classmethod
def region_2d_to_location_3d_np(
cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3d, coord: Vector, depth_location: Vector
cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3D, coord: Vector, depth_location: Vector
) -> Vector:
"""
Numpy version of view3d_utils.region_2d_to_location_3d
@@ -913,7 +913,7 @@ class Cad:
@classmethod
def region_2d_to_origin_3d_np(
cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3d, coord: Vector, *, clamp: float = None
cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3D, coord: Vector, *, clamp: float = None
) -> Vector:
"""
Numpy version of view3d_utils.region_2d_to_origin_3d
@@ -971,7 +971,7 @@ class Cad:
@classmethod
def location_3d_to_region_2d_np(
cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3d, coord: Vector, *, default=None
cls, region: bpy.types.Region, rv3d: bpy.types.RegionView3D, coord: Vector, *, default=None
) -> Vector:
"""
Numpy version of view3d_utils.location_3d_to_region_2d
+2 -1
View File
@@ -157,7 +157,8 @@ class Collector(bonsai.core.tool.Collector):
return
collection = bpy.data.collections.new(obj.name)
props.collection = collection
collection.BIMCollectionProperties.obj = obj
collection_props = tool.Blender.get_collection_props(collection)
collection_props.obj = obj
return collection
@classmethod
+1 -10
View File
@@ -1070,11 +1070,6 @@ class Loader(bonsai.core.tool.Loader):
layer_set = material.ForLayerSet
offset = usage.OffsetFromReferenceLine * cls.unit_scale
sense_factor = 1 if usage.DirectionSense == "POSITIVE" else -1
elif material.is_a("IfcMaterialLayerSet"):
usage = None
layer_set = material
offset = 0
sense_factor = 1
else:
return mesh
if len(layer_set.MaterialLayers) == 1:
@@ -1082,11 +1077,7 @@ class Loader(bonsai.core.tool.Loader):
bm = bmesh.new()
bm.from_mesh(mesh)
prev_co = None
if not usage:
sense_factor = 1 # Assume the extrusion vector points in the direction sense
no = cls.get_extrusion_vector(element).normalized()
co = Vector((0.0, 0.0, offset))
elif usage.LayerSetDirection == "AXIS2":
if usage.LayerSetDirection == "AXIS2":
co = Vector((0.0, offset, 0.0))
no = cls.get_extrusion_vector(element).normalized()
no = no.cross(Vector([1.0, 0.0, 0.0]))
+160 -1
View File
@@ -16,7 +16,10 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
from __future__ import annotations
import ctypes
from typing import TYPE_CHECKING, Any, NamedTuple, Union
import bmesh
import bpy
@@ -30,8 +33,164 @@ import bonsai.core.root
import bonsai.core.tool
import bonsai.tool as tool
if TYPE_CHECKING:
from bonsai.bim.module.misc.prop import BIMMiscProperties
class Misc(bonsai.core.tool.Misc):
class BlenderCStructs:
class ListBase(ctypes.Structure):
_fields_ = [("first", ctypes.c_void_p), ("last", ctypes.c_void_p)]
class bUserMenu(ctypes.Structure):
pass
bUserMenu._fields_ = [
("next", ctypes.c_void_p),
("prev", ctypes.c_void_p),
("space_type", ctypes.c_int8),
("_pad0", ctypes.c_int8 * 7),
("context", ctypes.c_char * 64),
("items", ListBase),
]
class bUserMenuItem(ctypes.Structure):
_fields_ = [
("next", ctypes.c_void_p),
("prev", ctypes.c_void_p),
("ui_name", ctypes.c_char * 64),
("type", ctypes.c_int8),
("_pad0", ctypes.c_int8 * 7),
]
class bUserMenuItem_Op(ctypes.Structure):
pass
bUserMenuItem_Op._fields_ = [
("item", bUserMenuItem),
("op_idname", ctypes.c_char * 64),
("prop", ctypes.c_void_p),
("op_prop_enum", ctypes.c_char * 64),
("opcontext", ctypes.c_int8),
("_pad0", ctypes.c_int8 * 7),
]
class IDPropertyData(ctypes.Structure):
pass
IDPropertyData._fields_ = [
("pointer", ctypes.c_void_p),
("group", ListBase),
("children_map", ctypes.c_void_p),
("val", ctypes.c_int),
("val2", ctypes.c_int),
]
class IDProperty(ctypes.Structure):
pass
IDProperty._fields_ = [
("next", ctypes.c_void_p),
("prev", ctypes.c_void_p),
("type", ctypes.c_int8),
("subtype", ctypes.c_int8),
("flag", ctypes.c_int16),
("name", ctypes.c_char * 64),
("_pad0", ctypes.c_int8 * 4),
("data", IDPropertyData),
("len", ctypes.c_int),
("totallen", ctypes.c_int),
("ui_data", ctypes.c_void_p),
]
class QuickFavorites:
"""Blender doesn't provide a good way to access or manage Quick Favorites
from the Python API. We use c-structs (ctypes) to read data directly from memory.
This is fragile and can break between Blender versions. We only use this for
reading data and never writing, to avoid the possibility of corrupting user preferences.
"""
OFFSET_USER_MENUS: dict[tuple[int, int], int] = {
(4, 5): 10032,
(5, 0): 10032,
(5, 1): 10032,
}
@classmethod
def _read_idprop_value(cls, prop_ptr: int) -> Any:
IDP_STRING = 0
IDP_INT = 1
IDP_FLOAT = 2
IDP_BOOLEAN = 10
p = Misc.BlenderCStructs.IDProperty.from_address(prop_ptr)
if p.type == IDP_INT:
return p.data.val
elif p.type == IDP_BOOLEAN:
return bool(p.data.val)
elif p.type == IDP_FLOAT:
return ctypes.c_float.from_buffer_copy(ctypes.c_int(p.data.val)).value
elif p.type == IDP_STRING:
return ctypes.string_at(p.data.pointer).decode()
return f"<type={p.type}>"
@classmethod
def _read_idprop_group(cls, group_ptr: int) -> dict[str, Any]:
root = Misc.BlenderCStructs.IDProperty.from_address(group_ptr)
result: dict[str, Any] = {}
child_ptr = root.data.group.first
while child_ptr:
child = Misc.BlenderCStructs.IDProperty.from_address(child_ptr)
result[child.name.decode()] = cls._read_idprop_value(child_ptr)
child_ptr = child.next
return result
class QuickFavoritesOperator(NamedTuple):
ui_name: str
op_idname_py: str
props: dict[str, Any]
@classmethod
def get_quick_favorites(cls) -> list[QuickFavoritesOperator]:
assert bpy.context.preferences
blender_version = bpy.app.version[:2]
offset = cls.OFFSET_USER_MENUS[blender_version]
prefs_address = bpy.context.preferences.as_pointer()
user_menus = Misc.BlenderCStructs.ListBase.from_address(prefs_address + offset)
result: list[cls.QuickFavoritesOperator] = []
SPACE_VIEW3D = 4
node = user_menus.first
while node:
user_menu = Misc.BlenderCStructs.bUserMenu.from_address(node)
if user_menu.space_type != SPACE_VIEW3D:
node = user_menu.next
continue
item_ptr = user_menu.items.first
while item_ptr:
umi = Misc.BlenderCStructs.bUserMenuItem.from_address(item_ptr)
if umi.type == 2: # OPERATOR
op = Misc.BlenderCStructs.bUserMenuItem_Op.from_address(item_ptr)
props = cls._read_idprop_group(op.prop) if op.prop else {}
result.append(
cls.QuickFavoritesOperator(
ui_name=op.item.ui_name.decode(),
op_idname_py=tool.Blender.operator_idname_to_py(op.op_idname.decode()),
props=props,
)
)
item_ptr = umi.next
node = user_menu.next
return result
@classmethod
def get_misc_props(cls) -> BIMMiscProperties:
return bpy.context.scene.BIMMiscProperties
@classmethod
def get_object_storey(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
element = tool.Ifc.get_entity(obj)
+10 -5
View File
@@ -76,6 +76,7 @@ if TYPE_CHECKING:
BIMRailingProperties,
BIMRoofProperties,
BIMStairProperties,
BIMSverchokProperties,
BIMWindowProperties,
)
@@ -87,23 +88,27 @@ class Model(bonsai.core.tool.Model):
@classmethod
def get_door_props(cls, obj: bpy.types.Object) -> BIMDoorProperties:
return obj.BIMDoorProperties
return obj.BIMDoorProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_window_props(cls, obj: bpy.types.Object) -> BIMWindowProperties:
return obj.BIMWindowProperties
return obj.BIMWindowProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_stair_props(cls, obj: bpy.types.Object) -> BIMStairProperties:
return obj.BIMStairProperties
return obj.BIMStairProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_roof_props(cls, obj: bpy.types.Object) -> BIMRoofProperties:
return obj.BIMRoofProperties
return obj.BIMRoofProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_railing_props(cls, obj: bpy.types.Object) -> BIMRailingProperties:
return obj.BIMRailingProperties
return obj.BIMRailingProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_sverchok_props(cls, obj: bpy.types.Object) -> BIMSverchokProperties:
return obj.BIMSverchokProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_array_props(cls, obj: bpy.types.Object) -> BIMArrayProperties:
+4 -4
View File
@@ -123,11 +123,11 @@ class Nest(bonsai.core.tool.Nest):
return {"FINISHED"}
@classmethod
def disable_nest_mode(cls):
context = bpy.context
props = context.scene.BIMNestProperties
def disable_nest_mode(cls) -> None:
props = cls.get_nest_props()
for obj_prop in props.not_editing_objects:
obj = obj_prop.obj
assert obj and obj.original
obj.original.display_type = obj_prop.previous_display_type
element = tool.Ifc.get_entity(obj)
if not element:
@@ -135,7 +135,7 @@ class Nest(bonsai.core.tool.Nest):
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(props.editing_nest))
objs = [tool.Ifc.get_object(component) for component in components]
if context.space_data.local_view:
if bpy.context.space_data.local_view:
bpy.ops.view3d.localview()
props.in_nest_mode = False
+103 -6
View File
@@ -24,7 +24,15 @@ import shutil
from collections import defaultdict
from math import radians
from pathlib import Path
from typing import TYPE_CHECKING, Any, NamedTuple, Optional
from typing import (
TYPE_CHECKING,
Any,
Literal,
NamedTuple,
NotRequired,
Optional,
TypedDict,
)
import bpy
import ifcopenshell
@@ -35,7 +43,7 @@ import ifcopenshell.util.shape_builder
import numpy as np
import numpy.typing as npt
from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES
from mathutils import Matrix
from mathutils import Matrix, Vector
import bonsai.bim.schema
import bonsai.core.aggregate
@@ -600,6 +608,49 @@ class Project(bonsai.core.tool.Project):
class Link:
"""Tools for working with linked models."""
class LinkedObjectChunk(TypedDict):
"""There's actually no dictionary with those keys,
just using this class to document what keys we do assign to the objects
that represent chunks of the linked models.
"""
guids: list[str]
"""List of guids present in the object."""
guid_ids: list[int]
"""Number of faces that belong to each guid.
E.g. if chunk consists of two 12 tris cubes:
```
guids = ["aaa", "bbb"]
# Meaning object has 24 polygons
# [0;11] is part of "aaa", [12:23] is part of "bbb".
guid_ids = [12, 24]
```
"""
db: str
"""Absolute filepath to .ifc.cache.sqlite."""
ifc_filepath: str
"""Absolute filepath to .ifc."""
# Only added when object is queried.
selected_vertices: NotRequired[list[tuple[int, int, int]]]
selected_edges: NotRequired[list[tuple[int, int]]]
selected_tris: NotRequired[list[tuple[int, int, int]]]
hidden_indices: NotRequired[list[int]]
"""List of hidden indices in "guids".
Note that entire object also can be hidden by ``hide_viewport`` and then "hidden_indices" won't be set.
```
guids = ["aaa", "bbb", "ccc"]
# guid "bbb" is hidden.
hidden_indices = [1]
```
"""
@classmethod
def is_linked_element(cls, obj: bpy.types.Object) -> bool:
return "guids" in obj
@@ -670,11 +721,17 @@ class Project(bonsai.core.tool.Project):
return slice(guid_start_index, guid_end_index)
@classmethod
def hide_linked_element(cls, obj: bpy.types.Object, guid: str) -> None:
verts = tool.Project.Link.get_linked_element_verts(obj, guid)
def setup_hide_modifier(
cls,
obj: bpy.types.Object,
hide_type: Literal["hide_selected", "hide_unselected"],
) -> bpy.types.VertexGroup:
# `MeshPolygon.hide` works only in EDIT mode,
# so we use vertex groups + Mask modifier.
# But since for hiding we're modifying object in a linked model,
# then those changes are ephemeral and not fully supported by Blender
# e.g. they're not tracked by UNDO system.
MODIFIER_VG_NAME = "BBIM_HIDE_LINKED_GEOMETRY"
vertex_groups = obj.vertex_groups
@@ -688,8 +745,15 @@ class Project(bonsai.core.tool.Project):
modifier = modifiers.new(MODIFIER_VG_NAME, "MASK")
assert isinstance(modifier, bpy.types.MaskModifier)
modifier.vertex_group = MODIFIER_VG_NAME
modifier.invert_vertex_group = True
# Mask modifier by default shows only geometry from the provided vertex group.
modifier.invert_vertex_group = hide_type == "hide_selected"
return vertex_group
@classmethod
def hide_linked_element(cls, obj: bpy.types.Object, guid: str) -> None:
verts = tool.Project.Link.get_linked_element_verts(obj, guid)
vertex_group = cls.setup_hide_modifier(obj, "hide_selected")
vertex_group.add(verts, 1.0, "REPLACE")
hidden_indices: list[int] = list(obj.get("hidden_indices") or [])
@@ -706,12 +770,45 @@ class Project(bonsai.core.tool.Project):
assert col
for obj_ in col.objects:
obj_.hide_viewport = False
if "hidden_indices" not in obj_:
continue
obj_.vertex_groups.clear()
obj_.modifiers.clear()
del obj_["hidden_indices"]
@classmethod
def hide_all_elements_except(cls, link: Link, queried_obj: bpy.types.Object, queried_guid: str) -> None:
handle = tool.Project.get_link_empty_handle(link)
assert handle
col = handle.instance_collection
assert col
for obj_ in col.objects:
if "guids" not in obj_:
continue
if obj_ != queried_obj:
# Just hide the entire chunk, if queried guid is not part of it.
obj_.hide_viewport = True
continue
guids: list[str] = obj_["guids"]
queried_guid_index = guids.index(queried_guid)
# Get vertices for the queried element before modifying hidden_indices.
queried_verts = cls.get_linked_element_verts(obj_, queried_guid)
vertex_group = cls.setup_hide_modifier(obj_, "hide_unselected")
assert isinstance(mesh := obj_.data, bpy.types.Mesh)
# Clean up possible previously hidden elements.
vertex_group.remove(range(len(mesh.vertices)))
vertex_group.add(queried_verts, 1.0, "REPLACE")
# Mark all guids as hidden except the queried one.
obj_["hidden_indices"] = [i for i in range(len(guids)) if i != queried_guid_index]
@classmethod
def select_linked_element_geom(cls, obj: bpy.types.Object, guid: str) -> None:
slice_ = cls.get_linked_element_geom_slice(obj, guid)
+1
View File
@@ -24,6 +24,7 @@ from typing import TYPE_CHECKING, Any, Literal, Union
import bpy
import ifcopenshell
import ifcopenshell.util.unit
from lark import Lark, Transformer
import bonsai.bim.helper
+2 -2
View File
@@ -273,8 +273,8 @@ if BPY_IS_LOADED:
f"Couldn't find locale path in the source directory, creating dummy directory: {source_locale_path}.",
)
from ui_translate.settings import (
settings as ui_translate_settings, # pyright: ignore[reportMissingImports]
from ui_translate.settings import ( # pyright: ignore[reportMissingImports]
settings as ui_translate_settings,
)
from ui_translate.update_ui import ( # pyright: ignore[reportMissingImports]
UI_OT_i18n_updatetranslation_init_settings,
+23
View File
@@ -24,6 +24,7 @@ import bpy
import ifcopenshell
import pytest
import bonsai
import bonsai.core.tool
import bonsai.tool as tool
from bonsai.tool.blender import Blender as subject
@@ -144,3 +145,25 @@ class TestGetSelectedFiles(NewFile):
assert subject.get_selected_files(Path(g.name).parent, [file], use_relative_path=True) == [
Path(g.name).name
]
class TestGetDebugInfo(NewFile):
# Only keys that are safe to set if Bonsai fails to load.
EXPECTED_KEYS = {
"os",
"os_version",
"python_version",
"architecture",
"machine",
"processor",
"blender_version",
"bonsai_version",
"bonsai_commit_hash",
"bonsai_commit_date",
"last_actions",
"last_error",
}
def test_failed_to_load_returns_only_base_keys(self):
info = bonsai.get_debug_info(bonsai_failed_to_load=True)
assert set(info.keys()) == self.EXPECTED_KEYS
+6
View File
@@ -171,6 +171,12 @@ class TestScaleObjectToHeight(test.bim.bootstrap.NewFile):
assert list(obj.scale) == [1.0, 1.0, 1.0]
class TestQuickFavoritesOffsetUserMenus(test.bim.bootstrap.NewFile):
def test_current_blender_version_is_supported(self):
version = bpy.app.version[:2]
assert version in subject.QuickFavorites.OFFSET_USER_MENUS
class TestSplitObjectsWithCutter(test.bim.bootstrap.NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add()
-7
View File
@@ -403,13 +403,6 @@ class TestLoadingIfcSqlite(NewFile):
class TestGettingLinkedElementGeomSlice:
def __init__(self):
self.test_get_first_element()
self.test_get_middle_element()
self.test_skip_hidden_first_element()
self.test_skip_hidden_middle_element()
self.test_handle_hidden_non_first_element()
TEST_OBJ = {
"guids": ["aaa", "bbb", "ccc"],
"guid_ids": [5, 10, 15],
+1
View File
@@ -23,6 +23,7 @@ import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.root
import ifcopenshell.api.system
import ifcopenshell.util.system
import ifcopenshell.util.unit
import numpy as np
from mathutils import Euler, Matrix, Vector
+5
View File
@@ -19,6 +19,11 @@ dependencies = [
"ifcopenshell",
]
[project.optional-dependencies]
advanced = [
"scikit-learn",
]
[project.urls]
Homepage = "http://ifcopenshell.org"
Documentation = "https://docs.ifcopenshell.org"
@@ -29,10 +29,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcAxis2PlacementLinear* inst)
Logger::Error(std::runtime_error("Location must be IfcPointByDistanceExpression for IfcAxis2PlacementLinear"));
}
Eigen::Vector3d o, axis(0, 0, 1), refDirection;
taxonomy::matrix4::ptr m = taxonomy::cast<taxonomy::matrix4>(map(inst->Location()));
o = m->components().col(3).head<3>();
Eigen::Vector3d o = m->components().col(3).head<3>();
// From 8.9.3.4 IfcAxis2PlacementLinear there are 4 cases that need to be considered
// 1) Axis is given but not RefDirection
@@ -40,43 +38,12 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcAxis2PlacementLinear* inst)
// 3) Neither Axis or RefDirection are provided
// 4) Both Axis and RefDirection are provided
const bool hasAxis = inst->Axis() != nullptr;
const bool hasRef = inst->RefDirection() != nullptr;
Eigen::Vector3d z = inst->Axis() ? *taxonomy::cast<taxonomy::direction3>(map(inst->Axis()))->components_ : Eigen::Vector3d(0,0,1); // Axis is (0,0,1) when omitted
Eigen::Vector3d rd = inst->RefDirection() ? *taxonomy::cast<taxonomy::direction3>(map(inst->RefDirection()))->components_ : m->components().col(0).head<3>(); // RefDirection is the curve tangent when omitted
Eigen::Vector3d y = z.cross(rd);
Eigen::Vector3d x = y.cross(z);
/*
if (hasAxis != hasRef) {
Logger::Warning("Axis and RefDirection should be specified together", inst);
}
*/
if (hasAxis && !hasRef) {
taxonomy::direction3::ptr a = taxonomy::cast<taxonomy::direction3>(map(inst->Axis()));
axis = *a->components_;
refDirection = m->components().col(0).head<3>(); // RefDirection is the curve tangent when omitted
// refDirection is not necessarily orthogonal to axis.
// axis.cross(refDirection) gives y. y.cross(axis) gives x=refDirection
refDirection = axis.cross(refDirection).cross(axis);
} else if (!hasAxis && hasRef) {
taxonomy::direction3::ptr r = taxonomy::cast<taxonomy::direction3>(map(inst->RefDirection()));
refDirection = *r->components_;
Eigen::Vector3d up(0, 0, 1);
axis = refDirection.cross(up.cross(refDirection));
} else if (!hasAxis && !hasRef) {
refDirection = m->components().col(0).head<3>(); // RefDirection is the curve tangent when omitted
Eigen::Vector3d up(0, 0, 1);
axis = refDirection.cross(up.cross(refDirection));
} else {
taxonomy::direction3::ptr a = taxonomy::cast<taxonomy::direction3>(map(inst->Axis()));
axis = *a->components_;
taxonomy::direction3::ptr r = taxonomy::cast<taxonomy::direction3>(map(inst->RefDirection()));
refDirection = *r->components_;
refDirection = axis.cross(refDirection).cross(axis); // refDirection needs to be orthogonal to axis
}
// axis and refDirection need to be orthogonal
return taxonomy::make<taxonomy::matrix4>(o, axis, refDirection);
return taxonomy::make<taxonomy::matrix4>(o, z, x);
}
#endif
@@ -30,7 +30,7 @@ would be with the use of templates as shown below.
#include "ifcparse/Ifc4.h"
#include "ifcparse/Ifc4x3_add2.h"
#define IFC_SCHEMA_SEQ (4x3_rc2)(4)(2x3) // TODO: Enumerate through all IFC schemas you want to be able to process
#define IFC_SCHEMA_SEQ (4x3_add2)(4)(2x3) // TODO: Enumerate through all IFC schemas you want to be able to process
#define EXPAND_AND_CONCATENATE(elem) Ifc##elem
#define PROCESS_FOR_SCHEMA(r, data, elem) if (schema_version == BOOST_PP_STRINGIZE(elem)) { parseIfc<EXPAND_AND_CONCATENATE(elem)>(file); } else
@@ -20,11 +20,11 @@ from __future__ import annotations
import math
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
import bmesh
import bpy.types
import bmesh # pyright: ignore[reportMissingImports]
import bpy # pyright: ignore[reportMissingImports]
import numpy as np
import numpy.typing as npt
from mathutils import Matrix, Vector
from mathutils import Matrix, Vector # pyright: ignore[reportMissingImports]
import ifcopenshell.util.shape_builder
import ifcopenshell.util.unit
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Any, Optional
import ifcopenshell
if TYPE_CHECKING:
import bpy
import bpy # pyright: ignore[reportMissingImports]
def add_surface_textures(
@@ -33,14 +33,14 @@ def _has_occ():
# Previous versions (pythonocc<=0.17.3) are using just OCC.
try:
import OCC.Core.BRepTools
import OCC.Core.BRepTools # pyright: ignore[reportMissingImports]
return True
except ImportError:
pass
try:
import OCC.BRepTools # noqa: F401
import OCC.BRepTools # noqa: F401 # pyright: ignore[reportMissingImports]
return True
except ImportError:
@@ -28,7 +28,7 @@ from ..file import file
from . import has_occ
if TYPE_CHECKING:
from OCC.Core import TopoDS
from OCC.Core import TopoDS # pyright: ignore[reportMissingImports]
IteratorOutput = Union["ShapeElementType", "utils.shape_tuple"]
@@ -47,9 +47,9 @@ if has_occ:
from . import occ_utils as utils
try:
from OCC.Core import TopoDS
from OCC.Core import TopoDS # pyright: ignore[reportMissingImports]
except ImportError:
from OCC import TopoDS
from OCC import TopoDS # pyright: ignore[reportMissingImports]
def wrap_shape_creation(settings: settings, shape: ifcopenshell_wrapper.Element):
if getattr(settings, "use_python_opencascade", False):
@@ -26,17 +26,17 @@ import warnings
from collections.abc import Iterable
from typing import NamedTuple, Union
import OCC
import OCC # pyright: ignore[reportMissingImports]
from typing_extensions import assert_never
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
try:
from OCC.Core import AIS, BRepTools, Graphic3d, Quantity, TopoDS, V3d, gp
from OCC.Core import AIS, BRepTools, Graphic3d, Quantity, TopoDS, V3d, gp # pyright: ignore[reportMissingImports]
USE_OCCT_HANDLE = False
except ImportError:
from OCC import AIS, BRepTools, Graphic3d, Quantity, TopoDS, V3d, gp
from OCC import AIS, BRepTools, Graphic3d, Quantity, TopoDS, V3d, gp # pyright: ignore[reportMissingImports]
USE_OCCT_HANDLE = True
@@ -68,7 +68,7 @@ DEFAULT_STYLES = {
def initialize_display():
import OCC.Display.SimpleGui
import OCC.Display.SimpleGui # pyright: ignore[reportMissingImports]
global handle, main_loop, add_menu, add_function_to_menu
handle, main_loop, add_menu, add_function_to_menu = OCC.Display.SimpleGui.init_display()
@@ -40,7 +40,7 @@ if TYPE_CHECKING:
# NOTE: mathutils is never used at runtime in ifcopenshell,
# only for type checking to ensure methods are compatible with
# Blender vectors.
from mathutils import Vector
from mathutils import Vector # pyright: ignore[reportMissingImports]
# Support both numpy arrays and python sequences as inputs.
VectorType = Union[Sequence[float], Vector, np.ndarray]
@@ -60,7 +60,6 @@ from typing import TYPE_CHECKING, Any, Optional, Union
import ifcopenshell
import ifcopenshell.express.rule_executor
import ifcopenshell.ifcopenshell_wrapper
import ifcopenshell.ifcopenshell_wrapper as W
if TYPE_CHECKING:
import ifcopenshell.simple_spf
@@ -705,10 +704,10 @@ def validate_ifc_header(
log_error(header_entity, name, index, STRING_TYPE, type(value).__name__)
# Ignore header.file_schema as file won't load to IfcOpenShell with invalid file_schema.
file_description: W.FileDescription = header.file_description
file_description = header.file_description
validate_attribute(file_description, "description", 0, aggregate=True)
validate_attribute(file_description, "implementation_level", 1)
file_name: W.FileName = header.file_name
file_name = header.file_name
validate_attribute(file_name, "name", 0)
validate_attribute(file_name, "time_stamp", 1)
validate_attribute(file_name, "author", 2, aggregate=True)
@@ -17,8 +17,18 @@ REPO_PATH_SRC = REPO_PATH / "src"
assert REPO_PATH_SRC.exists(), f"'{REPO_PATH_SRC}' doesn't exist."
packages = {
"bcf": REPO_PATH_SRC / "bcf" / "bcf",
"bsdd.py": REPO_PATH_SRC / "bsdd" / "bsdd.py",
"ifc4d": REPO_PATH_SRC / "ifc4d" / "ifc4d",
"ifc5d": REPO_PATH_SRC / "ifc5d" / "ifc5d",
"ifccityjson": REPO_PATH_SRC / "ifccityjson" / "ifccityjson",
"ifcclash": REPO_PATH_SRC / "ifcclash" / "ifcclash",
"ifccsv.py": REPO_PATH_SRC / "ifccsv" / "ifccsv.py",
"ifcdiff.py": REPO_PATH_SRC / "ifcdiff" / "ifcdiff.py",
"ifcfm": REPO_PATH_SRC / "ifcfm" / "ifcfm",
"ifcopenshell": REPO_PATH_SRC / "ifcopenshell-python" / "ifcopenshell",
"ifcpatch": REPO_PATH_SRC / "ifcpatch" / "ifcpatch",
"ifctester": REPO_PATH_SRC / "ifctester" / "ifctester",
}
@@ -35,10 +45,12 @@ for package, repo_package_path in packages.items():
continue
package_path.unlink()
if package_path.exists():
# I guess it's a directory.
shutil.rmtree(package_path)
if package_path.is_dir():
shutil.rmtree(package_path)
else:
package_path.unlink()
print(f"Symlinking {package_path} -> {repo_package_path}")
package_path.symlink_to(repo_package_path, True)
package_path.symlink_to(repo_package_path, target_is_directory=repo_package_path.is_dir())
PACKAGE_PATH = SITE / "ifcopenshell"
+2 -2
View File
@@ -215,11 +215,11 @@ def _extract_docs(cls: type, method_name: str, boilerplate_args: Union[Sequence[
input_data = inputs[input_name]
# E.g. list[str].
if isinstance(type_hint, typing.GenericAlias):
if isinstance(type_hint, typing.GenericAlias): # pyright: ignore[reportAttributeAccessIssue]
input_data["generic_type"] = type_hint.__name__
type_hint = typing.get_args(type_hint)[0]
if isinstance(type_hint, typing._UnionGenericAlias):
if isinstance(type_hint, typing._UnionGenericAlias): # pyright: ignore[reportAttributeAccessIssue]
inputs[input_name]["type"] = [t.__name__ for t in typing.get_args(type_hint)]
elif type_hint.__name__ == "Literal":
inputs[input_name]["type"] = "Literal"
@@ -67,9 +67,9 @@ class Patcher:
def patch(self) -> None:
import bonsai.tool as tool
import bpy
import bpy # pyright: ignore[reportMissingImports]
import ifcopenshell.util.element
from mathutils import Matrix, Vector
from mathutils import Matrix, Vector # pyright: ignore[reportMissingImports]
if len(bpy.data.objects) > 0:
bpy.data.batch_remove(bpy.data.objects)
@@ -18,11 +18,15 @@
import logging
from typing import Optional
from typing import Optional, TYPE_CHECKING
import ifcopenshell
import ifcopenshell.util.shape_builder
if TYPE_CHECKING:
import bpy # pyright: ignore[reportMissingImports]
class Patcher:
def __init__(
@@ -109,12 +113,14 @@ class Patcher:
self.should_create_edges = should_create_edges
def patch(self) -> None:
import bmesh
import bmesh # pyright: ignore[reportMissingImports]
import bonsai.tool as tool
import bpy
import ifcopenshell.util.shape_builder
import bpy # pyright: ignore[reportMissingImports]
import ifcopenshell.util.schema
import ifcopenshell.util.unit
bpy.context.scene.BIMProjectProperties.should_use_native_meshes = True
props = tool.Project.get_project_props()
props.should_use_native_meshes = True
bpy.ops.bim.load_project(filepath=self.filepath)
old_history_size = tool.Ifc.get().history_size
@@ -125,7 +131,7 @@ class Patcher:
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
for obj in bpy.data.objects:
if not obj.BIMObjectProperties.ifc_definition_id or not obj.data:
if not tool.Blender.get_ifc_definition_id(obj) or not obj.data:
continue
if not obj.data.polygons:
continue
@@ -159,14 +165,14 @@ class Patcher:
self.file = tool.Ifc.get()
def create_edges(self, obj):
import bmesh
def create_edges(self, obj: bpy.types.Object) -> None:
import bmesh # pyright: ignore[reportMissingImports]
import bonsai.tool as tool
import bpy
import bpy # pyright: ignore[reportMissingImports]
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.schema
element = tool.Ifc.get_entity(obj)
data = obj.data
@@ -224,16 +230,16 @@ class Patcher:
ifcopenshell.util.schema.reassign_class(tool.Ifc.get(), element2, "IfcVirtualElement")
bm.free()
def create_face_sampleable_object(self, obj):
def create_face_sampleable_object(self, obj: bpy.types.Object) -> None:
# No sharp faces
from math import degrees
import bmesh
import bmesh # pyright: ignore[reportMissingImports]
import bonsai.tool as tool
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
print("working on ", obj.name)
element = tool.Ifc.get_entity(obj)
@@ -271,17 +277,17 @@ class Patcher:
bm.free()
def create_edge_sampleable_object(self, obj):
def create_edge_sampleable_object(self, obj: bpy.types.Object) -> None:
# This is crazy but we need a sharp face per island
from math import degrees, radians, sin
import bmesh
import bmesh # pyright: ignore[reportMissingImports]
import bonsai.tool as tool
import ifcopenshell.api.geometry
import ifcopenshell.api.root
import ifcopenshell.util.element
import ifcopenshell.util.representation
from mathutils import Matrix
import ifcopenshell.util.shape_builder
from mathutils import Matrix # pyright: ignore[reportMissingImports]
# Get the active object (assumed to have a mesh)
mesh = obj.data
@@ -430,7 +436,6 @@ class Patcher:
bm.free()
print("Added a triangle to", islands_count, "mesh island(s).")
return
def create_curves_from_curve_ifc2x3(
self, is_2d: bool = False, curve_object_data=None
@@ -80,9 +80,9 @@ class Patcher:
def patch(self) -> None:
from math import degrees
import bmesh
import bmesh # pyright: ignore[reportMissingImports]
import bonsai.tool as tool
import bpy
import bpy # pyright: ignore[reportMissingImports]
props = tool.Project.get_project_props()
props.should_use_native_meshes = True
+6 -6
View File
@@ -1533,9 +1533,9 @@
}
},
"node_modules/devalue": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz",
"integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==",
"version": "5.6.4",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz",
"integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==",
"license": "MIT"
},
"node_modules/engine.io-client": {
@@ -2951,9 +2951,9 @@
}
},
"node_modules/tar": {
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
"integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz",
"integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
+14 -4
View File
@@ -5,11 +5,21 @@ but also archives them to '~/outputs'.
"""
import os
import platform
import subprocess
import zipfile
from pathlib import Path
from zipfile import ZipFile
def is_arm64() -> bool:
arch = os.environ.get("TARGET_ARCH", "").lower()
if arch in ("arm64", "aarch64"):
return True
if arch in ("x64", "amd64", "x86_64"):
return False
return platform.machine().lower() in ("arm64", "aarch64")
assert Path.cwd() == Path(__file__).parent, "Run this script from the 'win' directory."
PYTHON_VERSIONS = ["3.10.3", "3.11.8", "3.12.1", "3.13.0", "3.14.0"]
@@ -23,7 +33,7 @@ else:
OUTPUT_DIR = Path.home() / "output"
OUTPUT_DIR.mkdir(exist_ok=True)
print("Output directory:", OUTPUT_DIR)
ZIP_TEMPLATE = f"{{package_name}}-v{VERSION}-{SHA}-win64.zip"
ZIP_TEMPLATE = f"{{package_name}}-v{VERSION}-{SHA}-{'win-arm64' if is_arm64() else 'win64'}.zip"
def run(command: list[str]) -> None:
@@ -52,7 +62,7 @@ def build() -> None:
os.environ["PYTHON_VERSION"] = python_version
print(f"Building for Python {python_version}...")
subprocess.run(
[str(REPO_WIN / "build-deps.cmd"), "vs2022-x64", "Release"],
[str(REPO_WIN / "build-deps.cmd"), "vs2022-ARM64" if is_arm64() else "vs2022-x64", "Release"],
check=True,
text=True,
input="y\n",
@@ -61,13 +71,13 @@ def build() -> None:
run(
[
str(REPO_WIN / "run-cmake.bat"),
"vs2022-x64",
"vs2022-ARM64" if is_arm64() else "vs2022-x64",
"-DENABLE_BUILD_OPTIMIZATIONS=ON",
"-DGLTF_SUPPORT=ON",
]
)
restore_env(*OLD_ADD_COMMIT_SHA)
run([str(REPO_WIN / "install-ifcopenshell.bat"), "vs2022-x64", "Release"])
run([str(REPO_WIN / "install-ifcopenshell.bat"), "vs2022-ARM64" if is_arm64() else "vs2022-x64", "Release"])
def archive_executables() -> None:
+43 -12
View File
@@ -132,7 +132,7 @@ call cecho.cmd 0 10 "Script configuration:"
call cecho.cmd 0 13 "* CMake Generator`t= '`"%GENERATOR%`'`t
echo - Passed to CMake -G option.
call cecho.cmd 0 13 "* Target Architecture`t= %TARGET_ARCH%"
echo - Whether were doing 32-bit (x86) or 64-bit (x64) build.
echo - Whether were doing 32-bit (x86) or 64-bit (x64, arm64) build.
call cecho.cmd 0 13 "* Target Platform`t= %VS_PLATFORM%"
echo - Passed to CMake -A option.
call cecho.cmd 0 13 "* Target Toolset`t= %VS_TOOLSET%"
@@ -187,8 +187,15 @@ IF DEFINED PYTHON_VERSION (
)
:: VERSION DERIVATIONS
for /f "tokens=1,2,3 delims=." %%a in ("%PYTHON_VERSION%") do (
set PY_VER_MAJOR_MINOR=%%a%%b
)
IF "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
set PYTHONHOME=%DEPS_DIR%\python.%PYTHON_VERSION%\tools
IF /I "%TARGET_ARCH%"=="arm64" (
set PYTHONHOME=%DEPS_DIR%\pythonarm64.%PYTHON_VERSION%\tools
) ELSE (
set PYTHONHOME=%DEPS_DIR%\python.%PYTHON_VERSION%\tools
)
)
:: Cache last used CMake generator and configurable dependency dirs for other scripts to use
@@ -312,6 +319,11 @@ powershell -c "get-content %~dp0patches\mpir.patch | %%{$_ -replace \"sdk\",\"%U
IF NOT %ERRORLEVEL%==0 GOTO :Error
if NOT "%USE_STATIC_RUNTIME%"=="FALSE" git apply "%~dp0patches\mpir_runtime.patch" --unidiff-zero --ignore-whitespace
IF NOT %ERRORLEVEL%==0 GOTO :Error
IF /I "%VS_PLATFORM%"=="ARM64" (
echo "Applying ARM64 Patches for Mpir"
git apply "%~dp0patches\mpir-arm64-changes.patch" --unidiff-zero --ignore-whitespace
)
IF NOT %ERRORLEVEL%==0 GOTO :Error
cd msvc
cd vs%VS_VER:~2,2%
call .\msbuild.bat gc LIB %VS_PLATFORM% %DEBUG_OR_RELEASE%
@@ -338,6 +350,10 @@ powershell -c "get-content %~dp0patches\mpfr.patch | %%{$_ -replace \"sdk\",\"%U
IF NOT %ERRORLEVEL%==0 GOTO :Error
if NOT "%USE_STATIC_RUNTIME%"=="FALSE" git apply "%~dp0patches\mpfr_runtime.patch" --unidiff-zero --ignore-whitespace
IF NOT %ERRORLEVEL%==0 GOTO :Error
IF /I "%VS_PLATFORM%"=="ARM64" (
echo "Applying ARM64 Patches for Mpfr"
git apply "%~dp0patches\mpfr-arm64-changes.patch" --unidiff-zero --ignore-whitespace
)
if "%VS_VER%"=="2017" (
set mpfr_sln=build.vc15
set orig_platform_toolset=v141
@@ -406,6 +422,7 @@ cd "%DEPS_DIR%"
call :DownloadFile https://github.com/boostorg/boost/releases/download/boost-%BOOST_VERSION%/%BOOST_ZIP% "%DEPS_DIR%" %BOOST_ZIP%
IF NOT %ERRORLEVEL%==0 GOTO :Error
cd "%DEPS_DIR%"
call :ExtractArchive %BOOST_ZIP% "%DEPS_DIR%" %DEPENDENCY_DIR%
IF NOT %ERRORLEVEL%==0 GOTO :Error
@@ -429,13 +446,21 @@ if not exist "%DEPENDENCY_DIR%\project-config.jam". (
IF NOT %ERRORLEVEL%==0 GOTO :Error
)
if /I "%TARGET_ARCH%"=="x64" (
set B2_ARCH_FEATURE=x86
) else if /I "%TARGET_ARCH%"=="arm64" (
set B2_ARCH_FEATURE=arm
) else (
echo "Failed to identify architecture"
GOTO :Error
)
set BOOST_LIBS=--with-system --with-regex --with-thread --with-program_options --with-date_time --with-iostreams --with-filesystem
:: NOTE Boost is fast to build with limited set of libraries so build it always.
cd "%DEPENDENCY_DIR%"
call cecho.cmd 0 13 "Building %DEPENDENCY_NAME% %BOOST_LIBS% Please be patient, this will take a while."
IF EXIST "%DEPENDENCY_DIR%\bin.v2\project-cache.jam" del "%DEPENDENCY_DIR%\bin.v2\project-cache.jam"
call .\b2 toolset=%BOOST_TOOLSET% runtime-link=shared address-model=%ARCH_BITS% --abbreviate-paths -j%IFCOS_NUM_BUILD_PROCS% ^
call .\b2 toolset=%BOOST_TOOLSET% architecture=%B2_ARCH_FEATURE% runtime-link=shared address-model=%ARCH_BITS% --abbreviate-paths -j%IFCOS_NUM_BUILD_PROCS% ^
variant=%DEBUG_OR_RELEASE_LOWERCASE% %BOOST_WIN_API% %BOOST_LIBS% stage --stagedir=%DEPENDENCY_INSTALL_DIR%
IF NOT %ERRORLEVEL%==0 GOTO :Error
@@ -561,9 +586,10 @@ SET COMPILE_WITH_WPO=FALSE
:Python
set DEPENDENCY_NAME=Python %PYTHON_VERSION%
set DEPENDENCY_DIR=N/A
set PYTHON_AMD64_POSTFIX=-amd64
IF NOT %TARGET_ARCH%==x64 set PYTHON_AMD64_POSTFIX=
set PYTHON_INSTALLER=python-%PYTHON_VERSION%%PYTHON_AMD64_POSTFIX%.exe
set PYTHON_AMD64_POSTFIX=
IF /I "%TARGET_ARCH%"=="x64" set "PYTHON_AMD64_POSTFIX=-amd64"
IF /I "%TARGET_ARCH%"=="arm64" set "PYTHON_AMD64_POSTFIX=-arm64"
set "PYTHON_INSTALLER=python-%PYTHON_VERSION%%PYTHON_AMD64_POSTFIX%.exe"
IF NOT "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
call cecho.cmd 0 13 "IFCOS_INSTALL_PYTHON not 'TRUE', skipping installation of Python."
@@ -571,22 +597,25 @@ IF NOT "%IFCOS_INSTALL_PYTHON%"=="TRUE" (
)
:: nuget doesn't support providing architecture for packages.
if NOT %TARGET_ARCH%==x64 (
IF /I NOT "%TARGET_ARCH%"=="x64" IF /I NOT "%TARGET_ARCH%"=="arm64" (
call cecho.cmd 0 12 "Automatic insallation of Python for x86 builds is not supported,"
call cecho.cmd 0 12 "please install Python %PYTHON_VERSION% manually and ensure that it is available in PATH."
call cecho.cmd 0 12 "https://www.python.org/ftp/python/%PYTHON_VERSION%/%PYTHON_INSTALLER%"
goto :Error
)
if EXIST "%PYTHONHOME%" (
echo Found existing '%PYTHONHOME%', skipping installation.
goto :SWIG
)
"%NUGET_EXE%" install Python -Version %PYTHON_VERSION% -OutputDirectory "%DEPS_DIR%"
IF NOT %ERRORLEVEL%==0 GOTO :Error
IF /I "%TARGET_ARCH%"=="x64" (
"%NUGET_EXE%" install Python -Version %PYTHON_VERSION% -OutputDirectory "%DEPS_DIR%"
IF NOT %ERRORLEVEL%==0 GOTO :Error
) ELSE (
"%NUGET_EXE%" install pythonarm64 -Version %PYTHON_VERSION% -OutputDirectory "%DEPS_DIR%"
IF NOT %ERRORLEVEL%==0 GOTO :Error
)
:SWIG
set DEPENDENCY_NAME=SWIG
@@ -723,6 +752,9 @@ call :RunCMake -DCMAKE_INSTALL_PREFIX="%INSTALL_DIR%\%DEPENDENCY_INSTALL_NAME%"
-DWITH_CORE_TOOLS=OFF ^
-DROCKSDB_BUILD_SHARED=OFF ^
-DWITH_ZSTD=On ^
-DZSTD_INCLUDE_DIR="%ZSTD_INCLUDE%" ^
-DZSTD_LIBRARY_DEBUG="%ZSTD_LIB_DEBUG%" ^
-DZSTD_LIBRARY_RELEASE="%ZSTD_LIB_RELEASE%" ^
-DPORTABLE=1 ^
-DCMAKE_DEBUG_POSTFIX="_d"
IF NOT %ERRORLEVEL%==0 GOTO :Error
@@ -960,4 +992,3 @@ echo - https://msdn.microsoft.com/en-us/library/ms229859(v=vs.110).aspx
echo.
echo NB: This script needs to be ran from the directory directly containing it.
echo.
File diff suppressed because it is too large Load Diff
+519
View File
@@ -0,0 +1,519 @@
diff --git a/msvc/postbuild.bat b/msvc/postbuild.bat
index 1c301d1a..7d5362be 100644
--- a/msvc/postbuild.bat
+++ b/msvc/postbuild.bat
@@ -1,6 +1,6 @@
@echo off
rem %1 = full target path
-rem %2 = last two digits of the Visual Studio version number (e.g. 17)
+rem %2 = last two digits of the Visual Studio version number (e.g. 17 or 22)
set vs_ver=%2
set str=%~1
@@ -20,11 +20,13 @@ goto dele
set str=%str_confirmed%
echo "Final subpath %str%"
-rem we now have: msvc.\vs<nn>\<project_directory>\<win32|x64>\<debug|release>\mpir.<lib|dll>
-rem extract: project_directory, platform (plat=<win32|x64>), configuration (conf=<debug|release>) and file name
+rem we now have: msvc\vs<nn>\<project>\<plat>\<conf>\mpir(.xx).<lib|dll>
+rem extract: project_directory, platform (plat), configuration (conf) and file name
set file=
-for /f "tokens=1,2,3,4,5,6 delims=\" %%a in ("%str%") do set tloc=%%c&set plat=%%d&set conf=%%e&set file=%%f
+for /f "tokens=1,2,3,4,5,6 delims=\" %%a in ("%str%") do (
+ set tloc=%%c&set plat=%%d&set conf=%%e&set file=%%f
+)
if /i "%file%" NEQ "" (goto next)
call :seterr & echo ERROR: %1 is not supported & exit /b %errorlevel%
@@ -42,9 +44,9 @@ call :seterr & echo "postbuild copy error ERROR: target=%tloc%, plat=%plat%, con
:is2nd:
rem set the target and final binary output directories
-set tgt_dir="vs%vs_ver%\%loc%%plat%\%conf%\"
-set bin_dir="..\%extn%\%plat%\%conf%\"
-set hdr_dir="..\%extn%\%plat%\%conf%\"
+set "tgt_dir=vs%vs_ver%\%loc%%plat%\%conf%"
+set "bin_dir=..\%extn%\%plat%\%conf%"
+set "hdr_dir=..\%extn%\%plat%\%conf%"
rem output parametrers for the MPIR tests
if /i "%filename%" EQU "mpirxx" goto skip
@@ -66,43 +68,42 @@ rem %1 = target (build output) directory
rem %2 = binary destination directory
rem %3 = configuration (debug/release)
rem %4 = library (lib/dll)
-rem %5 = file name
+rem %5 = file name (mpir | mpirxx)
:copyb
if "%4" EQU "dll" (
- copy %1mpir.dll %2mpir.dll > nul 2>&1
- copy %1mpir.exp %2mpir.exp > nul 2>&1
- copy %1mpir.lib %2mpir.lib > nul 2>&1
- if exist %1mpir.pdb (copy %1mpir.pdb %2mpir.pdb > nul 2>&1)
+ copy "%~1\mpir.dll" "%~2\mpir.dll" > nul 2>&1
+ copy "%~1\mpir.exp" "%~2\mpir.exp" > nul 2>&1
+ copy "%~1\mpir.lib" "%~2\mpir.lib" > nul 2>&1
+ if exist "%~1\mpir.pdb" copy "%~1\mpir.pdb" "%~2\mpir.pdb" > nul 2>&1
) else if "%4" EQU "lib" (
if "%5" EQU "mpir" (
- if exist %1mpir.lib (
- copy %1mpir.lib %2mpir.lib > nul 2>&1
- if exist %1mpir.pdb (copy %1mpir.pdb %2mpir.pdb > nul 2>&1)
+ if exist "%~1\mpir.lib" (
+ copy "%~1\mpir.lib" "%~2\mpir.lib" > nul 2>&1
+ if exist "%~1\mpir.pdb" copy "%~1\mpir.pdb" "%~2\mpir.pdb" > nul 2>&1
+ ) else (
+ echo "Not Found MPIR at location" "%~1\mpir.lib"
)
) else if "%5" EQU "mpirxx" (
- if exist %1mpirxx.lib (
- copy %1mpirxx.lib %2mpirxx.lib > nul 2>&1
- if exist %1mpirxx.pdb (copy %1mpirxx.pdb %2mpirxx.pdb > nul 2>&1)
+ if exist "%~1\mpirxx.lib" (
+ copy "%~1\mpirxx.lib" "%~2\mpirxx.lib" > nul 2>&1
+ if exist "%~1\mpirxx.pdb" copy "%~1\mpirxx.pdb" "%~2\mpirxx.pdb" > nul 2>&1
)
)
) else (
- call :seterr & echo ERROR: illegal library type %4 & exit /b %errorlevel%
+ call :seterr & echo ERROR: illegal library type %4 & exit /b %errorlevel%
)
-
-rem set configuration for the tests
-call gen_test_config_props %plat% %conf% %vs_ver%
exit /b 0
rem copy headers to final destination directory
:copyh
-copy ..\config.h %1config.h > nul 2>&1
-copy ..\gmp-mparam.h %1gmp-mparam.h > nul 2>&1
-copy ..\mpir.h %1mpir.h > nul 2>&1
-copy ..\mpir.h %1gmp.h > nul 2>&1
-copy ..\gmp-impl.h %1gmp-impl.h > nul 2>&1
-copy ..\longlong.h %1longlong.h > nul 2>&1
-copy ..\mpirxx.h %1mpirxx.h > nul 2>&1
-copy ..\mpirxx.h %1gmpxx.h > nul 2>&1
+copy "..\config.h" "%~1\config.h" > nul 2>&1
+copy "..\gmp-mparam.h" "%~1\gmp-mparam.h" > nul 2>&1
+copy "..\mpir.h" "%~1\mpir.h" > nul 2>&1
+copy "..\mpir.h" "%~1\gmp.h" > nul 2>&1
+copy "..\gmp-impl.h" "%~1\gmp-impl.h" > nul 2>&1
+copy "..\longlong.h" "%~1\longlong.h" > nul 2>&1
+copy "..\mpirxx.h" "%~1\mpirxx.h" > nul 2>&1
+copy "..\mpirxx.h" "%~1\gmpxx.h" > nul 2>&1
exit /b 0
:seterr
diff --git a/msvc/prebuild.bat b/msvc/prebuild.bat
index 243d054f..c1d49c67 100644
--- a/msvc/prebuild.bat
+++ b/msvc/prebuild.bat
@@ -1,29 +1,56 @@
@echo off
-rem %1 = mpn directory (generic, x86\... or x86_64\...)
-rem %2 = platform (win32 or x64)
+rem %1 = mpn directory (generic, x86\... or x86_64\... or arm64\...)
+rem %2 = platform (win32, x64/amd64, or arm64)
rem %3 = MSVC version number (e.g. 14)
-if /i "%2" EQU "win32" ((set platform=win32) & (set bdir=x86w\)) else ((set platform=x64) & (set bdir=x86_64w\))
-set sdir=
-if /i "%1" EQU "gc" ((set sdir=generic) & (set bdir=generic)) else (set sdir=%bdir%%1)
-if not exist ..\mpn\%sdir% (call :seterr & echo ERROR: %1 is not supported & exit /b %errorlevel%)
+set "platform="
+set "bdir="
+
+if /i "%2"=="win32" (
+ set "platform=win32"
+ set "bdir=x86w\"
+) else if /i "%2"=="x64" (
+ set "platform=x64"
+ set "bdir=x86_64w\"
+) else if /i "%2"=="arm64" (
+ set "platform=arm64"
+ set "bdir=arm64w\"
+) else (
+ call :seterr & echo ERROR: Unsupported platform "%2" (expected win32, x64/amd64, or arm64) & exit /b %errorlevel%
+)
+
+set "sdir="
+if /i "%1"=="gc" (
+ rem Generic C (portable) implementation
+ set "sdir=generic"
+ set "bdir=generic"
+) else (
+ set "sdir=%bdir%%~1"
+)
+
+if not exist "..\mpn\%sdir%" (
+ call :seterr & echo ERROR: %1 is not supported & exit /b %errorlevel%
+)
echo building MPIR for %1 (%platform%) from directory mpn\%sdir%
-set cdir=vs%3\cdata\mpn\%sdir%\
-set sdir=..\mpn\%sdir%\
-set bdir=..\mpn\%bdir%\
+set "cdir=vs%3\cdata\mpn\%sdir%\"
+set "sdir=..\mpn\%sdir%\"
+set "bdir=..\mpn\%bdir%\"
call gen_mpir_h %platform%
call gen_config_h %cdir%
-if exist %sdir%\gmp-mparam.h (call out_copy_rename %sdir%\gmp-mparam.h ..\ gmp-mparam.h) else (
- call out_copy_rename %bdir%\gmp-mparam.h ..\ gmp-mparam.h)
+if exist "%sdir%\gmp-mparam.h" (
+ call out_copy_rename "%sdir%\gmp-mparam.h" "..\" "gmp-mparam.h"
+) else (
+ call out_copy_rename "%bdir%\gmp-mparam.h" "..\" "gmp-mparam.h"
+)
-type ..\longlong_pre.h >tmp.h
-type %bdir%\longlong_inc.h >>tmp.h
-type ..\longlong_post.h >>tmp.h
-call out_copy_rename tmp.h ..\ longlong.h
+type "..\longlong_pre.h" > tmp.h
+type "%bdir%\longlong_inc.h" >> tmp.h
+type "..\longlong_post.h" >> tmp.h
+call out_copy_rename tmp.h "..\" "longlong.h"
del tmp.h
exit /b 0
diff --git a/msvc/vs22/lib_mpir_cxx/lib_mpir_cxx.vcxproj b/msvc/vs22/lib_mpir_cxx/lib_mpir_cxx.vcxproj
index 0aca0c6e..e4bcf619 100644
--- a/msvc/vs22/lib_mpir_cxx/lib_mpir_cxx.vcxproj
+++ b/msvc/vs22/lib_mpir_cxx/lib_mpir_cxx.vcxproj
@@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="19.30.30401" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
@@ -40,11 +48,21 @@
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
@@ -58,17 +76,27 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
<Import Project="..\..\mpir_release_lib.props" />
</ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ <Import Project="..\..\mpir_release_lib.props" />
+ </ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
<Import Project="..\..\mpir_debug_lib.props" />
</ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ <Import Project="..\..\mpir_debug_lib.props" />
+ </ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.21006.1</_ProjectFileVersion>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">mpirxx</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">mpirxx</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">mpirxx</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">mpirxx</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">mpirxx</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">mpirxx</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
@@ -99,6 +127,17 @@ postbuild "$(TargetPath)" 22
</ClCompile>
<PostBuildEvent>
<Command>cd ..\..\
+postbuild "$(TargetPath)" 22
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;HAVE_CONFIG_H;_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <PostBuildEvent>
+ <Command>cd ..\..\
postbuild "$(TargetPath)" 22
</Command>
</PostBuildEvent>
@@ -110,6 +149,17 @@ postbuild "$(TargetPath)" 22
</ClCompile>
<PostBuildEvent>
<Command>cd ..\..\
+postbuild "$(TargetPath)" 22
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN32;_LIB;HAVE_CONFIG_H;_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <PostBuildEvent>
+ <Command>cd ..\..\
postbuild "$(TargetPath)" 22
</Command>
</PostBuildEvent>
@@ -141,7 +191,7 @@ postbuild "$(TargetPath)" 22
<ItemGroup>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-<ItemGroup>
+ <ItemGroup>
<None Include="..\..\..\gmp-h.in" />
- </ItemGroup>
-</Project>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/msvc/vs22/lib_mpir_gc/lib_mpir_gc.vcxproj b/msvc/vs22/lib_mpir_gc/lib_mpir_gc.vcxproj
index 350ee110..faf49ac3 100644
--- a/msvc/vs22/lib_mpir_gc/lib_mpir_gc.vcxproj
+++ b/msvc/vs22/lib_mpir_gc/lib_mpir_gc.vcxproj
@@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="19.30.30401" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
@@ -40,11 +48,21 @@
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
@@ -58,23 +76,32 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
<Import Project="..\..\mpir_release_lib.props" />
</ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ <Import Project="..\..\mpir_release_lib.props" />
+ </ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
<Import Project="..\..\mpir_debug_lib.props" />
</ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ <Import Project="..\..\mpir_debug_lib.props" />
+ </ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.21006.1</_ProjectFileVersion>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">mpir</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">mpir</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">mpir</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">mpir</TargetName>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">mpir</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">mpir</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<PreBuildEvent>
<Command>cd ..\..\
-prebuild gc Win32 22
- </Command>
+prebuild gc $(Platform) 22</Command>
</PreBuildEvent>
<ClCompile>
<AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
@@ -82,15 +109,13 @@ prebuild gc Win32 22
</ClCompile>
<PostBuildEvent>
<Command>cd ..\..\
-postbuild "$(TargetPath)" 22
- </Command>
+postbuild "$(TargetPath)" 22</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<PreBuildEvent>
<Command>cd ..\..\
-prebuild gc Win32 22
- </Command>
+prebuild gc $(Platform) 22</Command>
</PreBuildEvent>
<ClCompile>
<AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
@@ -98,15 +123,27 @@ prebuild gc Win32 22
</ClCompile>
<PostBuildEvent>
<Command>cd ..\..\
-postbuild "$(TargetPath)" 22
- </Command>
+postbuild "$(TargetPath)" 22</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PreBuildEvent>
<Command>cd ..\..\
-prebuild gc x64 22
- </Command>
+prebuild gc $(Platform) 22</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;HAVE_CONFIG_H;_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <PostBuildEvent>
+ <Command>cd ..\..\
+postbuild "$(TargetPath)" 22</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <PreBuildEvent>
+ <Command>cd ..\..\
+prebuild gc $(Platform) 22</Command>
</PreBuildEvent>
<ClCompile>
<AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
@@ -114,15 +151,13 @@ prebuild gc x64 22
</ClCompile>
<PostBuildEvent>
<Command>cd ..\..\
-postbuild "$(TargetPath)" 22
- </Command>
+postbuild "$(TargetPath)" 22</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PreBuildEvent>
<Command>cd ..\..\
-prebuild gc x64 22
- </Command>
+prebuild gc $(Platform) 22</Command>
</PreBuildEvent>
<ClCompile>
<AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
@@ -130,8 +165,21 @@ prebuild gc x64 22
</ClCompile>
<PostBuildEvent>
<Command>cd ..\..\
-postbuild "$(TargetPath)" 22
- </Command>
+postbuild "$(TargetPath)" 22</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <PreBuildEvent>
+ <Command>cd ..\..\
+prebuild gc $(Platform) 22</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalIncludeDirectories>..\..\..\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN32;_LIB;HAVE_CONFIG_H;_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <PostBuildEvent>
+ <Command>cd ..\..\
+postbuild "$(TargetPath)" 22</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ImportGroup>
@@ -667,7 +715,7 @@ postbuild "$(TargetPath)" 22
<ItemGroup>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-<ItemGroup>
+ <ItemGroup>
<None Include="..\..\..\gmp-h.in" />
- </ItemGroup>
-</Project>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/msvc/vs22/msbuild.bat b/msvc/vs22/msbuild.bat
index f119c3e9..36bfe28b 100644
--- a/msvc/vs22/msbuild.bat
+++ b/msvc/vs22/msbuild.bat
@@ -1,7 +1,7 @@
@echo off
rem %1 = architecture
rem %2 = library type (LIB|DLL)
-rem %3 = platform (Win32|x64)
+rem %3 = platform (Win32|x64|ARM64)
rem %4 = configuration (Release|Debug)
rem %5 = Windows SDK Version
rem %6 = build tests (|+tests)
@@ -31,15 +31,21 @@ if not exist !msb_exe! (
if "%4" NEQ "" if "%3" NEQ "" if "%2" NEQ "" if "%1" NEQ "" goto cont
call :get_architectures -
-echo usage: msbuild architecture=^<%architectures:|=^|%^> library_type=^<LIB^|DLL^> platform=^<Win32^|x64^> configuration=^<Release^|Debug^> [Windows_SDK_Version=^<n^>] [+tests]
+echo usage: msbuild architecture=^<%architectures:|=^|%^> library_type=^<LIB^|DLL^> platform=^<Win32^|x64^|ARM64^> configuration=^<Release^|Debug^> [Windows_SDK_Version=^<n^>] [+tests]
goto :eof
:cont
rem example use: msbuild sandybridge_ivybridge dll x64 release
if not exist "lib_mpir_%1" (call :get_architectures & call :seterr & echo ERROR: architecture is one of ^(%architectures%^) ^(not %1^) & exit /b %errorlevel%)
if /i "%2" EQU "DLL" (set libp=dll) else (if /i "%2" EQU "LIB" (set libp=lib) else ((call :seterr & echo ERROR: library type is "lib" or "dll" ^(not "%2"^) & exit /b %errorlevel%)))
-if /i "%3" EQU "x64" (set plat=x64) else (if /i "%3" EQU "Win32" (set plat=win32) else (call :seterr & echo ERROR: platform is "Win32" or "x64" ^(not "%3"^) & exit /b %errorlevel%))
-if /i "%4" EQU "Debug" (set conf=Debug) else (if /i "%4" EQU "Release" (set conf=Release) else (call :seterr & echo ERROR: configuration is "Release" or "Debug" ^(not "%4"^) & exit /b %errorlevel%))
+if /i "%3" EQU "x64" (set plat=x64) else (
+ if /i "%3" EQU "Win32" (set plat=win32) else (
+ if /i "%3" EQU "ARM64" (set plat=ARM64) else (
+ call :seterr & echo ERROR: platform is "Win32", "x64", or "ARM64" ^(not %3^) & exit /b %errorlevel%
+ )
+ )
+)
+if /i "%4" EQU "Debug" (set conf=Debug) else (if /i "%4" EQU "Release" (set conf=Release) else (call :seterr & echo ERROR: configuration is "Release" or "Debug" ^(not %4^) & exit /b %errorlevel%))
if /i "%5" NEQ "" if "%5" EQU "+tests" (set run_tests=y) else (set win_sdk=%5)
if /i "%6" NEQ "" if "%6" EQU "+tests" (set run_tests=y)