IFC/CityGMLビューア v29
プロパティ・断面表示可能
v29は、
IFC / CityGML
のプロパティ確認と断面表示が可能な整理版です。
CityGMLの経緯度座標はメートル相当に変換して表示します。
CityGMLの経緯度座標はメートル相当に変換して表示します。
CityGMLが経緯度座標の場合は、自動的にローカルメートル座標へ変換します。
表示・姿勢
IFCはON推奨です。CityGMLはZを高さ方向として読み込むため、この補正は適用しません。
断面表示
水平断面は、補正後の高さ方向で切ります。スライダーを動かすと断面位置が変わります。
プロパティ表示
3D画面上のIFC要素をクリックすると、Type、ExpressID、Name、GlobalIdなどを上部に表示します。
詳細は「詳細」ボタンで開けます。
初期化中…
IFCプロパティ
IFCプロパティ
部位をクリックしてください。
IFCプロパティ
${lastPropertyHtml}`; win.document.open(); win.document.write(html); win.document.close(); } function collectFacadeElementSegments(opts) { if (!modelGroup) throw new Error('IFCモデルがまだ読み込まれていません。'); modelGroup.updateMatrixWorld(true); const box = new THREE.Box3().setFromObject(modelGroup); const baseFront = opts.side || 'minY'; const spec = getFaceSpec(opts.face || 'front', baseFront); const depthTol = Number(opts.depthTol ?? 1.0); const minSeg = Number(opts.minSeg ?? 0.03); const snapStep = Number(opts.snapStep ?? 0.05); const useTypeFilter = opts.useTypeFilter !== false; const useBboxBand = opts.useBboxBand !== false; const pickedTypes = opts.pickedTypes || new Set(); const segments = new Map(); let meshSeen = 0, meshTypePassed = 0, meshBandPassed = 0, edgeSegments = 0; const typeCounts = {}; const selectedCounts = {}; const tmpBox = new THREE.Box3(); const p1 = new THREE.Vector3(), p2 = new THREE.Vector3(); const axisVal = spec.bandSide === 'min' ? box.min[spec.bandAxis] : box.max[spec.bandAxis]; modelGroup.traverse(mesh => { if (!mesh.isMesh || !mesh.geometry) return; meshSeen++; const typeName = mesh.userData.ifcTypeName || 'UNKNOWN'; typeCounts[typeName] = (typeCounts[typeName] || 0) + 1; if (useTypeFilter && !pickedTypes.has(typeName)) return; meshTypePassed++; tmpBox.setFromObject(mesh); let bandOK = true; if (useBboxBand) { bandOK = spec.bandSide === 'min' ? (tmpBox.min[spec.bandAxis] <= axisVal + depthTol) : (tmpBox.max[spec.bandAxis] >= axisVal - depthTol); } if (!bandOK) return; meshBandPassed++; selectedCounts[typeName] = (selectedCounts[typeName] || 0) + 1; const fc = facadeClassFromType(typeName); let edges; try { edges = new THREE.EdgesGeometry(mesh.geometry, Number(opts.edgeAngle || 20)); } catch (e) { edges = new THREE.EdgesGeometry(mesh.geometry); } const pos = edges.getAttribute('position'); if (!pos) { edges.dispose(); return; } for (let i = 0; i < pos.count; i += 2) { p1.set(pos.getX(i), pos.getY(i), pos.getZ(i)).applyMatrix4(mesh.matrixWorld); p2.set(pos.getX(i + 1), pos.getY(i + 1), pos.getZ(i + 1)).applyMatrix4(mesh.matrixWorld); addSegment2D(segments, projectPointForFace(p1, spec), projectPointForFace(p2, spec), minSeg, snapStep, fc.cls, fc.priority); edgeSegments++; } edges.dispose(); }); const rawArr = Array.from(segments.values()); const arr = simplifySegmentsArray(rawArr, opts); log('Facade element extraction completed', { method: 'IFC type filter + face band + EdgesGeometry projection', face: spec.face, label: spec.label, baseFront, bandAxis: spec.bandAxis, bandSide: spec.bandSide, axisVal, depthTol, snapStep, useTypeFilter, useBboxBand, pickedTypes: Array.from(pickedTypes), meshSeen, meshTypePassed, meshBandPassed, rawEdgeSegments: edgeSegments, rawSegmentCount: rawArr.length, segmentCount: arr.length, edgeAngle: Number(opts.edgeAngle || 20), reduceLines: opts.reduceLines !== false, maxLines: Number(opts.maxLines || 8000), typeCounts, selectedCounts, bbox: { min: { x: box.min.x, y: box.min.y, z: box.min.z }, max: { x: box.max.x, y: box.max.y, z: box.max.z } } }); return { segments: arr, bbox: box, stats: { method: 'facadeElement', face: spec.face, label: spec.label, baseFront, bandAxis: spec.bandAxis, bandSide: spec.bandSide, axisVal, depthTol, snapStep, meshSeen, meshTypePassed, meshBandPassed, rawEdgeSegments: edgeSegments, segmentCount: arr.length } }; } function buildSvgDocument(result, styleOpts={}) { const {segments, bbox, stats} = result; if (!segments.length) throw new Error('線が抽出できませんでした。奥行き厚みを増やすか、「多めに拾う」を試してください。'); const minU = Math.min(...segments.map(s => Math.min(s.u1, s.u2))); const maxU = Math.max(...segments.map(s => Math.max(s.u1, s.u2))); const minV = Math.min(...segments.map(s => Math.min(s.v1, s.v2))); const maxV = Math.max(...segments.map(s => Math.max(s.v1, s.v2))); const w = Math.max(1, maxU - minU); const h = Math.max(1, maxV - minV); const pad = Math.max(w, h) * 0.05; const vb = `${minU - pad} ${-(maxV + pad)} ${w + pad * 2} ${h + pad * 2}`; const lines = segments.map(s => `${title}
