mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-27 07:16:46 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d352ea07a | |||
| fd04066940 | |||
| d34956c2c7 | |||
| ad2585c571 | |||
| a08cf23d63 | |||
| 37fe04ffd2 | |||
| 248b21eab3 | |||
| 63f578315d | |||
| 19b6765994 | |||
| 7456c270ff | |||
| b480b43435 | |||
| 3614587556 | |||
| 86ce367ba8 | |||
| d320c1dac1 | |||
| cf48661a35 | |||
| 5e03846783 |
@@ -22,10 +22,15 @@ addons:
|
||||
- nlohmann-json3-dev
|
||||
- opencollada-dev
|
||||
- python3-all-dev
|
||||
- python3-pip
|
||||
- swig
|
||||
|
||||
before_script:
|
||||
- if [ $TRAVIS_OS_NAME == "linux" ]; then ccache -z; fi
|
||||
|
||||
install:
|
||||
# for IDS
|
||||
- python3 -m pip install xmlschema
|
||||
|
||||
script:
|
||||
- pwd
|
||||
@@ -39,6 +44,7 @@ script:
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 \
|
||||
-DPYTHON_INCLUDE_DIR:PATH=/usr/include/python3.8 \
|
||||
-DPYTHON_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libpython3.8.so \
|
||||
"-DSCHEMA_VERSIONS=2x3;4" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include \
|
||||
../cmake
|
||||
|
||||
@@ -494,7 +494,9 @@ function(files_for_ifc_version IFC_VERSION RESULT_NAME)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4")
|
||||
if(NOT SCHEMA_VERSIONS)
|
||||
set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4")
|
||||
endif()
|
||||
|
||||
foreach(s ${SCHEMA_VERSIONS})
|
||||
add_definitions(-DHAS_SCHEMA_${s})
|
||||
|
||||
+34
-1
@@ -13,7 +13,7 @@ is available via `bcfapi.py`.
|
||||
|
||||
The `bcfxml` module lets you interact with the BCF-XML standard.
|
||||
|
||||
```
|
||||
```python
|
||||
from bcf import bcfxml
|
||||
|
||||
|
||||
@@ -60,3 +60,36 @@ topic = bcfxml.get_topic(guid)
|
||||
topic.title = "New title"
|
||||
bcfxml.edit_topic(topic)
|
||||
```
|
||||
|
||||
## bcfapi
|
||||
|
||||
The `bcfapi` module lets you interact with the BCF-API standard.
|
||||
|
||||
```python
|
||||
from bcf.v3.bcfapi import Client
|
||||
|
||||
client_id = "YOUR_CLIENT_ID"
|
||||
client_secret = "YOUR_CLIENT_SECRET"
|
||||
|
||||
client = Client(client_id, client_secret)
|
||||
client.set_urls(base_url="OPENCDE_BASEURL")
|
||||
auth_methods = client.get_auth_methods()
|
||||
|
||||
# Our library currently only implements the authorization_code flow
|
||||
if "authorization_code" in auth_methods:
|
||||
client.login()
|
||||
|
||||
versions = client.get_versions()
|
||||
|
||||
if "3.0" in versions:
|
||||
client.set_version(version="3.0")
|
||||
|
||||
data = client.get_projects()
|
||||
print(data)
|
||||
project_id = data[0]["project_id"]
|
||||
print(project_id)
|
||||
data = client.get_project(project_id)
|
||||
print(data)
|
||||
data = client.get_extensions(project_id)
|
||||
print(data)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
import uuid
|
||||
import time
|
||||
import json
|
||||
import urllib
|
||||
import requests
|
||||
import webbrowser
|
||||
import http.server
|
||||
import base64
|
||||
|
||||
|
||||
client_id, client_secret = "", ""
|
||||
|
||||
|
||||
class OAuthReceiver(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
query = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query)
|
||||
self.server.auth_code = query.get("code", [""])[0]
|
||||
self.server.auth_state = query.get("state", [""])[0]
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.end_headers()
|
||||
self.wfile.write("You have now authenticated :) You may now close this browser window.".encode("utf-8"))
|
||||
|
||||
|
||||
class Client:
|
||||
def __init__(self, client_id, client_secret):
|
||||
self.baseurl = None
|
||||
self.access_token = ""
|
||||
self.refresh_token = ""
|
||||
self.access_token_expires_on = time.time()
|
||||
self.refresh_token_expires_on = float("inf")
|
||||
self.auth_endpoint = None
|
||||
self.token_endpoint = None
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.version_ids = {}
|
||||
self.version = None
|
||||
self.auth_method = None
|
||||
self.redirect_uri = None
|
||||
self.api_baseurl = None
|
||||
|
||||
def get(self, endpoint, params=None, is_auth_required=False):
|
||||
headers = {"Authorization": "Bearer " + self.get_access_token()}
|
||||
return requests.get(f"{self.api_baseurl}{endpoint}", headers=headers, params=params or None).json()
|
||||
|
||||
def post(self, endpoint, data=None, params=None):
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self.get_access_token(),
|
||||
"Content-type": "application/json",
|
||||
}
|
||||
resp = requests.post(
|
||||
f"{self.api_baseurl}{endpoint}",
|
||||
headers=headers,
|
||||
params=params or None,
|
||||
data=data or None,
|
||||
)
|
||||
return resp.status_code, resp.text
|
||||
|
||||
def put(self, endpoint, data=None, params=None):
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self.get_access_token(),
|
||||
"Content-type": "application/json",
|
||||
}
|
||||
resp = requests.put(
|
||||
f"{self.baseurl}{endpoint}",
|
||||
headers=headers,
|
||||
params=params or None,
|
||||
data=data or None,
|
||||
)
|
||||
return resp.status_code, resp.text
|
||||
|
||||
def set_urls(self, base_url=None, redirect_uri=None):
|
||||
self.baseurl = base_url
|
||||
self.redirect_uri = redirect_uri
|
||||
|
||||
def delete(self, endpoint, params=None):
|
||||
headers = {"Authorization": "Bearer " + self.get_access_token()}
|
||||
resp = requests.put(
|
||||
f"{self.baseurl}{endpoint}",
|
||||
headers=headers,
|
||||
params=params or None,
|
||||
)
|
||||
return resp.status_code
|
||||
|
||||
def get_access_token(self):
|
||||
if self.access_token and self.access_token_expires_on > time.time():
|
||||
return self.access_token
|
||||
elif self.refresh_token and self.refresh_token_expires_on > time.time():
|
||||
self.get_refresh_token()
|
||||
else:
|
||||
self.login()
|
||||
return self.access_token
|
||||
|
||||
def get_auth_methods(self):
|
||||
resp = requests.get(f"{self.baseurl}opencde/1.0/auth")
|
||||
return resp.json()["supported_oauth2_flows"]
|
||||
|
||||
def get_versions(self):
|
||||
resp = requests.get(f"{self.baseurl}opencde/versions")
|
||||
resp_values = resp.json()["versions"]
|
||||
for version in resp_values:
|
||||
if "api_base_url" in version:
|
||||
self.version_ids.update({version["version_id"]: version["api_base_url"]})
|
||||
return self.version_ids
|
||||
|
||||
def set_version(self, version=None):
|
||||
self.version = version
|
||||
self.api_baseurl = self.version_ids[self.version]
|
||||
|
||||
def login(self):
|
||||
resp = requests.get(f"{self.baseurl}opencde/1.0/auth")
|
||||
values = resp.json()
|
||||
self.auth_endpoint = values["oauth2_auth_url"]
|
||||
self.token_endpoint = values["oauth2_token_url"]
|
||||
|
||||
with http.server.HTTPServer(("", 8080), OAuthReceiver) as server:
|
||||
state = str(uuid.uuid4())
|
||||
query = urllib.parse.urlencode(
|
||||
{
|
||||
"client_id": self.client_id,
|
||||
"response_type": "code",
|
||||
"state": state,
|
||||
"redirect_uri": f"http://localhost:{server.server_address[1]}/{self.redirect_uri}",
|
||||
}
|
||||
)
|
||||
if "?" in self.auth_endpoint:
|
||||
webbrowser.open(f"{self.auth_endpoint}&{query}")
|
||||
else:
|
||||
webbrowser.open(f"{self.auth_endpoint}?{query}")
|
||||
server.timeout = 100
|
||||
server.state = state
|
||||
server.handle_request()
|
||||
if server.auth_code and server.auth_state == state:
|
||||
data = {
|
||||
"grant_type": "authorization_code",
|
||||
"code": server.auth_code,
|
||||
"redirect_uri": f"http://localhost:{server.server_address[1]}/{self.redirect_uri}",
|
||||
}
|
||||
auth_string = f"{self.client_id}:{self.client_secret}"
|
||||
header_string = base64.b64encode(auth_string.encode("utf-8")).decode("utf-8")
|
||||
headers = {"Authorization": f"Basic {header_string}"}
|
||||
self.set_tokens_from_response(requests.post(self.token_endpoint, data=data, headers=headers))
|
||||
|
||||
def get_refresh_token(self):
|
||||
self.set_tokens_from_response(
|
||||
requests.post(
|
||||
self.token_endpoint,
|
||||
params={
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": self.refresh_token,
|
||||
},
|
||||
).json()
|
||||
)
|
||||
|
||||
def get_new_access_token(self):
|
||||
auth_string = f"{self.client_id}:{self.client_secret}"
|
||||
header_string = base64.b64encode(auth_string.encode("utf-8")).decode("utf-8")
|
||||
headers = {"Authorization": f"Basic {header_string}"}
|
||||
self.set_tokens_from_response(
|
||||
requests.post(
|
||||
self.token_endpoint,
|
||||
data={
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": self.refresh_token,
|
||||
},
|
||||
headers=headers,
|
||||
).json()
|
||||
)
|
||||
|
||||
def set_auth_method(self, method="authorization_code_grant"):
|
||||
if method != "authorization_code_grant":
|
||||
raise NotImplementedError(f"{method} not supported")
|
||||
else:
|
||||
self.auth_method = method
|
||||
|
||||
def set_tokens_from_response(self, response):
|
||||
response = response.json()
|
||||
self.access_token = response["access_token"]
|
||||
self.refresh_token = response["refresh_token"]
|
||||
self.access_token_expires_on = time.time() + response["expires_in"]
|
||||
if "refresh_token_expires_in" in response:
|
||||
self.refresh_token_expires_on = time.time() + response["refresh_token_expires_in"]
|
||||
|
||||
def get_projects(self) -> list:
|
||||
return self.get(
|
||||
f"/projects",
|
||||
)
|
||||
|
||||
def get_project(
|
||||
self,
|
||||
project_id="",
|
||||
) -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}",
|
||||
{
|
||||
"project_id": project_id,
|
||||
},
|
||||
)
|
||||
|
||||
def update_project(self, project_id="", data=None) -> dict:
|
||||
url = f"{self.baseurl}/projects/{project_id}"
|
||||
headers = {"Authorization": "Bearer " + self.get_access_token()}
|
||||
resp = requests.put(url, headers=headers, data=data)
|
||||
return resp.status_code, resp.text
|
||||
|
||||
def get_extensions(
|
||||
self,
|
||||
project_id="",
|
||||
) -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/extensions",
|
||||
{
|
||||
"project_id": project_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_topics(
|
||||
self,
|
||||
project_id="",
|
||||
topics="",
|
||||
query_string=None,
|
||||
) -> list:
|
||||
# return self.get(
|
||||
# f"/projects/{project_id}/topics",
|
||||
# {
|
||||
# "project_id": project_id,
|
||||
# "topics": topics,
|
||||
# "query_string": query_string,
|
||||
# },
|
||||
# )
|
||||
pass
|
||||
|
||||
def get_topic(self, project_id="", topic_id="") -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def create_topic(self, project_id="", data=None):
|
||||
return self.post(f"/projects/{project_id}/topics", data=data)
|
||||
|
||||
def update_topic(self, project_id="", topic_id="", data=None) -> dict:
|
||||
return self.put(f"/projects/{project_id}/topics/{topic_id}", data=data)
|
||||
|
||||
def delete_topic(self, project_id="", topic_id=""):
|
||||
return self.delete(f"/projects/{project_id}/topics/{topic_id}")
|
||||
|
||||
def get_snippet(self, project_id="", topic_id="") -> str:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/snippet",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def update_snippet(self, project_id="", topic_id="", data=None):
|
||||
return self.put(f"/projects/{project_id}/topics", data=data)
|
||||
|
||||
def get_files_information(self, project_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/files_information",
|
||||
{
|
||||
"project_id": project_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_files(self, project_id="", topic_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/files",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def update_files(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
data=None,
|
||||
params=None,
|
||||
):
|
||||
return self.put(
|
||||
f"/projects/{project_id}/topics/{topic_id}/files",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def get_comments(self, project_id="", topic_id="") -> list:
|
||||
pass
|
||||
|
||||
def create_comments(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
data=None,
|
||||
params=None,
|
||||
):
|
||||
return self.post(
|
||||
f"/projects/{project_id}/topics/{topic_id}/comments",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def get_comment(self, project_id="", topic_id="", comment_id="") -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/comments/{comment_id}",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"comment_id": comment_id,
|
||||
},
|
||||
)
|
||||
|
||||
def delete_comment(self, project_id="", topic_id="", comment_id=""):
|
||||
return self.delete(f"/projects/{project_id}/topics/{topic_id}/comments/{comment_id}")
|
||||
|
||||
def update_comment(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
comment_id="",
|
||||
data=None,
|
||||
):
|
||||
return self.put(
|
||||
f"/projects/{project_id}/topics/{topic_id}/comments/{comment_id}",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def get_viewpoints(self, project_id="", topic_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def create_viewpoints(self, project_id="", topic_id="", data=None):
|
||||
return self.post(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def get_viewpoint(self, project_id="", topic_id="", viewpoint_id="") -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"viewpoint_id": viewpoint_id,
|
||||
},
|
||||
)
|
||||
|
||||
def delete_viewpoint(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
viewpoint_id="",
|
||||
):
|
||||
return self.delete(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}",
|
||||
)
|
||||
|
||||
def get_snapshot(self, project_id="", topic_id="", viewpoint_id="") -> str:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/snapshot",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"viewpoint_id": viewpoint_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_bitmap(self, project_id="", topic_id="", viewpoint_id="", bitmap_id="") -> str:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/bitmaps/{bitmap_id}",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"viewpoint_id": viewpoint_id,
|
||||
"bitmap_id": bitmap_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_selection(self, project_id="", topic_id="", viewpoint_id="") -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/selection",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"viewpoint_id": viewpoint_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_coloring(self, project_id="", topic_id="", viewpoint_id="") -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/coloring",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"viewpoint_id": viewpoint_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_visibility(self, project_id="", topic_id="", viewpoint_id="") -> dict:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/viewpoints/{viewpoint_id}/visibility",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"viewpoint_id": viewpoint_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_related_topics(self, project_id="", topic_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/related_topics",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def update_related_topics(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
data=None,
|
||||
):
|
||||
return self.put(
|
||||
f"/projects/{project_id}/topics/{topic_id}/related_topics",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def get_document_references(self, project_id="", topic_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/document_references",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def create_document_reference(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
data=None,
|
||||
):
|
||||
return self.post(
|
||||
f"/projects/{project_id}/topics/{topic_id}/document_references",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def update_document_references(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
document_reference_id="",
|
||||
data=None,
|
||||
):
|
||||
return self.put(
|
||||
f"/projects/{project_id}/topics/{topic_id}/document_references/{document_reference_id}",
|
||||
data=data,
|
||||
)
|
||||
|
||||
def get_documents(self, project_id="", topic_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/documents",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def create_document(
|
||||
self,
|
||||
project_id="",
|
||||
topic_id="",
|
||||
guid=None,
|
||||
data=None,
|
||||
):
|
||||
headers = {
|
||||
"Authorization": "Bearer " + self.get_access_token(),
|
||||
"Content-type": "application/octet-stream",
|
||||
}
|
||||
response = requests.post(
|
||||
f"/projects/{project_id}/topics/{topic_id}/documents",
|
||||
data=data,
|
||||
params={guid},
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
def get_document(self, project_id="", topic_id="", document_id="") -> str:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/documents/{document_id}",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"document_id": document_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_topics_events(self, project_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/events",
|
||||
{
|
||||
"project_id": project_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_topic_events(self, project_id="", topic_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/events",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_comments_events(self, project_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/comments/events",
|
||||
{
|
||||
"project_id": project_id,
|
||||
},
|
||||
)
|
||||
|
||||
def get_comment_events(self, project_id="", topic_id="", comment_id="") -> list:
|
||||
return self.get(
|
||||
f"/projects/{project_id}/topics/{topic_id}/comments/{comment_id}/events",
|
||||
{
|
||||
"project_id": project_id,
|
||||
"topic_id": topic_id,
|
||||
"comment_id": comment_id,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -68,6 +68,8 @@ class IfcExporter:
|
||||
|
||||
for ifc_definition_id, obj in IfcStore.id_map.items():
|
||||
try:
|
||||
if isinstance(obj, bpy.types.Material):
|
||||
continue
|
||||
self.sync_object_placement(obj)
|
||||
self.sync_object_container(ifc_definition_id, obj)
|
||||
except ReferenceError:
|
||||
|
||||
@@ -159,8 +159,16 @@ class IfcImporter:
|
||||
self.time = time.time()
|
||||
print("{} :: {:.2f}".format(message, time.time() - self.time))
|
||||
self.time = time.time()
|
||||
self.update_progress(self.progress + 1)
|
||||
|
||||
def update_progress(self, progress):
|
||||
if progress <= 100:
|
||||
self.progress = progress
|
||||
bpy.context.window_manager.progress_update(self.progress)
|
||||
|
||||
def execute(self):
|
||||
bpy.context.window_manager.progress_begin(0, 100)
|
||||
self.progress = 0
|
||||
self.profile_code("Starting import process")
|
||||
self.load_diff()
|
||||
self.profile_code("Load diff")
|
||||
@@ -200,7 +208,7 @@ class IfcImporter:
|
||||
self.profile_code("Create native products")
|
||||
self.create_products()
|
||||
self.profile_code("Create products")
|
||||
self.create_empty_products()
|
||||
self.create_empty_and_2d_elements()
|
||||
self.profile_code("Create empty products")
|
||||
self.create_type_products()
|
||||
self.profile_code("Create type products")
|
||||
@@ -228,6 +236,8 @@ class IfcImporter:
|
||||
self.profile_code("Mesh cleaning")
|
||||
self.set_default_context()
|
||||
self.profile_code("Setting default context")
|
||||
self.update_progress(100)
|
||||
bpy.context.window_manager.progress_end()
|
||||
|
||||
def is_element_far_away(self, element, is_meters=True):
|
||||
try:
|
||||
@@ -322,10 +332,10 @@ class IfcImporter:
|
||||
return
|
||||
project = self.file.by_type("IfcProject")[0]
|
||||
site = self.find_decomposed_ifc_class(project, "IfcSite")
|
||||
if site and self.is_element_far_away(site[0]):
|
||||
if site and self.is_element_far_away(site[0], is_meters=False):
|
||||
return self.guess_georeferencing(site[0])
|
||||
building = self.find_decomposed_ifc_class(project, "IfcBuilding")
|
||||
if building and self.is_element_far_away(building[0]):
|
||||
if building and self.is_element_far_away(building[0], is_meters=False):
|
||||
return self.guess_georeferencing(building[0])
|
||||
return self.guess_absolute_coordinate()
|
||||
|
||||
@@ -546,12 +556,21 @@ class IfcImporter:
|
||||
if not valid_file:
|
||||
return False
|
||||
checkpoint = time.time()
|
||||
total = 0
|
||||
total_created = 0
|
||||
approx_total_products = len(self.include_elements) or len(self.file.by_type("IfcElement"))
|
||||
start_progress = self.progress
|
||||
progress_range = 85 - start_progress
|
||||
while True:
|
||||
total += 1
|
||||
if total % 250 == 0:
|
||||
print("{} elements processed in {:.2f}s ...".format(total, time.time() - checkpoint))
|
||||
if total_created % 250 == 0:
|
||||
print(
|
||||
"{} / ~{} elements processed in {:.2f}s ...".format(
|
||||
total_created, approx_total_products, time.time() - checkpoint
|
||||
)
|
||||
)
|
||||
checkpoint = time.time()
|
||||
self.update_progress(
|
||||
((total_created / approx_total_products) * progress_range) + start_progress
|
||||
)
|
||||
shape = iterator.get()
|
||||
if shape:
|
||||
product = self.file.by_id(shape.guid)
|
||||
@@ -565,18 +584,24 @@ class IfcImporter:
|
||||
pass
|
||||
else:
|
||||
self.create_product(product, shape)
|
||||
total_created += 1
|
||||
if not iterator.next():
|
||||
break
|
||||
print("Done creating geometry")
|
||||
|
||||
def create_empty_products(self):
|
||||
for element in self.file.by_type("IfcProduct"):
|
||||
def create_empty_and_2d_elements(self):
|
||||
curve_products = []
|
||||
for element in self.file.by_type("IfcElement"):
|
||||
if element.id() in self.added_data:
|
||||
continue
|
||||
if element.is_a("IfcPort"):
|
||||
continue
|
||||
if not element.Representation:
|
||||
self.create_product(element)
|
||||
else:
|
||||
curve_products.append(element)
|
||||
if curve_products:
|
||||
self.create_curve_products(curve_products)
|
||||
|
||||
def create_annotation(self):
|
||||
self.create_curve_products(self.file.by_type("IfcAnnotation"))
|
||||
|
||||
@@ -117,19 +117,20 @@ class AddRepresentation(bpy.types.Operator):
|
||||
if s.material and not s.material.BIMMaterialProperties.ifc_style_id
|
||||
]
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
self.file,
|
||||
**{
|
||||
"shape_representation": result,
|
||||
"styles": [
|
||||
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
},
|
||||
)
|
||||
if isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons):
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
self.file,
|
||||
**{
|
||||
"shape_representation": result,
|
||||
"styles": [
|
||||
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
},
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"geometry.assign_representation", self.file, **{"product": product, "representation": result}
|
||||
)
|
||||
@@ -160,6 +161,7 @@ class SwitchRepresentation(bpy.types.Operator):
|
||||
ifc_definition_id: bpy.props.IntProperty()
|
||||
should_reload: bpy.props.BoolProperty()
|
||||
disable_opening_subtractions: bpy.props.BoolProperty()
|
||||
should_switch_all_meshes: bpy.props.BoolProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.element_obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
@@ -171,11 +173,17 @@ class SwitchRepresentation(bpy.types.Operator):
|
||||
|
||||
mesh = bpy.data.meshes.get(self.mesh_name)
|
||||
if mesh:
|
||||
self.element_obj.data.user_remap(mesh)
|
||||
self.switch_mesh(mesh)
|
||||
if not mesh or self.should_reload:
|
||||
self.pull_mesh_from_ifc()
|
||||
return {"FINISHED"}
|
||||
|
||||
def switch_mesh(self, mesh):
|
||||
if self.should_switch_all_meshes or self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcTypeProduct"):
|
||||
self.element_obj.data.user_remap(mesh)
|
||||
else:
|
||||
self.element_obj.data = mesh
|
||||
|
||||
def get_mesh_name(self):
|
||||
representation = self.resolve_mapped_representation(self.file.by_id(self.ifc_definition_id))
|
||||
return "{}/{}".format(self.context_of_items.id(), representation.id())
|
||||
@@ -205,7 +213,7 @@ class SwitchRepresentation(bpy.types.Operator):
|
||||
mesh = ifc_importer.create_mesh(element, shape)
|
||||
mesh.name = self.mesh_name
|
||||
mesh.BIMMeshProperties.ifc_definition_id = self.ifc_definition_id
|
||||
self.element_obj.data.user_remap(mesh)
|
||||
self.switch_mesh(mesh)
|
||||
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
|
||||
material_creator.load_existing_materials()
|
||||
material_creator.create(element, self.element_obj, mesh)
|
||||
@@ -330,19 +338,20 @@ class UpdateRepresentation(bpy.types.Operator):
|
||||
if s.material and not s.material.BIMMaterialProperties.ifc_style_id
|
||||
]
|
||||
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
self.file,
|
||||
**{
|
||||
"shape_representation": new_representation,
|
||||
"styles": [
|
||||
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
},
|
||||
)
|
||||
if isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons):
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
self.file,
|
||||
**{
|
||||
"shape_representation": new_representation,
|
||||
"styles": [
|
||||
self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id)
|
||||
for s in obj.material_slots
|
||||
if s.material
|
||||
],
|
||||
"should_use_presentation_style_assignment": context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
},
|
||||
)
|
||||
|
||||
# TODO: move this into a replace_representation usecase or something
|
||||
for inverse in self.file.get_inverse(old_representation):
|
||||
@@ -366,7 +375,9 @@ class UpdateParametricRepresentation(bpy.types.Operator):
|
||||
props = obj.data.BIMMeshProperties
|
||||
parameter = props.ifc_parameters[self.index]
|
||||
element = IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||
bpy.ops.bim.switch_representation(ifc_definition_id=props.ifc_definition_id, should_reload=True)
|
||||
bpy.ops.bim.switch_representation(
|
||||
ifc_definition_id=props.ifc_definition_id, should_reload=True, should_switch_all_meshes=True
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ class BIM_PT_representations(Panel):
|
||||
row.label(text=representation["ContextOfItems"]["ContextIdentifier"])
|
||||
row.label(text=representation["ContextOfItems"]["TargetView"])
|
||||
row.label(text=representation["RepresentationType"])
|
||||
op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="")
|
||||
op = row.operator(
|
||||
"bim.switch_representation", icon="OUTLINER_DATA_MESH", text="", should_switch_all_meshes=True
|
||||
)
|
||||
op.should_reload = True
|
||||
op.ifc_definition_id = ifc_definition_id
|
||||
op.disable_opening_subtractions = False
|
||||
@@ -70,11 +72,15 @@ class BIM_PT_mesh(Panel):
|
||||
props = context.active_object.data.BIMMeshProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.switch_representation", text="Bake Voids", icon="SELECT_SUBTRACT")
|
||||
op = row.operator(
|
||||
"bim.switch_representation", text="Bake Voids", icon="SELECT_SUBTRACT", should_switch_all_meshes=True
|
||||
)
|
||||
op.should_reload = True
|
||||
op.ifc_definition_id = props.ifc_definition_id
|
||||
op.disable_opening_subtractions = False
|
||||
op = row.operator("bim.switch_representation", text="Dynamic Voids", icon="SELECT_INTERSECT")
|
||||
op = row.operator(
|
||||
"bim.switch_representation", text="Dynamic Voids", icon="SELECT_INTERSECT", should_switch_all_meshes=True
|
||||
)
|
||||
op.should_reload = True
|
||||
op.ifc_definition_id = props.ifc_definition_id
|
||||
op.disable_opening_subtractions = True
|
||||
|
||||
@@ -473,7 +473,6 @@ class EditAssignedMaterial(bpy.types.Operator):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
|
||||
material_set = self.file.by_id(self.material_set)
|
||||
|
||||
if product_data["type"] == "IfcMaterial":
|
||||
bpy.ops.bim.unassign_material(obj=obj.name)
|
||||
@@ -482,6 +481,8 @@ class EditAssignedMaterial(bpy.types.Operator):
|
||||
bpy.ops.bim.disable_editing_assigned_material(obj=obj.name)
|
||||
return {"FINISHED"}
|
||||
|
||||
material_set = self.file.by_id(self.material_set)
|
||||
|
||||
attributes = {}
|
||||
for attribute in props.material_set_attributes:
|
||||
attributes[attribute.name] = None if attribute.is_null else attribute.string_value
|
||||
|
||||
@@ -97,6 +97,8 @@ class BIM_PT_object_material(Panel):
|
||||
self.material_set_data = Data.lists[self.material_set_id]
|
||||
self.set_items = self.material_set_data["Materials"] or []
|
||||
self.set_item_name = "list_item"
|
||||
else:
|
||||
self.material_set_id = 0
|
||||
return self.draw_material_ui()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -3,7 +3,7 @@ from . import handler, prop, ui, grid, product, wall, slab, stair, door, window,
|
||||
|
||||
classes = (
|
||||
product.AddTypeInstance,
|
||||
wall.AddWall,
|
||||
workspace.HotkeyE,
|
||||
wall.JoinWall,
|
||||
wall.AlignWall,
|
||||
wall.FlipWall,
|
||||
|
||||
@@ -120,7 +120,9 @@ class DumbColumnGenerator:
|
||||
profile_set_usage=profile_set_usage.id(),
|
||||
)
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
bpy.ops.bim.switch_representation(obj=obj.name, ifc_definition_id=representation.id(), should_reload=True)
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||
)
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbColumn"})
|
||||
MaterialData.load(self.file)
|
||||
@@ -165,4 +167,6 @@ class DumbColumnRegenerator:
|
||||
return
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if representation:
|
||||
bpy.ops.bim.switch_representation(obj=obj.name, ifc_definition_id=representation.id(), should_reload=True)
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||
)
|
||||
|
||||
@@ -98,6 +98,7 @@ def generate_box(usecase_path, ifc_file, settings):
|
||||
**{"product": product, "representation": new_box}
|
||||
)
|
||||
|
||||
|
||||
def regenerate_profile_usage(usecase_path, ifc_file, settings):
|
||||
elements = []
|
||||
if ifc_file.schema == "IFC2X3":
|
||||
@@ -117,4 +118,6 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
|
||||
continue
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if representation:
|
||||
bpy.ops.bim.switch_representation(obj=obj.name, ifc_definition_id=representation.id(), should_reload=True)
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||
)
|
||||
|
||||
@@ -36,21 +36,6 @@ def mode_callback(obj, data):
|
||||
IfcStore.edited_objs.add(obj)
|
||||
|
||||
|
||||
class AddWall(bpy.types.Operator):
|
||||
bl_idname = "bim.add_wall"
|
||||
bl_label = "Add Wall"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
join_type: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
bpy.ops.bim.add_type_instance(ifc_class="IfcWallType", relating_type=int(props.relating_type))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class JoinWall(bpy.types.Operator):
|
||||
bl_idname = "bim.join_wall"
|
||||
bl_label = "Join Wall"
|
||||
@@ -65,7 +50,13 @@ class JoinWall(bpy.types.Operator):
|
||||
for obj in selected_objs:
|
||||
DumbWallJoiner(obj, obj).unjoin()
|
||||
return {"FINISHED"}
|
||||
if len(selected_objs) < 2 or not context.active_object:
|
||||
if not context.active_object:
|
||||
return {"FINISHED"}
|
||||
if len(selected_objs) == 1:
|
||||
DumbWallJoiner(context.active_object, target_coordinate=context.scene.cursor.location).extend()
|
||||
IfcStore.edited_objs.add(context.active_object)
|
||||
return {"FINISHED"}
|
||||
if len(selected_objs) < 2:
|
||||
return {"FINISHED"}
|
||||
for obj in selected_objs:
|
||||
if obj == context.active_object:
|
||||
@@ -327,16 +318,19 @@ class DumbWallJoiner:
|
||||
# 2. Given an "end face", identify a side "target face" of the other wall
|
||||
# to project towards.
|
||||
# 3. Project the vertices of an "end face" to the "target face".
|
||||
def __init__(self, wall1, wall2):
|
||||
# Alternatively, a target coordinate may be provided as an imaginary point for the wall to join to
|
||||
def __init__(self, wall1, wall2=None, target_coordinate=None):
|
||||
self.wall1 = wall1
|
||||
self.wall2 = wall2
|
||||
self.target_coordinate = target_coordinate
|
||||
self.should_project_to_frontface = True
|
||||
self.should_attempt_v_junction_projection = False
|
||||
self.initialise_convenience_variables()
|
||||
|
||||
def initialise_convenience_variables(self):
|
||||
self.wall1_matrix = self.wall1.matrix_world
|
||||
self.wall2_matrix = self.wall2.matrix_world
|
||||
if self.wall2:
|
||||
self.wall2_matrix = self.wall2.matrix_world
|
||||
self.pos_x = self.wall1_matrix.to_quaternion() @ Vector((1, 0, 0))
|
||||
self.neg_x = self.wall1_matrix.to_quaternion() @ Vector((-1, 0, 0))
|
||||
|
||||
@@ -354,6 +348,26 @@ class DumbWallJoiner:
|
||||
self.wall1.data.vertices[v].co[0] = max_x
|
||||
self.recalculate_origins()
|
||||
|
||||
# An extension is where a single end of wall1 is projected to an imaginary
|
||||
# plane denoted by the target coordinate.
|
||||
def extend(self):
|
||||
wall1_min_faces, wall1_max_faces = self.get_wall_end_faces(self.wall1)
|
||||
ef1_distance = abs(mathutils.geometry.distance_point_to_plane(
|
||||
self.wall1_matrix @ self.wall1.data.vertices[wall1_min_faces[0].vertices[0]].co,
|
||||
self.target_coordinate,
|
||||
self.pos_x,
|
||||
))
|
||||
ef2_distance = abs(mathutils.geometry.distance_point_to_plane(
|
||||
self.wall1_matrix @ self.wall1.data.vertices[wall1_max_faces[0].vertices[0]].co,
|
||||
self.target_coordinate,
|
||||
self.neg_x,
|
||||
))
|
||||
if ef1_distance < ef2_distance:
|
||||
self.project_end_faces_to_target(wall1_min_faces)
|
||||
else:
|
||||
self.project_end_faces_to_target(wall1_max_faces)
|
||||
self.recalculate_origins()
|
||||
|
||||
# A T-junction is an ordered operation where a single end of wall1 is joined
|
||||
# to wall2 if possible (i.e. walls aren't parallel). Wall2 is not modified.
|
||||
# First, wall1 end faces are identified. We attempt to project an end face
|
||||
@@ -439,7 +453,8 @@ class DumbWallJoiner:
|
||||
def recalculate_origins(self):
|
||||
bpy.context.view_layer.update()
|
||||
recalculate_dumb_wall_origin(self.wall1)
|
||||
recalculate_dumb_wall_origin(self.wall2)
|
||||
if self.wall2:
|
||||
recalculate_dumb_wall_origin(self.wall2)
|
||||
|
||||
def swap_walls(self):
|
||||
self.wall1, self.wall2 = self.wall2, self.wall1
|
||||
@@ -467,6 +482,14 @@ class DumbWallJoiner:
|
||||
local_point = wall_matrix.inverted() @ point
|
||||
wall.data.vertices[v].co = local_point
|
||||
|
||||
def project_end_faces_to_target(self, end_faces):
|
||||
for end_face in end_faces:
|
||||
for v in end_face.vertices:
|
||||
vertex = self.wall1_matrix @ self.wall1.data.vertices[v].co
|
||||
self.wall1.data.vertices[v].co = self.wall1_matrix.inverted() @ mathutils.geometry.intersect_line_plane(
|
||||
vertex, vertex + self.pos_x, self.target_coordinate, self.pos_x
|
||||
)
|
||||
|
||||
# A projection target face is a side face on the target wall that has a
|
||||
# significant local Y component to its normal (i.e. is not pointing up or
|
||||
# down or something). In addition, its plane must intersect with the
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import bpy
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BimTool(WorkSpaceTool):
|
||||
@@ -16,8 +17,8 @@ class BimTool(WorkSpaceTool):
|
||||
bl_keymap = (
|
||||
# ("bim.wall_tool_op", {"type": 'MOUSEMOVE', "value": 'ANY'}, {"properties": []}),
|
||||
# ("mesh.add_wall", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": []}),
|
||||
("bim.add_wall", {"type": "A", "value": "PRESS", "shift": True}, {"properties": []}),
|
||||
("bim.join_wall", {"type": "E", "value": "PRESS", "shift": True}, {"properties": [("join_type", "T")]}),
|
||||
("bim.add_type_instance", {"type": "A", "value": "PRESS", "shift": True}, {"properties": []}),
|
||||
("bim.hotkey_e", {"type": "E", "value": "PRESS", "shift": True}, {"properties": []}),
|
||||
("bim.join_wall", {"type": "T", "value": "PRESS", "shift": True}, {"properties": [("join_type", "L")]}),
|
||||
("bim.join_wall", {"type": "Y", "value": "PRESS", "shift": True}, {"properties": [("join_type", "V")]}),
|
||||
("bim.flip_wall", {"type": "F", "value": "PRESS", "shift": True}, {"properties": []}),
|
||||
@@ -40,20 +41,38 @@ class BimTool(WorkSpaceTool):
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, tool):
|
||||
props = context.scene.BIMModelProperties
|
||||
row = layout.row(align=True)
|
||||
props = context.scene.BIMTypeProperties
|
||||
row.prop(props, "ifc_class", text="")
|
||||
row.prop(props, "relating_type", text="")
|
||||
|
||||
row.label(text="", icon="BLANK1")
|
||||
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="Add", icon="EVENT_A")
|
||||
row.label(text="Extend", icon="EVENT_E")
|
||||
row.label(text="Butt", icon="EVENT_T")
|
||||
row.label(text="Mitre", icon="EVENT_Y")
|
||||
row.label(text="Flip", icon="EVENT_F")
|
||||
row.label(text="Split", icon="EVENT_S")
|
||||
row.label(text="", icon="EVENT_X")
|
||||
row.label(text="", icon="EVENT_C")
|
||||
row.label(text="", icon="EVENT_V")
|
||||
row.label(text="Align")
|
||||
|
||||
if props.ifc_class == "IfcWallType":
|
||||
row.label(text="Extend", icon="EVENT_E")
|
||||
row.label(text="Butt", icon="EVENT_T")
|
||||
row.label(text="Mitre", icon="EVENT_Y")
|
||||
row.label(text="Flip", icon="EVENT_F")
|
||||
row.label(text="Split", icon="EVENT_S")
|
||||
row.label(text="", icon="EVENT_X")
|
||||
row.label(text="", icon="EVENT_C")
|
||||
row.label(text="", icon="EVENT_V")
|
||||
row.label(text="Align")
|
||||
|
||||
|
||||
class HotkeyE(bpy.types.Operator):
|
||||
bl_idname = "bim.hotkey_e"
|
||||
bl_label = "Hotkey E"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMTypeProperties
|
||||
if props.ifc_class == "IfcWallType":
|
||||
bpy.ops.bim.join_wall(join_type="T")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -22,7 +22,9 @@ class AssignType(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
relating_type = self.relating_type or int(context.active_object.BIMTypeProperties.relating_type)
|
||||
related_objects = (
|
||||
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
|
||||
[bpy.data.objects.get(self.related_object)]
|
||||
if self.related_object
|
||||
else bpy.context.selected_objects or [bpy.context.active_object]
|
||||
)
|
||||
for related_object in related_objects:
|
||||
oprops = related_object.BIMObjectProperties
|
||||
@@ -39,15 +41,19 @@ class AssignType(bpy.types.Operator):
|
||||
MaterialData.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||
representation_ids = GeometryData.products[oprops.ifc_definition_id]
|
||||
if not representation_ids:
|
||||
pass # TODO: clear geometry? Make void? Make none type?
|
||||
pass # TODO: clear geometry? Make void? Make none type?
|
||||
has_switched = False
|
||||
for representation_id in representation_ids:
|
||||
representation = GeometryData.representations[representation_id]
|
||||
if representation["ContextOfItems"]["ContextIdentifier"] == "Body":
|
||||
bpy.ops.bim.switch_representation(obj=related_object.name, ifc_definition_id=representation_id)
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=related_object.name, ifc_definition_id=representation_id, should_switch_all_meshes=False
|
||||
)
|
||||
has_switched = True
|
||||
if not has_switched and representation_ids:
|
||||
bpy.ops.bim.switch_representation(obj=related_object.name, ifc_definition_id=representation_id)
|
||||
bpy.ops.bim.switch_representation(
|
||||
obj=related_object.name, ifc_definition_id=representation_id, should_switch_all_meshes=False
|
||||
)
|
||||
|
||||
bpy.ops.bim.disable_editing_type(obj=related_object.name)
|
||||
MaterialData.load(self.file)
|
||||
|
||||
@@ -22,8 +22,6 @@ class Usecase:
|
||||
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
||||
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
||||
"should_force_triangulation": False, # If we should force triangulation for meshes
|
||||
"is_wireframe": False, # If the geometry is a wireframe
|
||||
"is_curve": False, # If the geometry is a Blender curve
|
||||
"is_point_cloud": False, # If the geometry is a point cloud
|
||||
# Possible IFC representation classes:
|
||||
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
|
||||
@@ -204,9 +202,9 @@ class Usecase:
|
||||
)
|
||||
|
||||
def create_variable_representation(self):
|
||||
if self.settings["is_wireframe"]:
|
||||
return self.create_wireframe_representation()
|
||||
elif self.settings["is_curve"]:
|
||||
if isinstance(self.settings["geometry"], bpy.types.Curve):
|
||||
return self.create_curve3d_representation()
|
||||
elif not len(self.settings["geometry"].polygons):
|
||||
return self.create_curve3d_representation()
|
||||
elif self.settings["is_point_cloud"]:
|
||||
return self.create_point_cloud_representation()
|
||||
|
||||
@@ -48,7 +48,7 @@ class entity_instance(object):
|
||||
>>> #423=IfcProductDefinitionShape($,$,(#409,#421))
|
||||
"""
|
||||
|
||||
def __init__(self, e, file):
|
||||
def __init__(self, e, file=None):
|
||||
if isinstance(e, tuple):
|
||||
e = ifcopenshell_wrapper.new_IfcBaseClass(*e)
|
||||
super(entity_instance, self).__setattr__("wrapped_data", e)
|
||||
@@ -126,7 +126,7 @@ class entity_instance(object):
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_argument(key), self.wrapped_data.file)
|
||||
|
||||
def __setitem__(self, idx, value):
|
||||
if self.wrapped_data.file.transaction:
|
||||
if self.wrapped_data.file and self.wrapped_data.file.transaction:
|
||||
self.wrapped_data.file.transaction.store_edit(self, idx, value)
|
||||
|
||||
attr_type = real_attr_type = self.attribute_type(idx).title().replace(" ", "")
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class Patcher:
|
||||
def __init__(self, src, file, logger, args=None):
|
||||
self.src = src
|
||||
self.file = file
|
||||
self.logger = logger
|
||||
self.args = args
|
||||
|
||||
def patch(self):
|
||||
curve_map = {}
|
||||
|
||||
for curve in self.file.by_type("IfcIndexedPolyCurve"):
|
||||
if "IfcArcIndex" in [s.is_a() for s in curve.Segments]:
|
||||
print("Could not convert curve due to arcs", curve)
|
||||
continue
|
||||
coordinates = curve.Points.CoordList
|
||||
points = []
|
||||
for i, segment in enumerate(curve.Segments):
|
||||
segment = segment.wrappedValue
|
||||
if i == 0:
|
||||
points.append(self.file.createIfcCartesianPoint(coordinates[segment[0] - 1]))
|
||||
points.append(self.file.createIfcCartesianPoint(coordinates[segment[1] - 1]))
|
||||
polyline = self.file.create_entity("IfcPolyline", points)
|
||||
curve_map[curve] = polyline
|
||||
|
||||
for curve, polyline in curve_map.items():
|
||||
for inverse in self.file.get_inverse(curve):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, curve, polyline)
|
||||
Reference in New Issue
Block a user