/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /** * Export Dialog for HBJSON (Honeybee / Ladybug Tools energy & daylight model). * * HBJSON is built analytically from the original IFC bytes (rooms from IfcSpace * volumes, windows/doors as apertures/doors, railings as shades, and material * layer sets as opaque constructions) — so it needs the model's `sourceFile`, * not the tessellated geometry. There are no per-export settings beyond the * model name, so the dialog is just a picker + a short description. */ import { useState, useCallback, useMemo, useEffect } from 'react'; import { Download, AlertCircle, Check, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Alert, AlertDescription, AlertTitle, } from '@/components/ui/alert'; import { useViewerStore } from '@/store'; import { toast } from '@/components/ui/toast'; import { GeometryProcessor } from '@ifc-lite/geometry'; interface HbjsonExportDialogProps { trigger?: React.ReactNode; } export function HbjsonExportDialog({ trigger }: HbjsonExportDialogProps) { const models = useViewerStore((s) => s.models); const [open, setOpen] = useState(false); const [selectedModelId, setSelectedModelId] = useState(''); const [isExporting, setIsExporting] = useState(false); const [exportResult, setExportResult] = useState<{ success: boolean; message: string } | null>(null); // Only models that still carry their original IFC bytes can be exported — // HBJSON is rebuilt from the source, not the tessellated geometry. Cache- // restored models (no `sourceFile`) and non-IFC sources (e.g. GLB / point clouds, which // can also be loaded) are omitted — HBJSON is rebuilt from the IFC source. const modelList = useMemo( () => Array.from(models.values()) .filter((m) => m.sourceFile && /\.(ifc|ifcx|ifczip)$/i.test(m.sourceFile.name)) .map((m) => ({ id: m.id, name: m.name, sourceFile: m.sourceFile as File })), [models], ); useEffect(() => { if (modelList.length > 0 && !modelList.some((m) => m.id === selectedModelId)) { setSelectedModelId(modelList[0].id); } }, [modelList, selectedModelId]); const selectedModel = useMemo( () => modelList.find((m) => m.id === selectedModelId), [modelList, selectedModelId], ); const handleExport = useCallback(async () => { if (!selectedModel?.sourceFile) return; setIsExporting(true); setExportResult(null); try { const bytes = new Uint8Array(await selectedModel.sourceFile.arrayBuffer()); const baseName = selectedModel.name.replace(/\.[^.]+$/, ''); // A fresh processor is cheap: wasm-bindgen shares one module singleton, // so init() no-ops when the viewer already initialised the engine. const processor = new GeometryProcessor(); await processor.init(); const hbjson = processor.exportHbjson(bytes, baseName); if (hbjson === null) { throw new Error('Geometry engine unavailable'); } const blob = new Blob([hbjson], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${baseName}.hbjson`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); const msg = `Exported HBJSON (${(blob.size / 1024).toFixed(0)} KB)`; setExportResult({ success: true, message: msg }); toast.success(msg); } catch (err) { console.error('HBJSON export failed:', err); const errMsg = `HBJSON export failed: ${err instanceof Error ? err.message : 'Unknown error'}`; setExportResult({ success: false, message: errMsg }); toast.error(errMsg); } finally { setIsExporting(false); } }, [selectedModel]); return ( {trigger || ( )} Export HBJSON (Energy Model) Honeybee / Ladybug Tools model for energy & daylight analysis
{/* Model selector — only shown when multiple are loaded */} {modelList.length > 1 && (
)} {/* Output format indicator */}
Honeybee Model .hbjson

Builds watertight rooms from IfcSpace volumes, places windows and doors as apertures, emits railings as shades, and maps material layer sets to opaque constructions. Loads directly in Honeybee / Pollination. Thermal properties are defaulted by material name and meant to be refined downstream.

{!selectedModel && ( No source available HBJSON export needs the original IFC file. Re-open the model from disk to enable it. )} {exportResult && ( {exportResult.success ? ( ) : ( )} {exportResult.success ? 'Success' : 'Error'} {exportResult.message} )}
); }