Skip to content

Commit

Permalink
add support for exporting tapered beams to gxml.
Browse files Browse the repository at this point in the history
Add support for offsetting tapered beams
  • Loading branch information
Krande committed Dec 19, 2024
1 parent 4e4d1a7 commit 493e3c9
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 85 deletions.
4 changes: 0 additions & 4 deletions examples/simple_beam.py

This file was deleted.

19 changes: 19 additions & 0 deletions examples/various_beams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import ada
from ada.api.beams.base_bm import TaperTypes

bm = ada.Beam("bm1", (0, 0, 0), (1, 0, 0), "IPE300")
beams = [bm]

bm2 = ada.BeamTapered("bm2", (0, 1, 0), (1, 1, 0), "IPE600", "IPE300")
beams.append(bm2)

bm3 = ada.BeamTapered("bm3", (0, 2, 0), (1, 2, 0), "IPE600", "IPE300", taper_type=TaperTypes.FLUSH_TOP)
beams.append(bm3)

bm4 = ada.BeamTapered("bm4", (0, 3, 0), (1, 3, 0), "IPE600", "IPE300", taper_type=TaperTypes.FLUSH_BOTTOM)
beams.append(bm4)

pl = ada.Plate("pl1", [(0, 0), (1, 0), (1, 3), (0, 3)], 0.01)
plates = [pl]

(ada.Part("Beams") / (beams + plates)).show()
33 changes: 32 additions & 1 deletion src/ada/api/beams/base_bm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import numpy as np

import ada
import ada.api.beams.geom_beams as geo_conv
from ada.api.beams.helpers import BeamConnectionProps
from ada.api.bounding_box import BoundingBox
Expand All @@ -24,7 +25,9 @@
vector_length,
)
from ada.geom import Geometry
from ada.geom.curves import IndexedPolyCurve
from ada.geom.placement import Direction
from ada.geom.surfaces import ArbitraryProfileDef
from ada.materials import Material
from ada.materials.utils import get_material
from ada.sections import Section
Expand Down Expand Up @@ -497,7 +500,35 @@ def taper_type(self, value: TaperTypes):
self._taper_type = value

def solid_geom(self) -> Geometry:
return geo_conv.straight_tapered_beam_to_geom(self)
geo = geo_conv.straight_tapered_beam_to_geom(self)
if self.taper_type == TaperTypes.CENTERED:
return geo
if self.taper_type == TaperTypes.FLUSH_TOP:
offset_dir_1 = ada.Direction(0, -self.section.h / 2)
offset_dir_2 = ada.Direction(0, -self.taper.h / 2)
elif self.taper_type == TaperTypes.FLUSH_BOTTOM:
offset_dir_1 = ada.Direction(0, self.section.h / 2)
offset_dir_2 = ada.Direction(0, self.taper.h / 2)
else:
raise ValueError(f"Unknown taper type {self.taper_type}")

profile_1 = geo.geometry.swept_area

if isinstance(profile_1, ArbitraryProfileDef):
if isinstance(profile_1.outer_curve, IndexedPolyCurve):
for curve in profile_1.outer_curve.segments:
curve.start = ada.Point(curve.start + offset_dir_1)
curve.end = ada.Point(curve.end + offset_dir_1)

profile_2 = geo.geometry.end_swept_area

if isinstance(profile_2, ArbitraryProfileDef):
if isinstance(profile_2.outer_curve, IndexedPolyCurve):
for curve in profile_2.outer_curve.segments:
curve.start = ada.Point(curve.start + offset_dir_2)
curve.end = ada.Point(curve.end + offset_dir_2)

return geo

def shell_geom(self) -> Geometry:
geom = geo_conv.straight_tapered_beam_to_geom(self, is_solid=False)
Expand Down
2 changes: 2 additions & 0 deletions src/ada/api/beams/geom_beams.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def ibeam_taper_to_geom(beam: BeamTapered) -> Geometry:
geom = geo_so.ExtrudedAreaSolidTapered(profile1, place, beam.length, Direction(0, 0, 1), profile2)
return Geometry(beam.guid, geom, beam.color)


def tbeam_taper_to_geom(beam: BeamTapered) -> Geometry:
profile1 = section_to_arbitrary_profile_def_with_voids(beam.section)
profile2 = section_to_arbitrary_profile_def_with_voids(beam.taper)
Expand All @@ -151,6 +152,7 @@ def tbeam_taper_to_geom(beam: BeamTapered) -> Geometry:
geom = geo_so.ExtrudedAreaSolidTapered(profile1, place, beam.length, Direction(0, 0, 1), profile2)
return Geometry(beam.guid, geom, beam.color)


def ibeam_taper_to_face_geom(beam: BeamTapered) -> Geometry:
profile1 = section_to_arbitrary_profile_def_with_voids(beam.section, solid=False)
profile2 = section_to_arbitrary_profile_def_with_voids(beam.taper, solid=False)
Expand Down
2 changes: 1 addition & 1 deletion src/ada/api/spatial/part.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ def get_all_physical_objects(
filter_by_guids: list[str] = None,
pipe_to_segments=False,
by_metadata: dict = None,
) -> Iterable[Beam | Plate | Wall | Pipe | Shape]:
) -> Iterable[Beam | BeamTapered | Plate | Wall | Pipe | Shape]:
physical_objects = []
if sub_elements_only:
iter_parts = iter([self])
Expand Down
13 changes: 11 additions & 2 deletions src/ada/cadit/gxml/write/write_beams.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import itertools
import xml.etree.ElementTree as ET
from typing import TYPE_CHECKING

Expand All @@ -11,9 +12,12 @@


def add_beams(root: ET.Element, part: Part, sw: SatWriter = None):
from ada import Beam
from ada import Beam, BeamTapered

for beam in part.get_all_physical_objects(by_type=Beam):
iter_beams = part.get_all_physical_objects(by_type=Beam)
iter_taper = part.get_all_physical_objects(by_type=BeamTapered)

for beam in itertools.chain(iter_beams, iter_taper):
add_straight_beam(beam, root)


Expand All @@ -37,8 +41,13 @@ def add_curve_orientation(beam: Beam, straight_beam: ET.Element):


def add_segments(beam: Beam):
from ada import BeamTapered

segments = ET.Element("segments")
props = dict(index="1", section_ref=beam.section.name, material_ref=beam.material.name)
if isinstance(beam, BeamTapered):
props.update(dict(section_ref=f"{beam.section.name}_{beam.taper.name}"))

straight_segment = ET.SubElement(segments, "straight_segment", props)

d = ["x", "y", "z"]
Expand Down
137 changes: 68 additions & 69 deletions src/ada/cadit/gxml/write/write_sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,69 @@
from ada import Part, Section


def get_section_props(section: Section) -> ET.Element | None:
if section.type == section.TYPES.ANGULAR:
return to_gxml_angular_section(section)
elif section.type == section.TYPES.TUBULAR:
return to_gxml_pipe_section(section)
elif section.type == section.TYPES.IPROFILE and section.w_btn == section.w_top:
return to_gxml_i_section(section)
elif section.type == section.TYPES.IPROFILE and section.w_btn != section.w_top:
return to_gxml_unsymm_i_section(section)
elif section.type == section.TYPES.TPROFILE and section.w_btn != section.w_top:
return to_gxml_unsymm_i_section(section)
elif section.type == section.TYPES.BOX:
return to_gxml_box_section(section)
elif section.type == section.TYPES.CHANNEL:
return to_gxml_channel_section(section)
elif section.type == section.TYPES.FLATBAR:
return to_gxml_bar_section(section)
else:
logger.error(f"The profile type {section.type} is not yet supported for Genie XML export")
return None


def add_sections(root: ET.Element, part: Part):
from ada import BeamTapered

sections_elem = ET.Element("sections")

# Add the new element underneath <properties>
root.append(sections_elem)

for section in part.sections:
if section.type == section.TYPES.ANGULAR:
add_angular_section(section, sections_elem)
elif section.type == section.TYPES.TUBULAR:
add_pipe_section(section, sections_elem)
elif section.type == section.TYPES.IPROFILE and section.w_btn == section.w_top:
add_i_section(section, sections_elem)
elif section.type == section.TYPES.IPROFILE and section.w_btn != section.w_top:
add_unsymm_i_section(section, sections_elem)
elif section.type == section.TYPES.TPROFILE and section.w_btn != section.w_top:
add_unsymm_i_section(section, sections_elem)
elif section.type == section.TYPES.BOX:
add_box_section(section, sections_elem)
elif section.type == section.TYPES.CHANNEL:
add_channel_section(section, sections_elem)
elif section.type == section.TYPES.FLATBAR:
add_bar_section(section, sections_elem)
else:
logger.error(f"The profile type {section.type} is not yet supported for Genie XML export")


def add_angular_section(section: Section, xml_root: ET.Element):
section_elem = ET.Element("section", {"name": section.name, "description": ""})
section_props = ET.Element(
section_elem = ET.SubElement(sections_elem, "section", {"name": section.name, "description": ""})
section_props = get_section_props(section)
section_elem.append(section_props)

tapered_sections: list[tuple[Section, Section]] = []
for bm in part.get_all_physical_objects(by_type=BeamTapered):
profile1 = bm.section
profile2 = bm.taper
profile_tup = (profile1, profile2)
if profile_tup not in tapered_sections:
tapered_sections.append(profile_tup)

for profile1, profile2 in tapered_sections:
section_elem = ET.SubElement(
sections_elem, "section", {"name": f"{profile1.name}_{profile2.name}", "description": ""}
)
tapered_section = ET.SubElement(
section_elem,
"tapered_section",
dict(fabrication="unknown", sfy="1", sfz="1", general_properties_method="computed"),
)
sec1_props = get_section_props(profile1)
section1 = ET.SubElement(tapered_section, "section1")
section1.append(sec1_props)
sec2_props = get_section_props(profile2)
section2 = ET.SubElement(tapered_section, "section2")
section2.append(sec2_props)


def to_gxml_angular_section(section: Section):

return ET.Element(
"l_section",
dict(
h=str(section.h),
Expand All @@ -52,14 +85,9 @@ def add_angular_section(section: Section, xml_root: ET.Element):
),
)

section_elem.append(section_props)
xml_root.append(section_elem)


def add_pipe_section(section: Section, xml_root: ET.Element):
section_elem = ET.Element("section", {"name": section.name, "description": ""})

section_props = ET.Element(
def to_gxml_pipe_section(section: Section):
return ET.Element(
"pipe_section",
dict(
od=str(section.r * 2),
Expand All @@ -71,13 +99,9 @@ def add_pipe_section(section: Section, xml_root: ET.Element):
),
)

section_elem.append(section_props)
xml_root.append(section_elem)


def add_i_section(section: Section, xml_root: ET.Element):
section_elem = ET.Element("section", {"name": section.name, "description": ""})
section_props = ET.Element(
def to_gxml_i_section(section: Section):
return ET.Element(
"i_section",
dict(
h=str(section.h),
Expand All @@ -92,14 +116,9 @@ def add_i_section(section: Section, xml_root: ET.Element):
),
)

section_elem.append(section_props)
xml_root.append(section_elem)


def add_box_section(section: Section, xml_root: ET.Element):
# Create the <section> element
xml_section = ET.Element("section", {"name": section.name})
box_section = ET.Element(
def to_gxml_box_section(section: Section):
return ET.Element(
"box_section",
dict(
h=str(section.h),
Expand All @@ -114,15 +133,9 @@ def add_box_section(section: Section, xml_root: ET.Element):
),
)

# Append the <box_section> element to the <section> element
xml_section.append(box_section)
xml_root.append(xml_section)
# You can now append the <section> element to your xml tree


def add_unsymm_i_section(section: Section, xml_root: ET.Element):
section_elem = ET.Element("section", {"name": section.name})
section_props = ET.Element(
def to_gxml_unsymm_i_section(section: Section):
return ET.Element(
"unsymmetrical_i_section",
dict(
h=str(section.h),
Expand All @@ -140,13 +153,9 @@ def add_unsymm_i_section(section: Section, xml_root: ET.Element):
),
)

section_elem.append(section_props)
xml_root.append(section_elem)


def add_channel_section(section: Section, xml_root: ET.Element):
section_elem = ET.Element("section", {"name": section.name})
section_props = ET.Element(
def to_gxml_channel_section(section: Section) -> ET.Element:
return ET.Element(
"channel_section",
dict(
h=str(section.h),
Expand All @@ -160,14 +169,9 @@ def add_channel_section(section: Section, xml_root: ET.Element):
),
)

section_elem.append(section_props)
xml_root.append(section_elem)


def add_bar_section(section: Section, xml_root: ET.Element):
# Create the <section> element
xml_section = ET.Element("section", {"name": section.name})
bar_section = ET.Element(
def to_gxml_bar_section(section: Section) -> ET.Element:
return ET.Element(
"bar_section",
dict(
h=str(section.h),
Expand All @@ -178,8 +182,3 @@ def add_bar_section(section: Section, xml_root: ET.Element):
general_properties_method="computed",
),
)

# Append the <bar_section> element to the <section> element
xml_section.append(bar_section)
xml_root.append(xml_section)
# You can now append the <section> element to your xml tree
5 changes: 4 additions & 1 deletion src/ada/cadit/ifc/write/write_beams.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
ifc_dir,
)
from ada.cadit.ifc.write.geom.points import cpt
from ada.cadit.ifc.write.geom.solids import extruded_area_solid, extruded_area_solid_tapered
from ada.cadit.ifc.write.geom.solids import (
extruded_area_solid,
extruded_area_solid_tapered,
)
from ada.cadit.ifc.write.write_curves import write_curve_poly
from ada.config import Config
from ada.core.guid import create_guid
Expand Down
1 change: 1 addition & 0 deletions src/ada/geom/solids.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,5 @@ class AdvancedBrep:
Sphere,
FixedReferenceSweptAreaSolid,
AdvancedBrep,
ExtrudedAreaSolidTapered,
]
18 changes: 11 additions & 7 deletions src/ada/visit/rendering/pygfx_offscreen_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import ada
import pygfx as gfx
import numpy as np
from wgpu.gui.offscreen import WgpuCanvas
import pygfx as gfx
from PIL import Image
from wgpu.gui.offscreen import WgpuCanvas

import ada


def screenshot(part: ada.Part, filename: str):
tri_scene = part.to_trimesh_scene()
Expand All @@ -14,10 +16,12 @@ def screenshot(part: ada.Part, filename: str):
geom = scene.add(gfx.Group())
meshes = []
for mesh in tri_scene.geometry.values():
meshes.append(gfx.Mesh(
gfx.geometry_from_trimesh(mesh),
gfx.MeshPhongMaterial(),
))
meshes.append(
gfx.Mesh(
gfx.geometry_from_trimesh(mesh),
gfx.MeshPhongMaterial(),
)
)
geom.add(*meshes)
dir_light = gfx.DirectionalLight()
scene.add(gfx.AmbientLight(intensity=0.5))
Expand Down

0 comments on commit 493e3c9

Please sign in to comment.