Closed RickBrice closed 5 months ago
If duplicate points can't be used, how can this type of beam be modeled?
I don't know. I think a key sentence from the documentation is this:
by matching labels using IfcIndexedPolyCurve with IfcCartesianPointList2D.TagList.
But this is unfortunately not very clearly explained. Maybe @SergejMuhic or @peterrdf have some ideas?
Note that specifically regarding duplicate points in loops there is an implementation agreement that forbids that. But maybe it needs to be refined with 4X3? Strictly speaking it doesn't concern IfcIndexedPolyCurve, but only because it didn't exist at the time of writing.
No duplicated points within a polyloop or polyline
I was indeed a bit lazy. I've added a warning message about this.
[Error] [2024-04-03 11:38:49] Mismatching number of edges for face boundary: 16 vs 18
Another thing to reword in the docs is that this is not only the number of edges. Also the number of bounds in a ArbitraryProfileDefWithVoids. (in our case it's already implemented like that).
IfcSectionedSolidHorizontal
was not introduced to fix all problems. It has clear restrictions, such as:
Profiles with varying number of points are not supported.
IfcIndexedPolyCurve
also has its restriction in duplication of points:
Is there something coming in 4.4 with the guide curves?
Sent from a mobile device, excuse my brevity. Kind regards, Thomas
Op wo 3 apr 2024 16:54 schreef SergejMuhic @.***>:
IfcSectionedSolidHorizontal was not introduced to fix all problems. It has clear restrictions, such as: image.png (view on web) https://github.com/IfcOpenShell/IfcOpenShell/assets/16683597/4a8217e7-0d24-445e-8505-9eacdf7f877e
Profiles with varying number of points are not supported.
— Reply to this email directly, view it on GitHub https://github.com/IfcOpenShell/IfcOpenShell/issues/4501#issuecomment-2034846765, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAILWV23YE7SRA2KNAWKCNLY3QJ4BAVCNFSM6AAAAABFTW5TTSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMZUHA2DMNZWGU . You are receiving this because you commented.Message ID: @.***>
It is very common in precast concrete bridge girders to have the webs thickened at the support points to handle shear forces and post-tensioning hardware.
I was able to re-work my program so the points around the perimeter of the section are the same in number and unique. I am now able to use IfcConvert to actually get something that can be displayed in Blender.
Some other viewers that I tried were able to handle the duplicate points and different number of points in each section. Not sure if this is something we want to attempt in the IFCOS geometry mapping layer. Maybe it is an OCCT limitation.
You mean something like this? Where the two vertices on the back profile need to be spaced a little bit creating a ruled surface as opposed to a triangular face?
I think we could experiment with bypassing the inability to have duplicate points. I actually thought there was an elimination of duplicate points in (ifcopenshell::geometry::polygon_from_points
), but that does not seem to be the case, probably later in the occt code then. Then your error indeed comes from opencascade where at some point it's trying to fit an edge through two coincident points. Maybe you can try with the following patch. We just retain the unconverted loop a bit longer and iterate over that, rather than iterate over the occt wire. Topologically something would still fail with the cap I think and maybe some intermediate sections where we interpolate very close to the cap.
diff --git a/src/ifcgeom/kernels/opencascade/loft.cpp b/src/ifcgeom/kernels/opencascade/loft.cpp
index 4ed9b59f4..fe4048a48 100644
--- a/src/ifcgeom/kernels/opencascade/loft.cpp
+++ b/src/ifcgeom/kernels/opencascade/loft.cpp
@@ -50,7 +50,7 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re
auto jt = it + 1;
std::array<taxonomy::face::ptr, 2> fa = { *it, *jt };
std::array<TopoDS_Shape, 2> shps;
- std::array<TopoDS_Wire, 2> ws;
+ std::array<taxonomy::loop::ptr, 2> ws;
for (int i = 0; i < 2; ++i) {
if (!convert(fa[i], shps[i])) {
return false;
@@ -59,7 +59,8 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re
return false;
}
// @todo this is only outer wire
- ws[i] = BRepTools::OuterWire(TopoDS::Face(shps[i]));
+ // ws[i] = BRepTools::OuterWire(TopoDS::Face(shps[i]));
+ ws[i] = fa[i]->children[0];
}
if (it == loft->children.begin()) {
// faces.Append(shps[0]);
@@ -69,6 +70,7 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re
// faces.Append(shps[1]);
BB.Add(comp, shps[1]);
}
+ /*
BRepTools_WireExplorer a(ws[0]);
BRepTools_WireExplorer b(ws[1]);
for (; a.More() && b.More(); a.Next(), b.Next()) {
@@ -80,10 +82,15 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re
TopoDS_Vertex e1a, e1b, e3a, e3b;
TopExp::Vertices(e1, e1a, e1b, true);
TopExp::Vertices(e3, e3a, e3b, true);
+ */
+
+ auto a = ws[0]->children.begin();
+ auto b = ws[1]->children.begin();
+ for (; a != ws[0]->children.end() && b != ws[1]->children.end(); ++a, ++b) {
+ /*
auto e2 = BRepBuilderAPI_MakeEdge(e1b, e3a).Edge();
auto e4 = BRepBuilderAPI_MakeEdge(e3b, e1a).Edge();
- /*
BRepFill_Filling fill;
fill.Add(e1, GeomAbs_C0);
fill.Add(e2, GeomAbs_C0);
@@ -94,10 +101,28 @@ bool OpenCascadeKernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape& re
BB.Add(comp, fill.Face());
*/
- auto f = BRepBuilderAPI_MakeFace(BRepBuilderAPI_MakePolygon(e1a, e1b, e3b, true).Wire()).Face();
- BB.Add(comp, f);
- auto g = BRepBuilderAPI_MakeFace(BRepBuilderAPI_MakePolygon(e3b, e3a, e1a, true).Wire()).Face();
- BB.Add(comp, g);
+ auto e1al = *boost::get<taxonomy::point3::ptr>((**a).start);
+ auto e1bl = *boost::get<taxonomy::point3::ptr>((**a).end);
+ auto e3al = *boost::get<taxonomy::point3::ptr>((**b).start);
+ auto e3bl = *boost::get<taxonomy::point3::ptr>((**b).end);
+
+ auto e1a = convert_xyz2<gp_Pnt>((fa[0]->matrix->ccomponents() * e1al.ccomponents().homogeneous()).eval());
+ auto e1b = convert_xyz2<gp_Pnt>((fa[0]->matrix->ccomponents() * e1bl.ccomponents().homogeneous()).eval());
+ auto e3a = convert_xyz2<gp_Pnt>((fa[1]->matrix->ccomponents() * e3al.ccomponents().homogeneous()).eval());
+ auto e3b = convert_xyz2<gp_Pnt>((fa[1]->matrix->ccomponents() * e3bl.ccomponents().homogeneous()).eval());
+
+ try {
+ auto f = BRepBuilderAPI_MakeFace(BRepBuilderAPI_MakePolygon(e1a, e1b, e3b, true).Wire()).Face();
+ BB.Add(comp, f);
+ } catch (const Standard_Failure&) {
+ // coincident points in profile cause degenerate triangles
+ }
+ try {
+ auto f = BRepBuilderAPI_MakeFace(BRepBuilderAPI_MakePolygon(e3b, e3a, e1a, true).Wire()).Face();
+ BB.Add(comp, f);
+ } catch (const Standard_Failure&) {
+ // coincident points in profile cause degenerate triangles
+ }
}
}
edit: updated diff to account for face transformation
Something like that. I'll take a look at the patch. Here is a photo of a girder with a thickened in real life- I think it's easier to see than my rendering.
Hi, I have met a similar problem. When I modeled the curved ramp, using IfcSectionedSolidHorizontal. It is unable to display the whole road in blender, as shown in the following figures. The entire road consists of four sections. I also tried modeling based on only part of the alignment, but there were still missing parts. test.ifc.txt
I'm not 100% sure, but I think it is because the length of the IfcCurveSegment making up the IfcGradientCurve is only about 122 m in length and the gradient curve is the basis for the IfcSectionedSolidHorizontal. The solid can't be generated in regions where the curve is not defined.
Correct, at least theoretical it is possible that the start and end of the vertical alignment definition is not in line with the length of the horizontal alignment. I see that even in toolboxes that support these cases the implementation is different, one is defining the vertical value to be constant and eaual to the start before the starting point in vertical alignment definition, the same at the end, another toolbox is using the start / end gradient. I think it should be more clearly be defined how to handle these cases as they are for sure not uncommon in real-life data.
Thank you for your suggestions. I attempted to increase the length of the IfcCurveSegment that constitutes the IfcGradientCurve, and it worked, as shown below. However, I observed some unexpected changes in the elevations of the road surface. Maybe I used the same cross section? Which resulted in unexpected changes between the curved and straight sections? test-2.ifc.txt
Using this silly little jupyter notebook, I plot the composite and gradient curves
import ifcopenshell
import ifcopenshell.geom as geom
import numpy as np
import matplotlib.pyplot as plt
def euclidean_distance(p1, p2):
return np.sqrt(np.sum((p1 - p2) ** 2))
model = ifcopenshell.open("test-2.ifc")
curves = model.by_type("IfcGradientCurve",False) + model.by_type("IfcSegmentedReferenceCurve",False)
settings = geom.settings()
fig1 = plt.figure().add_subplot()
fig2 = plt.figure().add_subplot()
fig3 = plt.figure(figsize=(8,8)).add_subplot(projection="3d")
for curve in curves:
shape = geom.create_shape(settings,curve)
verts = shape.verts
verts = np.array(verts).reshape((-1,3))
x,y,z = verts.T
fig1.plot(x,y)
# want to plot (dist along, elevation) so need to compute
# dist along from x,y. the following is probably bad code,
# I just used Microsoft Copilot to generate it
# Convert to Numpy arrays
points = np.array(list(zip(x, y)))
# Calculate pairwise distances using the Euclidean distance formula
distances = []
distances.append(0.0)
for i in range(len(points)-1):
dist = euclidean_distance(points[i], points[i+1])
distances.append(dist + distances[i])
fig2.plot(distances,z)
markerline, stemline, baseline = fig3.stem(x,y,z)
plt.setp(stemline,linewidth=0.01)
plt.setp(stemline,linestyles="dashed")
plt.setp(markerline,markersize=1)
fig1.set_title("IfcCompositeCurve")
fig1.set_aspect('equal',adjustable='box')
fig1.set_xlabel("X (East)")
fig1.set_ylabel("Y (North)")
fig1.grid(True)
fig2.set_title("IfcGradientCurve")
fig2.set_xlabel("S (Distance along IfcCompositeCurve)")
fig2.set_ylabel("Z (Elevation)")
fig2.set_box_aspect(0.25)
fig2.grid(True)
fig3.set_xlabel("X (East)")
fig3.set_ylabel("Y (North)")
fig3.set_zlabel("Z (Elevation)")
fig3.grid(True)
fig3.set_box_aspect([1,1,1],zoom=0.8)
plt.show()
The results kind of make sense. The end point elevation of #96 is approximately
96= IFCCURVESEGMENT(.CONTSAMEGRADIENTSAMECURVATURE.,#99,IFCLENGTHMEASURE(0.0),IFCLENGTHMEASURE(261.6051511047944),#100);
97= IFCCARTESIANPOINT((95.48996,35.98126));
98= IFCDIRECTION((0.99945934,0.03287895));
35.98 + 261.6*0.03288 = 44.58. This is also the start point of #2101 which is at elevation 38.33 so a jump is expected. These values agree with the plots
2101= IFCCURVESEGMENT(.CONTSAMEGRADIENTSAMECURVATURE.,#2104,IFCLENGTHMEASURE(0.0),IFCLENGTHMEASURE(-5.7960988213077),#2105);
2102= IFCCARTESIANPOINT((167.0564,38.33556));
2103= IFCDIRECTION((0.99945934,0.03287895));
2104= IFCAXIS2PLACEMENT2D(#2102,#2103);
I followed your suggestions and checked the elevation of #89, #97, #2102, and #2202. However, the exported Blender model did not change. Additionally, I tried running your code using IfcOpenShell v0.7, but it did not work. I think this might be the version of IfcOpenShell, so I attempted to install IfcOpenShell-0.8.0.
See the instructions here https://docs.ifcopenshell.org/ifcopenshell-python/installation.html#pre-built-packages with the links here https://github.com/IfcOpenBot/IfcOpenShell/commit/2a5e42e359aea9aef7b2832f5779a20d35b8e503#comments
You only need to unzip into the right spot, no need to build anything yourself.
Note that there are still quite a few fixes happening on v0.8.0 which are possibly not relevant to your tests.
I run the code under Ifcopenshell 0.8 and adjust the value of the IfcGradientCurve, as shown in following figures. However, there are still unexpected changes in the elevations of the road surface. test-4.ifc.txt
I'm using IfcOpenShell v0.8.0 compiled from source. Can't seem to get it to work with Blender, however I use IfcConvert to generate a DAE file. The surface looks relatively smooth. Perhaps you have an older build of v0.8 - there were a lot of growing pains getting alignment based geometry to work.
Thank you for the suggestion. I discovered that I had been using an older version of IfcConvert. After updating to the latest version, I am able to achieve a smooth surface.
Happy to see this addressed. @tutuXi1219 IFC4.3 and the v0.8 branch are still relatively recent efforts, hope you stay involved in the project.
On the v0.8.0 branch, I'm having some trouble with IfcSectionedSolidHorizontal.
I have two files (attached) of the same bridge. The first and last segment of each girder is a cross section that transitions over a distance.
The file named 26122_Mismatch_Polygon_Points.ifc models the cross sections with a different number of points. I think this is generally incorrect. All the sections should have the same number of points, but IfcConvert doesn't specifically indicate this as an error.
The file named 26122_Duplicate_Polygon_Points.ifc has the same cross sections but this time defined with the same number of points. Some of the points within a single cross section are duplicated. This results in a different error from IfcConvert
The attached Excel workbook plots the two different beam cross-sections.
1) Where to add an error message so IfcConvert notifies that the polygons have different number of points? 2) Why doesn't the duplicate point method work? 3) If duplicate points can't be used, how can this type of beam be modeled?
GirderSections.xlsx 26122_Duplicate_Polygon_Points.ifc.txt 26122_Mismatch_Polygon_Points.ifc.txt