IFC Viewer SDK · v1.18.0
Integra el visor IFC al teu propi producte
Insereix el mateix visor del navegador al teu CDE, bessó digital o eines internes de projecte. Carrega dades IFC des de la teva app mentre cada model s'analitza al client.
Tres línies fins al primer render
const viewer = new IfcViewer("#viewer");
await viewer.add("project.ifc", ifcBytes);
// → rendered, validated, all on the client- 100% al client
- Sense backend de pujada
- Zero dependències en execució
- Wrapper de ~6 KB
- Tipus TypeScript
- 10 idiomes
- Motor validat amb IDS
Posada en marxa
Inici ràpid
Tria el teu stack. Copia, enganxa i renderitza — sense pas de build, sense clau d’API.
<div id="viewer" style="height:520px"></div>
<script type="module">
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/ifc-viewer.es.js";
const viewer = new IfcViewer("#viewer");
// Load IFC bytes from your app — nothing is uploaded
const bytes = await fetch("/models/project.ifc").then(r => r.arrayBuffer());
await viewer.add("project.ifc", bytes);
</script>Zero JavaScript — posa l’etiqueta a qualsevol pàgina o dashboard i defineix atributs.
<script type="module" src="https://www.ifcvieweronline.eu/sdk/ifc-viewer.es.js"></script>
<ifc-viewer model="https://your-cde.com/model.ifc" ui="minimal" accent="#3fa866"
style="display:block;height:520px"></ifc-viewer>Munta en un effect i desmunta en desmuntar. Segur amb la doble invocació de l’StrictMode de React 18.
import { useEffect, useRef } from "react";
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/ifc-viewer.es.js";
export function ModelViewer({ url }) {
const host = useRef(null);
useEffect(() => {
const viewer = new IfcViewer(host.current, { model: url });
return () => viewer.dispose(); // StrictMode-safe teardown
}, [url]);
return <div ref={host} style={{ height: 520 }} />;
}Munta a onMounted, allibera a onUnmounted.
<script setup>
import { onMounted, onUnmounted, ref } from "vue";
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/ifc-viewer.es.js";
const host = ref(null);
let viewer;
onMounted(() => { viewer = new IfcViewer(host.value, { model: "https://host/m.ifc" }); });
onUnmounted(() => viewer && viewer.dispose());
</script>
<template>
<div ref="host" style="height: 520px" />
</template>Allotjat per nosaltres o per tu
Els exemples importen des de la nostra CDN, que funciona en qualsevol origen. Per allotjar-ho tu, serveix ifc-viewer.es.js des del teu domini — l’SDK descobreix l’URL de l’aplicació relativa a l’script. Tens l’aplicació en un altre lloc? Fes servir l’opció baseUrl.
Encara no hi ha paquet npm: importa el mòdul per URL (a dalt) o incorpora ifc-viewer.es.js a la teva aplicació.
Demostració
Demo en directe
Aquesta pàgina està executant el SDK. Fes clic en un control per baixar un IFC d’exemple i conduir el visor — cada botó mostra la crida que fa.
⚠ El host d’exemple pot estar limitant peticions. Torna-ho a provar d’aquí a un moment.
Arquitectura
Com funciona
Un wrapper mínim a la teva pàgina, el visor complet en un iframe aïllat, els teus bytes travessant entre tots dos — i processats on ja eren.
La teva aplicació
Un mòdul ES de ~6 KB que crea un iframe i una API JS tipada. Res del pes de 3D ni de parseig toca el teu bundle.
Pont postMessage
Els bytes IFC es transfereixen a l’iframe com a ArrayBuffer (sense còpia). Les ordres i les consultes són missatges correlacionats de petició/resposta.
El visor
three.js + web-ifc compilat a WebAssembly analitzen i renderitzen el model dins de l’iframe — a la màquina del visitant.
Per què és privat
Els models no arriben mai a un servidor. Cap cadena de pujada per protegir, cap dubte de residència de dades, cap cua per mida de fitxer.
Per què escala
El càlcul s’executa al dispositiu de cada visitant. Servir un model de 200 MB costa el mateix que un de 2 MB: és un fitxer estàtic.
Conceptes
Conceptes clau
Quatre coses que convé saber abans de construir.
Muntatge i disponibilitat
new IfcViewer(target) munta immediatament i encua les ordres fins que el visor està a punt. await IfcViewer.create(target) (o whenReady()) et dona una instància que pots fer servir de seguida.
// queues commands until ready
const viewer = new IfcViewer("#viewer");
// …or await an instance that is ready to use
const viewer = await IfcViewer.create("#viewer");Bytes o URL
add(name, bytes) transmet bytes que ja tens — sense CORS i sense pujar res; el buffer es transfereix (queda detached), així que passa-hi una còpia si encara el necessites. addFromUrl(url) descarrega un URL públic amb CORS.
// you already have the bytes — no CORS, nothing uploaded
await viewer.add("a.ifc", bytes);
// …or load a public, CORS-enabled URL
await viewer.addFromUrl("https://host/b.ifc");Esdeveniments
Subscriu-t’hi amb on(event, cb), que retorna una funció per cancel·lar la subscripció. Tens ready, model-progress, model-loaded, validation-completed, model-error i element-selected.
const off = viewer.on("model-loaded", (m) => {
console.log(m.fileName, m.elementCount);
});
// later: off();Consultes
Obtén dades sota demanda: getModels, getElement, getValidation, getStats, getIssues, checkIds, checkEir, screenshot. Cadascuna és petició/resposta i es rebutja després d’un temps d’espera (30 s; IDS / EIR fins a 120 s).
const models = await viewer.getModels();
const { qualityScore } = await viewer.getValidation();
const png = await viewer.screenshot();Referència
Referència de l’API
Tots els mètodes, agrupats — amb signatures i tipus de retorn. Després vénen les opcions del constructor, els esdeveniments i el web component <ifc-viewer>.
Constructor
new IfcViewer(target, options?)IfcViewerMunta en un selector CSS o element. Consulta les opcions a sota.
IfcViewer.create(target, options?)Promise<IfcViewer>Crea un visor i es resol quan ja pot acceptar ordres.
Càrrega de models
add(name, bytes)Promise<ModelLoadedEvent>Carrega bytes IFC (ArrayBuffer/Uint8Array) des de la teva app. El buffer es transfereix per a un traspàs sense còpia.
addFromUrl(url, name?)Promise<ModelLoadedEvent>Carrega un URL IFC públic amb CORS.
removeModel(modelId)voidDescarrega un model per id.
clear()voidElimina tots els models carregats.
Càmera i selecció
select(expressId | globalId, modelId?)voidSelecciona i enquadra un element — pel seu expressID IFC o pel GlobalId (que també troba el seu model).
setView(view)voidVola a una vista amb nom de l’escena — iso, front, back, left, right, top, bottom — amb tota l’escena enquadrada. Sense resposta (entre 1.14 i 1.17 el visor la ignorava).
frame({ view?, fill?, azimuth?, elevation?, animate? })Promise<{ scope }>Enquadrament per presentar: el model ocupa `fill` del marc (0,2–0,98; per defecte 0,85), ajustat a les seves cantonades i no a l’esfera que l’envolta. azimuth / elevation miren des de qualsevol angle.
frame(elementId, modelId?, options?)Promise<{ scope: 'element' }>Enquadra un element (expressID): des de l’angle actual tret que s’indiqui una vista. Rebutja si no és al model.
fit() · reset()voidEnquadra el model actiu des de l’angle actual, amb marge / reinicia la càmera.
setCamera(position, direction)voidCol·loca la càmera en una posició mirant en una direcció.
getCamera()Promise<CameraState | null>On és la càmera i cap on mira: desa-ho i restaura-ho amb lookAt.
lookAt(position, target, animate?)Promise<void>Porta la càmera a una posició mirant un objectiu (metres d'escena, Y amunt).
setWalkMode(enabled, { speed? })Promise<WalkState>Mode passeig en primera persona (WASD, arrossega per mirar, Esc per sortir). Emet walk-changed.
getWalkState()Promise<WalkState>Si el mode passeig és actiu i a quina velocitat.
Visibilitat
isolate(ifcType?)voidAïlla una categoria (p. ex. "IfcWall"); omet-ho per netejar.
showAll()voidRestaura tota la visibilitat (treu ocults + aïllament).
hideElements(ids, modelId?) · showElements(ids, modelId?)voidAmaga / mostra un conjunt d'elements per expressID.
setModelVisible(modelId, visible)Promise<void>Mostra o amaga un model d'una escena federada sense descarregar-lo.
setModelOpacity(opacity, modelId?)Promise<void>Fa translúcid un model (0,05–1) — p. ex. l'arquitectura al voltant de les instal·lacions.
isolateModel(modelId | null)Promise<void>Mostra només aquest model; null els torna a mostrar tots.
Aspecte i presentació
setBackground(background)Promise<BackgroundState>Canvia el fons de l'escena: un preset ('white', 'paper', 'blueprint', 'sky', 'studio'), '#rrggbb', '#dalt,#baix' o { top, bottom }. No es desa com a preferència del visitant.
getBackground()Promise<BackgroundState>El fons actual de l'escena.
setAccent(color)Promise<void>Canvia el color d'accent de la interfície en temps d'execució (#rrggbb).
setClientMode(enabled)Promise<void>Activa o desactiva la vista client — la de presentació als stakeholders, igual que ui: 'client'.
setRenderQuality(quality)Promise<void>'quality' activa el renderitzat més pesat per a una imatge destacada; 'standard' per al dia a dia.
Estudi solar
setSolar(opts?)Promise<SolarState>Ombres reals en una data i hora locals de l'emplaçament: { date: '06-21', time: '18:00' }. El lloc surt de la georeferència de l'IFC; passa location si el model no en té — mai una ciutat per defecte en silenci.
getSolar()Promise<SolarState>Data, hora i zona horària de l'estudi, i on és l'emplaçament.
Mode mapa
setSiteContext(opts?)Promise<SiteContextState>Col·loca el model al mapa amb terreny i entorn d'OpenStreetMap. Cridar-lo és la teva pàgina declarant el consentiment de tessel·les pels seus visitants; mostra les atribucions.
getSiteContext()Promise<SiteContextState>Estat del mode mapa, la ubicació i les atribucions que has de mostrar.
Escenes
openScene(url | SceneDocument)Promise<void>Obre una altra escena: l’URL d’un .scene.json o un document d’escena (viatja empaquetat a l’adreça del marc, així que ha de cabre en un enllaç). El visor es recarrega amb ella; les crides pendents es rebutgen i ready torna a disparar-se.
exportScene({ title?, description?, camera? })Promise<SceneExport>L’escena en pantalla com a document d’escena —models per URL, capes, bessó, mapa, càmera— amb les fonts que cal citar i un enllaç que la conté.
packScene(doc)Promise<string | null>Empaqueta tu mateix un document d’escena per a un enllaç #scene= (null si no cap en un enllaç).
Capes de dades i bessó
getLayerPresets()Promise<LayerPreset[]>El catàleg de fonts públiques en directe, amb nom en l’idioma del visor, el que necessita cadascuna en un navegador (una clau, un servidor intermediari) i si té dades a prop de l’emplaçament.
addLayer({ preset } | { url, live? } | { geojson })Promise<DataLayerInfo>Afegeix una capa: una font del catàleg ({ preset }), GeoJSON o un feed en directe en una URL ({ url, live }) o GeoJSON que ja tens ({ geojson }). Només per a la sessió: mai no es desa entre les capes del visitant.
getLayers()Promise<DataLayerInfo[]>Totes les capes de dades: estat, recompte d’elements, extensió, refresc i atribució.
setLayerVisible(id, visible)Promise<DataLayerInfo>Mostra o amaga una capa.
frameLayer(id)Promise<void>Porta la càmera als elements d’una capa.
removeLayer(id)Promise<void>Treu una capa.
getTwin()Promise<TwinState>El bessó operatiu ara: fonts de dispositius (estat, darrera lectura) i, per vincle, la regla que es compleix, el seu color, el valor de l’etiqueta i si està en alerta.
Seccions
addSection({ axis?, offset?, level?, flip? })Promise<SectionsState & { id }>Afegeix un pla de secció. { level: 'Planta 1' } és una planta en aquell nivell; { axis: 'x', offset: 4.5 } una secció a 4,5 m.
updateSection(id, { offset?, enabled?, flipped? })Promise<SectionsState>Mou, activa/desactiva o inverteix un pla.
removeSection(id?)Promise<SectionsState>Treu un pla, o tots els talls si no passes id.
setSectionBox('model' | 'selection' | false)Promise<SectionsState>Una caixa de secció al voltant del model o de la selecció; false la treu.
getSections()Promise<SectionsState>Tots els plans, la caixa i les plantes disponibles per a talls per nivell.
Tours
startTour(template?, { autoplay?, title? })Promise<TourState>Inicia un tour predefinit: 'social', 'client-walkthrough' o 'technical-review' (recorre les incidències de validació). El reprodueix la barra de tour del visor.
playTour({ title?, steps }, { startAt?, autoplay? })Promise<TourState>Reprodueix un tour teu: parades de càmera amb textos, ressaltats i classes aïllades. autoplay: true o ms per parada.
nextTourStep() · prevTourStep() · goToTourStep(i)Promise<TourState>Moure’s entre parades.
setTourAutoplay(autoplay)Promise<TourState>Activa o desactiva l’avançament automàtic del tour en curs.
stopTour()Promise<TourState>Atura el tour i retorna la càmera.
getTour()Promise<TourState>El tour i les seves parades, amb la forma que accepta playTour(): desa’l i reprodueix-lo més tard.
Director de presentacions
getPresentationRecipes()Promise<PresentationRecipe[]>Les receptes del director (demo per a reunió, reel, teaser de LinkedIn, llançament, looks editorials…).
createPresentation(recipe?, options?)Promise<PresentationState>Converteix el model en un vídeo editat a partir d’una recepta: preses planificades des de l’IFC, textos, música. Obre Clip Studio amb el resultat. Segueix presentation-progress.
exportPresentation({ resolution?, music? })Promise<PresentationVideo>Codifica la presentació al navegador del visitant i rep els bytes de l’MP4: puja’ls, adjunta’ls o reprodueix-los en un <video>.
closePresentation()Promise<void>Tanca Clip Studio.
Estudi de portades
getCoverOptions()Promise<CoverCatalog>Les receptes, plantilles, formats i paletes que ofereix l’estudi de portades.
createCover({ recipe?, template?, format?, palette?, text? })Promise<CoverState>Crea una portada: executa una recepta (captura les vistes que necessita) i després fixa plantilla, format, paleta i textos. Obre l’estudi, que continua sent editable.
getCover()Promise<CoverState | null>La portada tal com està, o null si l’estudi és tancat.
exportCover({ type?, slide? })Promise<CoverFile>Exporta-la: png / jpeg (una pàgina), pdf / pptx (tot el document), zip (totes les pàgines). Retorna els bytes.
closeCover()Promise<void>Tanca l’estudi de portades.
Kit d’article
activate()voidArrenca ara un visor diferit (el que fa el botó del pòster). Les crides anteriors van quedar en cua.
bindSteps(steps, { rootMargin? })() => voidScrollytelling: mentre cada paràgraf creua el centre de la pantalla, enquadra, aïlla, mou el sol o executa el teu codi. Retorna una funció per desvincular.
setTurntable(enabled | degPerSec)Promise<{ active, speed }>Una rotació lenta en repòs (graus per segon; false l’atura). S’atura al primer toc, mai amb moviment reduït.
toggleFullscreen()Promise<void>Amplia la figura a pantalla completa, o torna.
setPaused(paused)Promise<{ paused }>Atura o reprèn el pintat de fotogrames. pauseOffscreen ho fa per tu.
Grups d’escena
getGroups()Promise<SceneGroupsState>Tots els grups de l’escena: deduïts de l’IFC (projecte, emplaçament, ubicació) i creats pels usuaris.
createGroup(name, modelIds?)Promise<string>Crea un grup, opcionalment amb models. Retorna el seu id.
renameGroup(id, name) · deleteGroup(id)Promise<void>Reanomena o esborra un grup d’usuari. Els automàtics no s’editen.
assignToGroup(itemId, groupId | null | 'loose')Promise<void>Mou un model o núvol de punts a un grup; null el retorna a l’agrupament automàtic i 'loose' el deixa solt.
setGroupVisible(id, visible) · isolateGroup(id | null)Promise<void>Mostra o amaga un grup sencer, o mostra’n només un.
frameGroup(id)Promise<void>Enquadra la càmera en un grup, inclosos els membres amagats.
Mesures
setMeasureTool(tool)Promise<void>Arma una eina de mesura per al visitant (obre el tauler Mesurar), o 'none'. Els resultats arriben a measurements-changed.
getMeasurements()Promise<MeasurementsState>Totes les mesures a la pantalla. Els valors van en SI — metres, m², graus — es mostri el que es mostri.
clearMeasurements(id?)Promise<MeasurementsState>Treu una mesura, o totes.
Consultes
getModels()Promise<ModelSummary[]>Llista els models carregats. Retorna una promesa.
findElements({ ifcClass?, globalId?, name?, modelId?, limit? })Promise<ElementSummary[]>Cerca elements per classe IFC, GlobalId i/o nom als models carregats. Cada resultat porta el que demanen getElement(), select() i frame().
getElement(expressId | globalId, modelId?)Promise<IfcElementData | null>Per expressID o per GlobalId. Les dades IFC d’un element: atributs amb el GlobalId, els seus conjunts de propietats i quantitats, i els del seu tipus — typeName, typeProperties — amb unitats. effectivePropertySets els combina i guanya el valor propi de l’element.
validate(modelId?, { force? })Promise<ValidationRunResult>Valida un model ara (per defecte, l’actiu) i resol amb el seu Health Score i el recompte d’incidències. Rebutja amb el motiu.
getValidation()Promise<ValidationSummary | null>El resultat de validació en pantalla, o null abans que cap execució n’hagi produït un. Vegeu «Cicle de la validació».
getValidationStatus()Promise<ValidationStatus>En quin punt és la validació: idle, running (amb progrés), done o error.
getStats()Promise<StatsResult>Recompte d’elements per categoria i model — per a gràfics. Retorna una promesa.
getIssues({ severity?, limit?, modelId? })Promise<IssuesResult>Incidències de validació per a una taula (filtra per severitat / límit). Retorna una promesa.
screenshot()Promise<string>Captura la vista 3D actual com a data URL PNG. Retorna una promesa.
compare({ base, head, baseLabel?, headLabel? })Promise<{ changes }>Compara dues entregues per URL (per GlobalId) i obre l’espai de comparació amb el resultat.
Panells
openPanel(panel | null) · closePanel()voidObre un tauler d'eina, o passa null per tancar el que estigui obert. Un tauler no disponible — perquè el chrome l'amaga o no hi ha res carregat sobre què actuar — no fa res, i no és un error.
getPanels()Promise<PanelsResult>Quin tauler és obert i quins s'ofereixen ara mateix. Una eina que no apareix a `available` no es pot obrir; no està simplement deshabilitada.
setPanels(panels)voidLimita el carril a aquests taulers, en temps d'execució. Mateix vocabulari que el paràmetre d'URL `panels=` i hi té prioritat. Restringeix el que el visor ofereix i mai afegeix; un array buit vol dir sense carril.
Núvols de punts
addPointCloud(fileName, bytes)Promise<string>Carrega un escaneig des de bytes — LAS, LAZ, COPC, PLY, PCD o text. El búfer es transfereix, no es copia. Resol amb l'id del núvol.
addPointCloudFromUrl(url, fileName?)Promise<string>Carrega un escaneig que descarrega el mateix visor. L'URL ha de permetre CORS.
listPointClouds()Promise<PointCloudInfo[]>Tots els escaneigs carregats. `pointCount` és el que resideix en memòria, `declaredCount` el que conté el fitxer — difereixen quan el pressupost va truncar la lectura.
removePointCloud(cloudId) · clearPointClouds()Promise<void>Descarrega un escaneig i allibera els seus búfers de GPU, o descarrega'ls tots.
setPointCloudVisible(cloudId, visible)Promise<void>Mostra o amaga un escaneig sense descarregar-lo.
fitPointCloud(cloudId?)Promise<void>Enquadra la càmera en un escaneig, o en el primer carregat.
setPointCloudDisplay(display, renderBudget?)Promise<void>Mida del punt, opacitat, mode de color, densitat, llindar de confiança — compartits per tots. Cadascun és un uniform del shader, així que és instantani amb 20 milions de punts.
inspectPointCloud(enabled?)Promise<void>Arma el clic per llegir. Un cop armat, clicar un punt emet `pointcloud-picked` amb les coordenades DEL FITXER al costat de les de l'escena.
setPointCloudPlacement(placement, cloudId?)Promise<void>Ajusta un escaneig a mà: posició, guinyada, anivellament, escala. Parcial i acotat; s'aplica damunt de l'alineació deduïda sense substituir-la.
setPointCloudUpAxis(axis, cloudId?)Promise<void>Corregeix quin eix considera vertical l'escaneig. PLY, PCD i text no declaren orientació, el visor la dedueix — comprova `upAxisSource`.
Imported 3D models
addMesh(files)Promise<string>Importa un model des de bytes. Rep una LLISTA: un .gltf necessita el seu .bin i les textures, un .obj el seu .mtl — sol, el fitxer d'entrada dóna geometria grisa.
addMeshFromUrl(urls)Promise<string>Importa un model que descarrega el visor. Passa totes les URL que calguin; es descarreguen en paral·lel i totes han de permetre CORS.
listMeshes()Promise<MeshInfo[]>Tots els models importats, amb els recomptes de triangles i textures. Llegeix `unitSource` i `upAxisSource` abans de fiar-te dels valors del costat.
removeMesh(meshId?) · clearMeshes()Promise<void>Elimina una importació i allibera la geometria, els materials i les textures, o elimina-les totes.
setMeshVisible(visible, meshId?)Promise<void>Mostra o amaga una importació sense descarregar-la.
fitMesh(meshId?)Promise<void>Enquadra la càmera en una importació, o en totes.
setMeshPlacement(placement, meshId?)Promise<void>Col·loca una importació a mà: posició, guinyada, anivellament, escala. Parcial i acotat. Les importacions comencen centrades sobre l'IFC i recolzades al seu terra.
setMeshUpAxis(axis, meshId?)Promise<void>Corregeix l'eix vertical de l'origen. Només té sentit en OBJ — l'especificació de glTF obliga Y amunt, així que informa `upAxisSource: 'declared'`.
setMeshUnit(unitScale, meshId?)Promise<void>Corregeix la unitat d'origen: 1 metres, 0,01 centímetres, 0,001 mil·límetres, 0,3048 peus. Cap d'aquests formats la registra, el visor la dedueix de la mida del model.
Conformitat IDS i EIR
checkIds(idsXml)Promise<IdsResult>Comprova el model contra un IDS de buildingSMART (XML .ids). S’executa en un worker. Retorna aprovat/suspès per especificació.
checkEir(profile)Promise<IdsResult>Comprova el model amb un perfil de validació EIR / BIM — un objecte, el seu JSON (incl. el format compacte) o l'id d'un perfil integrat com 'builtin-en14351-1'. Compila a IDS i fa servir el mateix motor: el mateix IdsResult que checkIds.
Idiomes
setLanguage(lang)voidCanvia l'idioma de la interfície en temps d'execució.
getLanguages()string[]Llista els codis d'idioma admesos (etiquetes a IfcViewer.LANGUAGES).
Cicle de vida i esdeveniments
whenReady()Promise<void>Es resol quan el visor pot acceptar ordres.
isReadybooleanGetter — true tan bon punt el visor ha indicat que està a punt.
on(event, cb)() => voidSubscriu-te a un esdeveniment; retorna una funció per cancel·lar.
off(event, cb)voidElimina un listener d’esdeveniments afegit prèviament.
dispose()voidDesmunta el visor i elimina l'iframe.
Les crides concurrents a add() / addFromUrl() se serialitzen internament i cada promesa es correlaciona amb la seva pròpia càrrega — una càrrega iniciada per l’aplicació (paràmetre d’URL, pujada dins de l’iframe) mai no resol la teva promesa d’add().
Opcions del constructor
| Opció | Tipus | Per defecte | Descripció |
|---|---|---|---|
ui | 'minimal' | 'full' | 'kiosk' | 'client' | 'article' | 'embed' | 'minimal' | Preset de la interfície. 'embed': per a una app amfitriona que aporta el fitxer — 3D, propietats en seleccionar, una barra compacta amb Validar i Mesurar; sense Obrir, Compartir, compte ni idioma. |
toolbar | boolean | preset | Mostra la barra d’eines (per defecte, la del preset). false deixa la vista 3D i els panells. |
tools | ('validate' | 'measure')[] | ['validate', 'measure'] | Eines de la barra compacta de ui: 'embed' — 'validate', 'measure'. [] per a cap. |
autoFrame | boolean | true | Enquadra tota l’escena en iso, amb marge, cada cop que acaba una càrrega. false et deixa la càmera. |
validate | boolean | true | Executa la validació en carregar (alimenta l’Health Score). |
panel | boolean | false | Obre automàticament el panell de validació. |
panels | PanelName[] | — | Limita el carril d'eines a aquests taulers, des del primer fotograma — `['scene','map']`, o `[]` per no tenir carril. Mateixos noms que setPanels i que el paràmetre d'URL `panels=`. És preferible a cridar setPanels després de la càrrega, que mostra el carril complet i després treu eines. |
lang | string | auto | Força l’idioma de la interfície (p. ex. en, es, ja). |
accent | #rrggbb | brand | Tenyeix el visor perquè encaixi amb el teu dashboard. |
model | string | — | Carrega automàticament aquest URL públic d’IFC quan estigui a punt. |
background | BackgroundSpec | — | Fons d'escena des del primer fotograma — un preset, '#rrggbb' o '#dalt,#baix'. |
map | boolean | ('terrain' | 'buildings' | 'showcase')[] | — | Col·loca el model al mapa en carregar: true, o capes ['terrain','buildings','showcase']. |
solar · moon | 'MM-DDTHH:MM' · boolean | — | Obre l'estudi solar a una hora local de l'emplaçament: '06-21T18:00'. |
scans | string[] | — | URLs de núvols de punts per carregar amb el model. |
layers | string | — | Configuració de capes de dades a obrir: l’URL d’un JSON exportat des del tauler Capes de dades (fonts en directe, estils, alertes). Des de la 1.16. |
scene | string | — | Escena per obrir: l’URL d’un document d’escena (models, capes de dades, dispositius en directe, mapa, càmera). Des de la 1.17. |
view · fill | CameraView · number | — | Quan tots els models han carregat, enquadra’ls des d’aquesta vista amb un ajust estret. ui: 'article' implica 'iso'. |
wheel | 'always' | 'ctrl' | 'always' | 'ctrl': la roda desplaça la teva pàgina i només fa zoom amb Ctrl/⌘ premut, com un mapa incrustat. ui: 'article' ho implica. |
lazy | boolean | 'visible' | false | No carrega res fins que cal: true mostra un pòster amb botó; 'visible' arrenca quan la figura s’acosta a la pantalla. |
poster · posterTitle · posterText · launchLabel | string | — | Imatge, títol, text i botó del pòster, fins que el visor està llest. |
aspectRatio | string ('16/10') | — | Mida per proporció ('16/10'): pòster i visor comparteixen la caixa, res es mou a la pàgina. |
turntable | boolean | number | false | Rotació lenta en repòs després de carregar (true = 6°/s, o graus per segon). |
pauseOffscreen | boolean | ui === 'article' | Deixa de renderitzar fora de pantalla. true per defecte amb ui: 'article'. |
fullscreenButton | boolean | false | Un petit botó d’ampliar a la cantonada. |
height · width | number | string | '100%' | Mida de l’iframe (número → px). |
baseUrl | string | auto | URL base de l’aplicació. Es dedueix de l’URL de l’script. |
loadTimeout | number | 120000 | Rebutja add()/addFromUrl() després de N ms (0 ho desactiva). |
className · title | string | — | Classe extra / title de l’iframe. |
on* callbacks | function | — | Callbacks de conveniència — equivalen a .on(...). |
Esdeveniments
| Esdeveniment | Payload | Descripció |
|---|---|---|
ready | { languages } | Visor muntat i a punt. |
model-progress | { percent, phase } | Progrés de càrrega (baixada → anàlisi → render). |
model-loaded | { modelId, fileName, elementCount, fromCache } | Un model ha acabat de carregar. |
validation-started | { modelId } | Ha començat una validació sobre un model — després d’una càrrega (validate: true) o des de validate(). |
validation-completed | { modelId, qualityScore, errors, warnings, info, total } | Validació acabada: el Health Score i els recomptes, i el model al qual es refereixen (null per a l’agregat federat). |
validation-failed | { modelId, message } | Una validació no s’ha pogut executar o no ha acabat — amb el motiu. |
model-error | { message, url?, name? } | Una càrrega ha fallat. |
element-selected | { expressId, modelId, ifcType, name, globalId } | L’usuari ha triat un element — amb el seu GlobalId, per creuar-lo amb els teus registres. |
pointcloud-picked | { cloudId, position, sourcePosition, classification, intensity, distance } | S'ha llegit un punt d'un escaneig, en les coordenades del fitxer i no de l'escena. |
map-feature-picked | { id, name?, label?, featureKind, heightM?, heightEstimated } | S'ha clicat un edifici de l'entorn d'OpenStreetMap. Context, no model. |
walk-changed | { active, speed } | El mode passeig s'ha activat o desactivat — pel visitant o per l'amfitrió. |
measurements-changed | { tool, units, items } | S'ha afegit, tret o reanomenat una mesura. Porta la llista completa. |
tour-started | { title, total, template } | Ha començat un tour. |
tour-step | { index, total, caption } | El tour ha passat a una altra parada. |
tour-ended | { completed } | El tour s’ha aturat; completed si ha arribat a l’última parada. |
presentation-progress | { stage, label?, progress } | El director està generant o exportant una presentació. |
layer-feature-picked | { layerId, layer, featureId, geometry, lonLat, properties } | S’ha clicat un element d’una capa de dades: la seva capa, les propietats i un punt per marcar-lo. |
alert | { kind, from, id, name, rule, count, sample } | Una regla d’una capa de dades o del bessó ha començat o ha deixat d’alertar. |
Estàtics
IfcViewer.LANGUAGES ({ code, label }[], noms natius) i IfcViewer.SUPPORTED_LANGUAGES (codis) — per construir un selector d’idioma abans que el visor estigui a punt.
Web component
Zero JavaScript — posa l’etiqueta a qualsevol pàgina o dashboard i defineix atributs.
Atributs: modeluilangaccentvalidatepanelbase-urltoolbartoolsauto-frame
Els esdeveniments es reemeten com a CustomEvents del DOM anomenats ifcviewer:<type> (detail = payload). L’IfcViewer subjacent és a .viewer de l’element; getStats / getIssues / screenshot s’hi deleguen.
const el = document.querySelector("ifc-viewer");
el.addEventListener("ifcviewer:validation-completed", (e) => {
updateScore(e.detail.qualityScore);
});
const stats = await el.getStats();Guies
Receptes
Patrons per copiar i enganxar, del que els equips construeixen de debò.
Inspector d’elements
Fes clic al 3D i aboca els property sets IFC al teu propi panell.
viewer.on("element-selected", async (e) => {
const data = await viewer.getElement(e.expressId, e.modelId);
renderPanel(data.name, data.globalId, data.propertySets);
});Widget d’Health Score
Mostra la puntuació de qualitat del model i el recompte d’incidències al teu dashboard.
viewer.on("validation-completed", (v) => {
setScore(v.qualityScore); // 0–100, or null
setCounts(v.errors, v.warnings, v.info);
});Comprovació de conformitat IDS
Valida contra un IDS de buildingSMART i llista el resultat per especificació.
const ids = await fetch("/specs/project.ids").then(r => r.text());
const res = await viewer.checkIds(ids); // runs in a worker
res.specs.forEach((spec) => {
row(spec.name, spec.status, spec.passedCount + "/" + spec.applicableCount);
spec.failures.forEach((f) => onClick(() => viewer.select(f.expressId, f.modelId)));
});Validació EIR / BIM
Executa un perfil EIR editable (a l’estil ISO 19650) — sense cap fitxer .ids. Els errors porten el GlobalId de l’element.
// An editable EIR profile — object, JSON, or the compact shorthand below.
const profile = {
name: "Door QA",
rules: [
{ type: "requiredProperty", entity: "IfcDoor", property: "FireRating", severity: "error" },
{ type: "allowedValues", entity: "IfcDoor", property: "FireRating",
values: ["EI30", "EI60", "EI90"], severity: "warning" },
],
};
const res = await viewer.checkEir(profile); // same IdsResult as checkIds
res.specs.forEach((spec) => {
spec.failures.forEach((f) => row(spec.name, f.globalId, f.reasons.join(" · ")));
});Una escena per a un article de blog
Fons blanc, el model al seu emplaçament i l'ombra del vespre del solstici d'estiu — declarat d'entrada, sense clics.
// Declared up front: the iframe boots with all of it, no clicks, no flash.
const viewer = await IfcViewer.create("#viewer", {
model: "https://example.com/house.ifc",
ui: "client",
background: "white",
map: ["terrain", "buildings"], // your page declares tile consent
solar: "06-21T19:30", // summer solstice, site-local
});
// …or change the story as the reader scrolls
await viewer.setSolar({ date: "12-21", time: "09:30" });
const { attributions } = await viewer.getSiteContext(); // show thesePlanta i mesures
Talla una planta en un nivell pel nom, deixa mesurar el lector i llegeix els resultats en unitats SI.
const { levels } = await viewer.getSections();
await viewer.addSection({ level: levels[0].name }); // floor plan, 1.2 m up
viewer.setView("top");
await viewer.setMeasureTool("distance");
viewer.on("measurements-changed", ({ items }) => {
// value is SI — metres, m², degrees — whatever the viewer displays
render(items.map((m) => m.kind + ": " + m.value?.toFixed(2)));
});Un tour guiat en un article
Defineix les parades un cop amb getCamera() i deixa que el tour avanci sol al costat del text.
// 1. Once, while writing the post: frame each view and copy the camera.
console.log(JSON.stringify(await viewer.getCamera()));
// 2. In the page: play the stops, self-running, next to your text.
await viewer.playTour({
title: "Casa Poblenou",
steps: [
{ position: { x: 52, y: 29, z: 24 }, target: { x: 18, y: 7, z: -11 }, caption: "The site" },
{ position: { x: 18, y: 40, z: 11 }, target: { x: 18, y: 0, z: 10 },
caption: "Structure", isolate: ["IfcColumn", "IfcSlab"] },
],
}, { autoplay: 6000 });
viewer.on("tour-step", ({ index, caption }) => highlightParagraph(index));Un vídeo del model, des de la teva pàgina
Genera una presentació amb una recepta i rep l’MP4: sense gravar la pantalla, sense pujar res.
viewer.on("presentation-progress", ({ stage, progress }) => bar.value = progress ?? 0);
await viewer.createPresentation("linkedin-teaser", { title: "Casa Poblenou", targetSec: 20 });
const { bytes, mimeType } = await viewer.exportPresentation({ resolution: 1080 });
video.src = URL.createObjectURL(new Blob([bytes], { type: mimeType }));Una portada per al teu post
Genera un pin de Pinterest o una targeta de LinkedIn del model amb el teu títol i rep el PNG.
await viewer.createCover({
recipe: "pinterest",
text: { title: "Casa Poblenou", location: "Barcelona", website: "https://example.com" },
});
const { bytes, mimeType } = await viewer.exportCover({ type: "png" });
img.src = URL.createObjectURL(new Blob([bytes], { type: mimeType }));Un model dins d’un article
ui: 'article' és només el llenç, enquadrat perquè el model l’ompli, amb una roda que deixa el lector continuar llegint. Tria l’angle amb frame().
const viewer = new IfcViewer("#figure", {
ui: "article", // canvas only, tight iso framing, wheel scrolls the page
model: "https://example.com/pavilion.ifc",
lazy: true, // nothing loads until the reader asks
poster: "/img/pavilion.jpg",
posterTitle: "The pavilion, live",
aspectRatio: "16/10", // no layout shift from poster to viewer
background: "auto", // paper on a light page, studio on a dark one
turntable: true, // a slow turn until the reader takes over
fullscreenButton: true,
validate: false,
});Una història que avança amb el text
Fixa el visor i deixa que cada paràgraf triï el pla: aïlla els murs, gira al voltant, porta el sol del vespre.
// <div id="figure" style="position:sticky;top:1rem"></div>
// <p id="s1">…</p> <p id="s2">…</p> <p id="s3">…</p>
const viewer = new IfcViewer("#figure", { ui: "article", aspectRatio: "16/10", model: url });
viewer.bindSteps([
{ el: "#s1", frame: { view: "iso" } },
{ el: "#s2", isolate: "IfcWall", frame: { azimuth: 225, elevation: 20 } },
{ el: "#s3", isolate: null, solar: { active: true, date: "06-21", time: "19:30" } },
]);Un bessó de ciutat en directe a la teva pàgina
Obre una escena publicada, reacciona a les seves alertes i clics, afegeix-hi la teva pròpia font en directe i comparteix un enllaç al resultat.
const viewer = new IfcViewer("#twin", {
ui: "client",
scene: "https://www.ifcvieweronline.eu/scenes/barcelona-placa-catalunya.scene.json",
});
viewer.on("alert", (a) => { if (a.kind === "start") notify(a.name + ": " + a.rule) });
viewer.on("layer-feature-picked", (f) => showCard(f.layer, f.properties));
// Your own live source next to the scene's (GeoJSON with CORS, every 60 s)
await viewer.addLayer({ url: "https://example.org/sensors.geojson", live: 60 });
const { link, sources } = await viewer.exportScene({ title: "Our twin" });Una fitxa de producte a la teva app
Fixa la versió, lliura el fitxer i mostra el que el fabricant va posar al tipus: transmitància, classe de càrrega de vent, fabricant.
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js";
const viewer = await IfcViewer.create("#product-3d", {
ui: "embed", // 3D + properties, Validate and Measure only
panels: ["properties"],
lang: "es",
});
const bytes = await fetch(product.ifcUrl).then(r => r.arrayBuffer());
await viewer.add("V-70-PR.ifc", bytes); // framed for you
// The product, by class (or by its GlobalId) — no expressIDs to know
const [win] = await viewer.findElements({ ifcClass: "IfcWindow" });
// What the manufacturer put in the type (IfcWindowType → HasPropertySets)
const el = await viewer.getElement(win.globalId);
const pset = (name) => el.effectivePropertySets.find(s => s.name === name);
const uw = pset("Pset_WindowCommon")?.properties.find(p => p.name === "ThermalTransmittance");
console.log(el.typeName, el.globalId, uw?.value, uw?.unit); // "Ventana V-70 practicable" … 1.2 "W/(m²·K)"
const { qualityScore } = await viewer.validate(); // Health Score, on demand
// Declared performance per EN 14351-1: wind, water, acoustics, Uw, g / τv, DoP
const en = await viewer.checkEir("builtin-en14351-1");
const missing = en.specs.filter(s => s.status === "fail").map(s => s.name);Adapta’l a la teva marca
Iguala el visor al teu producte amb un sol color d’accent.
new IfcViewer("#viewer", { accent: "#3fa866" });
// …or on the web component:
// <ifc-viewer accent="#3fa866" model="…"></ifc-viewer>Selector d’idioma
Construeix un selector amb els idiomes que porta el visor.
// build a <select> from the bundled languages
IfcViewer.LANGUAGES.forEach(({ code, label }) => addOption(code, label));
// switch at runtime
viewer.setLanguage("ja");Health Score
Cicle de la validació
validate: true valida cada model quan acaba de carregar; validate() l’executa a demanda i resol amb el resultat. Què retorna getValidation() a cada moment:
| Quan | getValidation() retorna |
|---|---|
| Abans de cap validació | null — getValidationStatus() diu idle. |
| Durant la primera execució | null — getValidationStatus() diu running, amb progrés; ja s’ha emès validation-started. |
| Després d’una execució | { qualityScore, errors, warnings, info, total, status: 'done', modelId }. Amb diversos models, l’agregat (modelId: null). |
| Durant una execució més recent | Els números anteriors, amb status: 'running'. |
| Després d’un error | Els números anteriors amb status: 'error' i error — o null si no n’hi havia. S’ha emès validation-failed i validate() ha rebutjat. |
Versions
Fixa una versió
Des de la 1.18.0, cada versió també es publica en un camí que no canvia mai. Fixa-la en producció; el camí mòbil segueix la darrera versió.
<script type="module">
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js";
</script>| Camí | Memòria cau | Ús |
|---|---|---|
/sdk/ifc-viewer.es.js | es revalida a cada càrrega | Per provar — sempre la darrera. |
/sdk/1.18.0/ifc-viewer.es.js | immutable, en memòria cau un any | Producció — el codi que vas provar és el que s’executa. |
Quin visor obre un SDK fixat?
L’iframe és el visor en directe del mateix origen, no una còpia congelada: un SDK fixat conserva la seva API i el seu comportament mentre el visor de dins continua millorant. El visor respon a totes les ordres de qualsevol SDK publicat — el build ho comprova —, de manera que una versió fixada antiga continua funcionant.
Fixa també el contingut
versions.json, al costat de l’SDK, llista el hash Subresource Integrity de cada versió:
<script type="module" src="https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js"
integrity="sha384-WsT4d0DS1oqva4rLWCGBDrx50uZVA3B95IDWbfxaFt8lnjs6NgOrjKNcWQL57/0z" crossorigin="anonymous"></script>Registre de canvis
1.18.0 — objectes de catàleg en una app amfitriona. getElement() retorna el GlobalId i les dades del tipus (typeName, typeProperties amb unitats, effectivePropertySets; tipus IFC4 i estils IFC2x3). validate(), getValidationStatus(), validation-started / validation-failed, i validate: true valida de debò després de cada càrrega. Enquadrament automàtic després de carregar, fit() i frame() amb marge per a objectes petits, frame(elementId), i setView() torna a moure la càmera. ui: 'embed' amb toolbar i tools. Versions fixes a /sdk/1.18.0/. findElements(), GlobalIds a getElement() / select() / element-selected, conjunts de bastiment i fulla de finestres i portes, i els materials del tipus; a ui: 'embed' el panell de propietats deixa a la vista l’objecte triat. checkEir('builtin-en14351-1') comprova les prestacions declarades de finestres i portes exteriors segons l'EN 14351-1 — vent, estanquitat a l'aigua, acústica, transmitància tèrmica, g i τv, fabricant i codi de producte — en property sets estàndard d'IFC; les regles EIR guanyen where (només elements amb un valor de propietat) i optional.
Ajuda
Resolució de problemes
Les poques coses amb què la gent ensopega.
No es renderitza res
Comprova, en aquest ordre: que el visor va arribar a ready, que el model va carregar (model-loaded en lloc de model-error), que el contenidor té alçada diferent de zero i que l’origen de bytes és IFC vàlid. El missatge de model-error indica la causa.
Em surt un error de CORS carregant un model
El CORS només afecta addFromUrl(url). Si controles els bytes, descarrega’ls tu i crida add(name, bytes) — no s’hi aplica CORS perquè lliures les dades directament.
L’iframe surt en blanc o es nega a carregar
L’aplicació ha de ser incrustable — sense X-Frame-Options: DENY i sense una CSP frame-ancestors restrictiva al host. La nostra aplicació allotjada permet la incrustació.
El meu ArrayBuffer queda buit després d’add()
add() transfereix el buffer per lliurar-lo sense còpia, cosa que el deixa detached al teu codi. Passa-hi una còpia (bytes.slice(0)) si després encara necessites els bytes.
Les càrregues o les consultes fan timeout
add()/addFromUrl() es rebutgen després de loadTimeout (120 s per defecte; 0 ho desactiva). Les consultes es rebutgen als 30 s i checkIds als 120 s. Models grans en dispositius lents poden necessitar un loadTimeout més alt.
Quins navegadors estan suportats?
Qualsevol navegador modern amb WebAssembly i mòduls ES — Chrome, Edge, Firefox, Safari. Internet Explorer no.
Em dona 404 al meu servidor de desenvolupament local
Un servidor de desenvolupament SPA pot interceptar /sdk/ amb el seu fallback HTML. Prova-ho contra un build real o un allotjament estàtic, o importa directament el mòdul /sdk/ifc-viewer.es.js.