Skip to content

Commit be64ced

Browse files
authored
Merge pull request #1767 from Creoox/develop
Improve geometry debug dump to three.js viewer, plus some small checks in geometry processing
2 parents c9292ec + e8bb7b4 commit be64ced

File tree

7 files changed

+263
-386
lines changed

7 files changed

+263
-386
lines changed

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cpp/test/dumpToThree.h

Lines changed: 121 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include <fstream>
78
#include <vector>
89
#include <string>
910
#include <sstream>
@@ -25,7 +26,7 @@ namespace webifc::dump
2526
<head>
2627
<meta charset="UTF-8">
2728
<meta name="viewport" content="width=device-width, initial-scale=1.0">
28-
<title>DumpCurveToHtml</title>
29+
<title>IFC Geometry Viewer</title>
2930
<style>
3031
body { margin: 0; overflow: hidden; }
3132
canvas { display: block; }
@@ -47,7 +48,7 @@ namespace webifc::dump
4748
const controls = new THREE.OrbitControls(camera, renderer.domElement);
4849
controls.enableDamping = true;
4950
controls.dampingFactor = 0.05;
50-
controls.screenSpacePanning = true; // Allow panning in XY plane
51+
controls.screenSpacePanning = true;
5152
controls.minDistance = 1;
5253
controls.maxDistance = 500;
5354
controls.mouseButtons = {
@@ -57,21 +58,34 @@ namespace webifc::dump
5758
};
5859
controls.enablePan = true;
5960
controls.panSpeed = 0.5;
60-
controls.up = new THREE.Vector3(0, 0, 1); // Ensure Z is up for rotations
61-
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
61+
controls.up = new THREE.Vector3(0, 0, 1);
62+
63+
// Lighting setup
64+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4); // Soft ambient light
6265
scene.add(ambientLight);
63-
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
64-
directionalLight.position.set(1, 1, 1);
65-
scene.add(directionalLight);
66+
const directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.6); // Primary light
67+
directionalLight1.position.set(1, 1, 1).normalize();
68+
scene.add(directionalLight1);
69+
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.4); // Secondary light to reduce shadows
70+
directionalLight2.position.set(-1, -1, 1).normalize();
71+
scene.add(directionalLight2);
6672
6773
const objData = "{}";
68-
const blackMaterial = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2 });
69-
const grayMaterial = new THREE.LineBasicMaterial({ color: 0x888888, linewidth: 2 });
74+
const meshMaterial = new THREE.MeshStandardMaterial({
75+
color: 0x888888,
76+
roughness: 0.7,
77+
metalness: 0.1,
78+
side: THREE.DoubleSide ,
79+
opacity: 0.7
80+
});
81+
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2 });
82+
const edgeMaterial = new THREE.LineBasicMaterial({ color: 0x333333, linewidth: 2 });
7083
71-
// Parse OBJ data manually to create individual line segments
84+
// Parse OBJ data for vertices, lines, and faces
7285
function parseOBJ(objText) {
7386
const vertices = [];
7487
const lines = [];
88+
const faces = [];
7589
const linesArray = objText.split('\n');
7690
linesArray.forEach(line => {
7791
line = line.trim();
@@ -80,37 +94,70 @@ namespace webifc::dump
8094
vertices.push(new THREE.Vector3(parts[0], parts[1], parts[2]));
8195
} else if (line.startsWith('l ')) {
8296
const indices = line.split(/\s+/).slice(1).map(i => parseInt(i) - 1);
83-
lines.push(indices);
97+
if (indices.length >= 2) {
98+
lines.push(indices);
99+
}
100+
} else if (line.startsWith('f ')) {
101+
const indices = line.split(/\s+/).slice(1).map(part => parseInt(part.split('/')[0]) - 1);
102+
if (indices.length >= 3) {
103+
faces.push(indices.slice(0, 3)); // Only triangles
104+
}
84105
}
85106
});
86-
return { vertices, lines };
107+
return { vertices, lines, faces };
87108
}
88109
89-
// Create individual Line objects for each segment
90-
const { vertices, lines } = parseOBJ(objData);
110+
// Create mesh if faces exist
111+
const { vertices, lines, faces } = parseOBJ(objData);
112+
if (faces.length > 0) {
113+
const geometry = new THREE.BufferGeometry();
114+
const positions = new Float32Array(vertices.length * 3);
115+
const indices = new Uint32Array(faces.length * 3);
116+
vertices.forEach((v, i) => {
117+
positions[i * 3] = v.x;
118+
positions[i * 3 + 1] = v.y;
119+
positions[i * 3 + 2] = v.z;
120+
});
121+
faces.forEach((f, i) => {
122+
indices[i * 3] = f[0];
123+
indices[i * 3 + 1] = f[1];
124+
indices[i * 3 + 2] = f[2];
125+
});
126+
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
127+
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
128+
geometry.computeVertexNormals();
129+
const mesh = new THREE.Mesh(geometry, meshMaterial);
130+
scene.add(mesh);
131+
132+
// Add wireframe lines for triangle edges
133+
const edges = new THREE.EdgesGeometry(geometry, 1); // Threshold angle of 1 degree
134+
const edgeLines = new THREE.LineSegments(edges, edgeMaterial);
135+
scene.add(edgeLines);
136+
}
137+
138+
// Create lines if lines exist
91139
lines.forEach((lineIndices, index) => {
92140
const geometry = new THREE.BufferGeometry().setFromPoints([
93141
vertices[lineIndices[0]],
94142
vertices[lineIndices[1]]
95143
]);
96-
const material = (index % 2 === 0) ? blackMaterial : grayMaterial;
97-
const line = new THREE.Line(geometry, material);
144+
const line = new THREE.Line(geometry, lineMaterial);
98145
scene.add(line);
99146
});
100147
101-
// Compute bounding box from vertices
148+
// Compute bounding box
102149
const box = new THREE.Box3();
103150
vertices.forEach(vertex => box.expandByPoint(vertex));
104151
const minPoint = box.min;
105152
const center = box.getCenter(new THREE.Vector3());
106153
107-
// Add XY grid (10x10 lines, 10m spacing, 100x100m total) in XY plane at Z=0, positioned at minPoint
154+
// Add XY grid (100x100m, 10m spacing) at Z=min
108155
const grid = new THREE.GridHelper(100, 10, 0x888888, 0x888888);
109-
grid.rotation.x = -Math.PI / 2; // Rotate grid to lie in XY plane with Z up
110-
grid.position.set(minPoint.x, minPoint.y, minPoint.z); // Position at min point
156+
grid.rotation.x = -Math.PI / 2;
157+
grid.position.set(minPoint.x, minPoint.y, minPoint.z);
111158
scene.add(grid);
112159
113-
// Add coordinate axes (X: red, Y: green, Z: blue, 200m length), positioned at minPoint
160+
// Add coordinate axes (X: red, Y: green, Z: blue, 200m length)
114161
const axesGroup = new THREE.Group();
115162
axesGroup.position.set(minPoint.x, minPoint.y, minPoint.z);
116163
const axesMaterialX = new THREE.LineBasicMaterial({ color: 0xff0000 });
@@ -133,13 +180,24 @@ namespace webifc::dump
133180
axesGroup.add(zAxis);
134181
scene.add(axesGroup);
135182
136-
// Zoom to the bounding box
183+
// Zoom to bounding box
137184
const size = box.getSize(new THREE.Vector3());
138-
const maxDim = Math.max(size.x, size.y, size.z);
185+
let maxDim = Math.max(size.x, size.y, size.z);
186+
if (maxDim < 0.0001) {
187+
maxDim = 1; // Prevent division by zero
188+
}
189+
const aspect = window.innerWidth / window.innerHeight;
139190
const fov = camera.fov * (Math.PI / 180);
140-
let cameraDistance = maxDim / Math.tan(fov / 2);
191+
const fovHorizontal = 2 * Math.atan(Math.tan(fov / 2) * aspect);
192+
const maxDimHorizontal = Math.max(size.x, size.y);
193+
let cameraDistance = Math.max(
194+
maxDim / Math.tan(fov / 2),
195+
maxDimHorizontal / Math.tan(fovHorizontal / 2)
196+
);
141197
cameraDistance *= 1.1; // Padding
142-
camera.position.set(center.x, center.y, center.z + cameraDistance);
198+
cameraDistance = Math.max(cameraDistance, camera.near * 1.1);
199+
const offset = cameraDistance / Math.sqrt(3);
200+
camera.position.set(center.x + offset, center.y + offset, center.z + offset);
143201
camera.lookAt(center);
144202
controls.target.copy(center);
145203
@@ -464,7 +522,7 @@ namespace webifc::dump
464522
return obj.str();
465523
}
466524

467-
inline std::string ToObjThree(const webifc::geometry::IfcGeometry& geom, size_t& offset, glm::dmat4 transform, double inputScale = 1.0)
525+
inline std::string ToObjThree(const webifc::geometry::IfcGeometry& geom, size_t& offset, const glm::dmat4& transform, double inputScale = 1.0)
468526
{
469527
std::stringstream obj;
470528
double scale = inputScale;
@@ -521,7 +579,8 @@ namespace webifc::dump
521579
{
522580
size_t offset = 0;
523581
std::ofstream out(path.c_str());
524-
out << ToObjThree(geom, offset, glm::dmat4(1), inputScale);
582+
glm::dmat4 mat(1);
583+
out << ToObjThree(geom, offset, mat, inputScale);
525584
}
526585

527586
inline std::string ToObjThree(webifc::geometry::IfcGeometry& geom, size_t& offset, glm::dmat4 transform = glm::dmat4(1))
@@ -548,17 +607,49 @@ namespace webifc::dump
548607
std::string complete;
549608
for (auto& geom : mesh.geometries)
550609
{
551-
auto flatGeom = processor.GetGeometry(geom.geometryExpressID);
610+
webifc::geometry::IfcGeometry flatGeom = processor.GetGeometry(geom.geometryExpressID);
552611
glm::dmat4 trans = mat * geom.transformation;
553612
complete += ToObjThree(flatGeom, offset, trans);
554613
}
555614
return complete;
556615
}
557616

558-
inline void DumpIfcGeometryThree(webifc::geometry::IfcGeometry& geom, std::string filename)
617+
// Modified makeThreeJSViewer for IfcGeometry (triangle mesh)
618+
inline std::string makeThreeJSViewer(const std::vector<webifc::geometry::IfcGeometry>& vecGeom, double inputScale = 1.0)
559619
{
620+
std::stringstream obj;
560621
size_t offset = 0;
561-
writeFile(filename, ToObjThree(geom, offset));
622+
glm::dmat4 mat(1);
623+
for (const webifc::geometry::IfcGeometry& geom : vecGeom)
624+
{
625+
obj << ToObjThree(geom, offset, mat, inputScale);
626+
}
627+
628+
// Escape OBJ data for embedding
629+
std::string objData = obj.str();
630+
std::string escapedObjData;
631+
for (char c : objData)
632+
{
633+
if (c == '\n') escapedObjData += "\\n";
634+
else if (c == '"') escapedObjData += "\\\"";
635+
else escapedObjData += c;
636+
}
637+
638+
// Embed in HTML template
639+
std::string html = THREE_JS_VIEWER_TEMPLATE;
640+
size_t pos = html.find(R"({})");
641+
if (pos != std::string::npos)
642+
{
643+
html.replace(pos, 2, escapedObjData);
644+
}
645+
646+
return html;
647+
}
648+
649+
inline void DumpIfcGeometryThree(std::vector<webifc::geometry::IfcGeometry> vecGeom, std::string filename, double epsNormal = 1e-8, double epsDist = 1e-6, double inputScale = 1.0)
650+
{
651+
std::string viewer = makeThreeJSViewer(vecGeom, inputScale);
652+
writeFile(filename, viewer);
562653
}
563654

564655
inline void DumpFlatMeshThree(webifc::geometry::IfcFlatMesh& mesh, webifc::geometry::IfcGeometryProcessor& processor, std::string filename)

src/cpp/web-ifc/geometry/IfcGeometryLoader.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,17 @@ namespace webifc::geometry
193193
double length = glm::distance(point1, previousPoint);
194194
sumLength += length;
195195

196+
if (sumLength > maxDistance)
197+
{
198+
break;
199+
}
200+
196201
if (sumLength >= minDistance)
197202
{
198203
glm::dmat4 result = BasisCurve.getPlacementAtDistance(sumLength, IfcCurve::CurvePlacementMode::TangentAsZAxis);
199204
mapPlacements.insert({ sumLength, result });
200205
}
201-
202-
if (sumLength > maxDistance)
203-
{
204-
break;
205-
}
206+
206207
previousPoint = point1;
207208
}
208209

@@ -2204,7 +2205,8 @@ namespace webifc::geometry
22042205
}
22052206
}
22062207
curve.arcSegments.push_back(curve.points.size());
2207-
const int numPointsCurrentArc = _circleSegments;
2208+
int numPointsCurrentArc = _circleSegments;
2209+
numPointsCurrentArc = std::max(numPointsCurrentArc, 4);
22082210
double deltaAngle = openingAngleRad / (numPointsCurrentArc - 1);
22092211
double angle = startRad;
22102212
std::vector<glm::dvec3> points;
@@ -2328,8 +2330,6 @@ namespace webifc::geometry
23282330
}
23292331
break;
23302332
}
2331-
2332-
23332333
case schema::IFCGRADIENTCURVE:
23342334
{
23352335
// IfcGradientCurve -----------------------------------------------------------
@@ -3488,6 +3488,7 @@ namespace webifc::geometry
34883488
for (uint32_t i = 0; i < profile.curve.points.size(); i++)
34893489
{
34903490
profile.curve.points[i] = transformation * glm::dvec3(profile.curve.points[i].x, profile.curve.points[i].y, 1);
3491+
profile.curve.points[i].z = 0;
34913492
}
34923493
}
34933494
else

src/cpp/web-ifc/geometry/IfcGeometryProcessor.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#if defined(DEBUG_DUMP_SVG) || defined(DUMP_CSG_MESHES)
88
#include "../../test/io_helpers.h"
9+
#include "../../test/dumpToThree.h"
910
#endif
1011

1112
#include "IfcGeometryProcessor.h"
@@ -193,6 +194,7 @@ namespace webifc::geometry
193194

194195
for (auto relVoidExpressID : relVoidsIt->second)
195196
{
197+
196198
IfcComposedMesh voidGeom = GetMesh(relVoidExpressID);
197199
auto flatVoidMesh = flatten(voidGeom, _expressIDToGeometry, normalizeMat);
198200
voidGeoms.insert(voidGeoms.end(), flatVoidMesh.begin(), flatVoidMesh.end());
@@ -239,6 +241,7 @@ namespace webifc::geometry
239241
{
240242
return mesh;
241243
}
244+
242245
}
243246
else
244247
{
@@ -453,7 +456,7 @@ namespace webifc::geometry
453456
}
454457

455458
#ifdef DUMP_CSG_MESHES
456-
DumpIfcGeometry(geom, "pbhs.obj");
459+
io::DumpIfcGeometry(geom, "pbhs.obj");
457460
#endif
458461

459462
// TODO: this is getting problematic.....

0 commit comments

Comments
 (0)