mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-31 00:46:36 +00:00
moving code to shape builder
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -20,195 +20,8 @@ import ifcopenshell
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
from math import cos, tan, pi
|
from math import cos, tan, pi
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from mathutils import Vector
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder
|
|
||||||
|
|
||||||
V = lambda *x: Vector([float(i) for i in x])
|
|
||||||
|
|
||||||
|
|
||||||
def get_simple_2dcurve_data(coords, fillets=[], fillet_radius=[], closed=True, ifc_file=None):
|
|
||||||
"""
|
|
||||||
Creates simple 2D curve from set of 2d coords and list of points with fillets.
|
|
||||||
Simple curve means that all fillets are based on 90 degree angle.
|
|
||||||
|
|
||||||
> coords: list of 2d coords. Example: ((x0,y0), (x1,y1), (x2, y2))
|
|
||||||
> fillets: list of points from `coords` to base fillet on. Example: (1,)
|
|
||||||
> fillet_radius: list of fillet radius for each of corresponding point form `fillets`. Example: (5.,)
|
|
||||||
Note: filler_radius could be just 1 float value if it's the same for all fillets.
|
|
||||||
|
|
||||||
Optional arguments:
|
|
||||||
> closed: boolean whether curve should be closed (whether last point connected to first one). Default: True
|
|
||||||
> ifc_file: ifc file to create IfcIndexedPolyCurve for the function output
|
|
||||||
|
|
||||||
< returns (points, segments, ifc_curve) for the created simple curve
|
|
||||||
if both points in e are equally far from pt, then v1 is returned."""
|
|
||||||
|
|
||||||
# option to use same fillet radius for all fillets
|
|
||||||
if isinstance(fillet_radius, float):
|
|
||||||
fillet_radius = [fillet_radius] * len(fillets)
|
|
||||||
|
|
||||||
fillets = dict(zip(fillets, fillet_radius))
|
|
||||||
segments = []
|
|
||||||
points = []
|
|
||||||
for co_i, co in enumerate(coords, 0):
|
|
||||||
current_point = len(points)
|
|
||||||
if co_i in fillets:
|
|
||||||
r = fillets[co_i]
|
|
||||||
rsb = r * cos(pi / 4) # radius shift big
|
|
||||||
rss = r - rsb # radius shift small
|
|
||||||
|
|
||||||
next_co = coords[(co_i + 1) % len(coords)]
|
|
||||||
previous_co = coords[co_i - 1]
|
|
||||||
|
|
||||||
# identify fillet type (1 of 4 possible types)
|
|
||||||
x_direction = 1 if coords[co_i][0] < previous_co[0] or coords[co_i][0] < next_co[0] else -1
|
|
||||||
y_direction = 1 if coords[co_i][1] < previous_co[1] or coords[co_i][1] < next_co[1] else -1
|
|
||||||
|
|
||||||
xshift_point = (co[0] + r * x_direction, co[1])
|
|
||||||
middle_point = (co[0] + rss * x_direction, co[1] + rss * y_direction)
|
|
||||||
yshift_point = (co[0], co[1] + r * y_direction)
|
|
||||||
|
|
||||||
# identify fillet direction
|
|
||||||
if co[1] == previous_co[1]:
|
|
||||||
points.extend((xshift_point, middle_point, yshift_point))
|
|
||||||
else:
|
|
||||||
points.extend((yshift_point, middle_point, xshift_point))
|
|
||||||
|
|
||||||
segments.append([current_point - 1, current_point])
|
|
||||||
segments.append([current_point, current_point + 1, current_point + 2])
|
|
||||||
else:
|
|
||||||
points.append(co)
|
|
||||||
if co_i != 0:
|
|
||||||
segments.append([current_point - 1, current_point])
|
|
||||||
|
|
||||||
if closed:
|
|
||||||
segments.append([len(points) - 1, 0])
|
|
||||||
|
|
||||||
# replace negative index
|
|
||||||
if segments[0][0] == -1:
|
|
||||||
segments[0][0] = len(points) - 1
|
|
||||||
|
|
||||||
# sometime fillet points could match previous or next points in line
|
|
||||||
# I remove them at the end to avoid making fillet algorithm even less readable
|
|
||||||
points, segments = remove_redundant_points(points, segments)
|
|
||||||
ifc_curve = None
|
|
||||||
if ifc_file:
|
|
||||||
ifc_points = ifc_file.createIfcCartesianPointList2D(points)
|
|
||||||
ifc_segments = []
|
|
||||||
for segment in segments:
|
|
||||||
segment = [i + 1 for i in segment]
|
|
||||||
if len(segment) == 2:
|
|
||||||
ifc_segments.append(ifc_file.createIfcLineIndex(segment))
|
|
||||||
elif len(segment) == 3:
|
|
||||||
ifc_segments.append(ifc_file.createIfcArcIndex(segment))
|
|
||||||
|
|
||||||
ifc_curve = ifc_file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments)
|
|
||||||
return (points, segments, ifc_curve)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_redundant_points(points, segments):
|
|
||||||
# prevent mutating
|
|
||||||
points = [tuple(p) for p in points]
|
|
||||||
segments = segments.copy()
|
|
||||||
|
|
||||||
# find duplicate points, reindex them in segments
|
|
||||||
# and mark them to delete later
|
|
||||||
points_to_remove = []
|
|
||||||
prev_point = 0
|
|
||||||
for i, p in enumerate(points[1:], 1):
|
|
||||||
if p != points[prev_point]:
|
|
||||||
prev_point = i
|
|
||||||
continue
|
|
||||||
|
|
||||||
valid_segments = []
|
|
||||||
for s in segments:
|
|
||||||
s = [ps if ps != i else prev_point for ps in s]
|
|
||||||
valid_segments.append(s)
|
|
||||||
segments = valid_segments
|
|
||||||
points_to_remove.append(i)
|
|
||||||
|
|
||||||
# remove duplicate segments
|
|
||||||
valid_segments = [segment for segment in segments if len(set(segment)) != 1]
|
|
||||||
points = [point for i, point in enumerate(points) if i not in points_to_remove]
|
|
||||||
# correct the order in segments
|
|
||||||
unique_points = sorted(set(chain(*valid_segments)))
|
|
||||||
unique_points_translation = {prev: i for i, prev in enumerate(unique_points)}
|
|
||||||
valid_segments = [[unique_points_translation[p] for p in s] for s in valid_segments]
|
|
||||||
|
|
||||||
return points, valid_segments
|
|
||||||
|
|
||||||
|
|
||||||
def create_z_profile_lips_curve(
|
|
||||||
ifc_file, FirstFlangeWidth, SecondFlangeWidth, Depth, Girth, WallThickness, FilletRadius
|
|
||||||
):
|
|
||||||
x1 = FirstFlangeWidth
|
|
||||||
x2 = SecondFlangeWidth
|
|
||||||
y = Depth / 2
|
|
||||||
g = Girth
|
|
||||||
t = WallThickness
|
|
||||||
r = FilletRadius
|
|
||||||
|
|
||||||
coords = (
|
|
||||||
(-t / 2, y),
|
|
||||||
(x2, y),
|
|
||||||
(x2, y - g),
|
|
||||||
(x2 - t, y - g),
|
|
||||||
(x2 - t, y - t),
|
|
||||||
(t / 2, y - t),
|
|
||||||
(t / 2, -y),
|
|
||||||
(-x1, -y),
|
|
||||||
(-x1, -y + g),
|
|
||||||
(-x1 + t, -y + g),
|
|
||||||
(-x1 + t, -y + t),
|
|
||||||
(-t / 2, -y + t),
|
|
||||||
)
|
|
||||||
|
|
||||||
# no additional thickness in outer radius option
|
|
||||||
# points, segments, ifc_curve = create_curve_from_coords(coords, fillets = (0, 1, 4, 5, 6, 7, 10, 11), fillet_radius=r, closed=True, ifc_file=ifc_file)
|
|
||||||
points, segments, ifc_curve = get_simple_2dcurve_data(
|
|
||||||
coords,
|
|
||||||
fillets=(0, 1, 4, 5, 6, 7, 10, 11),
|
|
||||||
fillet_radius=(r + t, r + t, r, r, r + t, r + t, r, r),
|
|
||||||
closed=True,
|
|
||||||
ifc_file=ifc_file,
|
|
||||||
)
|
|
||||||
|
|
||||||
return ifc_curve
|
|
||||||
|
|
||||||
|
|
||||||
def create_transition_arc_ifc(width, height, ifc_file=None):
|
|
||||||
# create an arc in the rectangle with specified width and height
|
|
||||||
# if it's not possible to make a complete arc
|
|
||||||
# it will create arc with longest radius possible
|
|
||||||
# and straight segment in the middle
|
|
||||||
fillet_size = (width / 2) / height
|
|
||||||
if fillet_size <= 1:
|
|
||||||
fillet_radius = height * fillet_size
|
|
||||||
curve_coords = [
|
|
||||||
(0.0, 0.0),
|
|
||||||
(0.0, height),
|
|
||||||
(width * 0.5, height),
|
|
||||||
(width, height),
|
|
||||||
(width, 0.0),
|
|
||||||
]
|
|
||||||
fillets = (1, 3)
|
|
||||||
else:
|
|
||||||
fillet_radius = height
|
|
||||||
curve_coords = [
|
|
||||||
(0.0, 0.0),
|
|
||||||
(0.0, height),
|
|
||||||
(fillet_radius, height),
|
|
||||||
(width - fillet_radius, height),
|
|
||||||
(width, height),
|
|
||||||
(width, 0.0),
|
|
||||||
]
|
|
||||||
fillets = (1, 4)
|
|
||||||
points, segments, transition_arc = get_simple_2dcurve_data(
|
|
||||||
curve_coords, fillets, fillet_radius, closed=False, ifc_file=ifc_file
|
|
||||||
)
|
|
||||||
return points, segments, transition_arc
|
|
||||||
|
|
||||||
|
|
||||||
class LibraryGenerator:
|
class LibraryGenerator:
|
||||||
@@ -289,12 +102,12 @@ class LibraryGenerator:
|
|||||||
if position:
|
if position:
|
||||||
kwargs["position"] = position
|
kwargs["position"] = position
|
||||||
|
|
||||||
_, _, rectangle = get_simple_2dcurve_data(
|
_, _, rectangle = builder.get_simple_2dcurve_data(
|
||||||
coords=builder.get_rectangle_coords(**kwargs),
|
coords=builder.get_rectangle_coords(**kwargs),
|
||||||
fillets=(0, 1, 2, 3),
|
fillets=(0, 1, 2, 3),
|
||||||
fillet_radius=fillet_radius,
|
fillet_radius=fillet_radius,
|
||||||
closed=True,
|
closed=True,
|
||||||
ifc_file=self.file,
|
create_ifc_curve=True,
|
||||||
)
|
)
|
||||||
return rectangle
|
return rectangle
|
||||||
|
|
||||||
@@ -431,9 +244,9 @@ class LibraryGenerator:
|
|||||||
second_arc_depth = depth * (1 - 0.10)
|
second_arc_depth = depth * (1 - 0.10)
|
||||||
second_arc_width = width - (depth - second_arc_depth) * 2
|
second_arc_width = width - (depth - second_arc_depth) * 2
|
||||||
polyline = builder.polyline((V(0, 0), V(width, 0)))
|
polyline = builder.polyline((V(0, 0), V(width, 0)))
|
||||||
_, _, first_semicircle = create_transition_arc_ifc(width, depth, ifc_file=self.file)
|
_, _, first_semicircle = builder.create_transition_arc_ifc(width, depth, create_ifc_curve=True)
|
||||||
|
|
||||||
_, _, second_semicircle = create_transition_arc_ifc(second_arc_width, second_arc_depth, ifc_file=self.file)
|
_, _, second_semicircle = builder.create_transition_arc_ifc(second_arc_width, second_arc_depth, create_ifc_curve=True)
|
||||||
builder.translate(second_semicircle, V((width - second_arc_width) / 2, 0))
|
builder.translate(second_semicircle, V((width - second_arc_width) / 2, 0))
|
||||||
|
|
||||||
builder.translate([polyline, first_semicircle, second_semicircle], shift_to_center)
|
builder.translate([polyline, first_semicircle, second_semicircle], shift_to_center)
|
||||||
@@ -1105,8 +918,8 @@ class LibraryGenerator:
|
|||||||
fillets_radius = [fillets_radius * 5] * 2 + [fillets_radius] * 2
|
fillets_radius = [fillets_radius * 5] * 2 + [fillets_radius] * 2
|
||||||
else:
|
else:
|
||||||
fillets_radius = [fillets_radius] * 4
|
fillets_radius = [fillets_radius] * 4
|
||||||
_, _, fillet_rectangle = get_simple_2dcurve_data(
|
_, _, fillet_rectangle = builder.get_simple_2dcurve_data(
|
||||||
coords=coords, fillets=(0, 1, 2, 3), fillet_radius=fillets_radius, closed=True, ifc_file=self.file
|
coords=coords, fillets=(0, 1, 2, 3), fillet_radius=fillets_radius, closed=True, create_ifc_curve=True
|
||||||
)
|
)
|
||||||
|
|
||||||
circle_position = V(
|
circle_position = V(
|
||||||
@@ -1182,8 +995,8 @@ class LibraryGenerator:
|
|||||||
]
|
]
|
||||||
fillets = (0, 1, 6, 7)
|
fillets = (0, 1, 6, 7)
|
||||||
fillet_radius = (width / 2, width / 5, width / 5, width / 2)
|
fillet_radius = (width / 2, width / 5, width / 5, width / 2)
|
||||||
_, _, seat = get_simple_2dcurve_data(
|
_, _, seat = builder.get_simple_2dcurve_data(
|
||||||
coords, fillets, fillet_radius=fillet_radius, closed=True, ifc_file=self.file
|
coords, fillets, fillet_radius=fillet_radius, closed=True, create_ifc_curve=True
|
||||||
)
|
)
|
||||||
seat_first_part = ifcopenshell.util.element.copy_deep(self.file, seat)
|
seat_first_part = ifcopenshell.util.element.copy_deep(self.file, seat)
|
||||||
|
|
||||||
@@ -1251,8 +1064,8 @@ class LibraryGenerator:
|
|||||||
size=V(width, depth - cistern_depth), position=V(0, cistern_depth) + shift_to_center
|
size=V(width, depth - cistern_depth), position=V(0, cistern_depth) + shift_to_center
|
||||||
)
|
)
|
||||||
fillet_radius = min(width / 2, (depth - cistern_depth) / 2)
|
fillet_radius = min(width / 2, (depth - cistern_depth) / 2)
|
||||||
_, _, seat_main_curve = get_simple_2dcurve_data(
|
_, _, seat_main_curve = builder.get_simple_2dcurve_data(
|
||||||
seat_main_curve_points, fillets=(2, 3), fillet_radius=fillet_radius, closed=True, ifc_file=self.file
|
seat_main_curve_points, fillets=(2, 3), fillet_radius=fillet_radius, closed=True, create_ifc_curve=True
|
||||||
)
|
)
|
||||||
seat_main_curve_mask = builder.circle(center=V(0, depth - fillet_radius), radius=fillet_radius * 0.75)
|
seat_main_curve_mask = builder.circle(center=V(0, depth - fillet_radius), radius=fillet_radius * 0.75)
|
||||||
seat_main_curve_profile = builder.profile(seat_main_curve, inner_curves=seat_main_curve_mask)
|
seat_main_curve_profile = builder.profile(seat_main_curve, inner_curves=seat_main_curve_mask)
|
||||||
|
|||||||
@@ -24,119 +24,8 @@ import ifcopenshell.api
|
|||||||
import boltspy as bolts
|
import boltspy as bolts
|
||||||
from math import cos, pi
|
from math import cos, pi
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from mathutils import Vector
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
||||||
|
|
||||||
V = lambda *x: Vector([float(i) for i in x])
|
|
||||||
|
|
||||||
def create_simple_2dcurve(coords, fillets, fillet_radius, closed=True, ifc_file=None):
|
|
||||||
"""
|
|
||||||
Creates simple 2D curve from set of 2d coords and list of points with fillets.
|
|
||||||
Simple curve means that all fillets are based on 90 degree angle.
|
|
||||||
|
|
||||||
> coords: list of 2d coords. Example: ((x0,y0), (x1,y1), (x2, y2))
|
|
||||||
> fillets: list of points from `coords` to base fillet on. Example: (1,)
|
|
||||||
> fillet_radius: list of fillet radius for each of corresponding point form `fillets`. Example: (5.,)
|
|
||||||
Note: filler_radius could be just 1 float value if it's the same for all fillets.
|
|
||||||
|
|
||||||
Optional arguments:
|
|
||||||
> closed: boolean whether curve should be closed (whether last point connected to first one). Default: True
|
|
||||||
> ifc_file: ifc file to create IfcIndexedPolyCurve for the function output
|
|
||||||
|
|
||||||
< returns (points, segments, ifc_curve) for the created simple curve
|
|
||||||
if both points in e are equally far from pt, then v1 is returned."""
|
|
||||||
|
|
||||||
# option to use same fillet radius for all fillets
|
|
||||||
if isinstance(fillet_radius, float):
|
|
||||||
fillet_radius = [fillet_radius] * len(fillets)
|
|
||||||
|
|
||||||
fillets = dict(zip(fillets, fillet_radius))
|
|
||||||
segments = []
|
|
||||||
points = []
|
|
||||||
for co_i, co in enumerate(coords, 0):
|
|
||||||
current_point = len(points)
|
|
||||||
if co_i in fillets:
|
|
||||||
r = fillets[co_i]
|
|
||||||
rsb = r * cos(pi/4) # radius shift big
|
|
||||||
rss = r - rsb # radius shift small
|
|
||||||
|
|
||||||
next_co = coords[(co_i+1) % len(coords)]
|
|
||||||
previous_co = coords[co_i-1]
|
|
||||||
|
|
||||||
# identify fillet type (1 of 4 possible types)
|
|
||||||
x_direction = 1 if coords[co_i][0] < previous_co[0] or coords[co_i][0] < next_co[0] else -1
|
|
||||||
y_direction = 1 if coords[co_i][1] < previous_co[1] or coords[co_i][1] < next_co[1] else -1
|
|
||||||
|
|
||||||
xshift_point = (co[0] + r * x_direction, co[1])
|
|
||||||
middle_point = (co[0] + rss * x_direction, co[1] + rss * y_direction)
|
|
||||||
yshift_point = (co[0], co[1] + r * y_direction)
|
|
||||||
|
|
||||||
# identify fillet direction
|
|
||||||
if co[1] == previous_co[1]:
|
|
||||||
points.extend( (xshift_point, middle_point, yshift_point))
|
|
||||||
else:
|
|
||||||
points.extend( (yshift_point, middle_point, xshift_point))
|
|
||||||
|
|
||||||
segments.append( [current_point-1, current_point] )
|
|
||||||
segments.append( [current_point, current_point+1, current_point+2] )
|
|
||||||
else:
|
|
||||||
points.append( co )
|
|
||||||
if co_i != 0:
|
|
||||||
segments.append( [current_point-1, current_point] )
|
|
||||||
|
|
||||||
if closed:
|
|
||||||
segments.append( [0, len(points)-1] )
|
|
||||||
|
|
||||||
# replace negative index
|
|
||||||
if segments[0][0] == -1:
|
|
||||||
segments[0][0] = len(points) - 1
|
|
||||||
|
|
||||||
ifc_curve = None
|
|
||||||
if ifc_file:
|
|
||||||
ifc_points = ifc_file.createIfcCartesianPointList2D(points)
|
|
||||||
ifc_segments = []
|
|
||||||
for segment in segments:
|
|
||||||
segment = [i+1 for i in segment]
|
|
||||||
if len(segment) == 2:
|
|
||||||
ifc_segments.append( ifc_file.createIfcLineIndex( segment ))
|
|
||||||
elif len(segment) == 3:
|
|
||||||
ifc_segments.append( ifc_file.createIfcArcIndex( segment ))
|
|
||||||
|
|
||||||
ifc_curve = ifc_file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments)
|
|
||||||
|
|
||||||
return (points, segments, ifc_curve)
|
|
||||||
|
|
||||||
def create_z_profile_lips_curve(ifc_file, FirstFlangeWidth, SecondFlangeWidth, Depth, Girth, WallThickness, FilletRadius):
|
|
||||||
x1 = FirstFlangeWidth
|
|
||||||
x2 = SecondFlangeWidth
|
|
||||||
y = Depth / 2
|
|
||||||
g = Girth
|
|
||||||
t = WallThickness
|
|
||||||
r = FilletRadius
|
|
||||||
|
|
||||||
coords = (
|
|
||||||
(-t/2, y),
|
|
||||||
(x2, y),
|
|
||||||
(x2, y-g),
|
|
||||||
(x2-t, y-g),
|
|
||||||
(x2-t, y-t),
|
|
||||||
(t/2, y-t),
|
|
||||||
(t/2, -y),
|
|
||||||
(-x1, -y),
|
|
||||||
(-x1, -y+g),
|
|
||||||
(-x1+t, -y+g),
|
|
||||||
(-x1+t, -y+t),
|
|
||||||
(-t/2, -y+t)
|
|
||||||
)
|
|
||||||
|
|
||||||
# no additional thickness in outer radius option
|
|
||||||
# points, segments, ifc_curve = create_curve_from_coords(coords, fillets = (0, 1, 4, 5, 6, 7, 10, 11), fillet_radius=r, closed=True, ifc_file=ifc_file)
|
|
||||||
|
|
||||||
points, segments, ifc_curve = create_simple_2dcurve(coords,
|
|
||||||
fillets = (0, 1, 4, 5, 6, 7, 10, 11),
|
|
||||||
fillet_radius=(r+t, r+t, r, r, r+t, r+t, r, r),
|
|
||||||
closed=True, ifc_file=ifc_file)
|
|
||||||
|
|
||||||
return ifc_curve
|
|
||||||
|
|
||||||
class LibraryGenerator:
|
class LibraryGenerator:
|
||||||
def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"):
|
def generate(self, parse_profiles_type="EU", output_filename="IFC4 EU Steel.ifc"):
|
||||||
@@ -158,6 +47,7 @@ class LibraryGenerator:
|
|||||||
)
|
)
|
||||||
dim_exponents = self.file.createIfcDimensionalExponents(0, 0, 0, 0, 0, 0, 0)
|
dim_exponents = self.file.createIfcDimensionalExponents(0, 0, 0, 0, 0, 0, 0)
|
||||||
length_unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
|
length_unit = ifcopenshell.api.run("unit.add_si_unit", self.file, unit_type="LENGTHUNIT", prefix="MILLI")
|
||||||
|
builder = ShapeBuilder(self.file)
|
||||||
|
|
||||||
# define angle unit to use degrees for IfcPlaneAngleMeasure:
|
# define angle unit to use degrees for IfcPlaneAngleMeasure:
|
||||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcPlaneAngleMeasure.htm
|
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcPlaneAngleMeasure.htm
|
||||||
@@ -258,7 +148,7 @@ class LibraryGenerator:
|
|||||||
# by default bolts provides diameter, so we need to convert it to radius
|
# by default bolts provides diameter, so we need to convert it to radius
|
||||||
ifc_params["Radius"] /= 2
|
ifc_params["Radius"] /= 2
|
||||||
elif prof_type == "profile_z_lips":
|
elif prof_type == "profile_z_lips":
|
||||||
ifc_curve = create_z_profile_lips_curve(self.file, **ifc_params)
|
ifc_curve = builder.create_z_profile_lips_curve(**ifc_params)
|
||||||
ifc_params = {"OuterCurve": ifc_curve}
|
ifc_params = {"OuterCurve": ifc_curve}
|
||||||
elif prof_type == "profile_l*lbeam_2l":
|
elif prof_type == "profile_l*lbeam_2l":
|
||||||
profiles_gap = ifc_params["ProfilesGap"]
|
profiles_gap = ifc_params["ProfilesGap"]
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import ifcopenshell
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
from math import cos, sin, pi
|
from math import cos, sin, pi
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
V = lambda *x: Vector([float(i) for i in x])
|
V = lambda *x: Vector([float(i) for i in x])
|
||||||
sign = lambda x: x and (1, -1)[x < 0]
|
sign = lambda x: x and (1, -1)[x < 0]
|
||||||
@@ -635,3 +636,182 @@ class ShapeBuilder:
|
|||||||
elif polyline.is_a("IfcPolyline"):
|
elif polyline.is_a("IfcPolyline"):
|
||||||
for i, co in enumerate(coords):
|
for i, co in enumerate(coords):
|
||||||
polyline.Points[i].Coordinates = co
|
polyline.Points[i].Coordinates = co
|
||||||
|
|
||||||
|
def get_simple_2dcurve_data(self, coords, fillets=[], fillet_radius=[], closed=True, create_ifc_curve=None):
|
||||||
|
"""
|
||||||
|
Creates simple 2D curve from set of 2d coords and list of points with fillets.
|
||||||
|
Simple curve means that all fillets are based on 90 degree angle.
|
||||||
|
|
||||||
|
> coords: list of 2d coords. Example: ((x0,y0), (x1,y1), (x2, y2))
|
||||||
|
> fillets: list of points from `coords` to base fillet on. Example: (1,)
|
||||||
|
> fillet_radius: list of fillet radius for each of corresponding point form `fillets`. Example: (5.,)
|
||||||
|
Note: filler_radius could be just 1 float value if it's the same for all fillets.
|
||||||
|
|
||||||
|
Optional arguments:
|
||||||
|
> closed: boolean whether curve should be closed (whether last point connected to first one). Default: True
|
||||||
|
> create_ifc_curve: create IfcIndexedPolyCurve or just return the data. Default: False
|
||||||
|
|
||||||
|
< returns (points, segments, ifc_curve) for the created simple curve
|
||||||
|
if both points in e are equally far from pt, then v1 is returned."""
|
||||||
|
|
||||||
|
def remove_redundant_points(points, segments):
|
||||||
|
# prevent mutating
|
||||||
|
points = [tuple(p) for p in points]
|
||||||
|
segments = segments.copy()
|
||||||
|
|
||||||
|
# find duplicate points, reindex them in segments
|
||||||
|
# and mark them to delete later
|
||||||
|
points_to_remove = []
|
||||||
|
prev_point = 0
|
||||||
|
for i, p in enumerate(points[1:], 1):
|
||||||
|
if p != points[prev_point]:
|
||||||
|
prev_point = i
|
||||||
|
continue
|
||||||
|
|
||||||
|
valid_segments = []
|
||||||
|
for s in segments:
|
||||||
|
s = [ps if ps != i else prev_point for ps in s]
|
||||||
|
valid_segments.append(s)
|
||||||
|
segments = valid_segments
|
||||||
|
points_to_remove.append(i)
|
||||||
|
|
||||||
|
# remove duplicate segments
|
||||||
|
valid_segments = [segment for segment in segments if len(set(segment)) != 1]
|
||||||
|
points = [point for i, point in enumerate(points) if i not in points_to_remove]
|
||||||
|
# correct the order in segments
|
||||||
|
unique_points = sorted(set(chain(*valid_segments)))
|
||||||
|
unique_points_translation = {prev: i for i, prev in enumerate(unique_points)}
|
||||||
|
valid_segments = [[unique_points_translation[p] for p in s] for s in valid_segments]
|
||||||
|
|
||||||
|
return points, valid_segments
|
||||||
|
|
||||||
|
# option to use same fillet radius for all fillets
|
||||||
|
if isinstance(fillet_radius, float):
|
||||||
|
fillet_radius = [fillet_radius] * len(fillets)
|
||||||
|
|
||||||
|
fillets = dict(zip(fillets, fillet_radius))
|
||||||
|
segments = []
|
||||||
|
points = []
|
||||||
|
for co_i, co in enumerate(coords, 0):
|
||||||
|
current_point = len(points)
|
||||||
|
if co_i in fillets:
|
||||||
|
r = fillets[co_i]
|
||||||
|
rsb = r * cos(pi / 4) # radius shift big
|
||||||
|
rss = r - rsb # radius shift small
|
||||||
|
|
||||||
|
next_co = coords[(co_i + 1) % len(coords)]
|
||||||
|
previous_co = coords[co_i - 1]
|
||||||
|
|
||||||
|
# identify fillet type (1 of 4 possible types)
|
||||||
|
x_direction = 1 if coords[co_i][0] < previous_co[0] or coords[co_i][0] < next_co[0] else -1
|
||||||
|
y_direction = 1 if coords[co_i][1] < previous_co[1] or coords[co_i][1] < next_co[1] else -1
|
||||||
|
|
||||||
|
xshift_point = (co[0] + r * x_direction, co[1])
|
||||||
|
middle_point = (co[0] + rss * x_direction, co[1] + rss * y_direction)
|
||||||
|
yshift_point = (co[0], co[1] + r * y_direction)
|
||||||
|
|
||||||
|
# identify fillet direction
|
||||||
|
if co[1] == previous_co[1]:
|
||||||
|
points.extend((xshift_point, middle_point, yshift_point))
|
||||||
|
else:
|
||||||
|
points.extend((yshift_point, middle_point, xshift_point))
|
||||||
|
|
||||||
|
segments.append([current_point - 1, current_point])
|
||||||
|
segments.append([current_point, current_point + 1, current_point + 2])
|
||||||
|
else:
|
||||||
|
points.append(co)
|
||||||
|
if co_i != 0:
|
||||||
|
segments.append([current_point - 1, current_point])
|
||||||
|
|
||||||
|
if closed:
|
||||||
|
segments.append([len(points) - 1, 0])
|
||||||
|
|
||||||
|
# replace negative index
|
||||||
|
if segments[0][0] == -1:
|
||||||
|
segments[0][0] = len(points) - 1
|
||||||
|
|
||||||
|
# sometime fillet points could match previous or next points in line
|
||||||
|
# I remove them at the end to avoid making fillet algorithm even less readable
|
||||||
|
points, segments = remove_redundant_points(points, segments)
|
||||||
|
ifc_curve = None
|
||||||
|
if create_ifc_curve:
|
||||||
|
ifc_points = self.file.createIfcCartesianPointList2D(points)
|
||||||
|
ifc_segments = []
|
||||||
|
for segment in segments:
|
||||||
|
segment = [i + 1 for i in segment]
|
||||||
|
if len(segment) == 2:
|
||||||
|
ifc_segments.append(self.file.createIfcLineIndex(segment))
|
||||||
|
elif len(segment) == 3:
|
||||||
|
ifc_segments.append(self.file.createIfcArcIndex(segment))
|
||||||
|
|
||||||
|
ifc_curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments)
|
||||||
|
return (points, segments, ifc_curve)
|
||||||
|
|
||||||
|
def create_z_profile_lips_curve(self, FirstFlangeWidth, SecondFlangeWidth, Depth, Girth, WallThickness, FilletRadius):
|
||||||
|
x1 = FirstFlangeWidth
|
||||||
|
x2 = SecondFlangeWidth
|
||||||
|
y = Depth / 2
|
||||||
|
g = Girth
|
||||||
|
t = WallThickness
|
||||||
|
r = FilletRadius
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
coords = (
|
||||||
|
(-t/2, y),
|
||||||
|
(x2, y),
|
||||||
|
(x2, y-g),
|
||||||
|
(x2-t, y-g),
|
||||||
|
(x2-t, y-t),
|
||||||
|
(t/2, y-t),
|
||||||
|
(t/2, -y),
|
||||||
|
(-x1, -y),
|
||||||
|
(-x1, -y+g),
|
||||||
|
(-x1+t, -y+g),
|
||||||
|
(-x1+t, -y+t),
|
||||||
|
(-t/2, -y+t)
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
# option for no additional thickness in outer radius:
|
||||||
|
# points, segments, ifc_curve = create_curve_from_coords(
|
||||||
|
# coords, fillets = (0, 1, 4, 5, 6, 7, 10, 11), fillet_radius=r, closed=True, ifc_file=ifc_file
|
||||||
|
# )
|
||||||
|
|
||||||
|
points, segments, ifc_curve = self.get_simple_2dcurve_data(coords,
|
||||||
|
fillets = (0, 1, 4, 5, 6, 7, 10, 11),
|
||||||
|
fillet_radius=(r+t, r+t, r, r, r+t, r+t, r, r),
|
||||||
|
closed=True, create_ifc_curve=True)
|
||||||
|
|
||||||
|
return ifc_curve
|
||||||
|
|
||||||
|
def create_transition_arc_ifc(self, width, height, create_ifc_curve=False):
|
||||||
|
# create an arc in the rectangle with specified width and height
|
||||||
|
# if it's not possible to make a complete arc
|
||||||
|
# it will create arc with longest radius possible
|
||||||
|
# and straight segment in the middle
|
||||||
|
fillet_size = (width / 2) / height
|
||||||
|
if fillet_size <= 1:
|
||||||
|
fillet_radius = height * fillet_size
|
||||||
|
curve_coords = [
|
||||||
|
(0.0, 0.0),
|
||||||
|
(0.0, height),
|
||||||
|
(width * 0.5, height),
|
||||||
|
(width, height),
|
||||||
|
(width, 0.0),
|
||||||
|
]
|
||||||
|
fillets = (1, 3)
|
||||||
|
else:
|
||||||
|
fillet_radius = height
|
||||||
|
curve_coords = [
|
||||||
|
(0.0, 0.0),
|
||||||
|
(0.0, height),
|
||||||
|
(fillet_radius, height),
|
||||||
|
(width - fillet_radius, height),
|
||||||
|
(width, height),
|
||||||
|
(width, 0.0),
|
||||||
|
]
|
||||||
|
fillets = (1, 4)
|
||||||
|
points, segments, transition_arc = self.get_simple_2dcurve_data(
|
||||||
|
curve_coords, fillets, fillet_radius, closed=False, create_ifc_curve=create_ifc_curve
|
||||||
|
)
|
||||||
|
return points, segments, transition_arc
|
||||||
Reference in New Issue
Block a user