mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 02:07:36 +00:00
Merge remote-tracking branch 'origin/v0.6.0' into v0.7.0
This commit is contained in:
+10
-9
@@ -4,10 +4,10 @@ A simple Python implementation of BCF. The data model is described in `data.py`.
|
|||||||
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
|
Manipulation of BCF-XML is available via `bcfxml.py` and manipulation of BCF-API
|
||||||
is available via `bcfapi.py`.
|
is available via `bcfapi.py`.
|
||||||
|
|
||||||
- BCF-XML version 2.1: Fully supported
|
- BCF-XML version 2.1: Fully supported
|
||||||
- BCF-API version 2.1: Not supported, will probably tackle this after BCF-API v3.0
|
- BCF-API version 2.1: Not supported, will probably tackle this after BCF-API v3.0
|
||||||
- BCF-XML version 3.0: Almost fully supported, except for the documents module
|
- BCF-XML version 3.0: Almost fully supported, except for the documents module
|
||||||
- BCF-API version 3.0: Almost fully supported, except for two requests.
|
- BCF-API version 3.0: Almost fully supported, except for two requests.
|
||||||
|
|
||||||
## bcfxml
|
## bcfxml
|
||||||
|
|
||||||
@@ -73,9 +73,9 @@ auth_methods = foundation_client.get_auth_methods()
|
|||||||
|
|
||||||
# Our library currently only implements the authorization_code flow
|
# Our library currently only implements the authorization_code flow
|
||||||
if "authorization_code" in auth_methods:
|
if "authorization_code" in auth_methods:
|
||||||
auth_client.login()
|
foundation_client.login()
|
||||||
|
|
||||||
bcf_client = BcfClient()
|
bcf_client = BcfClient(foundation_client)
|
||||||
|
|
||||||
versions = foundation_client.get_versions()
|
versions = foundation_client.get_versions()
|
||||||
for version in versions:
|
for version in versions:
|
||||||
@@ -94,7 +94,8 @@ print(data)
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Todo List
|
## Todo List
|
||||||
The remaining work that needs to be completed in `bcfxml.py` and `bcfapi.py`.
|
|
||||||
* For `bcfxml.py` two xsds support is remaining namely 'documents.xsd` and `extensions.xsd`.
|
|
||||||
* For `bcfapi.py` two requests that are `get_topics` and `get_comments` are remaining.
|
|
||||||
|
|
||||||
|
The remaining work that needs to be completed in `bcfxml.py` and `bcfapi.py`.
|
||||||
|
|
||||||
|
- For `bcfxml.py` two xsds support is remaining namely 'documents.xsd`and`extensions.xsd`.
|
||||||
|
- For `bcfapi.py` two requests that are `get_topics` and `get_comments` are remaining.
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ class BcfClient:
|
|||||||
self.baseurl = version["api_base_url"]
|
self.baseurl = version["api_base_url"]
|
||||||
|
|
||||||
def get(self, endpoint, params=None, is_auth_required=False):
|
def get(self, endpoint, params=None, is_auth_required=False):
|
||||||
|
# TODO: handle error http status codes and raise exception. Follow error.json standard.
|
||||||
headers = {"Authorization": "Bearer " + self.foundation_client.get_access_token()}
|
headers = {"Authorization": "Bearer " + self.foundation_client.get_access_token()}
|
||||||
return requests.get(f"{self.baseurl}{endpoint}", headers=headers, params=params or None).json()
|
return requests.get(f"{self.baseurl}{endpoint}", headers=headers, params=params or None).json()
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
# Server-Test
|
|
||||||
|
|
||||||
1. Cd to the server directory i.e cd `IfcOpenShell\src\bcfserver`
|
|
||||||
|
|
||||||
2. Set up the server by installing the dependencies
|
|
||||||
|
|
||||||
3. Run `pip install -r requirements.txt` to install the dependencies
|
|
||||||
|
|
||||||
4. In Python Shell, do the following
|
|
||||||
|
|
||||||
- from run import db
|
|
||||||
- db.create_all() to setup the database table
|
|
||||||
|
|
||||||
5. Run `set FLASK_APP=run.py`
|
|
||||||
6. Run `flask run` to start the server
|
|
||||||
7. Go to [http://localhost:5000](http://localhost:5000) to see the server
|
|
||||||
|
|
||||||
# Register the user
|
|
||||||
|
|
||||||
1. Go to http://localhost:5000/register to register the user
|
|
||||||
2. Create the client
|
|
||||||
3. For grant type enter authorization_code
|
|
||||||
4. For response_type enter code secret
|
|
||||||
5. Enter the scope and create the client
|
|
||||||
|
|
||||||
### You will be redirected to the page with the details of your client id and secret
|
|
||||||
|
|
||||||
# Foundation API
|
|
||||||
|
|
||||||
- Set the Base URL will be `http://127.0.0.1:5000/`
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
from flask import jsonify, url_for, redirect, render_template, request, session, flash
|
|
||||||
from flask_login import login_user, logout_user, login_required, current_user
|
|
||||||
from flask.blueprints import Blueprint
|
|
||||||
from website.models import User, OAuth2AuthorizationCode, OAuth2Token, OAuth2Client
|
|
||||||
from run import app, db
|
|
||||||
import json
|
|
||||||
|
|
||||||
bcf = Blueprint("bcf", __name__, template_folder="templates", url_prefix="/bcf/3.0")
|
|
||||||
|
|
||||||
|
|
||||||
@bcf.route("/projects")
|
|
||||||
def projects():
|
|
||||||
Headers = str.split(request.headers["Authorization"])
|
|
||||||
token = Headers[1]
|
|
||||||
access_token = OAuth2Token.query.filter_by(access_token=token).first()
|
|
||||||
print(access_token)
|
|
||||||
if access_token:
|
|
||||||
Body = {
|
|
||||||
"project_id": "F445F4F2-4D02-4B2A-B612-5E456BEF9137",
|
|
||||||
"name": "Example project 1",
|
|
||||||
"authorization": {"project_actions": ["createTopic", "createDocument"]},
|
|
||||||
}, {
|
|
||||||
"project_id": "A233FBB2-3A3B-EFF4-C123-DE22ABC8414",
|
|
||||||
"name": "Example project 2",
|
|
||||||
"authorization": {"project_actions": []},
|
|
||||||
}
|
|
||||||
response = app.response_class(
|
|
||||||
response=json.dumps(Body),
|
|
||||||
status=200,
|
|
||||||
mimetype="application/json",
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
else:
|
|
||||||
message = {"error": "User not recognized"}
|
|
||||||
response = app.response_class(
|
|
||||||
response=jsonify(message),
|
|
||||||
status=200,
|
|
||||||
mimetype="application/json",
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
@bcf.route("/")
|
|
||||||
@login_required
|
|
||||||
def bcf_3():
|
|
||||||
return "<h1>BCF HOMPAGE</h1>"
|
|
||||||
|
|
||||||
|
|
||||||
@bcf.route("/projects/<project_id>")
|
|
||||||
def project_details(project_id):
|
|
||||||
return "Project details"
|
|
||||||
@@ -69,7 +69,7 @@ endif
|
|||||||
cp -r blenderbim/* dist/blenderbim/
|
cp -r blenderbim/* dist/blenderbim/
|
||||||
|
|
||||||
# Provides IfcOpenShell Python functionality
|
# Provides IfcOpenShell Python functionality
|
||||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-$(PYNUMBER)-v0.6.0-0087fa8-$(PLATFORM)64.zip
|
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-$(PYNUMBER)-v0.6.0-2f3c79a-$(PLATFORM)64.zip
|
||||||
cd dist/working && unzip ifcblender*
|
cd dist/working && unzip ifcblender*
|
||||||
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
|
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION((),'2;1');
|
||||||
|
FILE_NAME('EPset_Parametric.ifc','2020-01-01T00:00:00',(),(),'EPset_Parametric','EPset_Parametric',$);
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPROPERTYSETTEMPLATE('1Ja6GlovjFoeonRLXJwyyl',$,'EPset_Parametric','',.PSET_TYPEDRIVENOVERRIDE.,'IfcTypeObject',(#2,#3));
|
||||||
|
#2=IFCSIMPLEPROPERTYTEMPLATE('2ZxSvp00j5hxrWv$Q8ajzx',$,'Engine','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||||
|
#3=IFCSIMPLEPROPERTYTEMPLATE('2AN6WomqXCjRkP0g2iwEjp',$,'LayerSetDirection','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION((),'2;1');
|
||||||
|
FILE_NAME('Qto_TaskBaseQuantities.ifc','2020-01-01T00:00:00',(),(),'Qto_TaskBaseQuantities','Qto_TaskBaseQuantities',$);
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPROPERTYSETTEMPLATE('3uZCoCxrr8MA2yVRLzH6P4',$,'Qto_TaskBaseQuantities','',.QTO_OCCURRENCEDRIVEN.,'IfcTask',(#2,#3));
|
||||||
|
#2=IFCSIMPLEPROPERTYTEMPLATE('0q8nByVuP4cv46Pm4wfGpf',$,'StandardWork','',.Q_TIME.,$,$,$,$,$,$,.READWRITE.);
|
||||||
|
#3=IFCSIMPLEPROPERTYTEMPLATE('2uf5oHjAz4pRmtn9ihJiAs',$,'OvertimeWork','',.Q_TIME.,$,$,$,$,$,$,.READWRITE.);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -38,6 +38,7 @@ def import_attributes(ifc_class, props, data, callback=None):
|
|||||||
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||||
if data_type == "entity" or (isinstance(data_type, tuple) and "entity" in ".".join(data_type)):
|
if data_type == "entity" or (isinstance(data_type, tuple) and "entity" in ".".join(data_type)):
|
||||||
|
callback(attribute.name(), None, data) if callback else None
|
||||||
continue
|
continue
|
||||||
new = props.add()
|
new = props.add()
|
||||||
new.name = attribute.name()
|
new.name = attribute.name()
|
||||||
@@ -65,20 +66,21 @@ def import_attributes(ifc_class, props, data, callback=None):
|
|||||||
|
|
||||||
def export_attributes(props, callback=None):
|
def export_attributes(props, callback=None):
|
||||||
attributes = {}
|
attributes = {}
|
||||||
for attribute in props:
|
for prop in props:
|
||||||
is_handled_by_callback = callback(attributes, attribute) if callback else False
|
is_handled_by_callback = callback(attributes, prop) if callback else False
|
||||||
if attribute.is_null:
|
if is_handled_by_callback:
|
||||||
attributes[attribute.name] = None
|
continue # Our job is done
|
||||||
elif is_handled_by_callback:
|
|
||||||
pass # Our job is done
|
if prop.is_null:
|
||||||
elif attribute.data_type == "string":
|
attributes[prop.name] = None
|
||||||
attributes[attribute.name] = attribute.string_value
|
elif prop.data_type == "string":
|
||||||
elif attribute.data_type == "boolean":
|
attributes[prop.name] = prop.string_value
|
||||||
attributes[attribute.name] = attribute.bool_value
|
elif prop.data_type == "boolean":
|
||||||
elif attribute.data_type == "integer":
|
attributes[prop.name] = prop.bool_value
|
||||||
attributes[attribute.name] = attribute.int_value
|
elif prop.data_type == "integer":
|
||||||
elif attribute.data_type == "float":
|
attributes[prop.name] = prop.int_value
|
||||||
attributes[attribute.name] = attribute.float_value
|
elif prop.data_type == "float":
|
||||||
elif attribute.data_type == "enum":
|
attributes[prop.name] = prop.float_value
|
||||||
attributes[attribute.name] = attribute.enum_value
|
elif prop.data_type == "enum":
|
||||||
|
attributes[prop.name] = prop.enum_value
|
||||||
return attributes
|
return attributes
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ classes = (
|
|||||||
operator.ExpandCostItem,
|
operator.ExpandCostItem,
|
||||||
operator.ContractCostItem,
|
operator.ContractCostItem,
|
||||||
operator.RemoveCostItem,
|
operator.RemoveCostItem,
|
||||||
operator.AssignCostItemProduct,
|
operator.AssignCostItemQuantity,
|
||||||
operator.UnassignCostItemProduct,
|
operator.UnassignCostItemQuantity,
|
||||||
operator.AddCostItemQuantity,
|
operator.AddCostItemQuantity,
|
||||||
operator.RemoveCostItemQuantity,
|
operator.RemoveCostItemQuantity,
|
||||||
operator.AddCostValue,
|
operator.AddCostValue,
|
||||||
@@ -36,11 +36,15 @@ classes = (
|
|||||||
operator.SelectCostItemProducts,
|
operator.SelectCostItemProducts,
|
||||||
operator.SelectCostScheduleProducts,
|
operator.SelectCostScheduleProducts,
|
||||||
operator.ImportCostScheduleCsv,
|
operator.ImportCostScheduleCsv,
|
||||||
|
operator.LoadCostItemQuantities,
|
||||||
prop.CostItem,
|
prop.CostItem,
|
||||||
|
prop.CostItemQuantity,
|
||||||
prop.BIMCostProperties,
|
prop.BIMCostProperties,
|
||||||
ui.BIM_PT_cost_schedules,
|
ui.BIM_PT_cost_schedules,
|
||||||
|
ui.BIM_PT_cost_item_quantities,
|
||||||
ui.BIM_UL_cost_items,
|
ui.BIM_UL_cost_items,
|
||||||
ui.BIM_UL_cost_columns,
|
ui.BIM_UL_cost_columns,
|
||||||
|
ui.BIM_UL_cost_item_quantities,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from blenderbim.bim.module.cost.prop import purge
|
|||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from bpy_extras.io_utils import ImportHelper
|
from bpy_extras.io_utils import ImportHelper
|
||||||
from ifcopenshell.api.cost.data import Data
|
from ifcopenshell.api.cost.data import Data
|
||||||
|
from ifcopenshell.api.unit.data import Data as UnitData
|
||||||
|
|
||||||
|
|
||||||
class AddCostSchedule(bpy.types.Operator):
|
class AddCostSchedule(bpy.types.Operator):
|
||||||
@@ -100,22 +101,9 @@ class EnableEditingCostItems(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound:
|
if context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound:
|
||||||
# lol
|
self.play_chaching_sound() # lol
|
||||||
# TODO: make pitch higher as costs rise
|
|
||||||
try:
|
|
||||||
import aud
|
|
||||||
|
|
||||||
device = aud.Device()
|
|
||||||
# chaching.mp3 is by Lucish_ CC-BY-3.0 https://freesound.org/people/Lucish_/sounds/554841/
|
|
||||||
sound = aud.Sound(os.path.join(context.scene.BIMProperties.data_dir, "chaching.mp3"))
|
|
||||||
handle = device.play(sound)
|
|
||||||
sound_buffered = aud.Sound.buffer(sound)
|
|
||||||
handle_buffered = device.play(sound_buffered)
|
|
||||||
handle.stop()
|
|
||||||
handle_buffered.stop()
|
|
||||||
except:
|
|
||||||
pass # ah well
|
|
||||||
self.props = context.scene.BIMCostProperties
|
self.props = context.scene.BIMCostProperties
|
||||||
|
self.props.is_cost_update_enabled = False
|
||||||
self.props.active_cost_schedule_id = self.cost_schedule
|
self.props.active_cost_schedule_id = self.cost_schedule
|
||||||
while len(self.props.cost_items) > 0:
|
while len(self.props.cost_items) > 0:
|
||||||
self.props.cost_items.remove(0)
|
self.props.cost_items.remove(0)
|
||||||
@@ -124,13 +112,31 @@ class EnableEditingCostItems(bpy.types.Operator):
|
|||||||
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
|
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
|
||||||
self.create_new_cost_item_li(related_object_id, 0)
|
self.create_new_cost_item_li(related_object_id, 0)
|
||||||
self.props.is_editing = "COST_ITEMS"
|
self.props.is_editing = "COST_ITEMS"
|
||||||
|
self.props.is_cost_update_enabled = True
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def play_chaching_sound(self):
|
||||||
|
# TODO: make pitch higher as costs rise
|
||||||
|
try:
|
||||||
|
import aud
|
||||||
|
|
||||||
|
device = aud.Device()
|
||||||
|
# chaching.mp3 is by Lucish_ CC-BY-3.0 https://freesound.org/people/Lucish_/sounds/554841/
|
||||||
|
sound = aud.Sound(os.path.join(context.scene.BIMProperties.data_dir, "chaching.mp3"))
|
||||||
|
handle = device.play(sound)
|
||||||
|
sound_buffered = aud.Sound.buffer(sound)
|
||||||
|
handle_buffered = device.play(sound_buffered)
|
||||||
|
handle.stop()
|
||||||
|
handle_buffered.stop()
|
||||||
|
except:
|
||||||
|
pass # ah well
|
||||||
|
|
||||||
def create_new_cost_item_li(self, related_object_id, level_index):
|
def create_new_cost_item_li(self, related_object_id, level_index):
|
||||||
cost_item = Data.cost_items[related_object_id]
|
cost_item = Data.cost_items[related_object_id]
|
||||||
new = self.props.cost_items.add()
|
new = self.props.cost_items.add()
|
||||||
new.ifc_definition_id = related_object_id
|
new.ifc_definition_id = related_object_id
|
||||||
new.name = cost_item["Name"] or "Unnamed"
|
new.name = cost_item["Name"] or "Unnamed"
|
||||||
|
new.identification = cost_item["Identification"] or "XXX"
|
||||||
new.is_expanded = related_object_id not in self.contracted_cost_items
|
new.is_expanded = related_object_id not in self.contracted_cost_items
|
||||||
new.level_index = level_index
|
new.level_index = level_index
|
||||||
if cost_item["IsNestedBy"]:
|
if cost_item["IsNestedBy"]:
|
||||||
@@ -297,61 +303,81 @@ class EditCostItem(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class AssignCostItemProduct(bpy.types.Operator):
|
class AssignCostItemQuantity(bpy.types.Operator):
|
||||||
bl_idname = "bim.assign_cost_item_product"
|
bl_idname = "bim.assign_cost_item_quantity"
|
||||||
bl_label = "Assign Control"
|
bl_label = "Assign Cost Item Quantity"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
cost_item: bpy.props.IntProperty()
|
cost_item: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object_type: bpy.props.StringProperty()
|
||||||
|
prop_name: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
related_objects = (
|
|
||||||
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
|
||||||
)
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
|
if self.related_object_type == "PRODUCT":
|
||||||
|
products = [
|
||||||
|
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
||||||
|
for o in context.selected_objects
|
||||||
|
if o.BIMObjectProperties.ifc_definition_id
|
||||||
|
]
|
||||||
|
elif self.related_object_type == "PROCESS":
|
||||||
|
products = [
|
||||||
|
self.file.by_id(
|
||||||
|
context.scene.BIMTaskTreeProperties.tasks[
|
||||||
|
context.scene.BIMWorkScheduleProperties.active_task_index
|
||||||
|
].ifc_definition_id
|
||||||
|
)
|
||||||
|
]
|
||||||
|
elif self.related_object_type == "RESOURCE":
|
||||||
|
products = [
|
||||||
|
self.file.by_id(
|
||||||
|
context.scene.BIMResourceTreeProperties.resources[
|
||||||
|
context.scene.BIMResourceProperties.active_resource_index
|
||||||
|
].ifc_definition_id
|
||||||
|
)
|
||||||
|
]
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"cost.assign_cost_item_product",
|
"cost.assign_cost_item_quantity",
|
||||||
self.file,
|
self.file,
|
||||||
cost_item=self.file.by_id(self.cost_item),
|
cost_item=self.file.by_id(self.cost_item),
|
||||||
products=[
|
products=products,
|
||||||
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
prop_name=self.prop_name,
|
||||||
for o in related_objects
|
|
||||||
if o.BIMObjectProperties.ifc_definition_id
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
|
bpy.ops.bim.load_cost_item_quantities()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class UnassignCostItemProduct(bpy.types.Operator):
|
class UnassignCostItemQuantity(bpy.types.Operator):
|
||||||
bl_idname = "bim.unassign_cost_item_product"
|
bl_idname = "bim.unassign_cost_item_quantity"
|
||||||
bl_label = "Unassign Control"
|
bl_label = "Unassign Cost Item Quantity"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
cost_item: bpy.props.IntProperty()
|
cost_item: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
related_objects = (
|
|
||||||
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
|
||||||
)
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
|
if self.related_object:
|
||||||
|
products = [self.file.by_id(self.related_object)]
|
||||||
|
else:
|
||||||
|
products = [
|
||||||
|
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
||||||
|
for o in bpy.context.selected_objects
|
||||||
|
if o.BIMObjectProperties.ifc_definition_id
|
||||||
|
]
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"cost.unassign_cost_item_product",
|
"cost.unassign_cost_item_quantity",
|
||||||
self.file,
|
self.file,
|
||||||
cost_item=self.file.by_id(self.cost_item),
|
cost_item=self.file.by_id(self.cost_item),
|
||||||
products=[
|
products=products,
|
||||||
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
|
|
||||||
for o in related_objects
|
|
||||||
if o.BIMObjectProperties.ifc_definition_id
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
|
bpy.ops.bim.load_cost_item_quantities()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -396,21 +422,10 @@ class AddCostItemQuantity(bpy.types.Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
self.props = context.scene.BIMCostProperties
|
self.props = context.scene.BIMCostProperties
|
||||||
if self.props.quantity_types == "QTO":
|
self.add_manual_quantity()
|
||||||
self.add_quantities_from_qto_filter()
|
|
||||||
else:
|
|
||||||
self.add_manual_quantity()
|
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def add_quantities_from_qto_filter(self):
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"cost.assign_cost_item_product_quantities",
|
|
||||||
self.file,
|
|
||||||
cost_item=self.file.by_id(self.cost_item),
|
|
||||||
prop_name=self.props.quantity_names,
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_manual_quantity(self):
|
def add_manual_quantity(self):
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"cost.add_cost_item_quantity",
|
"cost.add_cost_item_quantity",
|
||||||
@@ -557,6 +572,35 @@ class EnableEditingCostItemValue(bpy.types.Operator):
|
|||||||
prop.data_type = "float"
|
prop.data_type = "float"
|
||||||
prop.float_value = 0.0 if prop.is_null else data[name]
|
prop.float_value = 0.0 if prop.is_null else data[name]
|
||||||
return True
|
return True
|
||||||
|
if (
|
||||||
|
name == "UnitBasis"
|
||||||
|
and Data.cost_schedules[bpy.context.scene.BIMCostProperties.active_cost_schedule_id]["PredefinedType"]
|
||||||
|
== "SCHEDULEOFRATES"
|
||||||
|
):
|
||||||
|
prop = self.props.cost_value_attributes.add()
|
||||||
|
prop.name = "UnitBasisValue"
|
||||||
|
prop.data_type = "float"
|
||||||
|
prop.is_null = data["UnitBasis"] is None
|
||||||
|
prop.is_optional = True
|
||||||
|
if data["UnitBasis"]:
|
||||||
|
prop.float_value = data["UnitBasis"]["ValueComponent"] or 0
|
||||||
|
else:
|
||||||
|
prop.float_value = 0
|
||||||
|
prop = self.props.cost_value_attributes.add()
|
||||||
|
prop.name = "UnitBasisUnit"
|
||||||
|
prop.data_type = "enum"
|
||||||
|
prop.is_null = prop.is_optional = False
|
||||||
|
units = {}
|
||||||
|
for unit_id, unit in UnitData.units.items():
|
||||||
|
if unit.get("UnitType", None) in ["AREAUNIT", "LENGTHUNIT", "TIMEUNIT", "VOLUMEUNIT", "MASSUNIT"]:
|
||||||
|
name = unit["Name"]
|
||||||
|
if unit.get("Prefix", None):
|
||||||
|
name = f"(unit['Prefix']) {name}"
|
||||||
|
units[unit_id] = f"{unit['UnitType']} / {name}"
|
||||||
|
prop.enum_items = json.dumps(units)
|
||||||
|
if data["UnitBasis"] and data["UnitBasis"]["UnitComponent"]:
|
||||||
|
prop.enum_value = str(data["UnitBasis"]["UnitComponent"])
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class DisableEditingCostItemValue(bpy.types.Operator):
|
class DisableEditingCostItemValue(bpy.types.Operator):
|
||||||
@@ -581,7 +625,7 @@ class EditCostValue(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMCostProperties
|
props = context.scene.BIMCostProperties
|
||||||
attributes = blenderbim.bim.helper.export_attributes(props.cost_value_attributes)
|
attributes = blenderbim.bim.helper.export_attributes(props.cost_value_attributes, self.export_attributes)
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"cost.edit_cost_value",
|
"cost.edit_cost_value",
|
||||||
@@ -592,6 +636,21 @@ class EditCostValue(bpy.types.Operator):
|
|||||||
bpy.ops.bim.disable_editing_cost_item_value()
|
bpy.ops.bim.disable_editing_cost_item_value()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def export_attributes(self, attributes, prop):
|
||||||
|
if prop.name == "UnitBasisValue":
|
||||||
|
if prop.is_null:
|
||||||
|
attributes["UnitBasis"] = None
|
||||||
|
return True
|
||||||
|
attributes["UnitBasis"] = {
|
||||||
|
"ValueComponent": prop.float_value or 1,
|
||||||
|
"UnitComponent": IfcStore.get_file().by_id(
|
||||||
|
int(bpy.context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
return True
|
||||||
|
if prop.name == "UnitBasisUnit":
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class CopyCostItemValues(bpy.types.Operator):
|
class CopyCostItemValues(bpy.types.Operator):
|
||||||
bl_idname = "bim.copy_cost_item_values"
|
bl_idname = "bim.copy_cost_item_values"
|
||||||
@@ -622,7 +681,7 @@ class SelectCostItemProducts(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
related_products = Data.cost_items[self.cost_item]["Controls"]
|
related_products = Data.cost_items[self.cost_item]["Controls"].keys()
|
||||||
for obj in context.visible_objects:
|
for obj in context.visible_objects:
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
if obj.BIMObjectProperties.ifc_definition_id in related_products:
|
if obj.BIMObjectProperties.ifc_definition_id in related_products:
|
||||||
@@ -649,7 +708,7 @@ class SelectCostScheduleProducts(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def get_related_products(self, cost_item):
|
def get_related_products(self, cost_item):
|
||||||
self.related_products.extend(cost_item["Controls"])
|
self.related_products.extend(cost_item["Controls"].keys())
|
||||||
for child_id in cost_item["IsNestedBy"]:
|
for child_id in cost_item["IsNestedBy"]:
|
||||||
self.get_related_products(Data.cost_items[child_id])
|
self.get_related_products(Data.cost_items[child_id])
|
||||||
|
|
||||||
@@ -702,3 +761,35 @@ class RemoveCostColumn(bpy.types.Operator):
|
|||||||
Data.set_categories([c.name for c in self.props.columns])
|
Data.set_categories([c.name for c in self.props.columns])
|
||||||
Data.load(IfcStore.get_file())
|
Data.load(IfcStore.get_file())
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class LoadCostItemQuantities(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.load_cost_item_quantities"
|
||||||
|
bl_label = "Load Cost Item Quantities"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.props = context.scene.BIMCostProperties
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
while len(self.props.cost_item_products) > 0:
|
||||||
|
self.props.cost_item_products.remove(0)
|
||||||
|
while len(self.props.cost_item_processes) > 0:
|
||||||
|
self.props.cost_item_processes.remove(0)
|
||||||
|
while len(self.props.cost_item_resources) > 0:
|
||||||
|
self.props.cost_item_resources.remove(0)
|
||||||
|
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
||||||
|
for control_id, quantity_ids in Data.cost_items[ifc_definition_id]["Controls"].items():
|
||||||
|
related_object = self.file.by_id(control_id)
|
||||||
|
if related_object.is_a("IfcProduct"):
|
||||||
|
new = self.props.cost_item_products.add()
|
||||||
|
elif related_object.is_a("IfcProcess"):
|
||||||
|
new = self.props.cost_item_processes.add()
|
||||||
|
elif related_object.is_a("IfcResource"):
|
||||||
|
new = self.props.cost_item_resources.add()
|
||||||
|
new.ifc_definition_id = control_id
|
||||||
|
new.name = related_object.Name or "Unnamed"
|
||||||
|
total_quantity = 0
|
||||||
|
for quantity_id in quantity_ids:
|
||||||
|
total_quantity += self.file.by_id(quantity_id)[3]
|
||||||
|
new.total_quantity = total_quantity
|
||||||
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -18,20 +18,35 @@ from bpy.props import (
|
|||||||
|
|
||||||
|
|
||||||
quantitytypes_enum = []
|
quantitytypes_enum = []
|
||||||
quantitynames_enum = []
|
productquantitynames_enum = []
|
||||||
|
productquantitynames_count = []
|
||||||
|
processquantitynames_enum = []
|
||||||
|
processquantitynames_id = 0
|
||||||
|
resourcequantitynames_enum = []
|
||||||
|
resourcequantitynames_id = 0
|
||||||
|
|
||||||
|
|
||||||
def purge():
|
def purge():
|
||||||
global quantitytypes_enum
|
global quantitytypes_enum
|
||||||
global quantitynames_enum
|
global productquantitynames_enum
|
||||||
|
global productquantitynames_count
|
||||||
|
global processquantitynames_enum
|
||||||
|
global processquantitynames_id
|
||||||
|
global resourcequantitynames_enum
|
||||||
|
global resourcequantitynames_id
|
||||||
quantitytypes_enum = []
|
quantitytypes_enum = []
|
||||||
quantitynames_enum = []
|
productquantitynames_enum = []
|
||||||
|
productquantitynames_count = []
|
||||||
|
processquantitynames_enum = []
|
||||||
|
processquantitynames_id = 0
|
||||||
|
resourcequantitynames_enum = []
|
||||||
|
resourcequantitynames_id = 0
|
||||||
|
|
||||||
|
|
||||||
def getQuantityTypes(self, context):
|
def getQuantityTypes(self, context):
|
||||||
global quantitytypes_enum
|
global quantitytypes_enum
|
||||||
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
||||||
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
|
quantitytypes_enum = []
|
||||||
quantitytypes_enum.extend(
|
quantitytypes_enum.extend(
|
||||||
[
|
[
|
||||||
(t.name(), t.name(), "")
|
(t.name(), t.name(), "")
|
||||||
@@ -41,26 +56,99 @@ def getQuantityTypes(self, context):
|
|||||||
return quantitytypes_enum
|
return quantitytypes_enum
|
||||||
|
|
||||||
|
|
||||||
def getQuantityNames(self, context):
|
def getProductQuantityNames(self, context):
|
||||||
global quantitynames_enum
|
global productquantitynames_enum
|
||||||
|
global productquantitynames_count
|
||||||
ifc_file = IfcStore.get_file()
|
ifc_file = IfcStore.get_file()
|
||||||
if len(quantitynames_enum) == 0 and ifc_file:
|
total_selected_objects = len(context.selected_objects)
|
||||||
|
if total_selected_objects != productquantitynames_count or total_selected_objects == 1:
|
||||||
|
productquantitynames_enum = []
|
||||||
|
productquantitynames_count = total_selected_objects
|
||||||
names = set()
|
names = set()
|
||||||
for element_id in Data.cost_items[self.active_cost_item_id]["Controls"]:
|
for obj in context.selected_objects:
|
||||||
|
element_id = obj.BIMObjectProperties.ifc_definition_id
|
||||||
|
if not element_id:
|
||||||
|
continue
|
||||||
|
potential_names = set()
|
||||||
if element_id not in PsetData.products:
|
if element_id not in PsetData.products:
|
||||||
PsetData.load(IfcStore.get_file(), element_id)
|
PsetData.load(ifc_file, element_id)
|
||||||
for qto_id in PsetData.products[element_id]["qtos"]:
|
for qto_id in PsetData.products[element_id]["qtos"]:
|
||||||
qto = PsetData.qtos[qto_id]
|
qto = PsetData.qtos[qto_id]
|
||||||
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
|
[potential_names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
|
||||||
quantitynames_enum.extend([(n, n, "") for n in names])
|
names = names.intersection(potential_names) if names else potential_names
|
||||||
return quantitynames_enum
|
productquantitynames_enum.extend([(n, n, "") for n in names])
|
||||||
|
return productquantitynames_enum
|
||||||
|
|
||||||
|
|
||||||
|
def getProcessQuantityNames(self, context):
|
||||||
|
global processquantitynames_enum
|
||||||
|
global processquantitynames_id
|
||||||
|
ifc_file = IfcStore.get_file()
|
||||||
|
active_task_index = context.scene.BIMWorkScheduleProperties.active_task_index
|
||||||
|
total_tasks = len(context.scene.BIMTaskTreeProperties.tasks)
|
||||||
|
if not total_tasks or active_task_index >= total_tasks:
|
||||||
|
return []
|
||||||
|
ifc_definition_id = context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id
|
||||||
|
if processquantitynames_id != ifc_definition_id:
|
||||||
|
processquantitynames_enum = []
|
||||||
|
processquantitynames_id = ifc_definition_id
|
||||||
|
names = set()
|
||||||
|
if ifc_definition_id not in PsetData.products:
|
||||||
|
PsetData.load(ifc_file, ifc_definition_id)
|
||||||
|
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
|
||||||
|
qto = PsetData.qtos[qto_id]
|
||||||
|
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
|
||||||
|
processquantitynames_enum.extend([(n, n, "") for n in names])
|
||||||
|
return processquantitynames_enum
|
||||||
|
|
||||||
|
|
||||||
|
def getResourceQuantityNames(self, context):
|
||||||
|
global resourcequantitynames_enum
|
||||||
|
global resourcequantitynames_id
|
||||||
|
ifc_file = IfcStore.get_file()
|
||||||
|
active_resource_index = context.scene.BIMResourceProperties.active_resource_index
|
||||||
|
total_resources = len(context.scene.BIMResourceTreeProperties.resources)
|
||||||
|
if not total_resources or active_resource_index >= total_resources:
|
||||||
|
return []
|
||||||
|
ifc_definition_id = context.scene.BIMResourceTreeProperties.resources[active_resource_index].ifc_definition_id
|
||||||
|
if resourcequantitynames_id != ifc_definition_id:
|
||||||
|
resourcequantitynames_enum = []
|
||||||
|
resourcequantitynames_id = ifc_definition_id
|
||||||
|
names = set()
|
||||||
|
if ifc_definition_id not in PsetData.products:
|
||||||
|
PsetData.load(ifc_file, ifc_definition_id)
|
||||||
|
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
|
||||||
|
qto = PsetData.qtos[qto_id]
|
||||||
|
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
|
||||||
|
resourcequantitynames_enum.extend([(n, n, "") for n in names])
|
||||||
|
return resourcequantitynames_enum
|
||||||
|
|
||||||
|
|
||||||
|
def update_cost_item_index(self, context):
|
||||||
|
bpy.ops.bim.load_cost_item_quantities()
|
||||||
|
|
||||||
|
|
||||||
|
def updateCostItemIdentification(self, context):
|
||||||
|
props = context.scene.BIMCostProperties
|
||||||
|
if not props.is_cost_update_enabled or self.identification == "XXX":
|
||||||
|
return
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"cost.edit_cost_item",
|
||||||
|
self.file,
|
||||||
|
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}},
|
||||||
|
)
|
||||||
|
Data.load(self.file)
|
||||||
|
if props.active_cost_item_id == self.ifc_definition_id:
|
||||||
|
attribute = props.cost_item_attributes.get("Identification")
|
||||||
|
attribute.string_value = self.identification
|
||||||
|
|
||||||
|
|
||||||
def updateCostItemName(self, context):
|
def updateCostItemName(self, context):
|
||||||
if self.name == "Unnamed":
|
props = context.scene.BIMCostProperties
|
||||||
|
if not props.is_cost_update_enabled or self.name == "Unnamed":
|
||||||
return
|
return
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
props = context.scene.BIMCostProperties
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"cost.edit_cost_item",
|
"cost.edit_cost_item",
|
||||||
self.file,
|
self.file,
|
||||||
@@ -74,24 +162,34 @@ def updateCostItemName(self, context):
|
|||||||
|
|
||||||
class CostItem(PropertyGroup):
|
class CostItem(PropertyGroup):
|
||||||
name: StringProperty(name="Name", update=updateCostItemName)
|
name: StringProperty(name="Name", update=updateCostItemName)
|
||||||
|
identification: StringProperty(name="Identification", update=updateCostItemIdentification)
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
has_children: BoolProperty(name="Has Children")
|
has_children: BoolProperty(name="Has Children")
|
||||||
is_expanded: BoolProperty(name="Is Expanded")
|
is_expanded: BoolProperty(name="Is Expanded")
|
||||||
level_index: IntProperty(name="Level Index")
|
level_index: IntProperty(name="Level Index")
|
||||||
|
|
||||||
|
|
||||||
|
class CostItemQuantity(PropertyGroup):
|
||||||
|
name: StringProperty(name="Name")
|
||||||
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
|
total_quantity: FloatProperty(name="Total Quantity")
|
||||||
|
|
||||||
|
|
||||||
class BIMCostProperties(PropertyGroup):
|
class BIMCostProperties(PropertyGroup):
|
||||||
|
is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True)
|
||||||
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
|
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
|
||||||
is_editing: StringProperty(name="Is Editing")
|
is_editing: StringProperty(name="Is Editing")
|
||||||
active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id")
|
active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id")
|
||||||
cost_items: CollectionProperty(name="Cost Items", type=CostItem)
|
cost_items: CollectionProperty(name="Cost Items", type=CostItem)
|
||||||
active_cost_item_id: IntProperty(name="Active Cost Id")
|
active_cost_item_id: IntProperty(name="Active Cost Id")
|
||||||
cost_item_editing_type: StringProperty(name="Cost Item Editing Type")
|
cost_item_editing_type: StringProperty(name="Cost Item Editing Type")
|
||||||
active_cost_item_index: IntProperty(name="Active Cost Item Index")
|
active_cost_item_index: IntProperty(name="Active Cost Item Index", update=update_cost_item_index)
|
||||||
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
||||||
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
|
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
|
||||||
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
|
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
|
||||||
quantity_names: EnumProperty(items=getQuantityNames, name="Quantity Names")
|
product_quantity_names: EnumProperty(items=getProductQuantityNames, name="Product Quantity Names")
|
||||||
|
process_quantity_names: EnumProperty(items=getProcessQuantityNames, name="Process Quantity Names")
|
||||||
|
resource_quantity_names: EnumProperty(items=getResourceQuantityNames, name="Resource Quantity Names")
|
||||||
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
|
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
|
||||||
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
|
||||||
cost_types: EnumProperty(
|
cost_types: EnumProperty(
|
||||||
@@ -109,3 +207,9 @@ class BIMCostProperties(PropertyGroup):
|
|||||||
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
|
||||||
columns: CollectionProperty(name="Columns", type=StrProperty)
|
columns: CollectionProperty(name="Columns", type=StrProperty)
|
||||||
active_column_index: IntProperty(name="Active Column Index")
|
active_column_index: IntProperty(name="Active Column Index")
|
||||||
|
cost_item_products: CollectionProperty(name="Cost Item Products", type=CostItemQuantity)
|
||||||
|
active_cost_item_product_index: IntProperty(name="Active Cost Item Product Index")
|
||||||
|
cost_item_processes: CollectionProperty(name="Cost Item Processes", type=CostItemQuantity)
|
||||||
|
active_cost_item_process_index: IntProperty(name="Active Cost Item Process Index")
|
||||||
|
cost_item_resources: CollectionProperty(name="Cost Item Resources", type=CostItemQuantity)
|
||||||
|
active_cost_item_resource_index: IntProperty(name="Active Cost Item Resource Index")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import blenderbim.bim.module.cost.prop as CostProp
|
||||||
from bpy.types import Panel, UIList
|
from bpy.types import Panel, UIList
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.cost.data import Data
|
from ifcopenshell.api.cost.data import Data
|
||||||
@@ -75,6 +76,32 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
|
|
||||||
def draw_editable_cost_item_ui(self, cost_schedule_id):
|
def draw_editable_cost_item_ui(self, cost_schedule_id):
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.alignment = "RIGHT"
|
||||||
|
ifc_definition_id = None
|
||||||
|
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
|
||||||
|
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
|
||||||
|
if ifc_definition_id:
|
||||||
|
|
||||||
|
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
|
||||||
|
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
||||||
|
op.cost_item = ifc_definition_id
|
||||||
|
|
||||||
|
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
|
||||||
|
op.cost_item = ifc_definition_id
|
||||||
|
|
||||||
|
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = ifc_definition_id
|
||||||
|
|
||||||
|
if self.props.active_cost_item_id == ifc_definition_id:
|
||||||
|
if self.props.cost_item_editing_type == "ATTRIBUTES":
|
||||||
|
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
|
||||||
|
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
|
||||||
|
else:
|
||||||
|
op = row.operator("bim.enable_editing_cost_item", text="", icon="GREASEPENCIL")
|
||||||
|
op.cost_item = ifc_definition_id
|
||||||
|
|
||||||
|
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = ifc_definition_id
|
||||||
|
|
||||||
self.layout.template_list(
|
self.layout.template_list(
|
||||||
"BIM_UL_cost_items",
|
"BIM_UL_cost_items",
|
||||||
"",
|
"",
|
||||||
@@ -108,8 +135,6 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
def draw_editable_cost_item_quantities_ui(self):
|
def draw_editable_cost_item_quantities_ui(self):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(self.props, "quantity_types", text="")
|
row.prop(self.props, "quantity_types", text="")
|
||||||
if self.props.quantity_types == "QTO":
|
|
||||||
row.prop(self.props, "quantity_names", text="")
|
|
||||||
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
|
||||||
op.cost_item = self.props.active_cost_item_id
|
op.cost_item = self.props.active_cost_item_id
|
||||||
op.ifc_class = self.props.quantity_types
|
op.ifc_class = self.props.quantity_types
|
||||||
@@ -244,10 +269,112 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_cost_item_quantities(Panel):
|
||||||
|
bl_label = "IFC Cost Item Quantities"
|
||||||
|
bl_idname = "BIM_PT_cost_item_quantities"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_parent_id = "BIM_PT_cost_schedules"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.BIMCostProperties
|
||||||
|
total_cost_items = len(props.cost_items)
|
||||||
|
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
self.props = context.scene.BIMCostProperties
|
||||||
|
|
||||||
|
cost_item = self.props.cost_items[self.props.active_cost_item_index]
|
||||||
|
|
||||||
|
grid = self.layout.grid_flow(columns=3, even_columns=True)
|
||||||
|
|
||||||
|
# Column1
|
||||||
|
col = grid.column()
|
||||||
|
|
||||||
|
row2 = col.row(align=True)
|
||||||
|
row2.label(text="Elements")
|
||||||
|
op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
|
|
||||||
|
row2 = col.row()
|
||||||
|
row2.template_list(
|
||||||
|
"BIM_UL_cost_item_quantities",
|
||||||
|
"",
|
||||||
|
self.props,
|
||||||
|
"cost_item_products",
|
||||||
|
self.props,
|
||||||
|
"active_cost_item_product_index",
|
||||||
|
)
|
||||||
|
|
||||||
|
row2 = col.row(align=True)
|
||||||
|
row2.prop(self.props, "product_quantity_names", text="")
|
||||||
|
op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE")
|
||||||
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
|
op.related_object = 0
|
||||||
|
if CostProp.productquantitynames_enum:
|
||||||
|
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
|
||||||
|
op.related_object_type = "PRODUCT"
|
||||||
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
|
op.prop_name = self.props.product_quantity_names
|
||||||
|
|
||||||
|
# Column2
|
||||||
|
col = grid.column()
|
||||||
|
|
||||||
|
row2 = col.row(align=True)
|
||||||
|
row2.label(text="Tasks")
|
||||||
|
|
||||||
|
row2 = col.row()
|
||||||
|
row2.template_list(
|
||||||
|
"BIM_UL_cost_item_quantities",
|
||||||
|
"",
|
||||||
|
self.props,
|
||||||
|
"cost_item_processes",
|
||||||
|
self.props,
|
||||||
|
"active_cost_item_process_index",
|
||||||
|
)
|
||||||
|
|
||||||
|
row2 = col.row(align=True)
|
||||||
|
row2.prop(self.props, "process_quantity_names", text="")
|
||||||
|
if CostProp.processquantitynames_enum:
|
||||||
|
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
|
||||||
|
op.related_object_type = "PROCESS"
|
||||||
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
|
op.prop_name = self.props.process_quantity_names
|
||||||
|
|
||||||
|
# Column3
|
||||||
|
col = grid.column()
|
||||||
|
|
||||||
|
row2 = col.row(align=True)
|
||||||
|
row2.label(text="Resources")
|
||||||
|
|
||||||
|
row2 = col.row()
|
||||||
|
row2.template_list(
|
||||||
|
"BIM_UL_cost_item_quantities",
|
||||||
|
"",
|
||||||
|
self.props,
|
||||||
|
"cost_item_resources",
|
||||||
|
self.props,
|
||||||
|
"active_cost_item_resource_index",
|
||||||
|
)
|
||||||
|
|
||||||
|
row2 = col.row(align=True)
|
||||||
|
row2.prop(self.props, "resource_quantity_names", text="")
|
||||||
|
if CostProp.resourcequantitynames_enum:
|
||||||
|
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD")
|
||||||
|
op.related_object_type = "RESOURCE"
|
||||||
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
|
op.prop_name = self.props.resource_quantity_names
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_cost_items(UIList):
|
class BIM_UL_cost_items(UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
if item:
|
if item:
|
||||||
props = context.scene.BIMCostProperties
|
self.props = context.scene.BIMCostProperties
|
||||||
cost_item = Data.cost_items[item.ifc_definition_id]
|
cost_item = Data.cost_items[item.ifc_definition_id]
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
|
||||||
@@ -265,51 +392,30 @@ class BIM_UL_cost_items(UIList):
|
|||||||
else:
|
else:
|
||||||
row.label(text="", icon="DOT")
|
row.label(text="", icon="DOT")
|
||||||
|
|
||||||
split1 = row.split(factor=0.7)
|
split1 = row.split(factor=0.1)
|
||||||
split1.prop(item, "name", emboss=False, text="")
|
split1.prop(item, "identification", emboss=False, text="")
|
||||||
|
split2 = split1.split(factor=0.5)
|
||||||
|
split2.alignment = "RIGHT"
|
||||||
|
split2.prop(item, "name", emboss=False, text="")
|
||||||
|
|
||||||
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
|
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
|
||||||
op.cost_item = item.ifc_definition_id
|
split2.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol'] or '?'})")
|
||||||
row.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol']})")
|
|
||||||
|
|
||||||
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
|
split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
|
||||||
op.cost_item = item.ifc_definition_id
|
|
||||||
row.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
|
|
||||||
|
|
||||||
for column in props.columns:
|
for column in self.props.columns:
|
||||||
row.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
|
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
|
||||||
|
|
||||||
row.label(text="{0:.2f}".format(cost_item["TotalCostValue"]), icon="CON_TRANSLIKE")
|
split2.label(text="{0:.2f}".format(cost_item["TotalCostValue"]))
|
||||||
|
self.draw_buttons(split2, item, cost_item)
|
||||||
|
|
||||||
if context.active_object:
|
def draw_buttons(self, row, item, cost_item):
|
||||||
oprops = context.active_object.BIMObjectProperties
|
pass # TODO: reimplement somewhere with better UX
|
||||||
row = layout.row(align=True)
|
# elif self.props.active_cost_item_id:
|
||||||
if oprops.ifc_definition_id in cost_item["Controls"]:
|
# if self.props.cost_item_editing_type == "VALUES":
|
||||||
op = row.operator("bim.unassign_cost_item_product", text="", icon="KEYFRAME_HLT", emboss=False)
|
# op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN")
|
||||||
op.cost_item = item.ifc_definition_id
|
# op.source = self.props.active_cost_item_id
|
||||||
else:
|
# op.destination = item.ifc_definition_id
|
||||||
op = row.operator("bim.assign_cost_item_product", text="", icon="KEYFRAME", emboss=False)
|
|
||||||
op.cost_item = item.ifc_definition_id
|
|
||||||
|
|
||||||
if props.active_cost_item_id == item.ifc_definition_id:
|
|
||||||
if props.cost_item_editing_type == "ATTRIBUTES":
|
|
||||||
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
|
|
||||||
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
|
|
||||||
elif props.active_cost_item_id:
|
|
||||||
if props.cost_item_editing_type == "VALUES":
|
|
||||||
op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN")
|
|
||||||
op.source = props.active_cost_item_id
|
|
||||||
op.destination = item.ifc_definition_id
|
|
||||||
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
|
|
||||||
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
|
|
||||||
else:
|
|
||||||
op = row.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="")
|
|
||||||
op.cost_item = item.ifc_definition_id
|
|
||||||
row.operator(
|
|
||||||
"bim.enable_editing_cost_item", text="", icon="GREASEPENCIL"
|
|
||||||
).cost_item = item.ifc_definition_id
|
|
||||||
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
|
|
||||||
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_UL_cost_columns(UIList):
|
class BIM_UL_cost_columns(UIList):
|
||||||
@@ -319,3 +425,18 @@ class BIM_UL_cost_columns(UIList):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(item, "name", emboss=False, text="")
|
row.prop(item, "name", emboss=False, text="")
|
||||||
row.operator("bim.remove_cost_column", text="", icon="X").name = item.name
|
row.operator("bim.remove_cost_column", text="", icon="X").name = item.name
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_UL_cost_item_quantities(UIList):
|
||||||
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
|
props = context.scene.BIMCostProperties
|
||||||
|
cost_item = props.cost_items[props.active_cost_item_index]
|
||||||
|
|
||||||
|
if item:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.split(factor=0.8)
|
||||||
|
row.label(text=item.name)
|
||||||
|
row.label(text="{0:.2f}".format(item.total_quantity))
|
||||||
|
op = row.operator("bim.unassign_cost_item_quantity", text="", icon="X")
|
||||||
|
op.cost_item = cost_item.ifc_definition_id
|
||||||
|
op.related_object = item.ifc_definition_id
|
||||||
|
|||||||
@@ -120,25 +120,8 @@ class AssignMaterial(bpy.types.Operator):
|
|||||||
)
|
)
|
||||||
Data.load(IfcStore.get_file())
|
Data.load(IfcStore.get_file())
|
||||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
self.set_default_material(obj, element)
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def set_default_material(self, obj, element):
|
|
||||||
element_material = ifcopenshell.util.element.get_material(element)
|
|
||||||
material = [m for m in self.file.traverse(element_material) if m.is_a("IfcMaterial")]
|
|
||||||
if not material:
|
|
||||||
return
|
|
||||||
|
|
||||||
object_material_ids = [
|
|
||||||
om.BIMObjectProperties.ifc_definition_id
|
|
||||||
for om in obj.data.materials
|
|
||||||
if om is not None and om.BIMObjectProperties.ifc_definition_id
|
|
||||||
]
|
|
||||||
|
|
||||||
if material[0].id() in object_material_ids:
|
|
||||||
return
|
|
||||||
obj.data.materials.append(IfcStore.get_element(material[0].id()))
|
|
||||||
|
|
||||||
|
|
||||||
class UnassignMaterial(bpy.types.Operator):
|
class UnassignMaterial(bpy.types.Operator):
|
||||||
bl_idname = "bim.unassign_material"
|
bl_idname = "bim.unassign_material"
|
||||||
@@ -546,25 +529,8 @@ class EditAssignedMaterial(bpy.types.Operator):
|
|||||||
elif material_set.is_a("IfcMaterialProfileSet"):
|
elif material_set.is_a("IfcMaterialProfileSet"):
|
||||||
Data.load_profiles()
|
Data.load_profiles()
|
||||||
bpy.ops.bim.disable_editing_assigned_material(obj=obj.name)
|
bpy.ops.bim.disable_editing_assigned_material(obj=obj.name)
|
||||||
self.set_default_material(obj, self.file.by_id(obj.BIMObjectProperties.ifc_definition_id))
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def set_default_material(self, obj, element):
|
|
||||||
element_material = ifcopenshell.util.element.get_material(element)
|
|
||||||
material = [m for m in self.file.traverse(element_material) if m.is_a("IfcMaterial")]
|
|
||||||
if not material:
|
|
||||||
return
|
|
||||||
|
|
||||||
object_material_ids = [
|
|
||||||
om.BIMObjectProperties.ifc_definition_id
|
|
||||||
for om in obj.data.materials
|
|
||||||
if om is not None and om.BIMObjectProperties.ifc_definition_id
|
|
||||||
]
|
|
||||||
|
|
||||||
if material[0].id() in object_material_ids:
|
|
||||||
return
|
|
||||||
obj.data.materials.append(IfcStore.get_element(material[0].id()))
|
|
||||||
|
|
||||||
|
|
||||||
class EnableEditingMaterialSetItem(bpy.types.Operator):
|
class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_editing_material_set_item"
|
bl_idname = "bim.enable_editing_material_set_item"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import bpy
|
|||||||
from . import handler, prop, ui, grid, product, wall, slab, stair, door, window, opening, pie, workspace
|
from . import handler, prop, ui, grid, product, wall, slab, stair, door, window, opening, pie, workspace
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
product.AddEmptyType,
|
||||||
product.AddTypeInstance,
|
product.AddTypeInstance,
|
||||||
product.AlignProduct,
|
product.AlignProduct,
|
||||||
product.DynamicallyVoidProduct,
|
product.DynamicallyVoidProduct,
|
||||||
@@ -10,8 +11,7 @@ classes = (
|
|||||||
wall.AlignWall,
|
wall.AlignWall,
|
||||||
wall.FlipWall,
|
wall.FlipWall,
|
||||||
wall.SplitWall,
|
wall.SplitWall,
|
||||||
wall.AddWallOpening,
|
opening.AddElementOpening,
|
||||||
slab.AddSlabOpening,
|
|
||||||
profile.ExtendProfile,
|
profile.ExtendProfile,
|
||||||
prop.BIMModelProperties,
|
prop.BIMModelProperties,
|
||||||
ui.BIM_PT_authoring,
|
ui.BIM_PT_authoring,
|
||||||
@@ -41,6 +41,7 @@ def register():
|
|||||||
bpy.types.VIEW3D_MT_mesh_add.append(door.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.append(door.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(window.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.append(window.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
|
||||||
|
bpy.types.VIEW3D_MT_add.append(product.add_empty_type_button)
|
||||||
bpy.app.handlers.load_post.append(handler.load_post)
|
bpy.app.handlers.load_post.append(handler.load_post)
|
||||||
wm = bpy.context.window_manager
|
wm = bpy.context.window_manager
|
||||||
if wm.keyconfigs.addon:
|
if wm.keyconfigs.addon:
|
||||||
@@ -60,6 +61,7 @@ def unregister():
|
|||||||
bpy.types.VIEW3D_MT_mesh_add.remove(door.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.remove(door.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.remove(window.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.remove(window.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.remove(opening.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.remove(opening.add_object_button)
|
||||||
|
bpy.types.VIEW3D_MT_add.remove(product.add_empty_type_button)
|
||||||
wm = bpy.context.window_manager
|
wm = bpy.context.window_manager
|
||||||
kc = wm.keyconfigs.addon
|
kc = wm.keyconfigs.addon
|
||||||
if kc:
|
if kc:
|
||||||
|
|||||||
@@ -1,20 +1,36 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
from blenderbim.bim.module.model import root, product, wall, slab, profile, opening
|
from blenderbim.bim.module.model import root, product, wall, slab, profile, opening, task
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def load_post(*args):
|
def load_post(*args):
|
||||||
ifcopenshell.api.add_pre_listener(
|
ifcopenshell.api.add_pre_listener("attribute.edit_attributes", "BlenderBIM.Root.SyncName", root.sync_name)
|
||||||
"attribute.edit_attributes", "BlenderBIM.Root.SyncName", root.sync_name
|
|
||||||
)
|
|
||||||
|
|
||||||
ifcopenshell.api.add_post_listener(
|
ifcopenshell.api.add_post_listener(
|
||||||
"geometry.add_representation", "BlenderBIM.Product.GenerateBox", product.generate_box
|
"geometry.add_representation", "BlenderBIM.Product.GenerateBox", product.generate_box
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ifcopenshell.api.add_post_listener(
|
||||||
|
"sequence.edit_task_time", "BlenderBIM.Task.CalculateQuantities", task.calculate_quantities
|
||||||
|
)
|
||||||
|
|
||||||
|
for usecase in [
|
||||||
|
"material.assign_material",
|
||||||
|
"material.edit_constituent",
|
||||||
|
"material.edit_layer",
|
||||||
|
"material.edit_profile",
|
||||||
|
"material.add_constituent",
|
||||||
|
"material.add_layer",
|
||||||
|
"material.add_profile",
|
||||||
|
]:
|
||||||
|
ifcopenshell.api.add_post_listener(
|
||||||
|
usecase, "BlenderBIM.Product.EnsureMaterialAssigned", product.ensure_material_assigned
|
||||||
|
)
|
||||||
|
|
||||||
ifcopenshell.api.add_post_listener(
|
ifcopenshell.api.add_post_listener(
|
||||||
"material.edit_profile_usage",
|
"material.edit_profile_usage",
|
||||||
"BlenderBIM.Product.RegenerateProfileUsage",
|
"BlenderBIM.Product.RegenerateProfileUsage",
|
||||||
@@ -22,8 +38,10 @@ def load_post(*args):
|
|||||||
)
|
)
|
||||||
|
|
||||||
IfcStore.add_element_listener(opening.element_listener)
|
IfcStore.add_element_listener(opening.element_listener)
|
||||||
|
|
||||||
IfcStore.add_element_listener(wall.element_listener)
|
IfcStore.add_element_listener(wall.element_listener)
|
||||||
|
IfcStore.add_element_listener(slab.element_listener)
|
||||||
|
IfcStore.add_element_listener(profile.element_listener)
|
||||||
|
|
||||||
ifcopenshell.api.add_pre_listener(
|
ifcopenshell.api.add_pre_listener(
|
||||||
"geometry.add_representation", "BlenderBIM.DumbWall.EnsureSolid", wall.ensure_solid
|
"geometry.add_representation", "BlenderBIM.DumbWall.EnsureSolid", wall.ensure_solid
|
||||||
)
|
)
|
||||||
@@ -40,7 +58,6 @@ def load_post(*args):
|
|||||||
"type.assign_type", "BlenderBIM.DumbWall.RegenerateFromType", wall.DumbWallPlaner().regenerate_from_type
|
"type.assign_type", "BlenderBIM.DumbWall.RegenerateFromType", wall.DumbWallPlaner().regenerate_from_type
|
||||||
)
|
)
|
||||||
|
|
||||||
IfcStore.add_element_listener(slab.element_listener)
|
|
||||||
ifcopenshell.api.add_pre_listener(
|
ifcopenshell.api.add_pre_listener(
|
||||||
"geometry.add_representation", "BlenderBIM.DumbSlab.EnsureSolid", slab.ensure_solid
|
"geometry.add_representation", "BlenderBIM.DumbSlab.EnsureSolid", slab.ensure_solid
|
||||||
)
|
)
|
||||||
@@ -57,7 +74,6 @@ def load_post(*args):
|
|||||||
"type.assign_type", "BlenderBIM.DumbSlab.RegenerateFromType", slab.DumbSlabPlaner().regenerate_from_type
|
"type.assign_type", "BlenderBIM.DumbSlab.RegenerateFromType", slab.DumbSlabPlaner().regenerate_from_type
|
||||||
)
|
)
|
||||||
|
|
||||||
IfcStore.add_element_listener(profile.element_listener)
|
|
||||||
ifcopenshell.api.add_pre_listener(
|
ifcopenshell.api.add_pre_listener(
|
||||||
"geometry.add_representation", "BlenderBIM.DumbProfile.EnsureSolid", profile.ensure_solid
|
"geometry.add_representation", "BlenderBIM.DumbProfile.EnsureSolid", profile.ensure_solid
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -52,6 +52,46 @@ def mode_callback(obj, data):
|
|||||||
bm.free()
|
bm.free()
|
||||||
|
|
||||||
|
|
||||||
|
class AddElementOpening(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_element_opening"
|
||||||
|
bl_label = "Add Element Opening"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
selected_objs = context.selected_objects
|
||||||
|
if len(selected_objs) == 0 or not context.active_object:
|
||||||
|
return {"FINISHED"}
|
||||||
|
obj = context.active_object
|
||||||
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
return {"FINISHED"}
|
||||||
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
local_location = obj.matrix_world.inverted() @ context.scene.cursor.location
|
||||||
|
raycast = obj.closest_point_on_mesh(local_location, distance=0.01)
|
||||||
|
if not raycast[0]:
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# The opening shall be based on the smallest bounding dimension of the element
|
||||||
|
dimension = min(obj.dimensions)
|
||||||
|
bpy.ops.mesh.primitive_cube_add(size=dimension * 2)
|
||||||
|
opening = context.selected_objects[0]
|
||||||
|
|
||||||
|
# Place the opening in the middle of the element
|
||||||
|
global_location = obj.matrix_world @ raycast[1]
|
||||||
|
normal = raycast[2]
|
||||||
|
normal.negate()
|
||||||
|
global_normal = obj.matrix_world.to_quaternion() @ normal
|
||||||
|
opening.location = global_location + (global_normal * (dimension / 2))
|
||||||
|
|
||||||
|
opening.rotation_euler = obj.rotation_euler
|
||||||
|
opening.name = "Opening"
|
||||||
|
bpy.ops.bim.add_opening(opening=opening.name, obj=obj.name)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_object(self, context):
|
def add_object(self, context):
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bmesh.ops.create_cube(bm, size=self.size)
|
bmesh.ops.create_cube(bm, size=self.size)
|
||||||
|
|||||||
@@ -8,6 +8,33 @@ from . import wall, slab, profile
|
|||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.pset.data import Data as PsetData
|
from ifcopenshell.api.pset.data import Data as PsetData
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
from bpy_extras.object_utils import AddObjectHelper
|
||||||
|
|
||||||
|
|
||||||
|
class AddEmptyType(bpy.types.Operator, AddObjectHelper):
|
||||||
|
bl_idname = "bim.add_empty_type"
|
||||||
|
bl_label = "Add Empty Type"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = bpy.data.objects.new("TYPEX", None)
|
||||||
|
for project in [c for c in context.view_layer.layer_collection.children if "IfcProject" in c.name]:
|
||||||
|
if not [c for c in project.children if "Types" in c.name]:
|
||||||
|
types = bpy.data.collections.new("Types")
|
||||||
|
project.collection.children.link(types)
|
||||||
|
for collection in [c for c in project.children if "Types" in c.name]:
|
||||||
|
collection.collection.objects.link(obj)
|
||||||
|
break
|
||||||
|
break
|
||||||
|
context.scene.BIMRootProperties.ifc_product = "IfcElementType"
|
||||||
|
bpy.ops.object.select_all(action="DESELECT")
|
||||||
|
context.view_layer.objects.active = obj
|
||||||
|
obj.select_set(True)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
def add_empty_type_button(self, context):
|
||||||
|
self.layout.operator(AddEmptyType.bl_idname, icon="FILE_3D")
|
||||||
|
|
||||||
|
|
||||||
class AddTypeInstance(bpy.types.Operator):
|
class AddTypeInstance(bpy.types.Operator):
|
||||||
@@ -214,3 +241,36 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
|
|||||||
bpy.ops.bim.switch_representation(
|
bpy.ops.bim.switch_representation(
|
||||||
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
obj=obj.name, ifc_definition_id=representation.id(), should_reload=True, should_switch_all_meshes=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_material_assigned(usecase_path, ifc_file, settings):
|
||||||
|
if usecase_path == "material.assign_material":
|
||||||
|
if not settings.get("Material", None):
|
||||||
|
return
|
||||||
|
elements = [self.settings["product"]]
|
||||||
|
else:
|
||||||
|
elements = []
|
||||||
|
for rel in ifc_file.by_type("IfcRelAssociatesMaterial"):
|
||||||
|
if rel.RelatingMaterial == settings["material"] or [
|
||||||
|
e for e in ifc_file.traverse(rel.RelatingMaterial) if e == settings["material"]
|
||||||
|
]:
|
||||||
|
elements.extend(rel.RelatedObjects)
|
||||||
|
|
||||||
|
for element in elements:
|
||||||
|
obj = IfcStore.get_element(element.GlobalId)
|
||||||
|
if not obj or not obj.data:
|
||||||
|
continue
|
||||||
|
|
||||||
|
element_material = ifcopenshell.util.element.get_material(element)
|
||||||
|
material = [m for m in ifc_file.traverse(element_material) if m.is_a("IfcMaterial")]
|
||||||
|
|
||||||
|
object_material_ids = [
|
||||||
|
om.BIMObjectProperties.ifc_definition_id
|
||||||
|
for om in obj.data.materials
|
||||||
|
if om is not None and om.BIMObjectProperties.ifc_definition_id
|
||||||
|
]
|
||||||
|
|
||||||
|
if material[0].id() in object_material_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
obj.data.materials.append(IfcStore.get_element(material[0].id()))
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ def element_listener(element, obj):
|
|||||||
|
|
||||||
|
|
||||||
def mode_callback(obj, data):
|
def mode_callback(obj, data):
|
||||||
for obj in bpy.context.selected_objects + [bpy.context.active_object]:
|
for obj in set(bpy.context.selected_objects + [bpy.context.active_object]):
|
||||||
if (
|
if (
|
||||||
obj.mode != "EDIT"
|
not obj.data
|
||||||
or not obj.data
|
|
||||||
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
|
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
|
||||||
or not obj.BIMObjectProperties.ifc_definition_id
|
or not obj.BIMObjectProperties.ifc_definition_id
|
||||||
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
||||||
@@ -33,27 +32,36 @@ def mode_callback(obj, data):
|
|||||||
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
||||||
if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3":
|
if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3":
|
||||||
return
|
return
|
||||||
IfcStore.edited_objs.add(obj)
|
if obj.mode == "EDIT":
|
||||||
modifier = [m for m in obj.modifiers if m.type == "SOLIDIFY"]
|
IfcStore.edited_objs.add(obj)
|
||||||
if modifier:
|
modifier = [m for m in obj.modifiers if m.type == "SOLIDIFY"]
|
||||||
return
|
if modifier:
|
||||||
depth = obj.dimensions.z
|
return
|
||||||
bm = bmesh.from_edit_mesh(obj.data)
|
depth = obj.dimensions.z
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
bm.faces.ensure_lookup_table()
|
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
||||||
non_bottom_faces = []
|
bm.faces.ensure_lookup_table()
|
||||||
for face in bm.faces:
|
non_bottom_faces = []
|
||||||
if face.normal.z > -0.9:
|
for face in bm.faces:
|
||||||
non_bottom_faces.append(face)
|
if face.normal.z > -0.9:
|
||||||
else:
|
non_bottom_faces.append(face)
|
||||||
face.normal_flip()
|
else:
|
||||||
bmesh.ops.delete(bm, geom=non_bottom_faces, context="FACES")
|
face.normal_flip()
|
||||||
bmesh.update_edit_mesh(obj.data)
|
bmesh.ops.delete(bm, geom=non_bottom_faces, context="FACES")
|
||||||
bm.free()
|
bmesh.update_edit_mesh(obj.data)
|
||||||
modifier = obj.modifiers.new("Slab Depth", "SOLIDIFY")
|
bm.free()
|
||||||
modifier.use_even_offset = True
|
modifier = obj.modifiers.new("Slab Depth", "SOLIDIFY")
|
||||||
modifier.offset = 1
|
modifier.use_even_offset = True
|
||||||
modifier.thickness = depth
|
modifier.offset = 1
|
||||||
|
modifier.thickness = depth
|
||||||
|
else:
|
||||||
|
new_origin = obj.matrix_world @ Vector(obj.bound_box[0])
|
||||||
|
obj.data.transform(
|
||||||
|
Matrix.Translation(
|
||||||
|
(obj.matrix_world.inverted().to_quaternion() @ (obj.matrix_world.translation - new_origin))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
obj.matrix_world.translation = new_origin
|
||||||
|
|
||||||
|
|
||||||
def ensure_solid(usecase_path, ifc_file, settings):
|
def ensure_solid(usecase_path, ifc_file, settings):
|
||||||
@@ -199,42 +207,6 @@ def calculate_quantities(usecase_path, ifc_file, settings):
|
|||||||
PsetData.load(ifc_file, obj.BIMObjectProperties.ifc_definition_id)
|
PsetData.load(ifc_file, obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
|
|
||||||
class AddSlabOpening(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.add_slab_opening"
|
|
||||||
bl_label = "Add Slab Opening"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
selected_objs = context.selected_objects
|
|
||||||
if len(selected_objs) == 0 or not context.active_object:
|
|
||||||
return {"FINISHED"}
|
|
||||||
slab_obj = context.active_object
|
|
||||||
if not slab_obj.BIMObjectProperties.ifc_definition_id:
|
|
||||||
return {"FINISHED"}
|
|
||||||
slab = IfcStore.get_file().by_id(slab_obj.BIMObjectProperties.ifc_definition_id)
|
|
||||||
local_location = slab_obj.matrix_world.inverted() @ context.scene.cursor.location
|
|
||||||
raycast = slab_obj.closest_point_on_mesh(local_location, distance=0.01)
|
|
||||||
if not raycast[0]:
|
|
||||||
return {"FINISHED"}
|
|
||||||
bpy.ops.mesh.primitive_cube_add(size=slab_obj.dimensions[2] * 2)
|
|
||||||
opening = context.selected_objects[0]
|
|
||||||
|
|
||||||
# Place the opening in the middle of the slab
|
|
||||||
global_location = slab_obj.matrix_world @ raycast[1]
|
|
||||||
normal = raycast[2]
|
|
||||||
normal.negate()
|
|
||||||
global_normal = slab_obj.matrix_world.to_quaternion() @ normal
|
|
||||||
opening.location = global_location + (global_normal * (slab_obj.dimensions[2] / 2))
|
|
||||||
|
|
||||||
opening.rotation_euler = slab_obj.rotation_euler
|
|
||||||
opening.name = "Opening"
|
|
||||||
bpy.ops.bim.add_opening(opening=opening.name, obj=slab_obj.name)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class DumbSlabGenerator:
|
class DumbSlabGenerator:
|
||||||
def __init__(self, relating_type):
|
def __init__(self, relating_type):
|
||||||
self.relating_type = relating_type
|
self.relating_type = relating_type
|
||||||
@@ -249,7 +221,7 @@ class DumbSlabGenerator:
|
|||||||
if material.is_a("IfcMaterialLayerSet"):
|
if material.is_a("IfcMaterialLayerSet"):
|
||||||
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
|
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
|
||||||
break
|
break
|
||||||
if not thicknesses:
|
if not sum(thicknesses):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.date
|
||||||
|
from ifcopenshell.api.pset.data import Data as PsetData
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_quantities(usecase_path, ifc_file, settings):
|
||||||
|
if not set(["ScheduleStart", "ScheduleFinish", "ScheduleDuration"]).intersection(
|
||||||
|
set(settings["attributes"].keys())
|
||||||
|
):
|
||||||
|
return
|
||||||
|
element = settings["task_time"]
|
||||||
|
if not element.ScheduleDuration:
|
||||||
|
return
|
||||||
|
task = [e for e in ifc_file.get_inverse(element) if e.is_a("IfcTask")][0]
|
||||||
|
qto = ifcopenshell.api.run(
|
||||||
|
"pset.add_qto", ifc_file, should_run_listeners=False, product=task, name="Qto_TaskBaseQuantities"
|
||||||
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"pset.edit_qto",
|
||||||
|
ifc_file,
|
||||||
|
should_run_listeners=False,
|
||||||
|
qto=qto,
|
||||||
|
properties={
|
||||||
|
"StandardWork": ifcopenshell.util.date.ifc2datetime(element.ScheduleDuration).days,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
PsetData.load(ifc_file, task.id())
|
||||||
@@ -22,8 +22,7 @@ def element_listener(element, obj):
|
|||||||
def mode_callback(obj, data):
|
def mode_callback(obj, data):
|
||||||
for obj in set(bpy.context.selected_objects + [bpy.context.active_object]):
|
for obj in set(bpy.context.selected_objects + [bpy.context.active_object]):
|
||||||
if (
|
if (
|
||||||
obj.mode != "EDIT"
|
not obj.data
|
||||||
or not obj.data
|
|
||||||
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
|
or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
|
||||||
or not obj.BIMObjectProperties.ifc_definition_id
|
or not obj.BIMObjectProperties.ifc_definition_id
|
||||||
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
or not bpy.context.scene.BIMProjectProperties.is_authoring
|
||||||
@@ -33,48 +32,21 @@ def mode_callback(obj, data):
|
|||||||
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
||||||
if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2":
|
if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2":
|
||||||
return
|
return
|
||||||
bpy.ops.bim.dynamically_void_product(obj=obj.name)
|
if obj.mode == "EDIT":
|
||||||
IfcStore.edited_objs.add(obj)
|
bpy.ops.bim.dynamically_void_product(obj=obj.name)
|
||||||
bm = bmesh.from_edit_mesh(obj.data)
|
IfcStore.edited_objs.add(obj)
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
bm = bmesh.from_edit_mesh(obj.data)
|
||||||
bmesh.update_edit_mesh(obj.data)
|
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
||||||
bm.free()
|
bmesh.update_edit_mesh(obj.data)
|
||||||
|
bm.free()
|
||||||
|
else:
|
||||||
class AddWallOpening(bpy.types.Operator):
|
new_origin = obj.matrix_world @ Vector(obj.bound_box[0])
|
||||||
bl_idname = "bim.add_wall_opening"
|
obj.data.transform(
|
||||||
bl_label = "Add Wall Opening"
|
Matrix.Translation(
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
(obj.matrix_world.inverted().to_quaternion() @ (obj.matrix_world.translation - new_origin))
|
||||||
|
)
|
||||||
def execute(self, context):
|
)
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
obj.matrix_world.translation = new_origin
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
selected_objs = context.selected_objects
|
|
||||||
if len(selected_objs) == 0 or not context.active_object:
|
|
||||||
return {"FINISHED"}
|
|
||||||
wall_obj = context.active_object
|
|
||||||
if not wall_obj.BIMObjectProperties.ifc_definition_id:
|
|
||||||
return {"FINISHED"}
|
|
||||||
wall = IfcStore.get_file().by_id(wall_obj.BIMObjectProperties.ifc_definition_id)
|
|
||||||
local_location = wall_obj.matrix_world.inverted() @ context.scene.cursor.location
|
|
||||||
raycast = wall_obj.closest_point_on_mesh(local_location, distance=0.01)
|
|
||||||
if not raycast[0]:
|
|
||||||
return {"FINISHED"}
|
|
||||||
bpy.ops.mesh.primitive_cube_add(size=wall_obj.dimensions[1] * 2)
|
|
||||||
opening = bpy.context.selected_objects[0]
|
|
||||||
|
|
||||||
# Place the opening in the middle of the wall
|
|
||||||
global_location = wall_obj.matrix_world @ raycast[1]
|
|
||||||
normal = raycast[2]
|
|
||||||
normal.negate()
|
|
||||||
global_normal = wall_obj.matrix_world.to_quaternion() @ normal
|
|
||||||
opening.location = global_location + (global_normal * (wall_obj.dimensions[1] / 2))
|
|
||||||
|
|
||||||
opening.rotation_euler = wall_obj.rotation_euler
|
|
||||||
opening.name = "Opening"
|
|
||||||
bpy.ops.bim.add_opening(opening=opening.name, obj=wall_obj.name)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class JoinWall(bpy.types.Operator):
|
class JoinWall(bpy.types.Operator):
|
||||||
@@ -643,7 +615,7 @@ class DumbWallGenerator:
|
|||||||
if material.is_a("IfcMaterialLayerSet"):
|
if material.is_a("IfcMaterialLayerSet"):
|
||||||
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
|
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
|
||||||
break
|
break
|
||||||
if not thicknesses:
|
if not sum(thicknesses):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
self.collection = bpy.context.view_layer.active_layer_collection.collection
|
||||||
@@ -960,15 +932,19 @@ class DumbWallPlaner:
|
|||||||
IfcStore.edited_objs.add(obj)
|
IfcStore.edited_objs.add(obj)
|
||||||
|
|
||||||
def thicken_face(self, face, delta_thickness):
|
def thicken_face(self, face, delta_thickness):
|
||||||
slide_magnitude = abs(delta_thickness) / 2
|
slide_magnitude = abs(delta_thickness)
|
||||||
for vert in face.verts:
|
for vert in face.verts:
|
||||||
slide_vector = None
|
slide_vector = None
|
||||||
for edge in vert.link_edges:
|
for edge in vert.link_edges:
|
||||||
other_vert = edge.verts[1] if edge.verts[0] == vert else edge.verts[0]
|
other_vert = edge.verts[1] if edge.verts[0] == vert else edge.verts[0]
|
||||||
if delta_thickness > 0:
|
if delta_thickness > 0:
|
||||||
potential_slide_vector = vert.co - other_vert.co
|
potential_slide_vector = vert.co - other_vert.co
|
||||||
|
if potential_slide_vector.y < 0:
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
potential_slide_vector = other_vert.co - vert.co
|
potential_slide_vector = other_vert.co - vert.co
|
||||||
|
if potential_slide_vector.y > 0:
|
||||||
|
continue
|
||||||
if abs(potential_slide_vector.x) > 0.9 or abs(potential_slide_vector.z) > 0.9:
|
if abs(potential_slide_vector.x) > 0.9 or abs(potential_slide_vector.z) > 0.9:
|
||||||
continue
|
continue
|
||||||
slide_vector = potential_slide_vector
|
slide_vector = potential_slide_vector
|
||||||
|
|||||||
@@ -64,14 +64,6 @@ class BimTool(WorkSpaceTool):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="Split", icon="EVENT_S")
|
row.label(text="Split", icon="EVENT_S")
|
||||||
row = layout.row(align=True)
|
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
|
||||||
row.label(text="Opening", icon="EVENT_O")
|
|
||||||
|
|
||||||
if props.ifc_class == "IfcSlabType":
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
|
||||||
row.label(text="Opening", icon="EVENT_O")
|
|
||||||
|
|
||||||
if props.ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]:
|
if props.ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]:
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@@ -80,6 +72,10 @@ class BimTool(WorkSpaceTool):
|
|||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="Extend", icon="EVENT_E")
|
row.label(text="Extend", icon="EVENT_E")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="Opening", icon="EVENT_O")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="Align")
|
row.label(text="Align")
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
@@ -142,10 +138,7 @@ class Hotkey(bpy.types.Operator):
|
|||||||
bpy.ops.bim.align_product(align_type="NEGATIVE")
|
bpy.ops.bim.align_product(align_type="NEGATIVE")
|
||||||
|
|
||||||
def hotkey_S_O(self):
|
def hotkey_S_O(self):
|
||||||
if self.props.ifc_class == "IfcWallType":
|
bpy.ops.bim.add_element_opening()
|
||||||
bpy.ops.bim.add_wall_opening()
|
|
||||||
elif self.props.ifc_class == "IfcSlabType":
|
|
||||||
bpy.ops.bim.add_slab_opening()
|
|
||||||
|
|
||||||
def hotkey_A_D(self):
|
def hotkey_A_D(self):
|
||||||
bpy.ops.bim.toggle_decomposition_parenting()
|
bpy.ops.bim.toggle_decomposition_parenting()
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
classes = ()
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
operator.LoadProfiles,
|
||||||
|
operator.DisableProfileEditingUI,
|
||||||
|
operator.RemoveProfileDef,
|
||||||
|
operator.EnableEditingProfile,
|
||||||
|
operator.DisableEditingProfile,
|
||||||
|
operator.EditProfile,
|
||||||
|
prop.Profile,
|
||||||
|
prop.BIMProfileProperties,
|
||||||
|
ui.BIM_PT_profiles,
|
||||||
|
ui.BIM_UL_profiles,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
pass
|
bpy.types.Scene.BIMProfileProperties = bpy.props.PointerProperty(type=prop.BIMProfileProperties)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
pass
|
del bpy.types.Scene.BIMProfileProperties
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import bpy
|
||||||
|
import ifcopenshell.api
|
||||||
|
import blenderbim.bim.helper
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from ifcopenshell.api.profile.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class LoadProfiles(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.load_profiles"
|
||||||
|
bl_label = "Load Profiles"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
props = context.scene.BIMProfileProperties
|
||||||
|
while len(props.profiles) > 0:
|
||||||
|
props.profiles.remove(0)
|
||||||
|
|
||||||
|
for ifc_definition_id, profile in Data.profiles.items():
|
||||||
|
new = props.profiles.add()
|
||||||
|
new.ifc_definition_id = ifc_definition_id
|
||||||
|
new.name = profile.get("ProfileName", "") or "Unnamed"
|
||||||
|
|
||||||
|
props.is_editing = True
|
||||||
|
bpy.ops.bim.disable_editing_profile()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class DisableProfileEditingUI(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.disable_profile_editing_ui"
|
||||||
|
bl_label = "Disable Profile Editing UI"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
context.scene.BIMProfileProperties.is_editing = False
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveProfileDef(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.remove_profile_def"
|
||||||
|
bl_label = "Remove Profile Definition"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
profile: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = context.scene.BIMProfileProperties
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
ifcopenshell.api.run("profile.remove_profile", self.file, **{"profile": self.file.by_id(self.profile)})
|
||||||
|
Data.load(self.file)
|
||||||
|
bpy.ops.bim.load_profiles()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EnableEditingProfile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.enable_editing_profile"
|
||||||
|
bl_label = "Enable Editing Profile"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
profile: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
props = context.scene.BIMProfileProperties
|
||||||
|
while len(props.profile_attributes) > 0:
|
||||||
|
props.profile_attributes.remove(0)
|
||||||
|
|
||||||
|
data = Data.profiles[self.profile]
|
||||||
|
|
||||||
|
blenderbim.bim.helper.import_attributes(data["type"], props.profile_attributes, data)
|
||||||
|
props.active_profile_id = self.profile
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class DisableEditingProfile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.disable_editing_profile"
|
||||||
|
bl_label = "Disable Editing Profile"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
context.scene.BIMProfileProperties.active_profile_id = 0
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EditProfile(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.edit_profile"
|
||||||
|
bl_label = "Edit Profile"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = context.scene.BIMProfileProperties
|
||||||
|
attributes = blenderbim.bim.helper.export_attributes(props.profile_attributes)
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
profile = self.file.by_id(props.active_profile_id)
|
||||||
|
ifcopenshell.api.run("profile.edit_profile", self.file, **{"profile": profile, "attributes": attributes})
|
||||||
|
Data.load(IfcStore.get_file())
|
||||||
|
bpy.ops.bim.load_profiles()
|
||||||
|
return {"FINISHED"}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.schema
|
||||||
|
import ifcopenshell.util.attribute
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Profile(PropertyGroup):
|
||||||
|
name: StringProperty(name="Name")
|
||||||
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
|
|
||||||
|
|
||||||
|
class BIMProfileProperties(PropertyGroup):
|
||||||
|
is_editing: BoolProperty(name="Is Editing")
|
||||||
|
profiles: CollectionProperty(name="Profiles", type=Profile)
|
||||||
|
active_profile_index: IntProperty(name="Active Profile Index")
|
||||||
|
active_profile_id: IntProperty(name="Active Profile Id")
|
||||||
|
profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute)
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import blenderbim.bim.helper
|
||||||
|
from bpy.types import Panel, UIList
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from ifcopenshell.api.profile.data import Data
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_profiles(Panel):
|
||||||
|
bl_label = "IFC Profiles"
|
||||||
|
bl_idname = "BIM_PT_profiles"
|
||||||
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
return file
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
if not Data.is_loaded:
|
||||||
|
Data.load(self.file)
|
||||||
|
self.props = context.scene.BIMProfileProperties
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="{} Profiles Found".format(len(Data.profiles)), icon="SNAP_GRID")
|
||||||
|
if self.props.is_editing:
|
||||||
|
row.operator("bim.disable_profile_editing_ui", text="", icon="CANCEL")
|
||||||
|
else:
|
||||||
|
row.operator("bim.load_profiles", text="", icon="GREASEPENCIL")
|
||||||
|
|
||||||
|
if not self.props.is_editing:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.layout.template_list(
|
||||||
|
"BIM_UL_profiles",
|
||||||
|
"",
|
||||||
|
self.props,
|
||||||
|
"profiles",
|
||||||
|
self.props,
|
||||||
|
"active_profile_index",
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.props.active_profile_id:
|
||||||
|
self.draw_editable_ui(context)
|
||||||
|
|
||||||
|
def draw_editable_ui(self, context):
|
||||||
|
blenderbim.bim.helper.draw_attributes(self.props.profile_attributes, self.layout)
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_UL_profiles(UIList):
|
||||||
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
|
props = context.scene.BIMProfileProperties
|
||||||
|
if item:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text=item.name or "Unnamed")
|
||||||
|
|
||||||
|
if props.active_profile_id == item.ifc_definition_id:
|
||||||
|
row.operator("bim.edit_profile", text="", icon="CHECKMARK")
|
||||||
|
row.operator("bim.disable_editing_profile", text="", icon="CANCEL")
|
||||||
|
elif props.active_profile_id:
|
||||||
|
row.operator("bim.remove_profile_def", text="", icon="X").profile = item.ifc_definition_id
|
||||||
|
else:
|
||||||
|
op = row.operator("bim.enable_editing_profile", text="", icon="GREASEPENCIL")
|
||||||
|
op.profile = item.ifc_definition_id
|
||||||
|
row.operator("bim.remove_profile_def", text="", icon="X").profile = item.ifc_definition_id
|
||||||
@@ -172,7 +172,7 @@ class RefreshLibrary(bpy.types.Operator):
|
|||||||
self.props.active_library_element = ""
|
self.props.active_library_element = ""
|
||||||
|
|
||||||
types = IfcStore.library_file.wrapped_data.types_with_super()
|
types = IfcStore.library_file.wrapped_data.types_with_super()
|
||||||
for importable_type in ["IfcTypeProduct", "IfcMaterial", "IfcCostSchedule"]:
|
for importable_type in ["IfcTypeProduct", "IfcMaterial", "IfcCostSchedule", "IfcProfileDef"]:
|
||||||
if importable_type in types:
|
if importable_type in types:
|
||||||
new = self.props.library_elements.add()
|
new = self.props.library_elements.add()
|
||||||
new.name = importable_type
|
new.name = importable_type
|
||||||
@@ -198,7 +198,10 @@ class ChangeLibraryElement(bpy.types.Operator):
|
|||||||
if len(ifc_classes) == 1 and list(ifc_classes)[0] == self.element_name:
|
if len(ifc_classes) == 1 and list(ifc_classes)[0] == self.element_name:
|
||||||
for element in elements:
|
for element in elements:
|
||||||
new = self.props.library_elements.add()
|
new = self.props.library_elements.add()
|
||||||
new.name = element.Name or "Unnamed"
|
if element.is_a("IfcProfileDef"):
|
||||||
|
new.name = element.ProfileName or "Unnamed"
|
||||||
|
else:
|
||||||
|
new.name = element.Name or "Unnamed"
|
||||||
new.ifc_definition_id = element.id()
|
new.ifc_definition_id = element.id()
|
||||||
if IfcStore.library_file.schema == "IFC2X3" or not IfcStore.library_file.by_type("IfcProjectLibrary"):
|
if IfcStore.library_file.schema == "IFC2X3" or not IfcStore.library_file.by_type("IfcProjectLibrary"):
|
||||||
new.is_declared = False
|
new.is_declared = False
|
||||||
|
|||||||
@@ -11,17 +11,30 @@ classes = (
|
|||||||
operator.AddQto,
|
operator.AddQto,
|
||||||
operator.GuessQuantity,
|
operator.GuessQuantity,
|
||||||
prop.PsetProperties,
|
prop.PsetProperties,
|
||||||
|
prop.MaterialPsetProperties,
|
||||||
|
prop.TaskPsetProperties,
|
||||||
|
prop.ResourcePsetProperties,
|
||||||
|
prop.ProfilePsetProperties,
|
||||||
ui.BIM_PT_object_psets,
|
ui.BIM_PT_object_psets,
|
||||||
ui.BIM_PT_object_qtos,
|
ui.BIM_PT_object_qtos,
|
||||||
ui.BIM_PT_material_psets,
|
ui.BIM_PT_material_psets,
|
||||||
|
ui.BIM_PT_task_qtos,
|
||||||
|
ui.BIM_PT_resource_qtos,
|
||||||
|
ui.BIM_PT_profile_psets,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||||
bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
bpy.types.Material.PsetProperties = bpy.props.PointerProperty(type=prop.MaterialPsetProperties)
|
||||||
|
bpy.types.Scene.TaskPsetProperties = bpy.props.PointerProperty(type=prop.TaskPsetProperties)
|
||||||
|
bpy.types.Scene.ResourcePsetProperties = bpy.props.PointerProperty(type=prop.ResourcePsetProperties)
|
||||||
|
bpy.types.Scene.ProfilePsetProperties = bpy.props.PointerProperty(type=prop.ProfilePsetProperties)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Object.PsetProperties
|
del bpy.types.Object.PsetProperties
|
||||||
del bpy.types.Material.PsetProperties
|
del bpy.types.Material.PsetProperties
|
||||||
|
del bpy.types.Scene.TaskPsetProperties
|
||||||
|
del bpy.types.Scene.ResourcePsetProperties
|
||||||
|
del bpy.types.Scene.ProfilePsetProperties
|
||||||
|
|||||||
@@ -11,6 +11,43 @@ from ifcopenshell.api.cost.data import Data as CostData
|
|||||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||||
|
|
||||||
|
|
||||||
|
def get_pset_props(context, obj, obj_type):
|
||||||
|
if obj_type == "Object":
|
||||||
|
obj = bpy.data.objects.get(obj)
|
||||||
|
return obj.PsetProperties
|
||||||
|
elif obj_type == "Material":
|
||||||
|
obj = bpy.data.materials.get(obj)
|
||||||
|
return obj.PsetProperties
|
||||||
|
elif obj_type == "Task":
|
||||||
|
return context.scene.TaskPsetProperties
|
||||||
|
elif obj_type == "Resource":
|
||||||
|
return context.scene.ResourcePsetProperties
|
||||||
|
elif obj_type == "Profile":
|
||||||
|
return context.scene.ProfilePsetProperties
|
||||||
|
|
||||||
|
|
||||||
|
def get_pset_obj_ifc_definition_id(context, obj, obj_type):
|
||||||
|
if obj_type == "Object":
|
||||||
|
obj = bpy.data.objects.get(obj)
|
||||||
|
return obj.BIMObjectProperties.ifc_definition_id
|
||||||
|
elif obj_type == "Material":
|
||||||
|
obj = bpy.data.materials.get(obj)
|
||||||
|
return obj.BIMObjectProperties.ifc_definition_id
|
||||||
|
elif obj_type == "Task":
|
||||||
|
return context.scene.BIMTaskTreeProperties.tasks[
|
||||||
|
context.scene.BIMWorkScheduleProperties.active_task_index
|
||||||
|
].ifc_definition_id
|
||||||
|
elif obj_type == "Resource":
|
||||||
|
return context.scene.BIMResourceTreeProperties.resources[
|
||||||
|
context.scene.BIMResourceProperties.active_resource_index
|
||||||
|
].ifc_definition_id
|
||||||
|
elif obj_type == "Profile":
|
||||||
|
return context.scene.BIMProfileProperties.profiles[
|
||||||
|
context.scene.BIMProfileProperties.active_profile_index
|
||||||
|
].ifc_definition_id
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TogglePsetExpansion(bpy.types.Operator):
|
class TogglePsetExpansion(bpy.types.Operator):
|
||||||
bl_idname = "bim.toggle_pset_expansion"
|
bl_idname = "bim.toggle_pset_expansion"
|
||||||
bl_label = "Toggle Pset Expansion"
|
bl_label = "Toggle Pset Expansion"
|
||||||
@@ -32,11 +69,7 @@ class EnablePsetEditing(bpy.types.Operator):
|
|||||||
obj_type: bpy.props.StringProperty()
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.obj_type == "Object":
|
self.props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
obj = bpy.data.objects.get(self.obj)
|
|
||||||
elif self.obj_type == "Material":
|
|
||||||
obj = bpy.data.materials.get(self.obj)
|
|
||||||
self.props = obj.PsetProperties
|
|
||||||
|
|
||||||
while len(self.props.properties) > 0:
|
while len(self.props.properties) > 0:
|
||||||
self.props.properties.remove(0)
|
self.props.properties.remove(0)
|
||||||
@@ -138,11 +171,7 @@ class DisablePsetEditing(bpy.types.Operator):
|
|||||||
obj_type: bpy.props.StringProperty()
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.obj_type == "Object":
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
obj = bpy.data.objects.get(self.obj)
|
|
||||||
elif self.obj_type == "Material":
|
|
||||||
obj = bpy.data.materials.get(self.obj)
|
|
||||||
props = obj.PsetProperties
|
|
||||||
props.active_pset_id = 0
|
props.active_pset_id = 0
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -161,12 +190,8 @@ class EditPset(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
if self.obj_type == "Object":
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
obj = bpy.data.objects.get(self.obj)
|
ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
||||||
elif self.obj_type == "Material":
|
|
||||||
obj = bpy.data.materials.get(self.obj)
|
|
||||||
oprops = obj.BIMObjectProperties
|
|
||||||
props = obj.PsetProperties
|
|
||||||
properties = {}
|
properties = {}
|
||||||
|
|
||||||
pset_id = self.pset_id or props.active_pset_id
|
pset_id = self.pset_id or props.active_pset_id
|
||||||
@@ -213,7 +238,7 @@ class EditPset(bpy.types.Operator):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
CostData.purge()
|
CostData.purge()
|
||||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
bpy.ops.bim.disable_pset_editing(obj=self.obj, obj_type=self.obj_type)
|
bpy.ops.bim.disable_pset_editing(obj=self.obj, obj_type=self.obj_type)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -231,20 +256,17 @@ class RemovePset(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
if self.obj_type == "Object":
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
obj = bpy.data.objects.get(self.obj)
|
ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
||||||
elif self.obj_type == "Material":
|
|
||||||
obj = bpy.data.materials.get(self.obj)
|
|
||||||
props = obj.BIMObjectProperties
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"pset.remove_pset",
|
"pset.remove_pset",
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
**{
|
||||||
"product": self.file.by_id(props.ifc_definition_id),
|
"product": self.file.by_id(ifc_definition_id),
|
||||||
"pset": self.file.by_id(self.pset_id),
|
"pset": self.file.by_id(self.pset_id),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
Data.load(IfcStore.get_file(), props.ifc_definition_id)
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -254,36 +276,24 @@ class AddPset(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
obj_type: bpy.props.StringProperty()
|
obj_type: bpy.props.StringProperty()
|
||||||
pset_name: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
if self.obj_type == "Object":
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
obj = bpy.data.objects.get(self.obj)
|
ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
||||||
elif self.obj_type == "Material":
|
|
||||||
obj = bpy.data.materials.get(self.obj)
|
|
||||||
oprops = obj.BIMObjectProperties
|
|
||||||
props = obj.PsetProperties
|
|
||||||
|
|
||||||
if self.pset_name:
|
|
||||||
pset_name = self.pset_name
|
|
||||||
elif self.obj_type == "Object":
|
|
||||||
pset_name = props.pset_name
|
|
||||||
elif self.obj_type == "Material":
|
|
||||||
pset_name = props.material_pset_name
|
|
||||||
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"pset.add_pset",
|
"pset.add_pset",
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
**{
|
||||||
"product": self.file.by_id(oprops.ifc_definition_id),
|
"product": self.file.by_id(ifc_definition_id),
|
||||||
"name": pset_name,
|
"name": props.pset_name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -291,24 +301,25 @@ class AddQto(bpy.types.Operator):
|
|||||||
bl_idname = "bim.add_qto"
|
bl_idname = "bim.add_qto"
|
||||||
bl_label = "Add Qto"
|
bl_label = "Add Qto"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
obj: bpy.props.StringProperty()
|
||||||
|
obj_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
obj = context.active_object
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
oprops = obj.BIMObjectProperties
|
ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
||||||
props = obj.PsetProperties
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"pset.add_qto",
|
"pset.add_qto",
|
||||||
self.file,
|
self.file,
|
||||||
**{
|
**{
|
||||||
"product": self.file.by_id(oprops.ifc_definition_id),
|
"product": self.file.by_id(ifc_definition_id),
|
||||||
"name": props.qto_name,
|
"name": props.qto_name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,36 @@ def getMaterialPsetNames(self, context):
|
|||||||
return psetnames[ifc_class]
|
return psetnames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
|
def getTaskQtoNames(self, context):
|
||||||
|
global qtonames
|
||||||
|
ifc_class = "IfcTask"
|
||||||
|
if ifc_class not in qtonames:
|
||||||
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
|
qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||||
|
return qtonames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
|
def getResourceQtoNames(self, context):
|
||||||
|
global qtonames
|
||||||
|
rprops = context.scene.BIMResourceProperties
|
||||||
|
rtprops = context.scene.BIMResourceTreeProperties
|
||||||
|
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||||
|
if ifc_class not in qtonames:
|
||||||
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
|
qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||||
|
return qtonames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
|
def getProfilePsetNames(self, context):
|
||||||
|
global psetnames
|
||||||
|
pprops = context.scene.BIMProfileProperties
|
||||||
|
ifc_class = IfcStore.get_file().by_id(pprops.profiles[pprops.active_profile_index].ifc_definition_id).is_a()
|
||||||
|
if ifc_class not in psetnames:
|
||||||
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
|
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||||
|
return psetnames[ifc_class]
|
||||||
|
|
||||||
|
|
||||||
def getQtoNames(self, context):
|
def getQtoNames(self, context):
|
||||||
global qtonames
|
global qtonames
|
||||||
if "/" in context.active_object.name:
|
if "/" in context.active_object.name:
|
||||||
@@ -69,4 +99,31 @@ class PsetProperties(PropertyGroup):
|
|||||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||||
pset_name: EnumProperty(items=getPsetNames, name="Pset Name")
|
pset_name: EnumProperty(items=getPsetNames, name="Pset Name")
|
||||||
qto_name: EnumProperty(items=getQtoNames, name="Qto Name")
|
qto_name: EnumProperty(items=getQtoNames, name="Qto Name")
|
||||||
material_pset_name: EnumProperty(items=getMaterialPsetNames, name="Pset Name")
|
|
||||||
|
|
||||||
|
class MaterialPsetProperties(PropertyGroup):
|
||||||
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
|
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||||
|
pset_name: EnumProperty(items=getMaterialPsetNames, name="Pset Name")
|
||||||
|
|
||||||
|
|
||||||
|
class TaskPsetProperties(PropertyGroup):
|
||||||
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
|
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||||
|
qto_name: EnumProperty(items=getTaskQtoNames, name="Qto Name")
|
||||||
|
|
||||||
|
|
||||||
|
class ResourcePsetProperties(PropertyGroup):
|
||||||
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
|
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||||
|
qto_name: EnumProperty(items=getResourceQtoNames, name="Qto Name")
|
||||||
|
|
||||||
|
|
||||||
|
class ProfilePsetProperties(PropertyGroup):
|
||||||
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
|
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||||
|
pset_name: EnumProperty(items=getProfilePsetNames, name="Pset Name")
|
||||||
|
|||||||
@@ -3,6 +3,14 @@ from ifcopenshell.api.pset.data import Data
|
|||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_pset_obj_name(context, obj_type):
|
||||||
|
if obj_type == "Object":
|
||||||
|
return context.active_object.name
|
||||||
|
elif obj_type == "Material":
|
||||||
|
return context.active_object.active_material.name
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
|
def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
row = box.row(align=True)
|
row = box.row(align=True)
|
||||||
@@ -10,29 +18,30 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
|
|||||||
pset["is_expanded"] = True
|
pset["is_expanded"] = True
|
||||||
icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT"
|
icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT"
|
||||||
row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id
|
row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id
|
||||||
|
obj_name = get_active_pset_obj_name(context, obj_type)
|
||||||
if not props.active_pset_id:
|
if not props.active_pset_id:
|
||||||
row.label(text=pset["Name"], icon="COPY_ID")
|
row.label(text=pset["Name"], icon="COPY_ID")
|
||||||
op = row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="")
|
op = row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="")
|
||||||
op.pset_id = pset_id
|
op.pset_id = pset_id
|
||||||
op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name
|
op.obj = obj_name
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
op = row.operator("bim.remove_pset", icon="X", text="")
|
op = row.operator("bim.remove_pset", icon="X", text="")
|
||||||
op.pset_id = pset_id
|
op.pset_id = pset_id
|
||||||
op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name
|
op.obj = obj_name
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
elif props.active_pset_id != pset_id:
|
elif props.active_pset_id != pset_id:
|
||||||
row.label(text=pset["Name"], icon="COPY_ID")
|
row.label(text=pset["Name"], icon="COPY_ID")
|
||||||
op = row.operator("bim.remove_pset", icon="X", text="")
|
op = row.operator("bim.remove_pset", icon="X", text="")
|
||||||
op.pset_id = pset_id
|
op.pset_id = pset_id
|
||||||
op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name
|
op.obj = obj_name
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
elif props.active_pset_id == pset_id:
|
elif props.active_pset_id == pset_id:
|
||||||
row.prop(props, "active_pset_name", icon="COPY_ID", text="")
|
row.prop(props, "active_pset_name", icon="COPY_ID", text="")
|
||||||
op = row.operator("bim.edit_pset", icon="CHECKMARK", text="")
|
op = row.operator("bim.edit_pset", icon="CHECKMARK", text="")
|
||||||
op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name
|
op.obj = obj_name
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
op = row.operator("bim.disable_pset_editing", icon="CANCEL", text="")
|
op = row.operator("bim.disable_pset_editing", icon="CANCEL", text="")
|
||||||
op.obj = context.active_object.name if obj_type == "Object" else context.active_object.active_material.name
|
op.obj = obj_name
|
||||||
op.obj_type = obj_type
|
op.obj_type = obj_type
|
||||||
if pset["is_expanded"]:
|
if pset["is_expanded"]:
|
||||||
if props.active_pset_id == pset_id:
|
if props.active_pset_id == pset_id:
|
||||||
@@ -123,7 +132,7 @@ class BIM_PT_object_psets(Panel):
|
|||||||
op.obj_type = "Object"
|
op.obj_type = "Object"
|
||||||
|
|
||||||
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]]
|
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]]
|
||||||
for pset_id, pset in sorted(psets, key = lambda v: v[1]["Name"]):
|
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
|
||||||
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Object")
|
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Object")
|
||||||
|
|
||||||
# TODO reimplement. See #1222.
|
# TODO reimplement. See #1222.
|
||||||
@@ -163,10 +172,12 @@ class BIM_PT_object_qtos(Panel):
|
|||||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(props, "qto_name", text="")
|
row.prop(props, "qto_name", text="")
|
||||||
row.operator("bim.add_qto", icon="ADD", text="")
|
op = row.operator("bim.add_qto", icon="ADD", text="")
|
||||||
|
op.obj = context.active_object.name
|
||||||
|
op.obj_type = "Object"
|
||||||
|
|
||||||
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]]
|
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]]
|
||||||
for qto_id, qto in sorted(qtos, key = lambda v: v[1]["Name"]):
|
for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]):
|
||||||
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Object")
|
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Object")
|
||||||
|
|
||||||
|
|
||||||
@@ -202,11 +213,111 @@ class BIM_PT_material_psets(Panel):
|
|||||||
if oprops.ifc_definition_id not in Data.products:
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(props, "material_pset_name", text="")
|
row.prop(props, "pset_name", text="")
|
||||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||||
op.obj = context.active_object.active_material.name
|
op.obj = context.active_object.active_material.name
|
||||||
op.obj_type = "Material"
|
op.obj_type = "Material"
|
||||||
|
|
||||||
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]]
|
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[oprops.ifc_definition_id]["psets"]]
|
||||||
for pset_id, pset in sorted(psets, key = lambda v: v[1]["Name"]):
|
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
|
||||||
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Material")
|
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Material")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_task_qtos(Panel):
|
||||||
|
bl_label = "IFC Task Quantity Sets"
|
||||||
|
bl_idname = "BIM_PT_task_qtos"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_parent_id = "BIM_PT_work_schedules"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.BIMWorkScheduleProperties
|
||||||
|
total_tasks = len(context.scene.BIMTaskTreeProperties.tasks)
|
||||||
|
if total_tasks > 0 and props.active_task_index < total_tasks:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.TaskPsetProperties
|
||||||
|
wprops = context.scene.BIMWorkScheduleProperties
|
||||||
|
tprops = context.scene.BIMTaskTreeProperties
|
||||||
|
ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id
|
||||||
|
if ifc_definition_id not in Data.products:
|
||||||
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "qto_name", text="")
|
||||||
|
op = row.operator("bim.add_qto", icon="ADD", text="")
|
||||||
|
op.obj_type = "Task"
|
||||||
|
|
||||||
|
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[ifc_definition_id]["qtos"]]
|
||||||
|
for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]):
|
||||||
|
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Task")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_resource_qtos(Panel):
|
||||||
|
bl_label = "IFC Resource Quantity Sets"
|
||||||
|
bl_idname = "BIM_PT_resource_qtos"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_parent_id = "BIM_PT_resources"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.BIMResourceProperties
|
||||||
|
total_resources = len(context.scene.BIMResourceTreeProperties.resources)
|
||||||
|
if total_resources > 0 and props.active_resource_index < total_resources:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.ResourcePsetProperties
|
||||||
|
rprops = context.scene.BIMResourceProperties
|
||||||
|
rtprops = context.scene.BIMResourceTreeProperties
|
||||||
|
ifc_definition_id = rtprops.resources[rprops.active_resource_index].ifc_definition_id
|
||||||
|
if ifc_definition_id not in Data.products:
|
||||||
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "qto_name", text="")
|
||||||
|
op = row.operator("bim.add_qto", icon="ADD", text="")
|
||||||
|
op.obj_type = "Resource"
|
||||||
|
|
||||||
|
qtos = [(qto_id, Data.qtos[qto_id]) for qto_id in Data.products[ifc_definition_id]["qtos"]]
|
||||||
|
for qto_id, qto in sorted(qtos, key=lambda v: v[1]["Name"]):
|
||||||
|
draw_psetqto_ui(context, qto_id, qto, props, self.layout, "Resource")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_profile_psets(Panel):
|
||||||
|
bl_label = "IFC Profile Property Sets"
|
||||||
|
bl_idname = "BIM_PT_profile_psets"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "scene"
|
||||||
|
bl_parent_id = "BIM_PT_profiles"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.BIMProfileProperties
|
||||||
|
if not props.is_editing:
|
||||||
|
return False
|
||||||
|
total_profiles = len(context.scene.BIMProfileProperties.profiles)
|
||||||
|
if total_profiles > 0 and props.active_profile_index < total_profiles:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.ProfilePsetProperties
|
||||||
|
pprops = context.scene.BIMProfileProperties
|
||||||
|
ifc_definition_id = pprops.profiles[pprops.active_profile_index].ifc_definition_id
|
||||||
|
if ifc_definition_id not in Data.products:
|
||||||
|
Data.load(IfcStore.get_file(), ifc_definition_id)
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.prop(props, "pset_name", text="")
|
||||||
|
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||||
|
op.obj_type = "Profile"
|
||||||
|
|
||||||
|
psets = [(pset_id, Data.psets[pset_id]) for pset_id in Data.products[ifc_definition_id]["psets"]]
|
||||||
|
for pset_id, pset in sorted(psets, key=lambda v: v[1]["Name"]):
|
||||||
|
draw_psetqto_ui(context, pset_id, pset, props, self.layout, "Profile")
|
||||||
|
|||||||
@@ -344,8 +344,14 @@ class EditResourceTime(bpy.types.Operator):
|
|||||||
|
|
||||||
def export_attributes(self, attributes, prop):
|
def export_attributes(self, attributes, prop):
|
||||||
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
||||||
return True
|
return True
|
||||||
elif prop.name =="LevelingDelay" or "Work" in prop.name:
|
elif prop.name =="LevelingDelay" or "Work" in prop.name:
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -189,6 +189,11 @@ class AssignClass(bpy.types.Operator):
|
|||||||
continue
|
continue
|
||||||
spatial_obj = bpy.data.objects.get(collection.name)
|
spatial_obj = bpy.data.objects.get(collection.name)
|
||||||
if spatial_obj and spatial_obj.BIMObjectProperties.ifc_definition_id:
|
if spatial_obj and spatial_obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
element = self.file.by_id(spatial_obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
if self.file.schema != "IFC2X3" and not element.is_a("IfcSpatialElement"):
|
||||||
|
continue
|
||||||
|
elif self.file.schema == "IFC2X3" and not element.is_a("IfcSpatialStructureElement"):
|
||||||
|
continue
|
||||||
bpy.ops.bim.assign_container(
|
bpy.ops.bim.assign_container(
|
||||||
relating_structure=spatial_obj.BIMObjectProperties.ifc_definition_id, related_element=obj.name
|
relating_structure=spatial_obj.BIMObjectProperties.ifc_definition_id, related_element=obj.name
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -76,9 +76,7 @@ classes = (
|
|||||||
operator.AddTaskColumn,
|
operator.AddTaskColumn,
|
||||||
operator.RemoveTaskColumn,
|
operator.RemoveTaskColumn,
|
||||||
operator.SetTaskSortColumn,
|
operator.SetTaskSortColumn,
|
||||||
operator.EnableAssigningResources,
|
operator.EnableAssigningProcessToResource,
|
||||||
operator.AssignResource,
|
|
||||||
operator.UnassignResource,
|
|
||||||
prop.WorkPlan,
|
prop.WorkPlan,
|
||||||
prop.BIMWorkPlanProperties,
|
prop.BIMWorkPlanProperties,
|
||||||
prop.Task,
|
prop.Task,
|
||||||
|
|||||||
@@ -58,9 +58,15 @@ class EditWorkPlan(bpy.types.Operator):
|
|||||||
|
|
||||||
def export_attributes(self, attributes, prop):
|
def export_attributes(self, attributes, prop):
|
||||||
if "Date" in prop.name or "Time" in prop.name:
|
if "Date" in prop.name or "Time" in prop.name:
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
||||||
return True
|
return True
|
||||||
elif prop.name == "Duration" or prop.name == "TotalFloat":
|
elif prop.name == "Duration" or prop.name == "TotalFloat":
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -214,9 +220,15 @@ class EditWorkSchedule(bpy.types.Operator):
|
|||||||
|
|
||||||
def export_attributes(self, attributes, prop):
|
def export_attributes(self, attributes, prop):
|
||||||
if "Date" in prop.name or "Time" in prop.name:
|
if "Date" in prop.name or "Time" in prop.name:
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
||||||
return True
|
return True
|
||||||
elif prop.name == "Duration" or prop.name == "TotalFloat":
|
elif prop.name == "Duration" or prop.name == "TotalFloat":
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -564,9 +576,15 @@ class EditTaskTime(bpy.types.Operator):
|
|||||||
|
|
||||||
def export_attributes(self, attributes, prop):
|
def export_attributes(self, attributes, prop):
|
||||||
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
||||||
return True
|
return True
|
||||||
elif prop.name == "ScheduleDuration":
|
elif prop.name == "ScheduleDuration":
|
||||||
|
if prop.is_null:
|
||||||
|
attributes[prop.name] = None
|
||||||
|
return True
|
||||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -800,22 +818,44 @@ class AssignProcess(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
task: bpy.props.IntProperty()
|
task: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
|
parent_resource: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
related_objects = (
|
self.file = IfcStore.get_file()
|
||||||
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
if self.parent_resource:
|
||||||
)
|
resource = ifcopenshell.api.run(
|
||||||
for related_object in related_objects:
|
"resource.add_resource",
|
||||||
self.file = IfcStore.get_file()
|
self.file,
|
||||||
|
**{
|
||||||
|
"parent_resource": self.file.by_id(self.parent_resource),
|
||||||
|
"ifc_class": self.file.by_id(self.parent_resource).is_a(),
|
||||||
|
"name": self.file.by_id(self.parent_resource).Name + ": " + self.file.by_id(self.task).Name,
|
||||||
|
},
|
||||||
|
)
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"sequence.assign_process",
|
"sequence.assign_process",
|
||||||
self.file,
|
self.file,
|
||||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
**{
|
||||||
relating_process=self.file.by_id(self.task),
|
"related_object": resource,
|
||||||
|
"relating_process": self.file.by_id(self.task),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
ResourceData.load(self.file)
|
||||||
|
bpy.ops.bim.load_resources()
|
||||||
|
else:
|
||||||
|
related_objects = (
|
||||||
|
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
||||||
|
)
|
||||||
|
for related_object in related_objects:
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"sequence.assign_process",
|
||||||
|
self.file,
|
||||||
|
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
||||||
|
relating_process=self.file.by_id(self.task),
|
||||||
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -826,22 +866,38 @@ class UnassignProcess(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
task: bpy.props.IntProperty()
|
task: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
|
resource: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
related_objects = (
|
self.file = IfcStore.get_file()
|
||||||
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
if self.resource:
|
||||||
)
|
|
||||||
for related_object in related_objects:
|
|
||||||
self.file = IfcStore.get_file()
|
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"sequence.unassign_process",
|
"sequence.unassign_process",
|
||||||
self.file,
|
self.file,
|
||||||
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
related_object=self.file.by_id(self.resource),
|
||||||
relating_process=self.file.by_id(self.task),
|
relating_process=self.file.by_id(self.task),
|
||||||
)
|
)
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"resource.remove_resource",
|
||||||
|
self.file,
|
||||||
|
resource=self.file.by_id(self.resource),
|
||||||
|
)
|
||||||
|
ResourceData.load(self.file)
|
||||||
|
bpy.ops.bim.load_resources()
|
||||||
|
else:
|
||||||
|
related_objects = (
|
||||||
|
[bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
||||||
|
)
|
||||||
|
for related_object in related_objects:
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"sequence.unassign_process",
|
||||||
|
self.file,
|
||||||
|
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
|
||||||
|
relating_process=self.file.by_id(self.task),
|
||||||
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -1962,8 +2018,8 @@ class SetTaskSortColumn(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class EnableAssigningResources(bpy.types.Operator):
|
class EnableAssigningProcessToResource(bpy.types.Operator):
|
||||||
bl_idname = "bim.enable_assigning_resources"
|
bl_idname = "bim.enable_assigning_process_to_resources"
|
||||||
bl_label = "Enable Assigning Resources To Tasks"
|
bl_label = "Enable Assigning Resources To Tasks"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
task: bpy.props.IntProperty()
|
task: bpy.props.IntProperty()
|
||||||
@@ -1973,66 +2029,3 @@ class EnableAssigningResources(bpy.types.Operator):
|
|||||||
self.props.active_task_id = self.task
|
self.props.active_task_id = self.task
|
||||||
self.props.editing_task_type = "RESOURCES"
|
self.props.editing_task_type = "RESOURCES"
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class AssignResource(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.assign_resource"
|
|
||||||
bl_label = "Assign Resource"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
task: bpy.props.IntProperty()
|
|
||||||
parent_resource: bpy.props.IntProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
self.file = IfcStore.get_file()
|
|
||||||
resource = ifcopenshell.api.run(
|
|
||||||
"resource.add_resource",
|
|
||||||
self.file,
|
|
||||||
**{
|
|
||||||
"parent_resource": self.file.by_id(self.parent_resource),
|
|
||||||
"ifc_class": self.file.by_id(self.parent_resource).is_a(),
|
|
||||||
"name": self.file.by_id(self.parent_resource).Name + ": " + self.file.by_id(self.task).Name,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"sequence.assign_process",
|
|
||||||
self.file,
|
|
||||||
**{
|
|
||||||
"related_object": resource,
|
|
||||||
"relating_process": self.file.by_id(self.task),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
Data.load(self.file)
|
|
||||||
ResourceData.load(self.file)
|
|
||||||
bpy.ops.bim.load_resources()
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
class UnassignResource(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.unassign_resource"
|
|
||||||
bl_label = "Unassign Resource"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
|
||||||
task: bpy.props.IntProperty()
|
|
||||||
resource: bpy.props.IntProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
|
||||||
|
|
||||||
def _execute(self, context):
|
|
||||||
self.file = IfcStore.get_file()
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"sequence.unassign_process",
|
|
||||||
self.file,
|
|
||||||
related_object=self.file.by_id(self.resource),
|
|
||||||
relating_process=self.file.by_id(self.task),
|
|
||||||
)
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"resource.remove_resource",
|
|
||||||
self.file,
|
|
||||||
resource=self.file.by_id(self.resource),
|
|
||||||
)
|
|
||||||
Data.load(self.file)
|
|
||||||
ResourceData.load(self.file)
|
|
||||||
bpy.ops.bim.load_resources()
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|||||||
@@ -293,20 +293,19 @@ class BIM_PT_work_schedules(Panel):
|
|||||||
def draw_editable_task_resource_ui(self):
|
def draw_editable_task_resource_ui(self):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(self.props, "resources", text="")
|
row.prop(self.props, "resources", text="")
|
||||||
op = row.operator("bim.assign_resource", text="", icon="ADD")
|
op = row.operator("bim.assign_process", text="", icon="ADD")
|
||||||
op.parent_resource = int(self.props.resources)
|
if self.props.resources:
|
||||||
op.task = self.props.active_task_id
|
op.parent_resource = int(self.props.resources)
|
||||||
task = Data.tasks[self.props.active_task_id]
|
|
||||||
ResourceData.load(IfcStore.get_file())
|
|
||||||
|
|
||||||
for related_obect_id in task["OperatesOn"]:
|
|
||||||
resource = ResourceData.resources[related_obect_id]
|
|
||||||
row = self.layout.row(align=True)
|
|
||||||
row.label(text=resource["Name"], icon="COMMUNITY")
|
|
||||||
op = row.operator("bim.unassign_resource", text="", icon="X")
|
|
||||||
op.task = self.props.active_task_id
|
op.task = self.props.active_task_id
|
||||||
op.resource = related_obect_id
|
task = Data.tasks[self.props.active_task_id]
|
||||||
|
ResourceData.load(IfcStore.get_file())
|
||||||
|
for related_obect_id in task["OperatesOn"]:
|
||||||
|
resource = ResourceData.resources[related_obect_id]
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text=resource["Name"], icon="COMMUNITY")
|
||||||
|
op = row.operator("bim.unassign_process", text="", icon="X")
|
||||||
|
op.task = self.props.active_task_id
|
||||||
|
op.resource = related_obect_id
|
||||||
|
|
||||||
class BIM_UL_task_columns(UIList):
|
class BIM_UL_task_columns(UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||||
@@ -436,7 +435,7 @@ class BIM_UL_tasks(UIList):
|
|||||||
row.operator(
|
row.operator(
|
||||||
"bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO"
|
"bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO"
|
||||||
).task = item.ifc_definition_id
|
).task = item.ifc_definition_id
|
||||||
row.operator("bim.enable_assigning_resources", text="", icon="COMMUNITY").task = item.ifc_definition_id
|
row.operator("bim.enable_assigning_process_to_resources", text="", icon="COMMUNITY").task = item.ifc_definition_id
|
||||||
row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id
|
row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id
|
||||||
row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id
|
row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id
|
||||||
row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id
|
row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ classes = (
|
|||||||
operator.DisableEditingType,
|
operator.DisableEditingType,
|
||||||
operator.SelectSimilarType,
|
operator.SelectSimilarType,
|
||||||
operator.SelectTypeObjects,
|
operator.SelectTypeObjects,
|
||||||
|
operator.SelectType,
|
||||||
prop.BIMTypeProperties,
|
prop.BIMTypeProperties,
|
||||||
prop.BIMTypeObjectProperties,
|
prop.BIMTypeObjectProperties,
|
||||||
ui.BIM_PT_type,
|
ui.BIM_PT_type,
|
||||||
|
|||||||
@@ -109,6 +109,24 @@ class DisableEditingType(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class SelectType(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_type"
|
||||||
|
bl_label = "Select Type"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
related_object: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
related_object = bpy.data.objects.get(self.related_object) if self.related_object else context.active_object
|
||||||
|
oprops = related_object.BIMObjectProperties
|
||||||
|
obj = IfcStore.get_element(
|
||||||
|
ifcopenshell.util.element.get_type(self.file.by_id(oprops.ifc_definition_id)).GlobalId
|
||||||
|
)
|
||||||
|
context.view_layer.objects.active = obj
|
||||||
|
obj.select_set(True)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class SelectSimilarType(bpy.types.Operator):
|
class SelectSimilarType(bpy.types.Operator):
|
||||||
bl_idname = "bim.select_similar_type"
|
bl_idname = "bim.select_similar_type"
|
||||||
bl_label = "Select Similar Type"
|
bl_label = "Select Similar Type"
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class BIM_PT_type(Panel):
|
|||||||
else:
|
else:
|
||||||
label = name
|
label = name
|
||||||
row.label(text=label)
|
row.label(text=label)
|
||||||
|
row.operator("bim.select_type", icon="TRACKER", text="")
|
||||||
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
|
row.operator("bim.select_similar_type", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
row.operator("bim.enable_editing_type", icon="GREASEPENCIL", text="")
|
row.operator("bim.enable_editing_type", icon="GREASEPENCIL", text="")
|
||||||
if name != "None/None":
|
if name != "None/None":
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ from . import ui, prop, operator
|
|||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.AssignUnit,
|
operator.AssignUnit,
|
||||||
|
operator.UnassignUnit,
|
||||||
operator.LoadUnits,
|
operator.LoadUnits,
|
||||||
operator.DisableUnitEditingUI,
|
operator.DisableUnitEditingUI,
|
||||||
operator.RemoveUnit,
|
operator.RemoveUnit,
|
||||||
operator.AddMonetaryUnit,
|
operator.AddMonetaryUnit,
|
||||||
|
operator.AddSIUnit,
|
||||||
operator.EnableEditingUnit,
|
operator.EnableEditingUnit,
|
||||||
operator.DisableEditingUnit,
|
operator.DisableEditingUnit,
|
||||||
operator.EditUnit,
|
operator.EditUnit,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.unit
|
||||||
import blenderbim.bim.helper
|
import blenderbim.bim.helper
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.unit.data import Data
|
from ifcopenshell.api.unit.data import Data
|
||||||
@@ -9,13 +10,20 @@ class AssignUnit(bpy.types.Operator):
|
|||||||
bl_idname = "bim.assign_unit"
|
bl_idname = "bim.assign_unit"
|
||||||
bl_label = "Assign Unit"
|
bl_label = "Assign Unit"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
unit: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
ifcopenshell.api.run("unit.assign_unit", IfcStore.get_file(), **self.get_units(context))
|
self.file = IfcStore.get_file()
|
||||||
Data.load(IfcStore.get_file())
|
if self.unit:
|
||||||
|
ifcopenshell.api.run("unit.assign_unit", self.file, units=[self.file.by_id(self.unit)])
|
||||||
|
else:
|
||||||
|
ifcopenshell.api.run("unit.assign_unit", self.file, **self.get_units(context))
|
||||||
|
Data.load(self.file)
|
||||||
|
if self.unit:
|
||||||
|
bpy.ops.bim.load_units()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def get_units(self, context):
|
def get_units(self, context):
|
||||||
@@ -46,6 +54,23 @@ class AssignUnit(bpy.types.Operator):
|
|||||||
return units
|
return units
|
||||||
|
|
||||||
|
|
||||||
|
class UnassignUnit(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.unassign_unit"
|
||||||
|
bl_label = "Unassign Unit"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
unit: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
ifcopenshell.api.run("unit.unassign_unit", IfcStore.get_file(), units=[self.file.by_id(self.unit)])
|
||||||
|
Data.load(self.file)
|
||||||
|
bpy.ops.bim.load_units()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class LoadUnits(bpy.types.Operator):
|
class LoadUnits(bpy.types.Operator):
|
||||||
bl_idname = "bim.load_units"
|
bl_idname = "bim.load_units"
|
||||||
bl_label = "Load Units"
|
bl_label = "Load Units"
|
||||||
@@ -56,8 +81,7 @@ class LoadUnits(bpy.types.Operator):
|
|||||||
while len(props.units) > 0:
|
while len(props.units) > 0:
|
||||||
props.units.remove(0)
|
props.units.remove(0)
|
||||||
|
|
||||||
for ifc_definition_id in Data.unit_assignment:
|
for ifc_definition_id, unit in Data.units.items():
|
||||||
unit = Data.units[ifc_definition_id]
|
|
||||||
name = unit.get("Name", "")
|
name = unit.get("Name", "")
|
||||||
|
|
||||||
if unit["type"] == "IfcMonetaryUnit":
|
if unit["type"] == "IfcMonetaryUnit":
|
||||||
@@ -86,6 +110,7 @@ class LoadUnits(bpy.types.Operator):
|
|||||||
new.ifc_definition_id = ifc_definition_id
|
new.ifc_definition_id = ifc_definition_id
|
||||||
new.name = name
|
new.name = name
|
||||||
new.unit_type = unit_type
|
new.unit_type = unit_type
|
||||||
|
new.is_assigned = ifc_definition_id in Data.unit_assignment
|
||||||
new.icon = icon
|
new.icon = icon
|
||||||
|
|
||||||
props.is_editing = True
|
props.is_editing = True
|
||||||
@@ -132,8 +157,29 @@ class AddMonetaryUnit(bpy.types.Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMUnitProperties
|
props = context.scene.BIMUnitProperties
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
unit = ifcopenshell.api.run("unit.add_monetary_unit", self.file)
|
ifcopenshell.api.run("unit.add_monetary_unit", self.file)
|
||||||
ifcopenshell.api.run("unit.assign_unit", self.file, units=[unit])
|
Data.load(self.file)
|
||||||
|
bpy.ops.bim.load_units()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class AddSIUnit(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_si_unit"
|
||||||
|
bl_label = "Add SI Unit"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = context.scene.BIMUnitProperties
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
unit = ifcopenshell.api.run(
|
||||||
|
"unit.add_si_unit",
|
||||||
|
self.file,
|
||||||
|
unit_type=props.named_unit_types,
|
||||||
|
name=ifcopenshell.util.unit.si_type_names[props.named_unit_types],
|
||||||
|
)
|
||||||
Data.load(self.file)
|
Data.load(self.file)
|
||||||
bpy.ops.bim.load_units()
|
bpy.ops.bim.load_units()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
|
import ifcopenshell.util.attribute
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
from blenderbim.bim.prop import StrProperty, Attribute
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
@@ -17,11 +18,14 @@ from bpy.props import (
|
|||||||
|
|
||||||
|
|
||||||
unitclasses_enum = []
|
unitclasses_enum = []
|
||||||
|
namedunittypes_enum = []
|
||||||
|
|
||||||
|
|
||||||
def purge():
|
def purge():
|
||||||
global unitclasses_enum
|
global unitclasses_enum
|
||||||
|
global namedunittypes_enum
|
||||||
unitclasses_enum = []
|
unitclasses_enum = []
|
||||||
|
namedunittypes_enum = []
|
||||||
|
|
||||||
|
|
||||||
def getUnitClasses(self, context):
|
def getUnitClasses(self, context):
|
||||||
@@ -33,9 +37,20 @@ def getUnitClasses(self, context):
|
|||||||
return unitclasses_enum
|
return unitclasses_enum
|
||||||
|
|
||||||
|
|
||||||
|
def getNamedUnitTypes(self, context):
|
||||||
|
global namedunittypes_enum
|
||||||
|
if not len(namedunittypes_enum) and IfcStore.get_file():
|
||||||
|
values = ifcopenshell.util.attribute.get_enum_items(
|
||||||
|
IfcStore.get_schema().declaration_by_name("IfcNamedUnit").all_attributes()[1]
|
||||||
|
)
|
||||||
|
namedunittypes_enum.extend([(c, c, "") for c in sorted(values)])
|
||||||
|
return namedunittypes_enum
|
||||||
|
|
||||||
|
|
||||||
class Unit(PropertyGroup):
|
class Unit(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
name: StringProperty(name="Name")
|
||||||
unit_type: StringProperty(name="Unit Type")
|
unit_type: StringProperty(name="Unit Type")
|
||||||
|
is_assigned: BoolProperty(name="Is Assigned")
|
||||||
icon: StringProperty(name="Icon")
|
icon: StringProperty(name="Icon")
|
||||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||||
|
|
||||||
@@ -46,4 +61,5 @@ class BIMUnitProperties(PropertyGroup):
|
|||||||
active_unit_index: IntProperty(name="Active Unit Index")
|
active_unit_index: IntProperty(name="Active Unit Index")
|
||||||
active_unit_id: IntProperty(name="Active Unit Id")
|
active_unit_id: IntProperty(name="Active Unit Id")
|
||||||
unit_classes: EnumProperty(items=getUnitClasses, name="Unit Classes")
|
unit_classes: EnumProperty(items=getUnitClasses, name="Unit Classes")
|
||||||
|
named_unit_types: EnumProperty(items=getNamedUnitTypes, name="Named Unit Types")
|
||||||
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
|
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
|
||||||
|
|||||||
@@ -40,8 +40,11 @@ class BIM_PT_units(Panel):
|
|||||||
row.operator("bim.add_monetary_unit", text="", icon="ADD")
|
row.operator("bim.add_monetary_unit", text="", icon="ADD")
|
||||||
elif self.props.unit_classes == "IfcDerivedUnit":
|
elif self.props.unit_classes == "IfcDerivedUnit":
|
||||||
pass # TODO
|
pass # TODO
|
||||||
|
elif self.props.unit_classes == "IfcSIUnit":
|
||||||
|
row.prop(self.props, "named_unit_types", text="")
|
||||||
|
row.operator("bim.add_si_unit", text="", icon="ADD")
|
||||||
else:
|
else:
|
||||||
pass # TODO
|
row.prop(self.props, "named_unit_types", text="")
|
||||||
|
|
||||||
self.layout.template_list(
|
self.layout.template_list(
|
||||||
"BIM_UL_units",
|
"BIM_UL_units",
|
||||||
@@ -67,6 +70,13 @@ class BIM_UL_units(UIList):
|
|||||||
row.label(text=item.unit_type or "No Type", icon=item.icon)
|
row.label(text=item.unit_type or "No Type", icon=item.icon)
|
||||||
row.label(text=item.name or "Unnamed")
|
row.label(text=item.name or "Unnamed")
|
||||||
|
|
||||||
|
if item.is_assigned:
|
||||||
|
op = row.operator("bim.unassign_unit", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||||
|
op.unit = item.ifc_definition_id
|
||||||
|
else:
|
||||||
|
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 props.active_unit_id == item.ifc_definition_id:
|
||||||
row.operator("bim.edit_unit", text="", icon="CHECKMARK")
|
row.operator("bim.edit_unit", text="", icon="CHECKMARK")
|
||||||
row.operator("bim.disable_editing_unit", text="", icon="CANCEL")
|
row.operator("bim.disable_editing_unit", text="", icon="CANCEL")
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class IfcSchema:
|
|||||||
self.property_files = []
|
self.property_files = []
|
||||||
property_paths = self.data_dir.joinpath("pset").glob("*.ifc")
|
property_paths = self.data_dir.joinpath("pset").glob("*.ifc")
|
||||||
# TODO: add IFC2X3 PsetQto template support
|
# TODO: add IFC2X3 PsetQto template support
|
||||||
self.psetqto = ifcopenshell.util.pset.PsetQto("IFC4")
|
self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||||
for path in property_paths:
|
for path in property_paths:
|
||||||
self.psetqto.templates.append(ifcopenshell.open(path))
|
self.psetqto.templates.append(ifcopenshell.open(path))
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
profile = self.file.create_entity(
|
profile = self.file.create_entity(
|
||||||
"IfcIShapeProfileDef",
|
"IfcIShapeProfileDef",
|
||||||
|
ProfileName="DEMO-I",
|
||||||
ProfileType="AREA",
|
ProfileType="AREA",
|
||||||
OverallWidth=0.1,
|
OverallWidth=0.1,
|
||||||
OverallDepth=0.2,
|
OverallDepth=0.2,
|
||||||
@@ -69,6 +70,7 @@ class LibraryGenerator:
|
|||||||
|
|
||||||
profile = self.file.create_entity(
|
profile = self.file.create_entity(
|
||||||
"IfcCShapeProfileDef",
|
"IfcCShapeProfileDef",
|
||||||
|
ProfileName="DEMO-C",
|
||||||
ProfileType="AREA",
|
ProfileType="AREA",
|
||||||
Depth=0.2,
|
Depth=0.2,
|
||||||
Width=0.1,
|
Width=0.1,
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Server-Test
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pip install -r requirements.txt
|
||||||
|
$ cd bcfserver/
|
||||||
|
$ python
|
||||||
|
>>> from run import db
|
||||||
|
>>> db.create_all()
|
||||||
|
$ export FLASK_APP=run.py
|
||||||
|
$ flask run
|
||||||
|
```
|
||||||
|
|
||||||
|
Go to [http://localhost:5000](http://localhost:5000) to see the server
|
||||||
|
|
||||||
|
# Register the user
|
||||||
|
|
||||||
|
1. Go to http://localhost:5000/register to register the user
|
||||||
|
2. Create the client
|
||||||
|
3. For grant type enter authorization_code
|
||||||
|
4. For response_type enter code secret
|
||||||
|
5. Enter the scope and create the client
|
||||||
|
|
||||||
|
### You will be redirected to the page with the details of your client id and secret
|
||||||
|
|
||||||
|
# Foundation API
|
||||||
|
|
||||||
|
- Set the Base URL will be `http://127.0.0.1:5000/`
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"Projects":[{
|
||||||
|
"project_id": "F445F4F2-4D02-4B2A-B612-5E456BEF9137",
|
||||||
|
"name": "Example project 1",
|
||||||
|
"authorization": {
|
||||||
|
"project_actions": [
|
||||||
|
"createTopic",
|
||||||
|
"createDocument"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project_id": "A233FBB2-3A3B-EFF4-C123-DE22ABC8414",
|
||||||
|
"name": "Example project 2",
|
||||||
|
"authorization": {
|
||||||
|
"project_actions": []
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"Extensions":[{
|
||||||
|
"topic_type": [
|
||||||
|
"Information",
|
||||||
|
"Error"
|
||||||
|
],
|
||||||
|
"topic_status": [
|
||||||
|
"Open",
|
||||||
|
"Closed",
|
||||||
|
"ReOpened"
|
||||||
|
],
|
||||||
|
"topic_label": [
|
||||||
|
"Architecture",
|
||||||
|
"Structural",
|
||||||
|
"MEP"
|
||||||
|
],
|
||||||
|
"snippet_type": [
|
||||||
|
".ifc",
|
||||||
|
".csv"
|
||||||
|
],
|
||||||
|
"priority": [
|
||||||
|
"Low",
|
||||||
|
"Medium",
|
||||||
|
"High"
|
||||||
|
],
|
||||||
|
"users": [
|
||||||
|
"Architect@example.com",
|
||||||
|
"BIM-Manager@example.com",
|
||||||
|
"bob_heater@example.com"
|
||||||
|
],
|
||||||
|
"stage": [
|
||||||
|
"Preliminary Planning End",
|
||||||
|
"Construction Start",
|
||||||
|
"Construction End"
|
||||||
|
],
|
||||||
|
"project_actions": [
|
||||||
|
"update",
|
||||||
|
"createTopic",
|
||||||
|
"createDocument"
|
||||||
|
],
|
||||||
|
"topic_actions": [
|
||||||
|
"update",
|
||||||
|
"updateBimSnippet",
|
||||||
|
"updateRelatedTopics",
|
||||||
|
"updateDocumentReferences",
|
||||||
|
"updateFiles",
|
||||||
|
"createComment",
|
||||||
|
"createViewpoint"
|
||||||
|
],
|
||||||
|
"comment_actions": [
|
||||||
|
"update"
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
@@ -0,0 +1,700 @@
|
|||||||
|
from operator import methodcaller
|
||||||
|
from flask import jsonify, url_for, redirect, render_template, request, session, flash
|
||||||
|
from flask_login import login_user, logout_user, login_required, current_user
|
||||||
|
from flask.blueprints import Blueprint
|
||||||
|
from foundation.models import User, OAuth2AuthorizationCode, OAuth2Token, OAuth2Client
|
||||||
|
from run import app, db
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
bcf = Blueprint("bcf", __name__, template_folder="templates", url_prefix="/bcf/3.0")
|
||||||
|
my_absolute_dirpath = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
data = open(
|
||||||
|
f"{my_absolute_dirpath}/project.json",
|
||||||
|
)
|
||||||
|
jdata = json.load(data)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_client(request):
|
||||||
|
Headers = str.split(request.headers["Authorization"])
|
||||||
|
token = Headers[1]
|
||||||
|
access_token = OAuth2Token.query.filter_by(access_token=token).first()
|
||||||
|
if access_token:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def invalid_user():
|
||||||
|
response = jsonify({"error": "User not recognized"})
|
||||||
|
response.status = 401
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def invalid_project():
|
||||||
|
response = jsonify({"message": "Project not found"})
|
||||||
|
response.status = 404
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects")
|
||||||
|
def projects():
|
||||||
|
return invalid_user()
|
||||||
|
if validate_client(request):
|
||||||
|
return jsonify(jdata["Projects"])
|
||||||
|
return invalid_user()
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/")
|
||||||
|
@login_required
|
||||||
|
def bcf_3():
|
||||||
|
return "<h1>BCF HOMPAGE</h1>"
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>")
|
||||||
|
def project_details(project_id):
|
||||||
|
if validate_client(request):
|
||||||
|
for project in jdata["Projects"]:
|
||||||
|
if project["project_id"] == project_id:
|
||||||
|
return jsonify(project)
|
||||||
|
return invalid_project()
|
||||||
|
return invalid_user()
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>", methods=["PUT"])
|
||||||
|
def update_project(project_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/extensions")
|
||||||
|
def extensions(project_id):
|
||||||
|
if validate_client(request):
|
||||||
|
for project in jdata["Projects"]:
|
||||||
|
if project["project_id"] == project_id:
|
||||||
|
return jsonify(jdata["Extensions"])
|
||||||
|
return invalid_project()
|
||||||
|
return invalid_user()
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics")
|
||||||
|
def topics(project_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token:
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["Topics"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>", methods=["POST"])
|
||||||
|
def create_topic(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "POST":
|
||||||
|
body = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=body,
|
||||||
|
status=201,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>")
|
||||||
|
def topic_details(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token:
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=j,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>", methods=["PUT"])
|
||||||
|
def update_topic(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>", methods=["DELETE"])
|
||||||
|
def delete_topic(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "DELETE":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
message = {"DELETED"}
|
||||||
|
response = app.response_class(
|
||||||
|
response=json.dumps(message),
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/snippet", methods=["GET"])
|
||||||
|
def get_snippet(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=json.dumps("Snippet test successfull"),
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/snippet", methods=["PUT"])
|
||||||
|
def update_snippet(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/files_information", methods=["GET"])
|
||||||
|
def get_files_information(project_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["Files"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/files", methods=["GET"])
|
||||||
|
def get_files(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=json.dumps("Get request for files successfull"),
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/files", methods=["PUT"])
|
||||||
|
def update_files(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/comments", methods=["GET"])
|
||||||
|
def get_comments(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["Comments"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/comments", methods=["POST"])
|
||||||
|
def create_comments(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "POST":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/comments/<comment_id>", methods=["GET"]
|
||||||
|
)
|
||||||
|
def get_comment(project_id, topic_id, comment_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["Comments"]:
|
||||||
|
if (k["guid"]) == comment_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=k,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/comments/<comment_id>", methods=["PUT"]
|
||||||
|
)
|
||||||
|
def update_comment(project_id, topic_id, comment_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["Comments"]:
|
||||||
|
if (k["guid"]) == comment_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/comments/<comment_id>", methods=["DELETE"]
|
||||||
|
)
|
||||||
|
def delete_comment(project_id, topic_id, comment_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "DELETE":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["Comments"]:
|
||||||
|
if (k["guid"]) == comment_id:
|
||||||
|
message = {"DELETED"}
|
||||||
|
response = app.response_class(
|
||||||
|
response=json.dumps(message),
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
***Add ViewPoints Routes Here ***
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/related_topics", methods=["GET"])
|
||||||
|
def get_related_topics(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["RelatedTopics"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/related_topics", methods=["PUT"])
|
||||||
|
def update_related_topics(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/document_references", methods=["GET"]
|
||||||
|
)
|
||||||
|
def get_document_references(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["DocumentReferences"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/document_references", methods=["POST"]
|
||||||
|
)
|
||||||
|
def create_document_references(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "POST":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/document_references/<document_reference_id>",
|
||||||
|
methods=["PUT"],
|
||||||
|
)
|
||||||
|
def update_document_references(project_id, topic_id, document_reference_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "PUT":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["DocumentReferences"]:
|
||||||
|
if (k["guid"]) == document_reference_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/documents", methods=["GET"])
|
||||||
|
def get_documents(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["Documents"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/documents", methods=["POST"])
|
||||||
|
def create_documents(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "POST":
|
||||||
|
data = request.form["data"]
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=data,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/documents/<document_id>", methods=["GET"]
|
||||||
|
)
|
||||||
|
def get_document(project_id, topic_id, document_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["Documents"]:
|
||||||
|
if (k["guid"]) == document_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=k,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/events", methods=["GET"])
|
||||||
|
def get_events(project_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["Events"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/<topic_id>/events", methods=["GET"])
|
||||||
|
def get_topic_events(project_id, topic_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["Events"]:
|
||||||
|
if (k["topic_guid"]) == topic_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=k,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route("/projects/<project_id>/topics/comments/events", methods=["GET"])
|
||||||
|
def get_comments_events(project_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
response = app.response_class(
|
||||||
|
response=jdata["EventComments"],
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@bcf.route(
|
||||||
|
"/projects/<project_id>/topics/<topic_id>/comments/<comment_id>/events",
|
||||||
|
methods=["GET"],
|
||||||
|
)
|
||||||
|
def get_comment_events(project_id, topic_id, comment_id):
|
||||||
|
access_token = validate_client(request)
|
||||||
|
if access_token and request.method == "GET":
|
||||||
|
for i in jdata["Projects"]:
|
||||||
|
if (i["project_id"]) == project_id:
|
||||||
|
for j in jdata["Topics"]:
|
||||||
|
if (j["guid"]) == topic_id:
|
||||||
|
for k in jdata["EventComments"]:
|
||||||
|
if (k["comment_guid"]) == comment_id:
|
||||||
|
response = app.response_class(
|
||||||
|
response=k,
|
||||||
|
status=200,
|
||||||
|
mimetype="application/json",
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
response = invalid_project()
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = invalid_user()
|
||||||
|
return response
|
||||||
+17
-17
@@ -11,8 +11,8 @@ import urllib
|
|||||||
import json
|
import json
|
||||||
from run import db, app
|
from run import db, app
|
||||||
|
|
||||||
website_obj = Blueprint(
|
foundation_obj = Blueprint(
|
||||||
"website_obj",
|
"foundation_obj",
|
||||||
__name__,
|
__name__,
|
||||||
template_folder="templates",
|
template_folder="templates",
|
||||||
)
|
)
|
||||||
@@ -22,13 +22,13 @@ def split_by_crlf(s):
|
|||||||
return [v for v in s.splitlines() if v]
|
return [v for v in s.splitlines() if v]
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/")
|
@foundation_obj.route("/")
|
||||||
def homepage():
|
def homepage():
|
||||||
return render_template("index.html")
|
return render_template("index.html")
|
||||||
# return "homepage"
|
# return "homepage"
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/register", methods=["GET", "POST"])
|
@foundation_obj.route("/register", methods=["GET", "POST"])
|
||||||
def register_page():
|
def register_page():
|
||||||
form = RegisterForm()
|
form = RegisterForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@@ -45,7 +45,7 @@ def register_page():
|
|||||||
f"Account created successfully! {user_to_create.username}",
|
f"Account created successfully! {user_to_create.username}",
|
||||||
category="success",
|
category="success",
|
||||||
)
|
)
|
||||||
return redirect(url_for("website_obj.homepage"))
|
return redirect(url_for("foundation_obj.homepage"))
|
||||||
if form.errors != {}:
|
if form.errors != {}:
|
||||||
for err_msg in form.errors.values():
|
for err_msg in form.errors.values():
|
||||||
flash(
|
flash(
|
||||||
@@ -55,7 +55,7 @@ def register_page():
|
|||||||
return render_template("register.html", form=form)
|
return render_template("register.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/createclient", methods=["GET", "POST"])
|
@foundation_obj.route("/createclient", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def create_client():
|
def create_client():
|
||||||
grants = [
|
grants = [
|
||||||
@@ -99,7 +99,7 @@ def create_client():
|
|||||||
return render_template("createclient.html", form=form, grants=grants)
|
return render_template("createclient.html", form=form, grants=grants)
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/login", methods=["GET", "POST"])
|
@foundation_obj.route("/login", methods=["GET", "POST"])
|
||||||
def login_page():
|
def login_page():
|
||||||
form = LoginForm()
|
form = LoginForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@@ -108,7 +108,7 @@ def login_page():
|
|||||||
attempted_password=form.password.data
|
attempted_password=form.password.data
|
||||||
):
|
):
|
||||||
login_user(attempted_user)
|
login_user(attempted_user)
|
||||||
return redirect(url_for("website_obj.homepage"))
|
return redirect(url_for("foundation_obj.homepage"))
|
||||||
else:
|
else:
|
||||||
flash(
|
flash(
|
||||||
"Invalid Credentials",
|
"Invalid Credentials",
|
||||||
@@ -118,14 +118,14 @@ def login_page():
|
|||||||
return render_template("login.html", form=form)
|
return render_template("login.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/logout")
|
@foundation_obj.route("/logout")
|
||||||
def logoutpage():
|
def logoutpage():
|
||||||
logout_user()
|
logout_user()
|
||||||
flash("You have been logged out!", category="info")
|
flash("You have been logged out!", category="info")
|
||||||
return redirect(url_for("website_obj.homepage"))
|
return redirect(url_for("foundation_obj.homepage"))
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/foundation/1.0/auth")
|
@foundation_obj.route("/foundation/1.0/auth")
|
||||||
def foundation_auth():
|
def foundation_auth():
|
||||||
data = {
|
data = {
|
||||||
"oauth2_auth_url": "http://127.0.0.1:5000/oauth/authorize",
|
"oauth2_auth_url": "http://127.0.0.1:5000/oauth/authorize",
|
||||||
@@ -138,7 +138,7 @@ def foundation_auth():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/foundation/versions")
|
@foundation_obj.route("/foundation/versions")
|
||||||
def foundation_versions():
|
def foundation_versions():
|
||||||
Body = {
|
Body = {
|
||||||
"versions": [
|
"versions": [
|
||||||
@@ -167,7 +167,7 @@ def foundation_versions():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/outh/login", methods=["GET", "POST"])
|
@foundation_obj.route("/outh/login", methods=["GET", "POST"])
|
||||||
def oauth_login():
|
def oauth_login():
|
||||||
form = LoginForm()
|
form = LoginForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@@ -185,7 +185,7 @@ def oauth_login():
|
|||||||
state = request.args.get("state")
|
state = request.args.get("state")
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"website_obj.authorize",
|
"foundation_obj.authorize",
|
||||||
client_id=client_id,
|
client_id=client_id,
|
||||||
redirect_uri=redirect_uri,
|
redirect_uri=redirect_uri,
|
||||||
state=state,
|
state=state,
|
||||||
@@ -199,7 +199,7 @@ def oauth_login():
|
|||||||
return render_template("ologin.html", form=form)
|
return render_template("ologin.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/oauth/authorize", methods=["GET", "POST"])
|
@foundation_obj.route("/oauth/authorize", methods=["GET", "POST"])
|
||||||
def authorize():
|
def authorize():
|
||||||
client_id = request.args.get("client_id")
|
client_id = request.args.get("client_id")
|
||||||
redirect_uri = request.args.get("redirect_uri")
|
redirect_uri = request.args.get("redirect_uri")
|
||||||
@@ -209,7 +209,7 @@ def authorize():
|
|||||||
query = request.query_string
|
query = request.query_string
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"website_obj.oauth_login",
|
"foundation_obj.oauth_login",
|
||||||
client_id=client_id,
|
client_id=client_id,
|
||||||
redirect_uri=redirect_uri,
|
redirect_uri=redirect_uri,
|
||||||
state=state,
|
state=state,
|
||||||
@@ -252,7 +252,7 @@ def authorize():
|
|||||||
return render_template("oauth.html")
|
return render_template("oauth.html")
|
||||||
|
|
||||||
|
|
||||||
@website_obj.route("/oauth/token", methods=["POST"])
|
@foundation_obj.route("/oauth/token", methods=["POST"])
|
||||||
def issue_token():
|
def issue_token():
|
||||||
try:
|
try:
|
||||||
code = request.form["code"]
|
code = request.form["code"]
|
||||||
+9
-5
@@ -30,7 +30,7 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="{{ url_for('website_obj.homepage') }}"
|
<a class="nav-link" href="{{ url_for('foundation_obj.homepage') }}"
|
||||||
>Home <span class="sr-only">(current)</span></a
|
>Home <span class="sr-only">(current)</span></a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
@@ -47,14 +47,16 @@
|
|||||||
<a class="nav-link">Welcome, {{ current_user.username }}</a>
|
<a class="nav-link">Welcome, {{ current_user.username }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ url_for('website_obj.logoutpage') }}"
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
href="{{ url_for('foundation_obj.logoutpage') }}"
|
||||||
>Logout</a
|
>Logout</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
href="{{ url_for('website_obj.create_client') }}"
|
href="{{ url_for('foundation_obj.create_client') }}"
|
||||||
>Create Client</a
|
>Create Client</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
@@ -62,14 +64,16 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{{ url_for('website_obj.login_page') }}"
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
href="{{ url_for('foundation_obj.login_page') }}"
|
||||||
>Login</a
|
>Login</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a
|
<a
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
href="{{ url_for('website_obj.register_page') }}"
|
href="{{ url_for('foundation_obj.register_page') }}"
|
||||||
>Register</a
|
>Register</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
<h6>Do not have an account?</h6>
|
<h6>Do not have an account?</h6>
|
||||||
<a
|
<a
|
||||||
class="btn btn-sm btn-secondary"
|
class="btn btn-sm btn-secondary"
|
||||||
href="{{ url_for('website_obj.register_page') }}"
|
href="{{ url_for('foundation_obj.register_page') }}"
|
||||||
>Register</a
|
>Register</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
<h6>Do not have an account?</h6>
|
<h6>Do not have an account?</h6>
|
||||||
<a
|
<a
|
||||||
class="btn btn-sm btn-secondary"
|
class="btn btn-sm btn-secondary"
|
||||||
href="{{ url_for('website_obj.register_page') }}"
|
href="{{ url_for('foundation_obj.register_page') }}"
|
||||||
>Register</a
|
>Register</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
<h6>Already have an account?</h6>
|
<h6>Already have an account?</h6>
|
||||||
<a
|
<a
|
||||||
class="btn btn-sm btn-secondary"
|
class="btn btn-sm btn-secondary"
|
||||||
href="{{ url_for('website_obj.login_page') }}"
|
href="{{ url_for('foundation_obj.login_page') }}"
|
||||||
>Login</a
|
>Login</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,14 +10,14 @@ login_manager = LoginManager(app)
|
|||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
app.config["SECRET_KEY"] = "f613729206685405cde0e388"
|
app.config["SECRET_KEY"] = "f613729206685405cde0e388"
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///sqlite.db"
|
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///sqlite.db"
|
||||||
login_manager.login_view = "website_obj.login_page"
|
login_manager.login_view = "foundation_obj.login_page"
|
||||||
login_manager.login_message_category = "info"
|
login_manager.login_message_category = "info"
|
||||||
|
|
||||||
from website.routes import website_obj
|
from foundation.routes import foundation_obj
|
||||||
from bcf.routes import bcf
|
from bcf.routes import bcf
|
||||||
|
|
||||||
|
|
||||||
app.register_blueprint(website_obj)
|
app.register_blueprint(foundation_obj)
|
||||||
app.register_blueprint(bcf)
|
app.register_blueprint(bcf)
|
||||||
|
|
||||||
|
|
||||||
@@ -60,18 +60,20 @@ class Csv2Ifc:
|
|||||||
|
|
||||||
def get_row_cost_data(self, row):
|
def get_row_cost_data(self, row):
|
||||||
name = row[self.headers["Name"]]
|
name = row[self.headers["Name"]]
|
||||||
|
identification = row[self.headers["Identification"]] if "Identification" in self.headers else None
|
||||||
cost_quantities = row[self.headers["Quantity"]]
|
cost_quantities = row[self.headers["Quantity"]]
|
||||||
cost_quantities_unit = row[self.headers["Unit"]]
|
cost_quantities_unit = row[self.headers["Unit"]]
|
||||||
if self.has_categories:
|
if self.has_categories:
|
||||||
cost_values = {
|
cost_values = {
|
||||||
k: float(row[v])
|
k: float(row[v])
|
||||||
for k, v in self.headers.items()
|
for k, v in self.headers.items()
|
||||||
if k not in ["Hierarchy", "Name", "Quantity", "Unit", "Subtotal"] and row[v]
|
if k not in ["Hierarchy", "Identification", "Name", "Quantity", "Unit", "Subtotal"] and row[v]
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
cost_values = row[self.headers["Value"]]
|
cost_values = row[self.headers["Value"]]
|
||||||
cost_values = float(cost_values) if cost_values else None
|
cost_values = float(cost_values) if cost_values else None
|
||||||
return {
|
return {
|
||||||
|
"Identification": str(identification) if identification else None,
|
||||||
"Name": str(name) if name else None,
|
"Name": str(name) if name else None,
|
||||||
"CostQuantities": float(cost_quantities) if cost_quantities else None,
|
"CostQuantities": float(cost_quantities) if cost_quantities else None,
|
||||||
"CostQuantitiesUnit": str(cost_quantities_unit) if cost_quantities_unit else None,
|
"CostQuantitiesUnit": str(cost_quantities_unit) if cost_quantities_unit else None,
|
||||||
@@ -99,6 +101,7 @@ class Csv2Ifc:
|
|||||||
cost_item["ifc"] = ifcopenshell.api.run("cost.add_cost_item", self.file, cost_item=parent)
|
cost_item["ifc"] = ifcopenshell.api.run("cost.add_cost_item", self.file, cost_item=parent)
|
||||||
|
|
||||||
cost_item["ifc"].Name = cost_item["Name"]
|
cost_item["ifc"].Name = cost_item["Name"]
|
||||||
|
cost_item["ifc"].Identification = cost_item["Identification"]
|
||||||
|
|
||||||
if not cost_item["CostValues"]:
|
if not cost_item["CostValues"]:
|
||||||
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
|
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
|
||||||
|
|||||||
+14
-14
@@ -1,14 +1,14 @@
|
|||||||
"Hierarchy","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal"
|
"Hierarchy","Identification","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal"
|
||||||
1,"Demolition",,,,,,301
|
1,1,"Demolition",,,,,,301
|
||||||
,,,,,,,
|
,,,,,,,,
|
||||||
2,"Building A",,,,,,96
|
2,1.1,"Building A",,,,,,96
|
||||||
,,,,,,,
|
,,,,,,,,
|
||||||
3,"Soft strip",1,"m",5,4,3,12
|
3,"1.1.1","Soft strip",1,"m",5,4,3,12
|
||||||
3,"Hard Strip",2,"m2",6,5,4,30
|
3,"1.1.2","Hard Strip",2,"m2",6,5,4,30
|
||||||
3,"Hazmat",3,"m3",7,6,5,54
|
3,"1.1.3","Hazmat",3,"m3",7,6,5,54
|
||||||
,,,,,,,
|
,,,,,,,,
|
||||||
2,"Building B",,,,,,205
|
2,1.2,"Building B",,,,,,205
|
||||||
,,,,,,,
|
,,,,,,,,
|
||||||
3,"Soft strip",4,"hr",8,7,,60
|
3,"1.2.1","Soft strip",4,"hr",8,7,,60
|
||||||
3,"Hard Strip",5,,9,,8,85
|
3,"1.2.2","Hard Strip",5,,9,,8,85
|
||||||
3,"Hazmat",6,"kg",10,,,60
|
3,"1.2.3","Hazmat",6,"kg",10,,,60
|
||||||
|
|||||||
|
@@ -24,15 +24,20 @@ from datetime import datetime
|
|||||||
|
|
||||||
JSON_TO_IFC = {
|
JSON_TO_IFC = {
|
||||||
"Building": ["IfcBuilding"],
|
"Building": ["IfcBuilding"],
|
||||||
"BuildingPart": ["IfcBuilding", {"CompositionType": "Partial"}], # CompositionType: Partial
|
"BuildingPart": ["IfcBuilding", {"CompositionType": "PARTIAL"}],
|
||||||
"BuildingInstallation": ["IfcDistributionElement"],
|
"BuildingInstallation": ["IfcBuildingElementProxy"],
|
||||||
"Road": ["IfcCivilElement"],
|
"Road": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"TransportSquare": ["IfcSpace"],
|
"Railway": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"TINRelief": ["IfcGeographicElement"],
|
"TransportSquare": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"WaterBody": ["IfcGeographicElement"], # Update for IFC4.3
|
"TINRelief": ["IfcGeographicElement", {"PredefinedType": "TERRAIN"}],
|
||||||
"LandUse": ["IfcGeographicElement"],
|
"WaterBody": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
"PlantCover": ["IfcGeographicElement"],
|
"ObjectType": "WaterBody"}], # Update for IFC4.3
|
||||||
"SolitaryVegetationObject": ["IfcGeographicElement"],
|
"LandUse": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
|
"ObjectType": "LandUse"}],
|
||||||
|
"PlantCover": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
|
"ObjectType": "Plantcover"}],
|
||||||
|
"SolitaryVegetationObject": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
|
"ObjectType": "SolitaryVegetationObject"}],
|
||||||
"CityFurniture": ["IfcFurnishingElement"],
|
"CityFurniture": ["IfcFurnishingElement"],
|
||||||
"GenericCityObject": ["IfcCivilElement"],
|
"GenericCityObject": ["IfcCivilElement"],
|
||||||
"Bridge": ["IfcCivilElement"], # Update for IFC4.3
|
"Bridge": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
@@ -42,7 +47,7 @@ JSON_TO_IFC = {
|
|||||||
"Tunnel": ["IfcCivilElement"], # Update for IFC4.3
|
"Tunnel": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"TunnelPart": ["IfcCivilElement"], # Update for IFC4.3
|
"TunnelPart": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"TunnelInstallation": ["IfcCivilElement"], # Update for IFC4.3
|
"TunnelInstallation": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"CityObjectGroup": ["IfcCivilElement"],
|
"CityObjectGroup": ["IfcBuilding"], # Update for IFC4.3
|
||||||
"GroundSurface": ["IfcSlab", {"PredefinedType": "BASESLAB"}],
|
"GroundSurface": ["IfcSlab", {"PredefinedType": "BASESLAB"}],
|
||||||
"RoofSurface": ["IfcRoof"],
|
"RoofSurface": ["IfcRoof"],
|
||||||
"WallSurface": ["IfcWall"],
|
"WallSurface": ["IfcWall"],
|
||||||
@@ -51,9 +56,12 @@ JSON_TO_IFC = {
|
|||||||
"OuterFloorSurface": ["IfcSlab", {"PredefinedType": "FLOOR"}],
|
"OuterFloorSurface": ["IfcSlab", {"PredefinedType": "FLOOR"}],
|
||||||
"Window": ["IfcWindow"],
|
"Window": ["IfcWindow"],
|
||||||
"Door": ["IfcDoor"],
|
"Door": ["IfcDoor"],
|
||||||
"WaterSurface": ["IfcGeographicElement"], # Update for IFC4.3
|
"WaterSurface": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
"WaterGroundSurface": ["IfcGeographicElement"], # Update for IFC4.3
|
"ObjectType": "WaterSurface"}], # Update for IFC4.3
|
||||||
"WaterClosureSurface": ["IfcGeographicElement"], # Update for IFC4.3
|
"WaterGroundSurface": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
|
"ObjectType": "WaterGroundSurface"}], # Update for IFC4.3
|
||||||
|
"WaterClosureSurface": ["IfcGeographicElement", {"PredefinedType": "USERDEFINED",
|
||||||
|
"ObjectType": "WaterClosureSurface"}], # Update for IFC4.3
|
||||||
"TrafficArea": ["IfcCivilElement"], # Update for IFC4.3
|
"TrafficArea": ["IfcCivilElement"], # Update for IFC4.3
|
||||||
"AuxiliaryTrafficArea": ["IfcCivilElement"] # Update for IFC4.3
|
"AuxiliaryTrafficArea": ["IfcCivilElement"] # Update for IFC4.3
|
||||||
}
|
}
|
||||||
@@ -137,6 +145,7 @@ class Cityjson2ifc:
|
|||||||
self.IFC_model.write(self.properties["file_destination"])
|
self.IFC_model.write(self.properties["file_destination"])
|
||||||
|
|
||||||
def create_IFC_classes(self):
|
def create_IFC_classes(self):
|
||||||
|
parents_children_relations = {"IfcSite": {'Parent': self.IFC_site, 'Children': []}}
|
||||||
for obj_id, obj in self.city_model.get_cityobjects().items():
|
for obj_id, obj in self.city_model.get_cityobjects().items():
|
||||||
|
|
||||||
# CityJSON type to class
|
# CityJSON type to class
|
||||||
@@ -153,22 +162,19 @@ class Cityjson2ifc:
|
|||||||
if "name_attribute" in self.properties and self.properties["name_attribute"] in obj.attributes:
|
if "name_attribute" in self.properties and self.properties["name_attribute"] in obj.attributes:
|
||||||
IFC_name = obj.attributes[self.properties["name_attribute"]]
|
IFC_name = obj.attributes[self.properties["name_attribute"]]
|
||||||
|
|
||||||
# TODO children
|
|
||||||
|
|
||||||
# TODO parents
|
|
||||||
|
|
||||||
# TODO geometry_type
|
|
||||||
|
|
||||||
# geometry_lod
|
# geometry_lod
|
||||||
lod = 0
|
lod = 0
|
||||||
geometry = None
|
geometry = None
|
||||||
|
if len(obj.geometry) == 0:
|
||||||
|
print(f"Warning: Object {obj_id} has no geometry.")
|
||||||
|
|
||||||
for geom in obj.geometry:
|
for geom in obj.geometry:
|
||||||
if geom.lod > lod:
|
if geom.lod > lod:
|
||||||
geometry = geom
|
geometry = geom
|
||||||
lod = geom.lod
|
lod = geom.lod
|
||||||
|
|
||||||
IFC_children = []
|
IFC_semantic_surface_children = []
|
||||||
if geometry.surfaces:
|
if geometry and geometry.surfaces:
|
||||||
for surface_id in geometry.surfaces:
|
for surface_id in geometry.surfaces:
|
||||||
IFC_child_class = JSON_TO_IFC[geometry.surfaces[surface_id]["type"]][0]
|
IFC_child_class = JSON_TO_IFC[geometry.surfaces[surface_id]["type"]][0]
|
||||||
child_data = {"GlobalId": ifcopenshell.guid.new(),
|
child_data = {"GlobalId": ifcopenshell.guid.new(),
|
||||||
@@ -179,9 +185,9 @@ class Cityjson2ifc:
|
|||||||
surface_geometry = self.geometry.create_IFC_surface(self.IFC_model, geometry, surface_id)
|
surface_geometry = self.geometry.create_IFC_surface(self.IFC_model, geometry, surface_id)
|
||||||
if surface_geometry:
|
if surface_geometry:
|
||||||
child_data["Representation"] = self.create_IFC_representation(surface_geometry, 'brep')
|
child_data["Representation"] = self.create_IFC_representation(surface_geometry, 'brep')
|
||||||
IFC_children.append(self.IFC_model.create_entity(IFC_child_class, **child_data))
|
IFC_semantic_surface_children.append(self.IFC_model.create_entity(IFC_child_class, **child_data))
|
||||||
|
|
||||||
else:
|
elif geometry:
|
||||||
IFC_geometry, shape_representation_type = self.geometry.create_IFC_geometry(self.IFC_model, geometry)
|
IFC_geometry, shape_representation_type = self.geometry.create_IFC_geometry(self.IFC_model, geometry)
|
||||||
if IFC_geometry:
|
if IFC_geometry:
|
||||||
data["Representation"] = self.create_IFC_representation(IFC_geometry, shape_representation_type)
|
data["Representation"] = self.create_IFC_representation(IFC_geometry, shape_representation_type)
|
||||||
@@ -189,20 +195,36 @@ class Cityjson2ifc:
|
|||||||
data["Name"] = IFC_name
|
data["Name"] = IFC_name
|
||||||
|
|
||||||
IFC_object = self.IFC_model.create_entity(IFC_class, **data)
|
IFC_object = self.IFC_model.create_entity(IFC_class, **data)
|
||||||
|
|
||||||
# Define aggregation
|
# Define aggregation
|
||||||
self.IFC_model.create_entity("IfcRelContainedInSpatialStructure",
|
if len(obj.parents) == 0:
|
||||||
**{"GlobalId": ifcopenshell.guid.new(),
|
parents_children_relations["IfcSite"]['Children'].append(IFC_object)
|
||||||
"RelatedElements": [IFC_object],
|
|
||||||
"RelatingStructure": self.IFC_site}
|
for parent in obj.parents:
|
||||||
)
|
if parent not in parents_children_relations:
|
||||||
if IFC_children:
|
parents_children_relations[parent] = {'Parent': None, 'Children': []}
|
||||||
|
parents_children_relations[parent]['Children'].append(IFC_object)
|
||||||
|
|
||||||
|
if len(obj.children) > 0:
|
||||||
|
if obj_id not in parents_children_relations:
|
||||||
|
parents_children_relations[obj_id] = {'Parent': None, 'Children': []}
|
||||||
|
parents_children_relations[obj_id]['Parent'] = IFC_object
|
||||||
|
|
||||||
|
if IFC_semantic_surface_children:
|
||||||
self.IFC_model.create_entity("IfcRelAggregates",
|
self.IFC_model.create_entity("IfcRelAggregates",
|
||||||
**{"GlobalId": ifcopenshell.guid.new(),
|
**{"GlobalId": ifcopenshell.guid.new(),
|
||||||
"RelatedObjects": IFC_children,
|
"RelatedObjects": IFC_semantic_surface_children,
|
||||||
"RelatingObject": IFC_object})
|
"RelatingObject": IFC_object})
|
||||||
|
|
||||||
self.create_property_set(obj.attributes, IFC_object)
|
self.create_property_set(obj.attributes, IFC_object)
|
||||||
|
|
||||||
|
for parent_children in parents_children_relations.values():
|
||||||
|
self.IFC_model.create_entity("IfcRelContainedInSpatialStructure",
|
||||||
|
**{"GlobalId": ifcopenshell.guid.new(),
|
||||||
|
"RelatedElements": parent_children['Children'],
|
||||||
|
"RelatingStructure": parent_children['Parent']}
|
||||||
|
)
|
||||||
|
|
||||||
def create_IFC_representation(self, IFC_geometry, shape_representation_type):
|
def create_IFC_representation(self, IFC_geometry, shape_representation_type):
|
||||||
if not isinstance(IFC_geometry, list):
|
if not isinstance(IFC_geometry, list):
|
||||||
IFC_geometry = [IFC_geometry]
|
IFC_geometry = [IFC_geometry]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -30,7 +30,11 @@ bool IfcGeom::Kernel::convert_shapes(const IfcBaseClass* l, IfcRepresentationSha
|
|||||||
if (shape_type(l) != ST_SHAPELIST) {
|
if (shape_type(l) != ST_SHAPELIST) {
|
||||||
TopoDS_Shape shp;
|
TopoDS_Shape shp;
|
||||||
if (convert_shape(l, shp)) {
|
if (convert_shape(l, shp)) {
|
||||||
r.push_back(IfcGeom::IfcRepresentationShapeItem(l->data().id(), shp, get_style(l->as<IfcSchema::IfcRepresentationItem>())));
|
const IfcGeom::SurfaceStyle* style = nullptr;
|
||||||
|
if (l->as<IfcSchema::IfcRepresentationItem>()) {
|
||||||
|
style = get_style(l->as<IfcSchema::IfcRepresentationItem>());
|
||||||
|
}
|
||||||
|
r.push_back(IfcGeom::IfcRepresentationShapeItem(l->data().id(), shp, style));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import ifcopenshell.api
|
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
|
||||||
def __init__(self, file, **settings):
|
|
||||||
self.file = file
|
|
||||||
self.settings = {"cost_item": None, "products": []}
|
|
||||||
for key, value in settings.items():
|
|
||||||
self.settings[key] = value
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
quantity_names = set()
|
|
||||||
for quantity in self.settings["cost_item"].CostQuantities or []:
|
|
||||||
if quantity.Name:
|
|
||||||
quantity_names.add(quantity.Name)
|
|
||||||
|
|
||||||
for product in self.settings["products"]:
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"control.assign_control",
|
|
||||||
self.file,
|
|
||||||
related_object=product,
|
|
||||||
relating_control=self.settings["cost_item"],
|
|
||||||
)
|
|
||||||
|
|
||||||
for name in quantity_names:
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"cost.assign_cost_item_product_quantities",
|
|
||||||
self.file,
|
|
||||||
cost_item=self.settings["cost_item"],
|
|
||||||
prop_name=name
|
|
||||||
)
|
|
||||||
+14
-11
@@ -4,25 +4,28 @@ import ifcopenshell.api
|
|||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, **settings):
|
def __init__(self, file, **settings):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {"cost_item": None, "prop_name": ""}
|
self.settings = {"cost_item": None, "products": [], "prop_name": ""}
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
|
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
|
||||||
for control in self.settings["cost_item"].Controls or []:
|
for product in self.settings["products"]:
|
||||||
for related_object in control.RelatedObjects:
|
ifcopenshell.api.run(
|
||||||
self.add_quantity_from_related_object(related_object)
|
"control.assign_control",
|
||||||
|
self.file,
|
||||||
|
related_object=product,
|
||||||
|
relating_control=self.settings["cost_item"],
|
||||||
|
)
|
||||||
|
self.add_quantity_from_related_object(product)
|
||||||
self.settings["cost_item"].CostQuantities = list(self.quantities)
|
self.settings["cost_item"].CostQuantities = list(self.quantities)
|
||||||
|
|
||||||
def add_quantity_from_related_object(self, element):
|
def add_quantity_from_related_object(self, element):
|
||||||
if element.is_a("IfcTypeObject"):
|
if not element.is_a("IfcObject"):
|
||||||
for definition in element.HasPropertySets or []:
|
return
|
||||||
self.add_quantity_from_qto(definition)
|
for relationship in element.IsDefinedBy:
|
||||||
else:
|
if relationship.is_a("IfcRelDefinesByProperties"):
|
||||||
for relationship in element.IsDefinedBy:
|
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
||||||
if relationship.is_a("IfcRelDefinesByProperties"):
|
|
||||||
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
|
||||||
|
|
||||||
def add_quantity_from_qto(self, qto):
|
def add_quantity_from_qto(self, qto):
|
||||||
if not qto.is_a("IfcElementQuantity"):
|
if not qto.is_a("IfcElementQuantity"):
|
||||||
@@ -51,21 +51,43 @@ class Data:
|
|||||||
del data["OwnerHistory"]
|
del data["OwnerHistory"]
|
||||||
del data["CostValues"]
|
del data["CostValues"]
|
||||||
data["IsNestedBy"] = []
|
data["IsNestedBy"] = []
|
||||||
data["Controls"] = []
|
data["Controls"] = {}
|
||||||
for rel in cost_item.IsNestedBy:
|
for rel in cost_item.IsNestedBy:
|
||||||
[data["IsNestedBy"].append(o.id()) for o in rel.RelatedObjects if o.is_a("IfcCostItem")]
|
[data["IsNestedBy"].append(o.id()) for o in rel.RelatedObjects if o.is_a("IfcCostItem")]
|
||||||
|
parametric_quantities = []
|
||||||
for rel in cost_item.Controls:
|
for rel in cost_item.Controls:
|
||||||
[data["Controls"].append(o.id()) for o in rel.RelatedObjects or []]
|
for related_object in rel.RelatedObjects or []:
|
||||||
|
quantities = cls.get_object_quantities(cost_item, related_object)
|
||||||
|
data["Controls"][related_object.id()] = quantities
|
||||||
|
parametric_quantities.extend(quantities)
|
||||||
cls.cost_items[cost_item.id()] = data
|
cls.cost_items[cost_item.id()] = data
|
||||||
cls.load_cost_item_quantities(cost_item, data)
|
cls.load_cost_item_quantities(cost_item, data, parametric_quantities)
|
||||||
cls.load_cost_item_values(cost_item, data)
|
cls.load_cost_item_values(cost_item, data)
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_cost_item_quantities(cls, cost_item, data):
|
def get_object_quantities(cls, cost_item, element):
|
||||||
|
if not element.is_a("IfcObject"):
|
||||||
|
return []
|
||||||
|
results = []
|
||||||
|
for relationship in element.IsDefinedBy:
|
||||||
|
if not relationship.is_a("IfcRelDefinesByProperties"):
|
||||||
|
continue
|
||||||
|
qto = relationship.RelatingPropertyDefinition
|
||||||
|
if not qto.is_a("IfcElementQuantity"):
|
||||||
|
continue
|
||||||
|
for prop in qto.Quantities:
|
||||||
|
if prop in cost_item.CostQuantities or []:
|
||||||
|
results.append(prop.id())
|
||||||
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_cost_item_quantities(cls, cost_item, data, parametric_quantities):
|
||||||
data["CostQuantities"] = []
|
data["CostQuantities"] = []
|
||||||
data["TotalCostQuantity"] = cls.get_total_quantity(cost_item)
|
data["TotalCostQuantity"] = cls.get_total_quantity(cost_item)
|
||||||
for quantity in cost_item.CostQuantities or []:
|
for quantity in cost_item.CostQuantities or []:
|
||||||
|
if quantity.id() in parametric_quantities:
|
||||||
|
continue
|
||||||
quantity_data = quantity.get_info()
|
quantity_data = quantity.get_info()
|
||||||
del quantity_data["Unit"]
|
del quantity_data["Unit"]
|
||||||
cls.physical_quantities[quantity.id()] = quantity_data
|
cls.physical_quantities[quantity.id()] = quantity_data
|
||||||
@@ -98,7 +120,11 @@ class Data:
|
|||||||
def load_cost_item_value(cls, cost_item_data, cost_item, cost_value):
|
def load_cost_item_value(cls, cost_item_data, cost_item, cost_value):
|
||||||
value_data = cost_value.get_info()
|
value_data = cost_value.get_info()
|
||||||
del value_data["AppliedValue"]
|
del value_data["AppliedValue"]
|
||||||
del value_data["UnitBasis"]
|
if value_data["UnitBasis"]:
|
||||||
|
data = cost_value.UnitBasis.get_info()
|
||||||
|
data["ValueComponent"] = data["ValueComponent"].wrappedValue
|
||||||
|
data["UnitComponent"] = data["UnitComponent"].id()
|
||||||
|
value_data["UnitBasis"] = data
|
||||||
if value_data["ApplicableDate"]:
|
if value_data["ApplicableDate"]:
|
||||||
value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"])
|
value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"])
|
||||||
if value_data["FixedUntilDate"]:
|
if value_data["FixedUntilDate"]:
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.unit
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
def __init__(self, file, **settings):
|
def __init__(self, file, **settings):
|
||||||
self.file = file
|
self.file = file
|
||||||
@@ -10,4 +15,19 @@ class Usecase:
|
|||||||
if name == "AppliedValue" and value is not None:
|
if name == "AppliedValue" and value is not None:
|
||||||
# TODO: support all applied value select types
|
# TODO: support all applied value select types
|
||||||
value = self.file.createIfcMonetaryMeasure(value)
|
value = self.file.createIfcMonetaryMeasure(value)
|
||||||
|
elif name == "UnitBasis":
|
||||||
|
self.remove_existing_unit_basis()
|
||||||
|
if value:
|
||||||
|
value_component = self.file.create_entity(
|
||||||
|
ifcopenshell.util.unit.get_unit_measure_type(value["UnitComponent"].UnitType),
|
||||||
|
value["ValueComponent"],
|
||||||
|
)
|
||||||
|
value = self.file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"])
|
||||||
setattr(self.settings["cost_value"], name, value)
|
setattr(self.settings["cost_value"], name, value)
|
||||||
|
|
||||||
|
def remove_existing_unit_basis(self):
|
||||||
|
if (
|
||||||
|
self.settings["cost_value"].UnitBasis
|
||||||
|
and len(self.file.get_inverse(self.settings["cost_value"].UnitBasis)) == 1
|
||||||
|
):
|
||||||
|
ifcopenshell.util.element.remove_deep(self.file, self.settings["cost_value"].UnitBasis)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class Usecase:
|
|||||||
layers = list(self.settings["layer_set"].MaterialLayers or [])
|
layers = list(self.settings["layer_set"].MaterialLayers or [])
|
||||||
layer = self.file.create_entity("IfcMaterialLayer", **{
|
layer = self.file.create_entity("IfcMaterialLayer", **{
|
||||||
"Material": self.settings["material"],
|
"Material": self.settings["material"],
|
||||||
"LayerThickness": 0.
|
"LayerThickness": 1.
|
||||||
})
|
})
|
||||||
layers.append(layer)
|
layers.append(layer)
|
||||||
self.settings["layer_set"].MaterialLayers = layers
|
self.settings["layer_set"].MaterialLayers = layers
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class Usecase():
|
|||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
# TODO: don't also edit the profile def in this usecase
|
||||||
for name, value in self.settings["attributes"].items():
|
for name, value in self.settings["attributes"].items():
|
||||||
setattr(self.settings["profile"], name, value)
|
setattr(self.settings["profile"], name, value)
|
||||||
self.settings["profile"].Material = self.settings["material"]
|
self.settings["profile"].Material = self.settings["material"]
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ class Usecase:
|
|||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.file.remove(self.settings["profile"])
|
self.file.remove(self.settings["profile"])
|
||||||
|
# TODO: deep purge
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class Usecase():
|
||||||
|
def __init__(self, file, **settings):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"profile": None, "attributes": {}}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
for name, value in self.settings["attributes"].items():
|
||||||
|
setattr(self.settings["profile"], name, value)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, **settings):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"profile": None}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.file.remove(self.settings["profile"])
|
||||||
|
# TODO: deep purge
|
||||||
@@ -11,12 +11,15 @@ class Usecase:
|
|||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.added_elements = set()
|
self.added_elements = set()
|
||||||
|
self.whitelisted_inverse_attributes = {}
|
||||||
if self.settings["element"].is_a("IfcTypeProduct"):
|
if self.settings["element"].is_a("IfcTypeProduct"):
|
||||||
return self.append_type_product()
|
return self.append_type_product()
|
||||||
elif self.settings["element"].is_a("IfcMaterial"):
|
elif self.settings["element"].is_a("IfcMaterial"):
|
||||||
return self.append_material()
|
return self.append_material()
|
||||||
elif self.settings["element"].is_a("IfcCostSchedule"):
|
elif self.settings["element"].is_a("IfcCostSchedule"):
|
||||||
return self.append_cost_schedule()
|
return self.append_cost_schedule()
|
||||||
|
elif self.settings["element"].is_a("IfcProfileDef"):
|
||||||
|
return self.append_profile_def()
|
||||||
|
|
||||||
def is_already_appended(self):
|
def is_already_appended(self):
|
||||||
try:
|
try:
|
||||||
@@ -36,6 +39,12 @@ class Usecase:
|
|||||||
self.whitelisted_inverse_attributes = {"IfcCostSchedule": ["Controls"], "IfcCostItem": ["IsNestedBy"]}
|
self.whitelisted_inverse_attributes = {"IfcCostSchedule": ["Controls"], "IfcCostItem": ["IsNestedBy"]}
|
||||||
return self.add_element(self.settings["element"])
|
return self.add_element(self.settings["element"])
|
||||||
|
|
||||||
|
def append_profile_def(self):
|
||||||
|
if [e for e in self.file.by_type("IfcProfileDef") if e.ProfileName == self.settings["element"].ProfileName]:
|
||||||
|
return
|
||||||
|
self.whitelisted_inverse_attributes = {"IfcProfileDef": ["HasProperties"]}
|
||||||
|
return self.add_element(self.settings["element"])
|
||||||
|
|
||||||
def append_type_product(self):
|
def append_type_product(self):
|
||||||
if self.is_already_appended():
|
if self.is_already_appended():
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -64,3 +64,15 @@ class Usecase:
|
|||||||
"Material": self.settings["product"],
|
"Material": self.settings["product"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
elif self.settings["product"].is_a("IfcProfileDef"):
|
||||||
|
for definition in self.settings["product"].HasProperties or []:
|
||||||
|
if definition.Name == self.settings["name"]:
|
||||||
|
return definition
|
||||||
|
|
||||||
|
return self.file.create_entity(
|
||||||
|
"IfcProfileProperties",
|
||||||
|
**{
|
||||||
|
"Name": self.settings["name"],
|
||||||
|
"ProfileDefinition": self.settings["product"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class Data:
|
|||||||
cls.add_type_product_psets(product, product_id)
|
cls.add_type_product_psets(product, product_id)
|
||||||
elif product.is_a("IfcMaterialDefinition"):
|
elif product.is_a("IfcMaterialDefinition"):
|
||||||
cls.add_material_psets(product, product_id)
|
cls.add_material_psets(product, product_id)
|
||||||
|
elif product.is_a("IfcProfileDef"):
|
||||||
|
cls.add_profile_psets(product, product_id)
|
||||||
else:
|
else:
|
||||||
cls.add_product_psets(product, product_id)
|
cls.add_product_psets(product, product_id)
|
||||||
|
|
||||||
@@ -44,6 +46,13 @@ class Data:
|
|||||||
for pset in product.HasProperties:
|
for pset in product.HasProperties:
|
||||||
cls.add_pset(pset, product_id)
|
cls.add_pset(pset, product_id)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_profile_psets(cls, product, product_id):
|
||||||
|
if not product.HasProperties:
|
||||||
|
return
|
||||||
|
for pset in product.HasProperties:
|
||||||
|
cls.add_pset(pset, product_id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_product_psets(cls, product, product_id):
|
def add_product_psets(cls, product, product_id):
|
||||||
if not hasattr(product, "IsDefinedBy") or not product.IsDefinedBy:
|
if not hasattr(product, "IsDefinedBy") or not product.IsDefinedBy:
|
||||||
@@ -59,7 +68,7 @@ class Data:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def add_pset(cls, pset, product_id):
|
def add_pset(cls, pset, product_id):
|
||||||
data = pset.get_info()
|
data = pset.get_info()
|
||||||
if not pset.is_a("IfcMaterialProperties"):
|
if not pset.is_a("IfcMaterialProperties") and not pset.is_a("IfcProfileProperties"):
|
||||||
del data["OwnerHistory"]
|
del data["OwnerHistory"]
|
||||||
del data["HasProperties"]
|
del data["HasProperties"]
|
||||||
if hasattr(pset, "HasProperties"):
|
if hasattr(pset, "HasProperties"):
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, **settings):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"unit_type": "LENGTHUNIT", "name": "METRE"}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
return self.file.create_entity("IfcSIUnit", UnitType=self.settings["unit_type"], Name=self.settings["name"])
|
||||||
@@ -24,6 +24,9 @@ class Data:
|
|||||||
return
|
return
|
||||||
for unit in unit_assignment[0].Units:
|
for unit in unit_assignment[0].Units:
|
||||||
cls.unit_assignment.append(unit.id())
|
cls.unit_assignment.append(unit.id())
|
||||||
|
for unit in (
|
||||||
|
cls.file.by_type("IfcDerivedUnit") + cls.file.by_type("IfcNamedUnit") + cls.file.by_type("IfcMonetaryUnit")
|
||||||
|
):
|
||||||
cls.load_unit(unit)
|
cls.load_unit(unit)
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, **settings):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"units": None}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
unit_assignment = self.file.by_type("IfcUnitAssignment")
|
||||||
|
if not unit_assignment:
|
||||||
|
return
|
||||||
|
unit_assignment = unit_assignment[0]
|
||||||
|
units = set(unit_assignment.Units or [])
|
||||||
|
units = units - set(self.settings["units"])
|
||||||
|
if units:
|
||||||
|
unit_assignment.Units = list(units)
|
||||||
|
return unit_assignment
|
||||||
@@ -359,19 +359,27 @@ class file(object):
|
|||||||
return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)]
|
return [entity_instance(e, self) for e in self.wrapped_data.by_type(type)]
|
||||||
return [entity_instance(e, self) for e in self.wrapped_data.by_type_excl_subtypes(type)]
|
return [entity_instance(e, self) for e in self.wrapped_data.by_type_excl_subtypes(type)]
|
||||||
|
|
||||||
def traverse(self, inst, max_levels=None):
|
def traverse(self, inst, max_levels=None, breadth_first=False):
|
||||||
"""Get a list of all referenced instances for a particular instance including itself
|
"""Get a list of all referenced instances for a particular instance including itself
|
||||||
|
|
||||||
:param inst: The entity instance to get all sub instances
|
:param inst: The entity instance to get all sub instances
|
||||||
:type inst: ifcopenshell.entity_instance.entity_instance
|
:type inst: ifcopenshell.entity_instance.entity_instance
|
||||||
:param max_levels: How far deep to recursively fetch sub instances. None or -1 means infinite.
|
:param max_levels: How far deep to recursively fetch sub instances. None or -1 means infinite.
|
||||||
:type max_levels: None|int
|
:type max_levels: None|int
|
||||||
|
:param breadth_first: Whether to use breadth-first search, the default is depth-first.
|
||||||
|
:type max_levels: bool
|
||||||
:returns: A list of ifcopenshell.entity_instance.entity_instance objects
|
:returns: A list of ifcopenshell.entity_instance.entity_instance objects
|
||||||
:rtype: list
|
:rtype: list
|
||||||
"""
|
"""
|
||||||
if max_levels is None:
|
if max_levels is None:
|
||||||
max_levels = -1
|
max_levels = -1
|
||||||
return [entity_instance(e, self) for e in self.wrapped_data.traverse(inst.wrapped_data, max_levels)]
|
|
||||||
|
if breadth_first:
|
||||||
|
fn = self.wrapped_data.traverse_breadth_first
|
||||||
|
else:
|
||||||
|
fn = self.wrapped_data.traverse
|
||||||
|
|
||||||
|
return [entity_instance(e, self) for e in fn(inst.wrapped_data, max_levels)]
|
||||||
|
|
||||||
def get_inverse(self, inst):
|
def get_inverse(self, inst):
|
||||||
"""Return a list of entities that reference this entity
|
"""Return a list of entities that reference this entity
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ def has_element_reference(value, element):
|
|||||||
def remove_deep(ifc_file, element):
|
def remove_deep(ifc_file, element):
|
||||||
# @todo maybe some sort of try-finally mechanism.
|
# @todo maybe some sort of try-finally mechanism.
|
||||||
ifc_file.batch()
|
ifc_file.batch()
|
||||||
subgraph = list(ifc_file.traverse(element))
|
subgraph = list(ifc_file.traverse(element, breadth_first=True))
|
||||||
subgraph_set = set(subgraph)
|
subgraph_set = set(subgraph)
|
||||||
for ref in subgraph[::-1]:
|
for ref in subgraph[::-1]:
|
||||||
if ref.id() and len(set(ifc_file.get_inverse(ref)) - subgraph_set) == 0:
|
if ref.id() and len(set(ifc_file.get_inverse(ref)) - subgraph_set) == 0:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ unit_names = [
|
|||||||
"CANDELA",
|
"CANDELA",
|
||||||
"COULOMB",
|
"COULOMB",
|
||||||
"CUBIC_METRE",
|
"CUBIC_METRE",
|
||||||
"DEGREE CELSIUS",
|
"DEGREE_CELSIUS",
|
||||||
"FARAD",
|
"FARAD",
|
||||||
"GRAM",
|
"GRAM",
|
||||||
"GRAY",
|
"GRAY",
|
||||||
@@ -43,7 +43,7 @@ unit_names = [
|
|||||||
"SECOND",
|
"SECOND",
|
||||||
"SIEMENS",
|
"SIEMENS",
|
||||||
"SIEVERT",
|
"SIEVERT",
|
||||||
"SQUARE METRE",
|
"SQUARE_METRE",
|
||||||
"METRE",
|
"METRE",
|
||||||
"STERADIAN",
|
"STERADIAN",
|
||||||
"TESLA",
|
"TESLA",
|
||||||
@@ -52,7 +52,6 @@ unit_names = [
|
|||||||
"WEBER",
|
"WEBER",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
si_dimensions = {
|
si_dimensions = {
|
||||||
"METRE": (1, 0, 0, 0, 0, 0, 0),
|
"METRE": (1, 0, 0, 0, 0, 0, 0),
|
||||||
"SQUARE_METRE": (2, 0, 0, 0, 0, 0, 0),
|
"SQUARE_METRE": (2, 0, 0, 0, 0, 0, 0),
|
||||||
@@ -87,6 +86,72 @@ si_dimensions = {
|
|||||||
"OTHERWISE": (0, 0, 0, 0, 0, 0, 0),
|
"OTHERWISE": (0, 0, 0, 0, 0, 0, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/72
|
||||||
|
si_type_names = {
|
||||||
|
"ABSORBEDDOSEUNIT": "GRAY",
|
||||||
|
"AMOUNTOFSUBSTANCEUNIT": "MOLE",
|
||||||
|
"AREAUNIT": "SQUARE_METRE",
|
||||||
|
"DOSEEQUIVALENTUNIT": "SIEVERT",
|
||||||
|
"ELECTRICCAPACITANCEUNIT": "FARAD",
|
||||||
|
"ELECTRICCHARGEUNIT": "COULOMB",
|
||||||
|
"ELECTRICCONDUCTANCEUNIT": "SIEMENS",
|
||||||
|
"ELECTRICCURRENTUNIT": "AMPERE",
|
||||||
|
"ELECTRICRESISTANCEUNIT": "OHM",
|
||||||
|
"ELECTRICVOLTAGEUNIT": "VOLT",
|
||||||
|
"ENERGYUNIT": "JOULE",
|
||||||
|
"FORCEUNIT": "NEWTON",
|
||||||
|
"FREQUENCYUNIT": "HERTZ",
|
||||||
|
"ILLUMINANCEUNIT": "LUX",
|
||||||
|
"INDUCTANCEUNIT": "HENRY",
|
||||||
|
"LENGTHUNIT": "METRE",
|
||||||
|
"LUMINOUSFLUXUNIT": "LUMEN",
|
||||||
|
"LUMINOUSINTENSITYUNIT": "CANDELA",
|
||||||
|
"MAGNETICFLUXDENSITYUNIT": "TESLA",
|
||||||
|
"MAGNETICFLUXUNIT": "WEBER",
|
||||||
|
"MASSUNIT": "GRAM",
|
||||||
|
"PLANEANGLEUNIT": "RADIAN",
|
||||||
|
"POWERUNIT": "WATT",
|
||||||
|
"PRESSUREUNIT": "PASCAL",
|
||||||
|
"RADIOACTIVITYUNIT": "BECQUEREL",
|
||||||
|
"SOLIDANGLEUNIT": "STERADIAN",
|
||||||
|
"THERMODYNAMICTEMPERATUREUNIT": "KELVIN", # Or, DEGREE_CELSIUS, but this is a quirk of IFC
|
||||||
|
"TIMEUNIT": "SECOND",
|
||||||
|
"VOLUMEUNIT": "CUBIC_METRE",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Are you good at physics? Want to help fill these in? :)
|
||||||
|
named_dimensions = {
|
||||||
|
# "ABSORBEDDOSEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"AMOUNTOFSUBSTANCEUNIT": (0, 0, 0, 0, 0, 1, 0),
|
||||||
|
"AREAUNIT": (2, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "DOSEEQUIVALENTUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "ELECTRICCAPACITANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "ELECTRICCHARGEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "ELECTRICCONDUCTANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"ELECTRICCURRENTUNIT": (0, 0, 0, 1, 0, 0, 0),
|
||||||
|
# "ELECTRICRESISTANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "ELECTRICVOLTAGEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"ENERGYUNIT": (2, 1, -2, 0, 0, 0, 0),
|
||||||
|
"FORCEUNIT": (1, 1, -2, 0, 0, 0, 0),
|
||||||
|
"FREQUENCYUNIT": (0, 0, -1, 0, 0, 0, 0),
|
||||||
|
# "ILLUMINANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "INDUCTANCEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"LENGTHUNIT": (1, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "LUMINOUSFLUXUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"LUMINOUSINTENSITYUNIT": (0, 0, 0, 0, 0, 0, 1),
|
||||||
|
# "MAGNETICFLUXDENSITYUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
# "MAGNETICFLUXUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"MASSUNIT": (0, 1, 0, 0, 0, 0, 0),
|
||||||
|
"PLANEANGLEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"POWERUNIT": (2, 1, -3, 0, 0, 0, 0),
|
||||||
|
"PRESSUREUNIT": (-1, 1, -2, 0, 0, 0, 0),
|
||||||
|
# "RADIOACTIVITYUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"SOLIDANGLEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
||||||
|
"THERMODYNAMICTEMPERATUREUNIT": (0, 0, 0, 0, 1, 0, 0),
|
||||||
|
"TIMEUNIT": (0, 0, 1, 0, 0, 0, 0),
|
||||||
|
"VOLUMEUNIT": (3, 0, 0, 0, 0, 0, 0),
|
||||||
|
}
|
||||||
|
|
||||||
si_conversions = {
|
si_conversions = {
|
||||||
"inch": 0.0254,
|
"inch": 0.0254,
|
||||||
"foot": 0.3048,
|
"foot": 0.3048,
|
||||||
@@ -169,7 +234,7 @@ def get_prefix_multiplier(text):
|
|||||||
def get_unit_name(text):
|
def get_unit_name(text):
|
||||||
text = text.upper().replace("METER", "METRE")
|
text = text.upper().replace("METER", "METRE")
|
||||||
for name in unit_names:
|
for name in unit_names:
|
||||||
if name in text:
|
if name.replace("_", " ") in text:
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
@@ -177,6 +242,10 @@ def get_si_dimensions(name):
|
|||||||
return si_dimensions.get(name, si_dimensions["OTHERWISE"])
|
return si_dimensions.get(name, si_dimensions["OTHERWISE"])
|
||||||
|
|
||||||
|
|
||||||
|
def get_named_dimensions(name):
|
||||||
|
return named_dimensions.get(name, (0, 0, 0, 0, 0, 0, 0))
|
||||||
|
|
||||||
|
|
||||||
def get_property_unit(prop, ifc_file):
|
def get_property_unit(prop, ifc_file):
|
||||||
unit = getattr(prop, "Unit", None)
|
unit = getattr(prop, "Unit", None)
|
||||||
if unit:
|
if unit:
|
||||||
@@ -197,6 +266,10 @@ def get_property_unit(prop, ifc_file):
|
|||||||
return units[0]
|
return units[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_unit_measure_type(unit_type):
|
||||||
|
return "Ifc" + unit_type[0:-4].lower().capitalize() + "Measure"
|
||||||
|
|
||||||
|
|
||||||
def get_symbol_quantity_class(symbol):
|
def get_symbol_quantity_class(symbol):
|
||||||
# Dumb, but everybody gets it, unlike regex golf
|
# Dumb, but everybody gets it, unlike regex golf
|
||||||
if not symbol:
|
if not symbol:
|
||||||
@@ -259,19 +332,17 @@ def convert(value, from_prefix, from_unit, to_prefix, to_unit):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
"""Returns a unit scale factor to convert to and from IFC project length units and SI meters
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
ifc_project_length * unit_scale = si_meters
|
|
||||||
si_meters / unit_scale = ifc_project_length
|
|
||||||
|
|
||||||
:returns: The scale factor
|
|
||||||
:rtype: float
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_unit_scale(file):
|
def calculate_unit_scale(file):
|
||||||
|
"""Returns a unit scale factor to convert to and from IFC project length units and SI meters
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
ifc_project_length * unit_scale = si_meters
|
||||||
|
si_meters / unit_scale = ifc_project_length
|
||||||
|
|
||||||
|
:returns: The scale factor
|
||||||
|
:rtype: float
|
||||||
|
"""
|
||||||
units = file.by_type("IfcUnitAssignment")[0]
|
units = file.by_type("IfcUnitAssignment")[0]
|
||||||
unit_scale = 1
|
unit_scale = 1
|
||||||
for unit in units.Units:
|
for unit in units.Units:
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class json_logger:
|
|||||||
self.instance = instance
|
self.instance = instance
|
||||||
|
|
||||||
def log(self, level, message, *args, **kwargs):
|
def log(self, level, message, *args, **kwargs):
|
||||||
self.statements.append(log_entry_type(level, message % args, kwargs.get("instance"))._asdict())
|
self.statements.append(
|
||||||
|
log_entry_type(level, message % args, kwargs.get("instance"))._asdict()
|
||||||
|
)
|
||||||
|
|
||||||
def __getattr__(self, level):
|
def __getattr__(self, level):
|
||||||
return functools.partial(self.log, level, instance=self.instance)
|
return functools.partial(self.log, level, instance=self.instance)
|
||||||
@@ -81,7 +83,9 @@ def assert_valid(attr, val, schema):
|
|||||||
if isinstance(attr_type, simple_type):
|
if isinstance(attr_type, simple_type):
|
||||||
invalid = type(val) != simple_type_python_mapping[attr_type.declared_type()]
|
invalid = type(val) != simple_type_python_mapping[attr_type.declared_type()]
|
||||||
elif isinstance(attr_type, (entity_type, type_declaration)):
|
elif isinstance(attr_type, (entity_type, type_declaration)):
|
||||||
invalid = not isinstance(val, ifcopenshell.entity_instance) or not val.is_a(attr_type.name())
|
invalid = not isinstance(val, ifcopenshell.entity_instance) or not val.is_a(
|
||||||
|
attr_type.name()
|
||||||
|
)
|
||||||
elif isinstance(attr_type, select_type):
|
elif isinstance(attr_type, select_type):
|
||||||
val_to_use = val
|
val_to_use = val
|
||||||
if isinstance(schema.declaration_by_name(val.is_a()), enumeration_type):
|
if isinstance(schema.declaration_by_name(val.is_a()), enumeration_type):
|
||||||
@@ -90,13 +94,19 @@ def assert_valid(attr, val, schema):
|
|||||||
else:
|
else:
|
||||||
invalid = True
|
invalid = True
|
||||||
if not invalid:
|
if not invalid:
|
||||||
invalid = not any(try_valid(x, val_to_use, schema) for x in attr_type.select_list())
|
invalid = not any(
|
||||||
|
try_valid(x, val_to_use, schema) for x in attr_type.select_list()
|
||||||
|
)
|
||||||
elif isinstance(attr_type, enumeration_type):
|
elif isinstance(attr_type, enumeration_type):
|
||||||
invalid = val not in attr_type.enumeration_items()
|
invalid = val not in attr_type.enumeration_items()
|
||||||
elif isinstance(attr_type, aggregation_type):
|
elif isinstance(attr_type, aggregation_type):
|
||||||
b1, b2 = attr_type.bound1(), attr_type.bound2()
|
b1, b2 = attr_type.bound1(), attr_type.bound2()
|
||||||
ty = attr_type.type_of_element()
|
ty = attr_type.type_of_element()
|
||||||
invalid = len(val) < b1 or (b2 != -1 and len(val) > b2) or not all(assert_valid(ty, v, schema) for v in val)
|
invalid = (
|
||||||
|
len(val) < b1
|
||||||
|
or (b2 != -1 and len(val) > b2)
|
||||||
|
or not all(assert_valid(ty, v, schema) for v in val)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Not impl %s %s" % (type(attr_type), attr_type))
|
raise NotImplementedError("Not impl %s %s" % (type(attr_type), attr_type))
|
||||||
|
|
||||||
@@ -135,6 +145,7 @@ def validate(f, logger):
|
|||||||
logger.set_instance(inst)
|
logger.set_instance(inst)
|
||||||
|
|
||||||
entity = schema.declaration_by_name(inst.is_a())
|
entity = schema.declaration_by_name(inst.is_a())
|
||||||
|
attrs = entity.all_attributes()
|
||||||
|
|
||||||
if entity.is_abstract():
|
if entity.is_abstract():
|
||||||
e = "Entity %s is abstract" % entity.name()
|
e = "Entity %s is abstract" % entity.name()
|
||||||
@@ -143,20 +154,38 @@ def validate(f, logger):
|
|||||||
else:
|
else:
|
||||||
logger.error("In %s\n%s", inst, e)
|
logger.error("In %s\n%s", inst, e)
|
||||||
|
|
||||||
for attr, val, is_derived in zip(entity.all_attributes(), inst, entity.derived()):
|
has_invalid_value = False
|
||||||
|
for i in range(len(attrs)):
|
||||||
|
try:
|
||||||
|
inst[i]
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
if hasattr(logger, "set_instance"):
|
||||||
|
logger.error("Invalid attribute value for %s.%s", entity, attrs[i])
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
"In %s\nInvalid attribute value for %s.%s",
|
||||||
|
inst,
|
||||||
|
entity,
|
||||||
|
attrs[i],
|
||||||
|
)
|
||||||
|
has_invalid_value = True
|
||||||
|
|
||||||
if val is None and not (is_derived or attr.optional()):
|
if not has_invalid_value:
|
||||||
logger.error("Attribute %s.%s not optional", entity, attr)
|
for attr, val, is_derived in zip(attrs, inst, entity.derived()):
|
||||||
|
|
||||||
if val is not None:
|
if val is None and not (is_derived or attr.optional()):
|
||||||
attr_type = attr.type_of_attribute()
|
logger.error("Attribute %s.%s not optional", entity, attr)
|
||||||
try:
|
|
||||||
assert_valid(attr, val, schema)
|
if val is not None:
|
||||||
except ValidationError as e:
|
attr_type = attr.type_of_attribute()
|
||||||
if hasattr(logger, "set_instance"):
|
try:
|
||||||
logger.error(str(e))
|
assert_valid(attr, val, schema)
|
||||||
else:
|
except ValidationError as e:
|
||||||
logger.error("In %s\n%s", inst, e)
|
if hasattr(logger, "set_instance"):
|
||||||
|
logger.error(str(e))
|
||||||
|
else:
|
||||||
|
logger.error("In %s\n%s", inst, e)
|
||||||
|
|
||||||
for attr in entity.all_inverse_attributes():
|
for attr in entity.all_inverse_attributes():
|
||||||
val = getattr(inst, attr.name())
|
val = getattr(inst, attr.name())
|
||||||
|
|||||||
+18
-1
@@ -24,6 +24,10 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <boost/unordered_map.hpp>
|
#include <boost/unordered_map.hpp>
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
#include <boost/multi_index/sequenced_index.hpp>
|
||||||
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
|
#include <boost/multi_index/random_access_index.hpp>
|
||||||
|
|
||||||
#include "ifc_parse_api.h"
|
#include "ifc_parse_api.h"
|
||||||
|
|
||||||
@@ -132,7 +136,16 @@ private:
|
|||||||
|
|
||||||
void build_inverses_(IfcUtil::IfcBaseClass*);
|
void build_inverses_(IfcUtil::IfcBaseClass*);
|
||||||
|
|
||||||
std::set<int> batch_deletion_ids_;
|
typedef boost::multi_index_container<
|
||||||
|
int,
|
||||||
|
boost::multi_index::indexed_by<
|
||||||
|
boost::multi_index::sequenced<>,
|
||||||
|
boost::multi_index::ordered_unique<
|
||||||
|
boost::multi_index::identity<int>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> batch_deletion_ids_t;
|
||||||
|
batch_deletion_ids_t batch_deletion_ids_;
|
||||||
bool batch_mode_ = false;
|
bool batch_mode_ = false;
|
||||||
void process_deletion_();
|
void process_deletion_();
|
||||||
|
|
||||||
@@ -220,6 +233,10 @@ public:
|
|||||||
/// in the first function argument.
|
/// in the first function argument.
|
||||||
aggregate_of_instance::ptr traverse(IfcUtil::IfcBaseClass* instance, int max_level=-1);
|
aggregate_of_instance::ptr traverse(IfcUtil::IfcBaseClass* instance, int max_level=-1);
|
||||||
|
|
||||||
|
/// Same as traverse() but maintains topological order by using a
|
||||||
|
/// breadth-first search
|
||||||
|
aggregate_of_instance::ptr traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level=-1);
|
||||||
|
|
||||||
aggregate_of_instance::ptr getInverse(int instance_id, const IfcParse::declaration* type, int attribute_index);
|
aggregate_of_instance::ptr getInverse(int instance_id, const IfcParse::declaration* type, int attribute_index);
|
||||||
|
|
||||||
/// Marks entity as modified so that potential cache for it is invalidated.
|
/// Marks entity as modified so that potential cache for it is invalidated.
|
||||||
|
|||||||
+62
-11
@@ -1538,15 +1538,52 @@ void IfcFile::recalculate_id_counter() {
|
|||||||
MaxId = (unsigned int)k;
|
MaxId = (unsigned int)k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class traversal_recorder {
|
||||||
|
aggregate_of_instance::ptr list_;
|
||||||
|
std::map<int, aggregate_of_instance::ptr> instances_by_level_;
|
||||||
|
int mode_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
traversal_recorder(int mode) : mode_(mode) {
|
||||||
|
if (mode == 0) {
|
||||||
|
list_.reset(new aggregate_of_instance);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void push_back(int level, IfcUtil::IfcBaseClass* instance) {
|
||||||
|
if (mode_ == 0) {
|
||||||
|
list_->push(instance);
|
||||||
|
} else {
|
||||||
|
auto& l = instances_by_level_[level];
|
||||||
|
if (!l) {
|
||||||
|
l.reset(new aggregate_of_instance);
|
||||||
|
}
|
||||||
|
l->push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregate_of_instance::ptr get_list() const {
|
||||||
|
if (mode_ == 0) {
|
||||||
|
return list_;
|
||||||
|
} else {
|
||||||
|
aggregate_of_instance::ptr l(new aggregate_of_instance);
|
||||||
|
for (auto& p : instances_by_level_) {
|
||||||
|
l->push(p.second);
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class traversal_visitor {
|
class traversal_visitor {
|
||||||
private:
|
private:
|
||||||
std::set<IfcUtil::IfcBaseClass*>& visited_;
|
std::set<IfcUtil::IfcBaseClass*>& visited_;
|
||||||
aggregate_of_instance::ptr& list_;
|
traversal_recorder& list_;
|
||||||
int level_;
|
int level_;
|
||||||
int max_level_;
|
int max_level_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
traversal_visitor(std::set<IfcUtil::IfcBaseClass*>& visited, aggregate_of_instance::ptr& list, int level, int max_level)
|
traversal_visitor(std::set<IfcUtil::IfcBaseClass*>& visited, traversal_recorder& list, int level, int max_level)
|
||||||
: visited_(visited)
|
: visited_(visited)
|
||||||
, list_(list)
|
, list_(list)
|
||||||
, level_(level)
|
, level_(level)
|
||||||
@@ -1556,12 +1593,12 @@ public:
|
|||||||
void operator()(IfcUtil::IfcBaseClass* inst);
|
void operator()(IfcUtil::IfcBaseClass* inst);
|
||||||
};
|
};
|
||||||
|
|
||||||
void traverse_(IfcUtil::IfcBaseClass* instance, std::set<IfcUtil::IfcBaseClass*>& visited, aggregate_of_instance::ptr list, int level, int max_level) {
|
void traverse_(IfcUtil::IfcBaseClass* instance, std::set<IfcUtil::IfcBaseClass*>& visited, traversal_recorder& list, int level, int max_level) {
|
||||||
if (visited.find(instance) != visited.end()) {
|
if (visited.find(instance) != visited.end()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
visited.insert(instance);
|
visited.insert(instance);
|
||||||
list->push(instance);
|
list.push_back(level, instance);
|
||||||
|
|
||||||
if (level >= max_level && max_level > 0) return;
|
if (level >= max_level && max_level > 0) return;
|
||||||
|
|
||||||
@@ -1575,9 +1612,18 @@ void traversal_visitor::operator()(IfcUtil::IfcBaseClass* inst) {
|
|||||||
|
|
||||||
aggregate_of_instance::ptr IfcParse::traverse(IfcUtil::IfcBaseClass* instance, int max_level) {
|
aggregate_of_instance::ptr IfcParse::traverse(IfcUtil::IfcBaseClass* instance, int max_level) {
|
||||||
std::set<IfcUtil::IfcBaseClass*> visited;
|
std::set<IfcUtil::IfcBaseClass*> visited;
|
||||||
aggregate_of_instance::ptr return_value(new aggregate_of_instance);
|
traversal_recorder r(0);
|
||||||
traverse_(instance, visited, return_value, 0, max_level);
|
traverse_(instance, visited, r, 0, max_level);
|
||||||
return return_value;
|
return r.get_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
// I'm cheating this isn't breadth-first, but rather we record visited instances
|
||||||
|
// keeping track of their rank and return a list ordered by rank. Is this equivalent?
|
||||||
|
aggregate_of_instance::ptr IfcParse::traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level) {
|
||||||
|
std::set<IfcUtil::IfcBaseClass*> visited;
|
||||||
|
traversal_recorder r(1);
|
||||||
|
traverse_(instance, visited, r, 0, max_level);
|
||||||
|
return r.get_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @note: for backwards compatibility
|
/// @note: for backwards compatibility
|
||||||
@@ -1585,6 +1631,11 @@ aggregate_of_instance::ptr IfcFile::traverse(IfcUtil::IfcBaseClass* instance, in
|
|||||||
return IfcParse::traverse(instance, max_level);
|
return IfcParse::traverse(instance, max_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @note: for backwards compatibility
|
||||||
|
aggregate_of_instance::ptr IfcFile::traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level) {
|
||||||
|
return IfcParse::traverse_breadth_first(instance, max_level);
|
||||||
|
}
|
||||||
|
|
||||||
void IfcFile::mark_entity_as_modified(int /*id*/)
|
void IfcFile::mark_entity_as_modified(int /*id*/)
|
||||||
{
|
{
|
||||||
by_ref_cached_.clear();
|
by_ref_cached_.clear();
|
||||||
@@ -1857,7 +1908,7 @@ void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
|
|||||||
throw IfcParse::IfcException("Instance not part of this file");
|
throw IfcParse::IfcException("Instance not part of this file");
|
||||||
}
|
}
|
||||||
|
|
||||||
batch_deletion_ids_.insert(id);
|
batch_deletion_ids_.push_back(id);
|
||||||
|
|
||||||
if (!batch_mode_) {
|
if (!batch_mode_) {
|
||||||
process_deletion_();
|
process_deletion_();
|
||||||
@@ -1866,7 +1917,7 @@ void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
|
|||||||
|
|
||||||
void IfcFile::process_deletion_() {
|
void IfcFile::process_deletion_() {
|
||||||
|
|
||||||
for (auto& id : batch_deletion_ids_) {
|
for (auto& id : batch_deletion_ids_.get<0>()) {
|
||||||
auto entity = instance_by_id(id);
|
auto entity = instance_by_id(id);
|
||||||
|
|
||||||
aggregate_of_instance::ptr references = instances_by_reference(id);
|
aggregate_of_instance::ptr references = instances_by_reference(id);
|
||||||
@@ -2012,10 +2063,10 @@ void IfcFile::process_deletion_() {
|
|||||||
|
|
||||||
if (batch_mode_) {
|
if (batch_mode_) {
|
||||||
for (auto it = byref.begin(); it != byref.end();) {
|
for (auto it = byref.begin(); it != byref.end();) {
|
||||||
bool do_delete = batch_deletion_ids_.find(it->first) != batch_deletion_ids_.end();
|
bool do_delete = batch_deletion_ids_.get<1>().find(it->first) != batch_deletion_ids_.get<1>().end();
|
||||||
if (!do_delete) {
|
if (!do_delete) {
|
||||||
it->second.erase(std::remove_if(it->second.begin(), it->second.end(), [this](int x) {
|
it->second.erase(std::remove_if(it->second.begin(), it->second.end(), [this](int x) {
|
||||||
return batch_deletion_ids_.find(x) != batch_deletion_ids_.end();
|
return batch_deletion_ids_.get<1>().find(x) != batch_deletion_ids_.get<1>().end();
|
||||||
}), it->second.end());
|
}), it->second.end());
|
||||||
do_delete = it->second.empty();
|
do_delete = it->second.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,6 +273,8 @@ namespace IfcParse {
|
|||||||
IFC_PARSE_API IfcEntityInstanceData* read(unsigned int i, IfcFile* t, boost::optional<unsigned> offset = boost::none);
|
IFC_PARSE_API IfcEntityInstanceData* read(unsigned int i, IfcFile* t, boost::optional<unsigned> offset = boost::none);
|
||||||
|
|
||||||
IFC_PARSE_API aggregate_of_instance::ptr traverse(IfcUtil::IfcBaseClass* instance, int max_level = -1);
|
IFC_PARSE_API aggregate_of_instance::ptr traverse(IfcUtil::IfcBaseClass* instance, int max_level = -1);
|
||||||
|
|
||||||
|
IFC_PARSE_API aggregate_of_instance::ptr traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level = -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
IFC_PARSE_API std::ostream& operator<< (std::ostream& os, const IfcParse::IfcFile& f);
|
IFC_PARSE_API std::ostream& operator<< (std::ostream& os, const IfcParse::IfcFile& f);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ class Patcher:
|
|||||||
self.args = args
|
self.args = args
|
||||||
|
|
||||||
def patch(self):
|
def patch(self):
|
||||||
|
if self.file.schema == "IFC2X3":
|
||||||
|
return
|
||||||
curve_map = {}
|
curve_map = {}
|
||||||
|
|
||||||
for curve in self.file.by_type("IfcIndexedPolyCurve"):
|
for curve in self.file.by_type("IfcIndexedPolyCurve"):
|
||||||
|
|||||||
@@ -421,7 +421,11 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!representation) {
|
if (!representation) {
|
||||||
if (instance->declaration().is(Schema::IfcRepresentationItem::Class()) || instance->declaration().is(Schema::IfcRepresentation::Class())) {
|
if (instance->declaration().is(Schema::IfcRepresentationItem::Class()) ||
|
||||||
|
instance->declaration().is(Schema::IfcRepresentation::Class()) ||
|
||||||
|
// https://github.com/IfcOpenShell/IfcOpenShell/issues/1649
|
||||||
|
instance->declaration().is(Schema::IfcProfileDef::Class())
|
||||||
|
) {
|
||||||
IfcGeom::IfcRepresentationShapeItems shapes = kernel.convert(instance);
|
IfcGeom::IfcRepresentationShapeItems shapes = kernel.convert(instance);
|
||||||
|
|
||||||
IfcGeom::ElementSettings element_settings(settings, kernel.getValue(IfcGeom::Kernel::GV_LENGTH_UNIT), instance->declaration().name());
|
IfcGeom::ElementSettings element_settings(settings, kernel.getValue(IfcGeom::Kernel::GV_LENGTH_UNIT), instance->declaration().name());
|
||||||
|
|||||||
Reference in New Issue
Block a user