IFC Viewer SDK · v1.18.0
Intégrez le visualiseur IFC dans votre propre produit
Insérez le même visualiseur navigateur dans votre CDE, jumeau numérique ou outils internes de projet. Chargez des données IFC depuis votre app, chaque modèle étant analysé côté client.
Trois lignes jusqu’au premier rendu
const viewer = new IfcViewer("#viewer");
await viewer.add("project.ifc", ifcBytes);
// → rendered, validated, all on the client- 100% côté client
- Aucun backend de téléversement
- Zéro dépendance à l’exécution
- Wrapper de ~6 Ko
- Types TypeScript
- 10 langues
- Moteur validé IDS
Mise en place
Démarrage rapide
Choisissez votre stack. Copiez, collez, affichez — sans étape de build, sans clé 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>Zéro JavaScript — placez la balise dans n’importe quelle page ou tableau de bord et définissez les attributs.
<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>Montez dans un effect, démontez au unmount. Sûr avec la double invocation du 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 }} />;
}Montez dans onMounted, libérez dans 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>Hébergé par nous ou par vous
Les extraits importent depuis notre CDN, qui fonctionne sur n’importe quelle origine. Pour l’héberger vous-même, servez ifc-viewer.es.js depuis votre propre domaine — le SDK découvre l’URL de l’application relativement au script. L’application est ailleurs ? Utilisez l’option baseUrl.
Pas encore de paquet npm : importez le module par URL (ci-dessus) ou intégrez ifc-viewer.es.js à votre application.
Démonstration
Démo en direct
Cette page exécute le SDK. Cliquez un contrôle pour charger un IFC d’exemple et piloter le visualiseur — chaque bouton affiche l’appel qu’il effectue.
⚠ L’hôte d’exemple limite peut-être le débit. Réessayez dans un instant.
Architecture
Comment ça marche
Un wrapper minuscule dans votre page, le visualiseur complet dans un iframe isolé, vos octets transmis entre les deux — et analysés là où ils se trouvent déjà.
Votre application
Un module ES de ~6 Ko qui crée un iframe et une API JS typée. Rien du poids 3D ni de l’analyse ne touche votre bundle.
Pont postMessage
Les octets IFC sont transférés à l’iframe sous forme d’ArrayBuffer (sans copie). Les commandes et les requêtes sont des messages requête/réponse corrélés.
Le visualiseur
three.js + web-ifc compilé en WebAssembly analysent et affichent le modèle dans l’iframe — sur la machine du visiteur.
Pourquoi c’est privé
Les modèles n’atteignent jamais un serveur. Aucun pipeline de téléversement à sécuriser, aucune question de résidence des données, aucune file d’attente liée à la taille des fichiers.
Pourquoi ça passe à l’échelle
Le calcul s’exécute sur l’appareil de chaque visiteur. Servir un modèle de 200 Mo coûte autant qu’un de 2 Mo — c’est un fichier statique.
Concepts
Concepts essentiels
Quatre choses à savoir avant de construire.
Montage et disponibilité
new IfcViewer(target) monte immédiatement et met les commandes en file d’attente jusqu’à ce que le visualiseur soit prêt. await IfcViewer.create(target) (ou whenReady()) vous donne une instance utilisable tout de suite.
// queues commands until ready
const viewer = new IfcViewer("#viewer");
// …or await an instance that is ready to use
const viewer = await IfcViewer.create("#viewer");Octets ou URL
add(name, bytes) transmet des octets que vous avez déjà — pas de CORS, rien de téléversé ; le buffer est transféré (détaché), passez donc une copie si vous en avez encore besoin. addFromUrl(url) récupère plutôt une URL publique avec 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");Événements
Abonnez-vous avec on(event, cb), qui renvoie une fonction de désabonnement. Vous disposez de ready, model-progress, model-loaded, validation-completed, model-error et element-selected.
const off = viewer.on("model-loaded", (m) => {
console.log(m.fileName, m.elementCount);
});
// later: off();Requêtes
Récupérez les données à la demande : getModels, getElement, getValidation, getStats, getIssues, checkIds, checkEir, screenshot. Chacune est en requête/réponse et échoue après un délai (30 s ; IDS / EIR jusqu’à 120 s).
const models = await viewer.getModels();
const { qualityScore } = await viewer.getValidation();
const png = await viewer.screenshot();Référence
Référence de l’API
Toutes les méthodes, regroupées — avec signatures et types de retour. Suivent les options du constructeur, les événements et le web component <ifc-viewer>.
Constructeur
new IfcViewer(target, options?)IfcViewerMonter dans un sélecteur CSS ou un élément. Voir les options ci-dessous.
IfcViewer.create(target, options?)Promise<IfcViewer>Crée un visualiseur et se résout dès qu’il peut accepter des commandes.
Chargement des modèles
add(name, bytes)Promise<ModelLoadedEvent>Charger des octets IFC (ArrayBuffer/Uint8Array) depuis votre app. Le buffer est transféré pour un passage sans copie.
addFromUrl(url, name?)Promise<ModelLoadedEvent>Charger une URL IFC publique compatible CORS.
removeModel(modelId)voidDécharge un modèle par id.
clear()voidSupprimer tous les modèles chargés.
Caméra et sélection
select(expressId | globalId, modelId?)voidSélectionner et cadrer un élément — par son expressID IFC ou par son GlobalId (qui trouve aussi son modèle).
setView(view)voidVoler vers une vue nommée de la scène — iso, front, back, left, right, top, bottom — avec toute la scène dans le cadre. Sans réponse (de 1.14 à 1.17 la visionneuse l’ignorait).
frame({ view?, fill?, azimuth?, elevation?, animate? })Promise<{ scope }>Cadrage de présentation : le modèle occupe `fill` du cadre (0,2–0,98, 0,85 par défaut), ajusté à ses coins plutôt qu’à sa sphère englobante. azimuth / elevation choisissent l’angle.
frame(elementId, modelId?, options?)Promise<{ scope: 'element' }>Cadrer un élément (expressID) : depuis l’angle actuel sauf si une vue est donnée. Rejette s’il n’est pas dans le modèle.
fit() · reset()voidCadrer le modèle actif depuis l’angle actuel, avec une marge / réinitialiser la caméra.
setCamera(position, direction)voidPlace la caméra à une position regardant dans une direction.
getCamera()Promise<CameraState | null>Où est la caméra et ce qu’elle regarde — enregistrez-le, restaurez-le avec lookAt.
lookAt(position, target, animate?)Promise<void>Amène la caméra à une position, regardant une cible (mètres de scène, Y vers le haut).
setWalkMode(enabled, { speed? })Promise<WalkState>Mode visite à la première personne (WASD, glisser pour regarder, Échap pour sortir). Émet walk-changed.
getWalkState()Promise<WalkState>Si le mode visite est actif, et à quelle vitesse.
Visibilité
isolate(ifcType?)voidIsoler une catégorie (p. ex. "IfcWall") ; omettre pour réinitialiser.
showAll()voidRestaurer toute la visibilité (annuler masquage + isolation).
hideElements(ids, modelId?) · showElements(ids, modelId?)voidMasque / affiche un ensemble d’éléments par expressID.
setModelVisible(modelId, visible)Promise<void>Affiche ou masque un modèle d'une scène fédérée sans le décharger.
setModelOpacity(opacity, modelId?)Promise<void>Rend un modèle translucide (0,05–1) — p. ex. l’architecture autour des lots techniques.
isolateModel(modelId | null)Promise<void>N’affiche que ce modèle ; null les réaffiche tous.
Apparence et présentation
setBackground(background)Promise<BackgroundState>Change le fond de la scène : un préréglage ('white', 'paper', 'blueprint', 'sky', 'studio'), '#rrggbb', '#haut,#bas' ou { top, bottom }. Non enregistré comme préférence du visiteur.
getBackground()Promise<BackgroundState>Le fond actuel de la scène.
setAccent(color)Promise<void>Change la couleur d'accent de l'interface à l'exécution (#rrggbb).
setClientMode(enabled)Promise<void>Active ou désactive la vue client — celle des parties prenantes, comme ui: 'client'.
setRenderQuality(quality)Promise<void>'quality' active le rendu plus lourd pour une image phare ; 'standard' au quotidien.
Étude solaire
setSolar(opts?)Promise<SolarState>Ombres réelles à une date et une heure locales du site : { date: '06-21', time: '18:00' }. Le site vient du géoréférencement IFC ; passez location pour un modèle qui n’en a pas — jamais une ville par défaut en silence.
getSolar()Promise<SolarState>Date, heure et fuseau de l'étude, et où se trouve le site.
Mode carte
setSiteContext(opts?)Promise<SiteContextState>Place le modèle sur la carte avec le relief et les environs OpenStreetMap. L’appeler, c’est votre page qui déclare le consentement aux tuiles pour ses visiteurs ; affichez les attributions.
getSiteContext()Promise<SiteContextState>État du mode carte, le placement et les attributions à afficher.
Scènes
openScene(url | SceneDocument)Promise<void>Ouvrir une autre scène : l’URL d’un .scene.json, ou un document de scène (il voyage compressé dans l’adresse du cadre et doit tenir dans un lien). La visionneuse se recharge ; les appels en attente sont rejetés et ready se déclenche de nouveau.
exportScene({ title?, description?, camera? })Promise<SceneExport>La scène à l’écran comme document de scène — modèles par URL, couches, jumeau, carte, caméra — avec les sources à créditer et un lien qui la contient.
packScene(doc)Promise<string | null>Compresser soi-même un document de scène pour un lien #scene= (null s’il est trop gros pour un lien).
Couches de données et jumeau
getLayerPresets()Promise<LayerPreset[]>Le catalogue des sources publiques en direct, nommées dans la langue de la visionneuse, avec ce qu’il faut à chacune dans un navigateur (une clé, un proxy) et si elle a des données près du site.
addLayer({ preset } | { url, live? } | { geojson })Promise<DataLayerInfo>Ajouter une couche : une source du catalogue ({ preset }), du GeoJSON ou un flux en direct à une URL ({ url, live }), ou du GeoJSON que vous avez déjà ({ geojson }). Pour la session seulement : jamais enregistrée dans les couches du visiteur.
getLayers()Promise<DataLayerInfo[]>Toutes les couches de données : état, nombre d’entités, emprise, rafraîchissement et attribution.
setLayerVisible(id, visible)Promise<DataLayerInfo>Afficher ou masquer une couche.
frameLayer(id)Promise<void>Amener la caméra sur les entités d’une couche.
removeLayer(id)Promise<void>Retirer une couche.
getTwin()Promise<TwinState>Le jumeau opérationnel maintenant : sources d’appareils (état, dernière lecture) et, par liaison, la règle vérifiée, sa couleur, la valeur de l’étiquette et si elle est en alerte.
Coupes
addSection({ axis?, offset?, level?, flip? })Promise<SectionsState & { id }>Ajoute un plan de coupe. { level: 'RDC' } est un plan à cet étage ; { axis: 'x', offset: 4.5 } une coupe à 4,5 m.
updateSection(id, { offset?, enabled?, flipped? })Promise<SectionsState>Déplace, active/désactive ou inverse un plan.
removeSection(id?)Promise<SectionsState>Retire un plan, ou toutes les coupes sans id.
setSectionBox('model' | 'selection' | false)Promise<SectionsState>Une boîte de coupe autour du modèle ou de la sélection ; false la retire.
getSections()Promise<SectionsState>Tous les plans, la boîte et les niveaux disponibles pour les coupes par étage.
Visites
startTour(template?, { autoplay?, title? })Promise<TourState>Lance une visite prédéfinie : 'social', 'client-walkthrough' ou 'technical-review' (parcourt les anomalies de validation). Jouée par la barre de visite du visualiseur.
playTour({ title?, steps }, { startAt?, autoplay? })Promise<TourState>Joue une visite que vous avez écrite : arrêts de caméra avec légende, surlignages et classes isolées. autoplay : true ou ms par arrêt.
nextTourStep() · prevTourStep() · goToTourStep(i)Promise<TourState>Passer d’un arrêt à l’autre.
setTourAutoplay(autoplay)Promise<TourState>Active ou désactive l’avance automatique de la visite en cours.
stopTour()Promise<TourState>Arrête la visite et rend la caméra.
getTour()Promise<TourState>La visite et ses arrêts, au format de playTour() — enregistrez-la, rejouez-la plus tard.
Réalisateur de présentations
getPresentationRecipes()Promise<PresentationRecipe[]>Les recettes intégrées du réalisateur (démo de réunion, reel, teaser LinkedIn, lancement, looks éditoriaux…).
createPresentation(recipe?, options?)Promise<PresentationState>Transforme le modèle en vidéo montée à partir d’une recette : plans tirés de l’IFC, légendes, musique. Ouvre Clip Studio avec le résultat. Suivez presentation-progress.
exportPresentation({ resolution?, music? })Promise<PresentationVideo>Encode la présentation dans le navigateur du visiteur et renvoie les octets du MP4 — à téléverser, joindre ou lire dans une <video>.
closePresentation()Promise<void>Ferme Clip Studio.
Studio de couvertures
getCoverOptions()Promise<CoverCatalog>Les recettes, modèles, formats et palettes du studio de couvertures.
createCover({ recipe?, template?, format?, palette?, text? })Promise<CoverState>Créer une couverture : lancer une recette (elle capture les vues nécessaires), puis fixer modèle, format, palette et textes. Ouvre le studio, toujours modifiable.
getCover()Promise<CoverState | null>La couverture en l’état, ou null si le studio est fermé.
exportCover({ type?, slide? })Promise<CoverFile>L’exporter : png / jpeg (une page), pdf / pptx (tout le document), zip (toutes les pages). Renvoie les octets.
closeCover()Promise<void>Ferme le studio de couvertures.
Kit article
activate()voidDémarrer maintenant une visionneuse différée (ce que fait le bouton de l’affiche). Les appels précédents attendaient.
bindSteps(steps, { rootMargin? })() => voidScrollytelling : quand chaque paragraphe passe au milieu de l’écran, cadrer, isoler, déplacer le soleil ou lancer votre code. Renvoie une fonction de détachement.
setTurntable(enabled | degPerSec)Promise<{ active, speed }>Une lente rotation au repos (degrés par seconde ; false l’arrête). S’arrête au premier contact, jamais en mouvement réduit.
toggleFullscreen()Promise<void>Passer la figure en plein écran, ou revenir.
setPaused(paused)Promise<{ paused }>Suspendre ou reprendre le rendu. pauseOffscreen le fait pour vous.
Groupes de scène
getGroups()Promise<SceneGroupsState>Tous les groupes de la scène — déduits de l’IFC (projet, site, localisation) et créés par les utilisateurs.
createGroup(name, modelIds?)Promise<string>Crée un groupe, éventuellement avec des modèles. Renvoie son id.
renameGroup(id, name) · deleteGroup(id)Promise<void>Renomme ou supprime un groupe utilisateur. Les groupes automatiques ne sont pas modifiables.
assignToGroup(itemId, groupId | null | 'loose')Promise<void>Place un modèle ou un nuage de points dans un groupe ; null le rend au regroupement automatique, 'loose' le laisse sans groupe.
setGroupVisible(id, visible) · isolateGroup(id | null)Promise<void>Affiche ou masque un groupe entier, ou n’en affiche qu’un.
frameGroup(id)Promise<void>Cadre la caméra sur un groupe, membres masqués compris.
Mesures
setMeasureTool(tool)Promise<void>Arme un outil de mesure pour le visiteur (ouvre le panneau Mesurer), ou 'none'. Les résultats arrivent sur measurements-changed.
getMeasurements()Promise<MeasurementsState>Toutes les mesures à l'écran. Valeurs en SI — mètres, m², degrés — quel que soit l'affichage.
clearMeasurements(id?)Promise<MeasurementsState>Retire une mesure, ou toutes.
Requêtes
getModels()Promise<ModelSummary[]>Liste les modèles chargés. Renvoie une promesse.
findElements({ ifcClass?, globalId?, name?, modelId?, limit? })Promise<ElementSummary[]>Trouver des éléments par classe IFC, GlobalId et/ou nom dans les modèles chargés. Chaque résultat contient ce qu’attendent getElement(), select() et frame().
getElement(expressId | globalId, modelId?)Promise<IfcElementData | null>Par expressID ou GlobalId. Les données IFC d’un élément : attributs avec le GlobalId, ses jeux de propriétés et quantités, et ceux de son type — typeName, typeProperties — avec unités. effectivePropertySets fusionne les deux, la valeur propre de l’élément l’emporte.
validate(modelId?, { force? })Promise<ValidationRunResult>Valider un modèle maintenant (par défaut l’actif) et résoudre avec son Health Score et le nombre d’anomalies. Rejette avec la raison.
getValidation()Promise<ValidationSummary | null>Le résultat de validation affiché, ou null avant qu’une exécution en ait produit un. Voir « Cycle de la validation ».
getValidationStatus()Promise<ValidationStatus>Où en est la validation : idle, running (avec progression), done ou error.
getStats()Promise<StatsResult>Nombre d’éléments par catégorie et par modèle — pour les graphiques. Renvoie une promesse.
getIssues({ severity?, limit?, modelId? })Promise<IssuesResult>Anomalies de validation pour un tableau (filtre par sévérité / limite). Renvoie une promesse.
screenshot()Promise<string>Capture la vue 3D actuelle en data URL PNG. Renvoie une promesse.
compare({ base, head, baseLabel?, headLabel? })Promise<{ changes }>Compare deux livraisons par URL (par GlobalId) et ouvre l’espace de comparaison sur le résultat.
Panneaux
openPanel(panel | null) · closePanel()voidOuvre un panneau d'outil, ou null pour fermer celui qui est ouvert. Un panneau indisponible — masqué par le chrome, ou sans contenu sur lequel agir — ne fait rien et n'est pas une erreur.
getPanels()Promise<PanelsResult>Quel panneau est ouvert et lesquels sont proposés à cet instant. Un outil absent de `available` ne peut pas être ouvert ; il n'est pas simplement désactivé.
setPanels(panels)voidLimite la barre à ces panneaux, à l'exécution. Même vocabulaire que le paramètre d'URL `panels=`, et il prime sur lui. Restreint ce que le visualiseur propose et n'ajoute jamais ; un tableau vide signifie aucune barre.
Nuages de points
addPointCloud(fileName, bytes)Promise<string>Charge un relevé depuis des octets — LAS, LAZ, COPC, PLY, PCD ou texte. Le tampon est transféré, pas copié. Résout avec l'identifiant du nuage.
addPointCloudFromUrl(url, fileName?)Promise<string>Charge un relevé que le visualiseur récupère lui-même. L'URL doit autoriser CORS.
listPointClouds()Promise<PointCloudInfo[]>Tous les relevés chargés. `pointCount` est ce qui réside en mémoire, `declaredCount` ce que contient le fichier — ils diffèrent quand le budget a tronqué l'analyse.
removePointCloud(cloudId) · clearPointClouds()Promise<void>Décharge un relevé et libère ses tampons GPU, ou décharge-les tous.
setPointCloudVisible(cloudId, visible)Promise<void>Affiche ou masque un relevé sans le décharger.
fitPointCloud(cloudId?)Promise<void>Cadre la caméra sur un relevé, ou sur le premier chargé.
setPointCloudDisplay(display, renderBudget?)Promise<void>Taille des points, opacité, mode de couleur, densité, seuil de confiance — partagés par tous les relevés. Chacun est un uniform de shader, donc instantané sur 20 millions de points.
inspectPointCloud(enabled?)Promise<void>Arme le clic-pour-lire. Une fois armé, cliquer un point émet `pointcloud-picked` avec les coordonnées DU FICHIER à côté de celles de la scène.
setPointCloudPlacement(placement, cloudId?)Promise<void>Ajuste un relevé à la main : position, lacet, mise à niveau, échelle. Partiel et borné ; s'applique par-dessus l'alignement déduit sans le remplacer.
setPointCloudUpAxis(axis, cloudId?)Promise<void>Corrige l'axe que le relevé considère comme vertical. PLY, PCD et texte ne déclarent aucune orientation, le visualiseur la déduit — vérifiez `upAxisSource`.
Imported 3D models
addMesh(files)Promise<string>Importe un modèle depuis des octets. Prend une LISTE : un .gltf a besoin de son .bin et de ses textures, un .obj de son .mtl — seul, le fichier d'entrée donne de la géométrie grise.
addMeshFromUrl(urls)Promise<string>Importe un modèle que le visualiseur récupère. Passez toutes les URL nécessaires ; elles sont récupérées en parallèle et doivent toutes autoriser CORS.
listMeshes()Promise<MeshInfo[]>Tous les modèles importés, avec leurs nombres de triangles et de textures. Lisez `unitSource` et `upAxisSource` avant de faire confiance aux valeurs voisines.
removeMesh(meshId?) · clearMeshes()Promise<void>Retire un import et libère sa géométrie, ses matériaux et ses textures, ou retire-les tous.
setMeshVisible(visible, meshId?)Promise<void>Affiche ou masque un import sans le décharger.
fitMesh(meshId?)Promise<void>Cadre la caméra sur un import, ou sur tous.
setMeshPlacement(placement, meshId?)Promise<void>Place un import à la main : position, lacet, mise à niveau, échelle. Partiel et borné. Un import démarre centré sur l'IFC, posé sur son sol.
setMeshUpAxis(axis, meshId?)Promise<void>Corrige l'axe vertical de la source. Utile seulement pour OBJ — la spécification glTF impose Y vers le haut, elle rapporte donc `upAxisSource: 'declared'`.
setMeshUnit(unitScale, meshId?)Promise<void>Corrige l'unité source : 1 mètres, 0,01 centimètres, 0,001 millimètres, 0,3048 pieds. Aucun de ces formats ne l'enregistre, le visualiseur la déduit de la taille du modèle.
Conformité IDS et EIR
checkIds(idsXml)Promise<IdsResult>Vérifie le modèle par rapport à un IDS buildingSMART (XML .ids). S’exécute dans un worker. Renvoie réussite/échec par spécification.
checkEir(profile)Promise<IdsResult>Vérifie la maquette avec un profil de validation EIR / BIM — un objet, son JSON (y compris la forme compacte) ou l'id d'un profil intégré comme 'builtin-en14351-1'. Compilé en IDS et exécuté par le même moteur : même IdsResult que checkIds.
Langues
setLanguage(lang)voidChanger la langue de l'interface à l'exécution.
getLanguages()string[]Lister les codes de langue pris en charge (libellés via IfcViewer.LANGUAGES).
Cycle de vie et événements
whenReady()Promise<void>Se résout quand le visualiseur peut accepter des commandes.
isReadybooleanGetter — true dès que le visualiseur a signalé qu’il est prêt.
on(event, cb)() => voidS’abonner à un événement ; renvoie une fonction de désabonnement.
off(event, cb)voidSupprime un écouteur d’événement ajouté précédemment.
dispose()voidDémonter le visualiseur et retirer l’iframe.
Les appels concurrents à add() / addFromUrl() sont sérialisés en interne et chaque promesse est corrélée à son propre chargement — un chargement initié par l’application (paramètre d’URL, téléversement dans l’iframe) ne résout jamais votre promesse add().
Options du constructeur
| Option | Type | Par défaut | Description |
|---|---|---|---|
ui | 'minimal' | 'full' | 'kiosk' | 'client' | 'article' | 'embed' | 'minimal' | Preset de l’interface. 'embed' : pour une app hôte qui fournit le fichier — 3D, propriétés à la sélection, une barre compacte avec Valider et Mesurer ; sans Ouvrir, Partager, compte ni langue. |
toolbar | boolean | preset | Afficher la barre d’outils (par défaut, celle du preset). false laisse la vue 3D et les panneaux. |
tools | ('validate' | 'measure')[] | ['validate', 'measure'] | Outils de la barre compacte de ui: 'embed' — 'validate', 'measure'. [] pour aucun. |
autoFrame | boolean | true | Cadrer toute la scène en iso, avec une marge, à chaque fin de chargement. false vous laisse la caméra. |
validate | boolean | true | Lancer la validation au chargement (alimente le Health Score). |
panel | boolean | false | Ouvrir automatiquement le panneau de validation. |
panels | PanelName[] | — | Limite la barre d'outils à ces panneaux, dès la première image — `['scene','map']`, ou `[]` pour aucune barre. Mêmes noms que setPanels et que le paramètre d'URL `panels=`. Préférable à un setPanels après chargement, qui affiche la barre complète puis retire des outils. |
lang | string | auto | Forcer la langue de l’interface (ex. en, es, ja). |
accent | #rrggbb | brand | Teinter le visualiseur aux couleurs de votre tableau de bord. |
model | string | — | Charger automatiquement cette URL IFC publique dès que possible. |
background | BackgroundSpec | — | Fond de scène dès la première image — un préréglage, '#rrggbb' ou '#haut,#bas'. |
map | boolean | ('terrain' | 'buildings' | 'showcase')[] | — | Place le modèle sur la carte au chargement : true, ou les couches ['terrain','buildings','showcase']. |
solar · moon | 'MM-DDTHH:MM' · boolean | — | Ouvre l'étude solaire à une heure locale du site : '06-21T18:00'. |
scans | string[] | — | URL de nuages de points à charger avec le modèle. |
layers | string | — | Configuration de couches de données à ouvrir : l’URL d’un JSON exporté depuis le panneau Couches de données (sources en direct, styles, alertes). Depuis 1.16. |
scene | string | — | Scène à ouvrir : l’URL d’un document de scène (modèles, couches de données, appareils en direct, carte, caméra). Depuis 1.17. |
view · fill | CameraView · number | — | Une fois tous les modèles chargés, les cadrer serré depuis cette vue. ui: 'article' implique 'iso'. |
wheel | 'always' | 'ctrl' | 'always' | 'ctrl' : la molette fait défiler votre page et ne zoome qu’avec Ctrl/⌘, comme une carte intégrée. ui: 'article' l’implique. |
lazy | boolean | 'visible' | false | Ne rien charger avant d’en avoir besoin : true affiche une affiche avec bouton, 'visible' démarre à l’approche de l’écran. |
poster · posterTitle · posterText · launchLabel | string | — | Image, titre, texte et libellé du bouton de l’affiche, jusqu’à ce que la visionneuse soit prête. |
aspectRatio | string ('16/10') | — | Taille par proportion ('16/10') : affiche et visionneuse partagent la boîte, rien ne bouge sur la page. |
turntable | boolean | number | false | Lente rotation au repos après chargement (true = 6°/s, ou degrés par seconde). |
pauseOffscreen | boolean | ui === 'article' | Ne plus rendre hors écran. true par défaut avec ui: 'article'. |
fullscreenButton | boolean | false | Un petit bouton d’agrandissement dans le coin. |
height · width | number | string | '100%' | Taille de l’iframe (nombre → px). |
baseUrl | string | auto | URL de base de l’application. Déduite de l’URL du script. |
loadTimeout | number | 120000 | Rejeter add()/addFromUrl() après N ms (0 désactive). |
className · title | string | — | Classe supplémentaire / title de l’iframe. |
on* callbacks | function | — | Callbacks pratiques — équivalents à .on(...). |
Événements
| Événement | Payload | Description |
|---|---|---|
ready | { languages } | Visualiseur monté et prêt. |
model-progress | { percent, phase } | Progression du chargement (téléchargement → analyse → rendu). |
model-loaded | { modelId, fileName, elementCount, fromCache } | Un modèle a fini de charger. |
validation-started | { modelId } | Une validation a commencé sur un modèle — après un chargement (validate: true) ou via validate(). |
validation-completed | { modelId, qualityScore, errors, warnings, info, total } | Validation terminée : le Health Score et les comptes, et le modèle concerné (null pour l’agrégat fédéré). |
validation-failed | { modelId, message } | Une validation n’a pas pu s’exécuter ou n’a pas abouti — avec la raison. |
model-error | { message, url?, name? } | Un chargement a échoué. |
element-selected | { expressId, modelId, ifcType, name, globalId } | L’utilisateur a choisi un élément — avec son GlobalId, pour le relier à vos propres données. |
pointcloud-picked | { cloudId, position, sourcePosition, classification, intensity, distance } | Un point a été lu sur un scan, dans les coordonnées du fichier et non de la scène. |
map-feature-picked | { id, name?, label?, featureKind, heightM?, heightEstimated } | Un bâtiment des environs OpenStreetMap a été cliqué. Du contexte, pas le modèle. |
walk-changed | { active, speed } | Le mode visite a été activé ou désactivé — par le visiteur ou par l’hôte. |
measurements-changed | { tool, units, items } | Une mesure a été ajoutée, retirée ou renommée. Contient la liste complète. |
tour-started | { title, total, template } | Une visite a commencé. |
tour-step | { index, total, caption } | La visite est passée à un autre arrêt. |
tour-ended | { completed } | La visite s’est arrêtée ; completed si le dernier arrêt était atteint. |
presentation-progress | { stage, label?, progress } | Le réalisateur génère ou exporte une présentation. |
layer-feature-picked | { layerId, layer, featureId, geometry, lonLat, properties } | Une entité d’une couche de données a été cliquée : sa couche, ses propriétés et un point à marquer. |
alert | { kind, from, id, name, rule, count, sample } | Une règle d’une couche de données ou du jumeau a commencé ou cessé d’alerter. |
Membres statiques
IfcViewer.LANGUAGES ({ code, label }[], noms natifs) et IfcViewer.SUPPORTED_LANGUAGES (codes) — pour construire un sélecteur de langue avant que le visualiseur ne soit prêt.
Web component
Zéro JavaScript — placez la balise dans n’importe quelle page ou tableau de bord et définissez les attributs.
Attributs: modeluilangaccentvalidatepanelbase-urltoolbartoolsauto-frame
Les événements sont réémis en CustomEvents DOM nommés ifcviewer:<type> (detail = payload). L’IfcViewer sous-jacent est sur le .viewer de l’élément ; getStats / getIssues / screenshot y sont délégués.
const el = document.querySelector("ifc-viewer");
el.addEventListener("ifcviewer:validation-completed", (e) => {
updateScore(e.detail.qualityScore);
});
const stats = await el.getStats();Guides
Recettes
Des motifs à copier-coller pour ce que les équipes construisent vraiment.
Inspecteur d’éléments
Cliquez dans la 3D et versez les property sets IFC dans votre propre panneau.
viewer.on("element-selected", async (e) => {
const data = await viewer.getElement(e.expressId, e.modelId);
renderPanel(data.name, data.globalId, data.propertySets);
});Widget Health Score
Affichez le score de qualité du modèle et le nombre d’anomalies dans votre tableau de bord.
viewer.on("validation-completed", (v) => {
setScore(v.qualityScore); // 0–100, or null
setCounts(v.errors, v.warnings, v.info);
});Contrôle de conformité IDS
Validez contre un IDS buildingSMART et listez réussite / échec par spécification.
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)));
});Validation EIR / BIM
Exécutez un profil EIR modifiable (style ISO 19650) — sans fichier .ids. Les échecs portent le GlobalId de l’élément.
// 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(" · ")));
});Une scène pour un article de blog
Fond blanc, le modèle sur son site et l’ombre du soir du solstice d’été — déclaré d’avance, sans clic.
// 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 thesePlan et mesures
Coupez un plan à un niveau nommé, laissez le lecteur mesurer, puis relisez les résultats en unités 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)));
});Une visite guidée dans un article
Définissez les arrêts une fois avec getCamera(), puis laissez la visite défiler seule à côté du texte.
// 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));Une vidéo du modèle, depuis votre page
Générez une présentation avec une recette et récupérez le MP4 — sans capture d’écran, sans téléversement.
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 }));Une couverture pour votre article
Générez une épingle Pinterest ou une carte LinkedIn du modèle avec votre titre et récupérez le 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 modèle dans un article
ui: 'article', c’est le canevas seul, cadré pour que le modèle le remplisse, avec une molette qui laisse le lecteur continuer à défiler. L’angle se choisit avec 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,
});Une histoire qui avance avec le texte
Épinglez la visionneuse et laissez chaque paragraphe choisir le plan : isoler les murs, tourner autour, faire venir le soleil du soir.
// <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 jumeau urbain en direct dans votre page
Ouvrir une scène publiée, réagir à ses alertes et à ses clics, ajouter votre propre source en direct à côté et partager un lien vers le résultat.
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" });Une fiche produit dans votre app
Figez la version, transmettez le fichier et montrez ce que le fabricant a mis dans le type : coefficient U, classe 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);Aux couleurs de votre marque
Alignez le visualiseur sur votre produit avec une seule couleur d’accent.
new IfcViewer("#viewer", { accent: "#3fa866" });
// …or on the web component:
// <ifc-viewer accent="#3fa866" model="…"></ifc-viewer>Sélecteur de langue
Construisez un sélecteur à partir des langues livrées avec le visualiseur.
// build a <select> from the bundled languages
IfcViewer.LANGUAGES.forEach(({ code, label }) => addOption(code, label));
// switch at runtime
viewer.setLanguage("ja");Health Score
Cycle de la validation
validate: true valide chaque modèle une fois chargé ; validate() la lance à la demande et résout avec le résultat. Ce que renvoie getValidation() à chaque moment :
| Quand | getValidation() renvoie |
|---|---|
| Avant toute validation | null — getValidationStatus() indique idle. |
| Pendant la première exécution | null — getValidationStatus() indique running, avec la progression ; validation-started a été émis. |
| Après une exécution | { qualityScore, errors, warnings, info, total, status: 'done', modelId }. Avec plusieurs modèles, l’agrégat (modelId: null). |
| Pendant une exécution plus récente | Les chiffres précédents, avec status: 'running'. |
| Après un échec | Les chiffres précédents avec status: 'error' et error — ou null s’il n’y en avait pas. validation-failed a été émis et validate() a rejeté. |
Versions
Figer une version
Depuis la 1.18.0, chaque version est aussi publiée à un chemin qui ne change jamais. Figez-la en production ; le chemin mobile suit la dernière version.
<script type="module">
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js";
</script>| Chemin | Cache | Usage |
|---|---|---|
/sdk/ifc-viewer.es.js | revalidé à chaque chargement | Pour essayer — toujours la dernière. |
/sdk/1.18.0/ifc-viewer.es.js | immuable, en cache un an | Production — le code testé est le code exécuté. |
Quelle visionneuse ouvre un SDK figé ?
L’iframe est la visionneuse en direct de la même origine, pas une copie figée : un SDK figé garde son API et son comportement tandis que la visionneuse continue de s’améliorer. Elle répond à chaque commande de tout SDK publié — le build le vérifie —, donc une ancienne version figée continue de fonctionner.
Figer aussi le contenu
versions.json, à côté du SDK, liste le hash Subresource Integrity de chaque version :
<script type="module" src="https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js"
integrity="sha384-WsT4d0DS1oqva4rLWCGBDrx50uZVA3B95IDWbfxaFt8lnjs6NgOrjKNcWQL57/0z" crossorigin="anonymous"></script>Journal des modifications
1.18.0 — objets de catalogue dans une app hôte. getElement() renvoie le GlobalId et les données du type (typeName, typeProperties avec unités, effectivePropertySets ; types IFC4 et styles IFC2x3). validate(), getValidationStatus(), validation-started / validation-failed, et validate: true valide vraiment après chaque chargement. Cadrage automatique après chargement, fit() et frame() avec marge pour les petits objets, frame(elementId), et setView() déplace de nouveau la caméra. ui: 'embed' avec toolbar et tools. Versions figées sous /sdk/1.18.0/. findElements(), GlobalIds pour getElement() / select() / element-selected, jeux de dormant et d’ouvrant des fenêtres et portes, et matériaux du type ; en ui: 'embed' le panneau de propriétés garde l’objet choisi visible. checkEir('builtin-en14351-1') vérifie les performances déclarées des fenêtres et portes extérieures selon l'EN 14351-1 — vent, étanchéité à l'eau, acoustique, transmission thermique, g et τv, fabricant et code produit — dans les property sets IFC standard ; les règles EIR gagnent where (seulement les éléments ayant une valeur de propriété) et optional.
Aide
Dépannage
Les quelques points sur lesquels on trébuche.
Rien ne s’affiche
Vérifiez, dans cet ordre : que le visualiseur a atteint ready, que le modèle s’est chargé (model-loaded plutôt que model-error), que le conteneur a une hauteur non nulle et que la source d’octets est bien de l’IFC valide. Le message de model-error nomme la cause.
J’obtiens une erreur CORS au chargement d’un modèle
CORS ne concerne que addFromUrl(url). Si vous maîtrisez les octets, récupérez-les vous-même et appelez add(name, bytes) — CORS ne s’applique pas puisque vous transmettez les données directement.
L’iframe reste blanc ou refuse de se charger
L’application doit être intégrable — pas de X-Frame-Options: DENY ni de CSP frame-ancestors restrictive sur l’hôte. Notre application hébergée autorise l’intégration.
Mon ArrayBuffer est vide après add()
add() transfère le buffer pour une remise sans copie, ce qui le détache dans votre code. Passez une copie (bytes.slice(0)) si vous avez encore besoin des octets ensuite.
Les chargements ou les requêtes expirent
add()/addFromUrl() échouent après loadTimeout (120 s par défaut ; 0 désactive). Les requêtes échouent après 30 s, checkIds après 120 s. De gros modèles sur des appareils lents peuvent nécessiter un loadTimeout plus élevé.
Quels navigateurs sont pris en charge ?
Tout navigateur moderne à mises à jour continues avec WebAssembly et modules ES — Chrome, Edge, Firefox, Safari. Pas Internet Explorer.
J’obtiens un 404 sur mon serveur de développement local
Un serveur de développement SPA peut intercepter /sdk/ avec son fallback HTML. Testez contre un vrai build ou un hébergement statique, ou importez directement le module /sdk/ifc-viewer.es.js.