mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
typing
This commit is contained in:
@@ -224,7 +224,7 @@ def refresh_ui_data():
|
||||
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.should_draw_decorations = props.should_draw_decorations
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
if tool.Web.get_web_props().is_connected:
|
||||
tool.Web.send_webui_data()
|
||||
|
||||
|
||||
|
||||
@@ -884,7 +884,8 @@ class IfcImporter:
|
||||
for material in self.material_creator.materials.values():
|
||||
bpy.data.materials.remove(material)
|
||||
|
||||
def add_project_to_scene(self):
|
||||
def add_project_to_scene(self) -> None:
|
||||
assert bpy.context.scene
|
||||
try:
|
||||
bpy.context.scene.collection.children.link(self.project["blender"])
|
||||
except:
|
||||
@@ -909,6 +910,7 @@ class IfcImporter:
|
||||
bpy.ops.mesh.normals_make_consistent()
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
assert bpy.context.view_layer
|
||||
bpy.context.view_layer.objects.active = last_obj
|
||||
IfcStore.edited_objs.clear()
|
||||
|
||||
@@ -927,6 +929,7 @@ class IfcImporter:
|
||||
if not (assignment := self.file.by_type("IfcProject")[0].UnitsInContext):
|
||||
return # Geometry is optional in IFC
|
||||
props = tool.Blender.get_bim_props()
|
||||
assert bpy.context.scene
|
||||
for unit in assignment.Units:
|
||||
if unit.is_a("IfcNamedUnit") and unit.UnitType == "LENGTHUNIT":
|
||||
if unit.is_a("IfcSIUnit"):
|
||||
@@ -1065,11 +1068,11 @@ class IfcImporter:
|
||||
def create_mesh(
|
||||
self,
|
||||
element: ifcopenshell.entity_instance,
|
||||
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
|
||||
shape: Union[W.Triangulation, W.TriangulationElement],
|
||||
cartesian_point_offset: Union[npt.NDArray[np.float64], Literal[False]] = None,
|
||||
) -> Union[bpy.types.Mesh, None]:
|
||||
try:
|
||||
if isinstance(shape, ifcopenshell.geom.ShapeElementType):
|
||||
if isinstance(shape, W.TriangulationElement):
|
||||
# shape is ShapeElementType
|
||||
geometry = shape.geometry
|
||||
else:
|
||||
|
||||
@@ -269,7 +269,7 @@ class ExportIfcCsv(bpy.types.Operator, ExportHelper):
|
||||
schedule_creator = scheduler.Scheduler()
|
||||
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
|
||||
if props.format == "web":
|
||||
if not context.scene.WebProperties.is_connected:
|
||||
if not tool.Web.get_web_props().is_connected:
|
||||
bpy.ops.bim.connect_websocket_server()
|
||||
df = ifc_csv.dataframe
|
||||
assert df is not None
|
||||
|
||||
@@ -88,7 +88,7 @@ class SendWebUiDemoMessage(bpy.types.Operator):
|
||||
# First, we need to make sure that we are connected to the Web UI
|
||||
# We do that by checking the Web module properties' is_connected prop
|
||||
# and calling the operator connect_websocket_server if we aren't connected to a Web UI
|
||||
if not context.scene.WebProperties.is_connected:
|
||||
if not tool.Web.get_web_props().is_connected:
|
||||
bpy.ops.bim.connect_websocket_server(page="demo")
|
||||
props = tool.Demo.get_demo_props()
|
||||
core.send_webui_demo_message(tool.Web, message=props.webui_message)
|
||||
|
||||
@@ -134,6 +134,7 @@ def format_distance(
|
||||
custom_unit=None,
|
||||
):
|
||||
# Get Blender Scene Unit Settings
|
||||
assert bpy.context.scene
|
||||
unit_scale = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
|
||||
@@ -3735,7 +3735,7 @@ class OpenDocumentationWebUi(bpy.types.Operator):
|
||||
bl_description = "Open the documentation web UI page"
|
||||
|
||||
def execute(self, context):
|
||||
if not context.scene.WebProperties.is_connected:
|
||||
if not tool.Web.get_web_props().is_connected:
|
||||
bpy.ops.bim.connect_websocket_server(page="documentation")
|
||||
else:
|
||||
bpy.ops.bim.open_web_browser(page="documentation")
|
||||
|
||||
@@ -29,11 +29,12 @@ class ConnectToWebsocketServer(bpy.types.Operator):
|
||||
page: bpy.props.StringProperty(default="")
|
||||
|
||||
def execute(self, context):
|
||||
port = context.scene.WebProperties.webserver_port
|
||||
props = tool.Web.get_web_props()
|
||||
port = props.webserver_port
|
||||
if port == 0:
|
||||
context.scene.WebProperties.webserver_port = core.generate_port_number(tool.Web)
|
||||
props.webserver_port = core.generate_port_number(tool.Web)
|
||||
|
||||
port = context.scene.WebProperties.webserver_port
|
||||
port = props.webserver_port
|
||||
core.connect_websocket_server(tool.Web, port, self.page)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -65,6 +66,6 @@ class OpenWebBrowser(bpy.types.Operator):
|
||||
page: bpy.props.StringProperty(default="")
|
||||
|
||||
def execute(self, context):
|
||||
port = context.scene.WebProperties.webserver_port
|
||||
port = tool.Web.get_web_props().webserver_port
|
||||
core.open_web_browser(tool.Web, port, self.page)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -31,6 +32,21 @@ from bpy.props import (
|
||||
|
||||
|
||||
class WebProperties(PropertyGroup):
|
||||
webserver_port: IntProperty(name="Webserver Port", min=0, max=65535)
|
||||
is_running: BoolProperty(name="Webserver Running Status", default=False)
|
||||
is_connected: BoolProperty(name="Connection Status", default=False)
|
||||
webserver_port: IntProperty( # pyright: ignore[reportRedeclaration]
|
||||
name="Webserver Port",
|
||||
min=0,
|
||||
max=65535,
|
||||
)
|
||||
is_running: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
name="Webserver Running Status",
|
||||
default=False,
|
||||
)
|
||||
is_connected: BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||
name="Connection Status",
|
||||
default=False,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
webserver_port: int
|
||||
is_running: bool
|
||||
is_connected: bool
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from bpy.types import Panel
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.web.data import WebData
|
||||
|
||||
|
||||
@@ -34,10 +35,10 @@ class BIM_PT_webui(Panel):
|
||||
if not WebData.is_loaded:
|
||||
WebData.load()
|
||||
|
||||
assert self.layout
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
props = scene.WebProperties
|
||||
props = tool.Web.get_web_props()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "webserver_port", text="Websocket server Port")
|
||||
|
||||
@@ -1085,8 +1085,8 @@ class Cost(bonsai.core.tool.Cost):
|
||||
return {"id": unit.id(), "name": unit.Currency}
|
||||
|
||||
@classmethod
|
||||
def generate_cost_schedule_browser(cls, cost_chedule) -> None:
|
||||
if not bpy.context.scene.WebProperties.is_connected:
|
||||
def generate_cost_schedule_browser(cls, cost_chedule: ifcopenshell.entity_instance) -> None:
|
||||
if not tool.Web.get_web_props().is_connected:
|
||||
bpy.ops.bim.connect_websocket_server(page="costing")
|
||||
tool.Web.load_cost_schedule_web_ui(cost_chedule)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import ifcopenshell.api.root
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
@@ -883,6 +884,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if iterator and iterator.initialize():
|
||||
while True:
|
||||
shape = iterator.get()
|
||||
assert isinstance(shape, W.TriangulationElement)
|
||||
element = tool.Ifc.get().by_id(shape.id)
|
||||
if obj := tool.Ifc.get_object(element):
|
||||
# It's possible that there will be multiple shapes for the same context,
|
||||
|
||||
@@ -23,6 +23,7 @@ import bpy
|
||||
import bmesh
|
||||
import logging
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.placement
|
||||
@@ -814,7 +815,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
cls,
|
||||
element: ifcopenshell.entity_instance,
|
||||
representation: ifcopenshell.entity_instance,
|
||||
shape: ifcopenshell.geom.ShapeElementType,
|
||||
shape: W.TriangulationElement,
|
||||
) -> bpy.types.Camera:
|
||||
"""Create camera data.
|
||||
|
||||
@@ -968,7 +969,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
@classmethod
|
||||
def convert_geometry_to_mesh(
|
||||
cls,
|
||||
geometry: ifcopenshell.geom.ShapeType,
|
||||
geometry: W.Triangulation,
|
||||
mesh: bpy.types.Mesh,
|
||||
verts: Optional[npt.NDArray[np.float64]] = None,
|
||||
*,
|
||||
|
||||
@@ -30,6 +30,7 @@ import ifcopenshell.api.geometry
|
||||
import ifcopenshell.api.grid
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
@@ -1864,6 +1865,7 @@ class Model(bonsai.core.tool.Model):
|
||||
polygons = {}
|
||||
for curve in curves:
|
||||
geometry = ifcopenshell.geom.create_shape(settings, curve)
|
||||
assert isinstance(geometry, W.Triangulation)
|
||||
v = ifcopenshell.util.shape.get_vertices(geometry, is_2d=True)
|
||||
v = np.round(v, 4) # Round to nearest 0.1mm, otherwise things like circles don't polygonise reliably
|
||||
edges = ifcopenshell.util.shape.get_edges(geometry)
|
||||
|
||||
@@ -21,6 +21,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.profile
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.ifcopenshell_wrapper as W
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.placement
|
||||
@@ -52,6 +53,7 @@ class Profile(bonsai.core.tool.Profile):
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS)
|
||||
shape = ifcopenshell.geom.create_shape(settings, profile)
|
||||
|
||||
assert isinstance(shape, W.Triangulation)
|
||||
verts = ifcopenshell.util.shape.get_vertices(shape)
|
||||
if verts.size == 0:
|
||||
raise RuntimeError(f"Profile shape has no vertices, it probably is invalid: '{profile}'.")
|
||||
|
||||
@@ -1650,7 +1650,7 @@ class Sequence(bonsai.core.tool.Sequence):
|
||||
def generate_gantt_browser_chart(
|
||||
cls, task_json: list[dict[str, Any]], work_schedule: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
if not bpy.context.scene.WebProperties.is_connected:
|
||||
if not tool.Web.get_web_props().is_connected:
|
||||
bpy.ops.bim.connect_websocket_server(page="sequencing")
|
||||
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
||||
tool.Web.send_webui_data(data=gantt_data, data_key="gantt_data", event="gantt_data")
|
||||
|
||||
@@ -27,7 +27,7 @@ import bonsai.tool as tool
|
||||
import ifcopenshell.api.sequence
|
||||
import ifcopenshell.api.classification
|
||||
import ifcopenshell.api.cost
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
import time
|
||||
import socket
|
||||
import sys
|
||||
@@ -47,6 +47,9 @@ import bonsai.core.cost
|
||||
from ifc5d.ifc2json import ifc5D2json
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.web.prop import WebProperties
|
||||
|
||||
sio = None
|
||||
ws_process = None
|
||||
ws_thread = None
|
||||
@@ -62,6 +65,11 @@ IFC_TASK_ATTRIBUTE_MAP = {
|
||||
|
||||
|
||||
class Web(bonsai.core.tool.Web):
|
||||
@classmethod
|
||||
def get_web_props(cls) -> WebProperties:
|
||||
assert (scene := bpy.context.scene)
|
||||
return scene.BIMProperties # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
||||
@classmethod
|
||||
def generate_port_number(cls) -> int:
|
||||
"""
|
||||
@@ -151,7 +159,7 @@ class Web(bonsai.core.tool.Web):
|
||||
"""
|
||||
global ws_thread, sio
|
||||
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
if tool.Web.get_web_props().is_connected:
|
||||
print(f"Already connected to websocket server on port: {port}")
|
||||
return
|
||||
|
||||
@@ -200,7 +208,7 @@ class Web(bonsai.core.tool.Web):
|
||||
print("No Websocket server running")
|
||||
return
|
||||
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
if tool.Web.get_web_props().is_connected:
|
||||
cls.disconnect_websocket_server()
|
||||
|
||||
# sleep(0.5)
|
||||
@@ -282,7 +290,7 @@ class Web(bonsai.core.tool.Web):
|
||||
if data is not None:
|
||||
payload[data_key] = data
|
||||
|
||||
if ws_thread is not None and bpy.context.scene.WebProperties.is_connected:
|
||||
if ws_thread is not None and tool.Web.get_web_props().is_connected:
|
||||
ws_thread.run_coro(cls.sio_send(payload, event, namespace))
|
||||
|
||||
@classmethod
|
||||
@@ -295,7 +303,7 @@ class Web(bonsai.core.tool.Web):
|
||||
Returns:
|
||||
(Optional[float]): Returns None if the WebProperties.is_connected is False, otherwise returns 1.0 to continue the timer.
|
||||
"""
|
||||
if not bpy.context.scene.WebProperties.is_connected:
|
||||
if not tool.Web.get_web_props().is_connected:
|
||||
with web_operator_queue.mutex:
|
||||
web_operator_queue.queue.clear()
|
||||
return None # unregister timer if not connected
|
||||
@@ -879,11 +887,13 @@ class Web(bonsai.core.tool.Web):
|
||||
|
||||
@classmethod
|
||||
def set_is_running(cls, is_running: bool) -> None:
|
||||
bpy.context.scene.WebProperties.is_running = is_running
|
||||
props = tool.Web.get_web_props()
|
||||
props.is_running = is_running
|
||||
|
||||
@classmethod
|
||||
def set_is_connected(cls, is_connected: bool) -> None:
|
||||
bpy.context.scene.WebProperties.is_connected = is_connected
|
||||
props = tool.Web.get_web_props()
|
||||
props.is_connected = is_connected
|
||||
|
||||
|
||||
class AsyncioThread(threading.Thread):
|
||||
|
||||
@@ -94,6 +94,7 @@ class FileDescription(HeaderEntity):
|
||||
|
||||
class FileName(HeaderEntity):
|
||||
name: str
|
||||
"""Updated automatically only on ``file.write``."""
|
||||
time_stamp: str
|
||||
author: tuple[str, ...]
|
||||
organization: tuple[str, ...]
|
||||
@@ -358,7 +359,7 @@ class Iterator:
|
||||
"""
|
||||
...
|
||||
|
||||
def set_cache(self, cache: GeometrySerializer) -> None: ...
|
||||
def set_cache(self, cache: Union[GeometrySerializer, None]) -> None: ...
|
||||
def unit_magnitude(self): ...
|
||||
def unit_name(self): ...
|
||||
|
||||
@@ -1351,8 +1352,8 @@ class style(item):
|
||||
- IFC style id if style assigned to the representation items directly
|
||||
or through material with a style;
|
||||
- IFC material id if both true:
|
||||
- element has a material without a style;
|
||||
- there are parts of the geometry that has no other style assigned to them;
|
||||
- element has a material without a style;
|
||||
- there are parts of the geometry that has no other style assigned to them;
|
||||
- -1 in case if there is no material;
|
||||
- 0 in case if there are default materials used.
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user