IFC Viewer SDK · v1.18.0
Incorpore o visualizador IFC no seu próprio produto
Insira o mesmo visualizador do navegador no seu CDE, gémeo digital ou ferramentas internas de projeto. Carregue dados IFC da sua app enquanto cada modelo é analisado no cliente.
Três linhas até ao primeiro render
const viewer = new IfcViewer("#viewer");
await viewer.add("project.ifc", ifcBytes);
// → rendered, validated, all on the client- 100% do lado do cliente
- Sem backend de upload
- Zero dependências em runtime
- Wrapper de ~6 KB
- Tipos TypeScript
- 10 idiomas
- Motor validado com IDS
Instalação
Início rápido
Escolha a sua stack. Copie, cole e renderize — sem passo de build, sem chave de 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 — coloque a tag em qualquer página ou dashboard e defina atributos.
<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>Monte num effect e desmonte no unmount. Seguro com a dupla invocação do StrictMode do 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 }} />;
}Monte em onMounted, liberte em 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>Alojado por nós ou por si
Os exemplos importam do nosso CDN, que funciona em qualquer origem. Para alojar por si, sirva ifc-viewer.es.js a partir do seu domínio — o SDK descobre o URL da aplicação relativamente ao script. A aplicação está noutro sítio? Use a opção baseUrl.
Ainda não há pacote npm: importe o módulo por URL (acima) ou incorpore ifc-viewer.es.js na sua aplicação.
Demonstração
Demo ao vivo
Esta página está a executar o SDK. Clique num controlo para obter um IFC de exemplo e conduzir o visualizador — cada botão mostra a chamada que faz.
⚠ O host de exemplo pode estar a limitar pedidos. Tente novamente daqui a pouco.
Arquitetura
Como funciona
Um wrapper mínimo na sua página, o visualizador completo num iframe isolado, os seus bytes a atravessar — e processados onde já estavam.
A sua aplicação
Um módulo ES de ~6 KB que cria um iframe e uma API JS tipada. Nada do peso de 3D nem de parsing toca no seu bundle.
Ponte postMessage
Os bytes IFC são transferidos para o iframe como ArrayBuffer (sem cópia). Comandos e consultas são mensagens correlacionadas de pedido/resposta.
O visualizador
three.js + web-ifc compilado para WebAssembly fazem o parsing e o render do modelo dentro do iframe — na máquina do visitante.
Porque é privado
Os modelos nunca chegam a um servidor. Nenhuma cadeia de upload para proteger, nenhuma questão de residência de dados, nenhuma fila por tamanho de ficheiro.
Porque escala
O cálculo corre no dispositivo de cada visitante. Servir um modelo de 200 MB custa o mesmo que um de 2 MB — é um ficheiro estático.
Conceitos
Conceitos essenciais
Quatro coisas a saber antes de construir.
Montagem e prontidão
new IfcViewer(target) monta de imediato e põe os comandos em fila até o visualizador estar pronto. await IfcViewer.create(target) (ou whenReady()) devolve uma instância utilizável de imediato.
// 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 ou URL
add(name, bytes) transmite bytes que já tem — sem CORS e sem enviar nada; o buffer é transferido (fica detached), por isso passe uma cópia se ainda precisar dele. addFromUrl(url) descarrega um URL público com 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");Eventos
Subscreva com on(event, cb), que devolve uma função para cancelar a subscrição. Tem ready, model-progress, model-loaded, validation-completed, model-error e element-selected.
const off = viewer.on("model-loaded", (m) => {
console.log(m.fileName, m.elementCount);
});
// later: off();Consultas
Obtenha dados a pedido: getModels, getElement, getValidation, getStats, getIssues, checkIds, checkEir, screenshot. Cada uma é pedido/resposta e é rejeitada após um tempo limite (30 s; IDS / EIR até 120 s).
const models = await viewer.getModels();
const { qualityScore } = await viewer.getValidation();
const png = await viewer.screenshot();Referência
Referência da API
Todos os métodos, agrupados — com assinaturas e tipos de retorno. A seguir vêm as opções do construtor, os eventos e o web component <ifc-viewer>.
Construtor
new IfcViewer(target, options?)IfcViewerMontar num seletor CSS ou elemento. Veja as opções abaixo.
IfcViewer.create(target, options?)Promise<IfcViewer>Cria um visualizador e resolve assim que estiver pronto para aceitar comandos.
Carregamento de modelos
add(name, bytes)Promise<ModelLoadedEvent>Carregar bytes IFC (ArrayBuffer/Uint8Array) da sua app. O buffer é transferido para uma passagem sem cópia.
addFromUrl(url, name?)Promise<ModelLoadedEvent>Carregar um URL IFC público com CORS.
removeModel(modelId)voidDescarrega um modelo por id.
clear()voidRemover todos os modelos carregados.
Câmara e seleção
select(expressId | globalId, modelId?)voidSelecionar e enquadrar um elemento — pelo seu expressID IFC ou pelo GlobalId (que também encontra o modelo).
setView(view)voidVoar para uma vista com nome da cena — iso, front, back, left, right, top, bottom — com toda ela no enquadramento. Sem resposta (entre 1.14 e 1.17 o visualizador ignorava-a).
frame({ view?, fill?, azimuth?, elevation?, animate? })Promise<{ scope }>Enquadramento para apresentar: o modelo ocupa `fill` do quadro (0,2–0,98, por omissão 0,85), ajustado aos seus cantos e não à esfera envolvente. azimuth / elevation olham de qualquer ângulo.
frame(elementId, modelId?, options?)Promise<{ scope: 'element' }>Enquadrar um elemento (expressID): a partir do ângulo atual, salvo se for indicada uma vista. Rejeita se não estiver no modelo.
fit() · reset()voidEnquadrar o modelo ativo a partir do ângulo atual, com margem / repor a câmara.
setCamera(position, direction)voidColoca a câmara numa posição a olhar numa direção.
getCamera()Promise<CameraState | null>Onde está a câmara e para onde olha — guarde e restaure com lookAt.
lookAt(position, target, animate?)Promise<void>Leva a câmara a uma posição a olhar para um alvo (metros de cena, Y para cima).
setWalkMode(enabled, { speed? })Promise<WalkState>Modo passeio na primeira pessoa (WASD, arrastar para olhar, Esc para sair). Emite walk-changed.
getWalkState()Promise<WalkState>Se o modo passeio está ativo e a que velocidade.
Visibilidade
isolate(ifcType?)voidIsolar uma categoria (ex.: "IfcWall"); omitir para limpar.
showAll()voidRestaurar toda a visibilidade (limpar ocultos + isolamento).
hideElements(ids, modelId?) · showElements(ids, modelId?)voidOculta / mostra um conjunto de elementos por expressID.
setModelVisible(modelId, visible)Promise<void>Mostra ou oculta um modelo de uma cena federada sem o descarregar.
setModelOpacity(opacity, modelId?)Promise<void>Torna um modelo translúcido (0,05–1) — p. ex. a arquitetura à volta das instalações.
isolateModel(modelId | null)Promise<void>Mostra só este modelo; null volta a mostrá-los todos.
Aspeto e apresentação
setBackground(background)Promise<BackgroundState>Muda o fundo da cena: um preset ('white', 'paper', 'blueprint', 'sky', 'studio'), '#rrggbb', '#cima,#baixo' ou { top, bottom }. Não é guardado como preferência do visitante.
getBackground()Promise<BackgroundState>O fundo atual da cena.
setAccent(color)Promise<void>Muda a cor de destaque da interface em tempo de execução (#rrggbb).
setClientMode(enabled)Promise<void>Liga ou desliga a vista cliente — a de apresentação a stakeholders, igual a ui: 'client'.
setRenderQuality(quality)Promise<void>'quality' ativa a renderização mais pesada para uma imagem de destaque; 'standard' para o dia a dia.
Estudo solar
setSolar(opts?)Promise<SolarState>Sombras reais numa data e hora locais do local: { date: '06-21', time: '18:00' }. O local vem da georreferência do IFC; passe location para um modelo sem ela — nunca uma cidade por omissão em silêncio.
getSolar()Promise<SolarState>Data, hora e fuso do estudo, e onde fica o local.
Modo mapa
setSiteContext(opts?)Promise<SiteContextState>Coloca o modelo no mapa com terreno e envolvente OpenStreetMap. Chamá-lo é a sua página a declarar o consentimento de mosaicos pelos visitantes; mostre as atribuições.
getSiteContext()Promise<SiteContextState>Estado do modo mapa, a colocação e as atribuições a mostrar.
Cenas
openScene(url | SceneDocument)Promise<void>Abrir outra cena: o URL de um .scene.json ou um documento de cena (viaja comprimido no endereço da moldura, por isso tem de caber numa ligação). O visualizador recarrega com ela; as chamadas pendentes são rejeitadas e ready volta a disparar.
exportScene({ title?, description?, camera? })Promise<SceneExport>A cena no ecrã como documento de cena — modelos por URL, camadas, gémeo, mapa, câmara — com as fontes a creditar e uma ligação que a contém.
packScene(doc)Promise<string | null>Comprimir você mesmo um documento de cena para uma ligação #scene= (null se for grande demais para uma ligação).
Camadas de dados e gémeo
getLayerPresets()Promise<LayerPreset[]>O catálogo de fontes públicas em direto, com nome no idioma do visualizador, o que cada uma precisa num navegador (uma chave, um proxy) e se tem dados perto do local.
addLayer({ preset } | { url, live? } | { geojson })Promise<DataLayerInfo>Adicionar uma camada: uma fonte do catálogo ({ preset }), GeoJSON ou um feed em direto num URL ({ url, live }), ou GeoJSON que já tem ({ geojson }). Só para a sessão: nunca guardada nas camadas do visitante.
getLayers()Promise<DataLayerInfo[]>Todas as camadas de dados: estado, número de elementos, extensão, atualização e atribuição.
setLayerVisible(id, visible)Promise<DataLayerInfo>Mostrar ou ocultar uma camada.
frameLayer(id)Promise<void>Levar a câmara aos elementos de uma camada.
removeLayer(id)Promise<void>Remover uma camada.
getTwin()Promise<TwinState>O gémeo operacional agora: fontes de dispositivos (estado, última leitura) e, por ligação, a regra que se cumpre, a sua cor, o valor da etiqueta e se está em alerta.
Cortes
addSection({ axis?, offset?, level?, flip? })Promise<SectionsState & { id }>Adiciona um plano de corte. { level: 'Piso 1' } é uma planta nesse piso; { axis: 'x', offset: 4.5 } um corte a 4,5 m.
updateSection(id, { offset?, enabled?, flipped? })Promise<SectionsState>Move, liga/desliga ou inverte um plano.
removeSection(id?)Promise<SectionsState>Remove um plano, ou todos os cortes sem id.
setSectionBox('model' | 'selection' | false)Promise<SectionsState>Uma caixa de corte à volta do modelo ou da seleção; false remove-a.
getSections()Promise<SectionsState>Todos os planos, a caixa e os pisos disponíveis para cortes por nível.
Tours
startTour(template?, { autoplay?, title? })Promise<TourState>Inicia um tour predefinido: 'social', 'client-walkthrough' ou 'technical-review' (percorre as incidências de validação). Reproduzido pela barra de tour do visualizador.
playTour({ title?, steps }, { startAt?, autoplay? })Promise<TourState>Reproduz um tour criado por si: paragens de câmara com legendas, destaques e classes isoladas. autoplay: true ou ms por paragem.
nextTourStep() · prevTourStep() · goToTourStep(i)Promise<TourState>Mudar de paragem.
setTourAutoplay(autoplay)Promise<TourState>Liga ou desliga o avanço automático do tour em curso.
stopTour()Promise<TourState>Para o tour e devolve a câmara.
getTour()Promise<TourState>O tour e as suas paragens, no formato de playTour() — guarde-o e reproduza-o mais tarde.
Realizador de apresentações
getPresentationRecipes()Promise<PresentationRecipe[]>As receitas do realizador (demo para reunião, reel, teaser do LinkedIn, lançamento, looks editoriais…).
createPresentation(recipe?, options?)Promise<PresentationState>Transforma o modelo num vídeo editado a partir de uma receita: planos gerados do IFC, legendas, música. Abre o Clip Studio com o resultado. Acompanhe presentation-progress.
exportPresentation({ resolution?, music? })Promise<PresentationVideo>Codifica a apresentação no navegador do visitante e devolve os bytes do MP4 — para enviar, anexar ou reproduzir num <video>.
closePresentation()Promise<void>Fecha o Clip Studio.
Estúdio de capas
getCoverOptions()Promise<CoverCatalog>As receitas, modelos, formatos e paletas do estúdio de capas.
createCover({ recipe?, template?, format?, palette?, text? })Promise<CoverState>Cria uma capa: executa uma receita (captura as vistas de que precisa) e depois define modelo, formato, paleta e textos. Abre o estúdio, que continua editável.
getCover()Promise<CoverState | null>A capa tal como está, ou null se o estúdio estiver fechado.
exportCover({ type?, slide? })Promise<CoverFile>Exporta-a: png / jpeg (uma página), pdf / pptx (todo o documento), zip (todas as páginas). Devolve os bytes.
closeCover()Promise<void>Fecha o estúdio de capas.
Kit de artigo
activate()voidArrancar já um visualizador diferido (o que faz o botão do póster). As chamadas anteriores ficaram em fila.
bindSteps(steps, { rootMargin? })() => voidScrollytelling: enquanto cada parágrafo cruza o meio do ecrã, enquadrar, isolar, mover o sol ou correr o seu código. Devolve uma função para desligar.
setTurntable(enabled | degPerSec)Promise<{ active, speed }>Uma rotação lenta em repouso (graus por segundo; false para-a). Para ao primeiro toque, nunca com movimento reduzido.
toggleFullscreen()Promise<void>Ampliar a figura para ecrã inteiro, ou voltar.
setPaused(paused)Promise<{ paused }>Parar ou retomar a renderização. pauseOffscreen fá-lo por si.
Grupos de cena
getGroups()Promise<SceneGroupsState>Todos os grupos da cena — deduzidos do IFC (projeto, local, localização) e criados pelos utilizadores.
createGroup(name, modelIds?)Promise<string>Cria um grupo, opcionalmente com modelos. Devolve o seu id.
renameGroup(id, name) · deleteGroup(id)Promise<void>Muda o nome ou apaga um grupo de utilizador. Os automáticos não se editam.
assignToGroup(itemId, groupId | null | 'loose')Promise<void>Move um modelo ou nuvem de pontos para um grupo; null devolve-o ao agrupamento automático, 'loose' deixa-o sem grupo.
setGroupVisible(id, visible) · isolateGroup(id | null)Promise<void>Mostra ou oculta um grupo inteiro, ou mostra só um.
frameGroup(id)Promise<void>Enquadra a câmara num grupo, incluindo membros ocultos.
Medições
setMeasureTool(tool)Promise<void>Arma uma ferramenta de medição para o visitante (abre o painel Medir), ou 'none'. Os resultados chegam em measurements-changed.
getMeasurements()Promise<MeasurementsState>Todas as medições no ecrã. Valores em SI — metros, m², graus — seja qual for a apresentação.
clearMeasurements(id?)Promise<MeasurementsState>Remove uma medição, ou todas.
Consultas
getModels()Promise<ModelSummary[]>Lista os modelos carregados. Devolve uma promessa.
findElements({ ifcClass?, globalId?, name?, modelId?, limit? })Promise<ElementSummary[]>Encontrar elementos por classe IFC, GlobalId e/ou nome nos modelos carregados. Cada resultado traz o que getElement(), select() e frame() pedem.
getElement(expressId | globalId, modelId?)Promise<IfcElementData | null>Por expressID ou GlobalId. Os dados IFC de um elemento: atributos com o GlobalId, os seus conjuntos de propriedades e quantidades, e os do seu tipo — typeName, typeProperties — com unidades. effectivePropertySets junta ambos e prevalece o valor do próprio elemento.
validate(modelId?, { force? })Promise<ValidationRunResult>Validar um modelo agora (por omissão, o ativo) e resolver com o seu Health Score e as contagens de problemas. Rejeita com o motivo.
getValidation()Promise<ValidationSummary | null>O resultado de validação no ecrã, ou null antes de uma execução ter produzido um. Ver «Ciclo da validação».
getValidationStatus()Promise<ValidationStatus>Em que ponto está a validação: idle, running (com progresso), done ou error.
getStats()Promise<StatsResult>Contagem de elementos por categoria e modelo — para gráficos. Devolve uma promessa.
getIssues({ severity?, limit?, modelId? })Promise<IssuesResult>Problemas de validação para uma tabela (filtra por severidade / limite). Devolve uma promessa.
screenshot()Promise<string>Captura a vista 3D atual como data URL PNG. Devolve uma promessa.
compare({ base, head, baseLabel?, headLabel? })Promise<{ changes }>Compara duas entregas por URL (por GlobalId) e abre a área de comparação com o resultado.
Painéis
openPanel(panel | null) · closePanel()voidAbre um painel de ferramenta, ou passa null para fechar o que estiver aberto. Um painel indisponível — escondido pelo chrome, ou sem nada carregado sobre o que atuar — não faz nada, e não é um erro.
getPanels()Promise<PanelsResult>Que painel está aberto e quais são oferecidos neste momento. Uma ferramenta ausente de `available` não pode ser aberta; não está apenas desativada.
setPanels(panels)voidLimita a barra a estes painéis, em tempo de execução. Mesmo vocabulário do parâmetro de URL `panels=` e tem prioridade sobre ele. Restringe o que o visualizador oferece e nunca acrescenta; um array vazio significa sem barra.
Nuvens de pontos
addPointCloud(fileName, bytes)Promise<string>Carrega um levantamento a partir de bytes — LAS, LAZ, COPC, PLY, PCD ou texto. O buffer é transferido, não copiado. Resolve com o id da nuvem.
addPointCloudFromUrl(url, fileName?)Promise<string>Carrega um levantamento que o próprio visualizador obtém. O URL tem de permitir CORS.
listPointClouds()Promise<PointCloudInfo[]>Todos os levantamentos carregados. `pointCount` é o residente, `declaredCount` o que o ficheiro contém — diferem quando o orçamento truncou a leitura.
removePointCloud(cloudId) · clearPointClouds()Promise<void>Descarrega um levantamento e liberta os seus buffers de GPU, ou descarrega todos.
setPointCloudVisible(cloudId, visible)Promise<void>Mostra ou oculta um levantamento sem o descarregar.
fitPointCloud(cloudId?)Promise<void>Enquadra a câmara num levantamento, ou no primeiro carregado.
setPointCloudDisplay(display, renderBudget?)Promise<void>Tamanho do ponto, opacidade, modo de cor, densidade, limiar de confiança — partilhados por todos. Cada um é um uniform do shader, logo instantâneo com 20 milhões de pontos.
inspectPointCloud(enabled?)Promise<void>Arma o clique para ler. Enquanto armado, clicar num ponto emite `pointcloud-picked` com as coordenadas DO FICHEIRO ao lado das da cena.
setPointCloudPlacement(placement, cloudId?)Promise<void>Ajusta um levantamento à mão: posição, guinada, nivelamento, escala. Parcial e limitado; aplica-se sobre o alinhamento deduzido em vez de o substituir.
setPointCloudUpAxis(axis, cloudId?)Promise<void>Corrige que eixo o levantamento trata como vertical. PLY, PCD e texto não declaram orientação, o visualizador deduz — verifique `upAxisSource`.
Imported 3D models
addMesh(files)Promise<string>Importa um modelo a partir de bytes. Recebe uma LISTA: um .gltf precisa do seu .bin e texturas, um .obj do seu .mtl — sozinho, o ficheiro de entrada dá geometria cinzenta.
addMeshFromUrl(urls)Promise<string>Importa um modelo que o visualizador obtém. Passe todos os URL necessários; são obtidos em paralelo e todos têm de permitir CORS.
listMeshes()Promise<MeshInfo[]>Todos os modelos importados, com contagens de triângulos e texturas. Leia `unitSource` e `upAxisSource` antes de confiar nos valores ao lado.
removeMesh(meshId?) · clearMeshes()Promise<void>Remove uma importação e liberta a geometria, materiais e texturas, ou remove todas.
setMeshVisible(visible, meshId?)Promise<void>Mostra ou oculta uma importação sem a descarregar.
fitMesh(meshId?)Promise<void>Enquadra a câmara numa importação, ou em todas.
setMeshPlacement(placement, meshId?)Promise<void>Coloca uma importação à mão: posição, guinada, nivelamento, escala. Parcial e limitado. As importações começam centradas no IFC, assentes no seu piso.
setMeshUpAxis(axis, meshId?)Promise<void>Corrige o eixo vertical da origem. Só faz sentido em OBJ — a especificação glTF obriga a Y para cima, por isso reporta `upAxisSource: 'declared'`.
setMeshUnit(unitScale, meshId?)Promise<void>Corrige a unidade de origem: 1 metros, 0,01 centímetros, 0,001 milímetros, 0,3048 pés. Nenhum destes formatos a regista, o visualizador deduz do tamanho do modelo.
Conformidade IDS e EIR
checkIds(idsXml)Promise<IdsResult>Verifica o modelo contra um IDS da buildingSMART (XML .ids). Corre num worker. Devolve aprovado/reprovado por especificação.
checkEir(profile)Promise<IdsResult>Verifica o modelo contra um perfil de validação EIR / BIM — um objeto, o seu JSON (incl. o formato compacto) ou o id de um perfil incorporado como 'builtin-en14351-1'. Compila para IDS e corre no mesmo motor: o mesmo IdsResult que checkIds.
Idiomas
setLanguage(lang)voidMudar o idioma da interface em tempo de execução.
getLanguages()string[]Listar os códigos de idioma suportados (rótulos em IfcViewer.LANGUAGES).
Ciclo de vida e eventos
whenReady()Promise<void>Resolve quando o visualizador está pronto para aceitar comandos.
isReadybooleanGetter — true assim que o visualizador sinaliza que está pronto.
on(event, cb)() => voidSubscrever um evento; devolve uma função para cancelar.
off(event, cb)voidRemove um listener de eventos adicionado anteriormente.
dispose()voidDesmontar o visualizador e remover o iframe.
As chamadas concorrentes a add() / addFromUrl() são serializadas internamente e cada promessa é correlacionada com o seu próprio carregamento — um carregamento iniciado pela aplicação (parâmetro de URL, upload dentro do iframe) nunca resolve a sua promessa de add().
Opções do construtor
| Opção | Tipo | Predefinição | Descrição |
|---|---|---|---|
ui | 'minimal' | 'full' | 'kiosk' | 'client' | 'article' | 'embed' | 'minimal' | Preset da interface. 'embed': para uma app anfitriã que fornece o ficheiro — 3D, propriedades ao selecionar, uma barra compacta com Validar e Medir; sem Abrir, Partilhar, conta nem idioma. |
toolbar | boolean | preset | Mostrar a barra de ferramentas (por omissão, a do preset). false deixa a vista 3D e os painéis. |
tools | ('validate' | 'measure')[] | ['validate', 'measure'] | Ferramentas da barra compacta de ui: 'embed' — 'validate', 'measure'. [] para nenhuma. |
autoFrame | boolean | true | Enquadrar toda a cena em iso, com margem, sempre que um carregamento termina. false deixa-lhe a câmara. |
validate | boolean | true | Executa a validação ao carregar (alimenta o Health Score). |
panel | boolean | false | Abre automaticamente o painel de validação. |
panels | PanelName[] | — | Limita a barra de ferramentas a estes painéis, desde o primeiro fotograma — `['scene','map']`, ou `[]` para não ter barra. Mesmos nomes que setPanels e que o parâmetro de URL `panels=`. Preferível a chamar setPanels após a carga, que mostra a barra completa e depois retira ferramentas. |
lang | string | auto | Força o idioma da interface (ex. en, es, ja). |
accent | #rrggbb | brand | Colore o visualizador a condizer com o seu dashboard. |
model | string | — | Carrega automaticamente este URL público de IFC assim que estiver pronto. |
background | BackgroundSpec | — | Fundo da cena desde o primeiro fotograma — um preset, '#rrggbb' ou '#cima,#baixo'. |
map | boolean | ('terrain' | 'buildings' | 'showcase')[] | — | Coloca o modelo no mapa ao carregar: true, ou camadas ['terrain','buildings','showcase']. |
solar · moon | 'MM-DDTHH:MM' · boolean | — | Abre o estudo solar a uma hora local do local: '06-21T18:00'. |
scans | string[] | — | URLs de nuvens de pontos a carregar com o modelo. |
layers | string | — | Configuração de camadas de dados a abrir: o URL de um JSON exportado do painel Camadas de dados (fontes em direto, estilos, alertas). Desde 1.16. |
scene | string | — | Cena a abrir: o URL de um documento de cena (modelos, camadas de dados, dispositivos em direto, mapa, câmara). Desde 1.17. |
view · fill | CameraView · number | — | Quando todos os modelos carregam, enquadrá-los a partir desta vista, de forma justa. ui: 'article' implica 'iso'. |
wheel | 'always' | 'ctrl' | 'always' | 'ctrl': a roda desloca a sua página e só faz zoom com Ctrl/⌘ premido, como um mapa incorporado. ui: 'article' implica-o. |
lazy | boolean | 'visible' | false | Não carregar nada até ser preciso: true mostra um póster com botão, 'visible' arranca quando a figura se aproxima do ecrã. |
poster · posterTitle · posterText · launchLabel | string | — | Imagem, título, texto e botão do póster, até o visualizador estar pronto. |
aspectRatio | string ('16/10') | — | Tamanho por proporção ('16/10'): póster e visualizador partilham a caixa, nada se mexe na página. |
turntable | boolean | number | false | Rotação lenta depois de carregar (true = 6°/s, ou graus por segundo). |
pauseOffscreen | boolean | ui === 'article' | Parar de renderizar fora do ecrã. true por omissão com ui: 'article'. |
fullscreenButton | boolean | false | Um pequeno botão de ampliar no canto. |
height · width | number | string | '100%' | Tamanho do iframe (número → px). |
baseUrl | string | auto | URL base da aplicação. Deduzido do URL do script. |
loadTimeout | number | 120000 | Rejeita add()/addFromUrl() após N ms (0 desativa). |
className · title | string | — | Classe extra / title do iframe. |
on* callbacks | function | — | Callbacks de conveniência — equivalentes a .on(...). |
Eventos
| Evento | Payload | Descrição |
|---|---|---|
ready | { languages } | Visualizador montado e pronto. |
model-progress | { percent, phase } | Progresso do carregamento (download → análise → render). |
model-loaded | { modelId, fileName, elementCount, fromCache } | Um modelo terminou de carregar. |
validation-started | { modelId } | Começou uma validação num modelo — após um carregamento (validate: true) ou via validate(). |
validation-completed | { modelId, qualityScore, errors, warnings, info, total } | Validação concluída: o Health Score e as contagens, e o modelo a que se referem (null para o agregado federado). |
validation-failed | { modelId, message } | Uma validação não pôde correr ou não terminou — com o motivo. |
model-error | { message, url?, name? } | Um carregamento falhou. |
element-selected | { expressId, modelId, ifcType, name, globalId } | O utilizador escolheu um elemento — com o seu GlobalId, para o cruzar com os seus registos. |
pointcloud-picked | { cloudId, position, sourcePosition, classification, intensity, distance } | Leu-se um ponto de um scan, nas coordenadas do ficheiro e não da cena. |
map-feature-picked | { id, name?, label?, featureKind, heightM?, heightEstimated } | Clicou-se num edifício da envolvente OpenStreetMap. Contexto, não modelo. |
walk-changed | { active, speed } | O modo passeio foi ligado ou desligado — pelo visitante ou pelo host. |
measurements-changed | { tool, units, items } | Uma medição foi adicionada, removida ou renomeada. Traz a lista completa. |
tour-started | { title, total, template } | Começou um tour. |
tour-step | { index, total, caption } | O tour passou a outra paragem. |
tour-ended | { completed } | O tour parou; completed se chegou à última paragem. |
presentation-progress | { stage, label?, progress } | O realizador está a gerar ou exportar uma apresentação. |
layer-feature-picked | { layerId, layer, featureId, geometry, lonLat, properties } | Clicou-se num elemento de uma camada de dados: a sua camada, propriedades e um ponto para marcar. |
alert | { kind, from, id, name, rule, count, sample } | Uma regra de uma camada de dados ou do gémeo começou ou deixou de alertar. |
Estáticos
IfcViewer.LANGUAGES ({ code, label }[], nomes nativos) e IfcViewer.SUPPORTED_LANGUAGES (códigos) — para construir um seletor de idioma antes de o visualizador estar pronto.
Web component
Zero JavaScript — coloque a tag em qualquer página ou dashboard e defina atributos.
Atributos: modeluilangaccentvalidatepanelbase-urltoolbartoolsauto-frame
Os eventos são reemitidos como CustomEvents do DOM chamados ifcviewer:<type> (detail = payload). O IfcViewer subjacente está em .viewer do elemento; getStats / getIssues / screenshot são delegados para ele.
const el = document.querySelector("ifc-viewer");
el.addEventListener("ifcviewer:validation-completed", (e) => {
updateScore(e.detail.qualityScore);
});
const stats = await el.getStats();Guias
Receitas
Padrões para copiar e colar, do que as equipas constroem de facto.
Inspetor de elementos
Clique no 3D e leia os property sets IFC para o seu próprio painel.
viewer.on("element-selected", async (e) => {
const data = await viewer.getElement(e.expressId, e.modelId);
renderPanel(data.name, data.globalId, data.propertySets);
});Widget de Health Score
Mostre a pontuação de qualidade do modelo e a contagem de problemas no seu dashboard.
viewer.on("validation-completed", (v) => {
setScore(v.qualityScore); // 0–100, or null
setCounts(v.errors, v.warnings, v.info);
});Verificação de conformidade IDS
Valide contra um IDS da buildingSMART e liste aprovado / reprovado por especificação.
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)));
});Validação EIR / BIM
Execute um perfil EIR editável (ao estilo ISO 19650) — sem ficheiro .ids. As falhas trazem o GlobalId do elemento.
// 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(" · ")));
});Uma cena para um artigo de blog
Fundo branco, o modelo no seu local e a sombra do fim de tarde do solstício de verão — declarado à partida, sem cliques.
// 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 e medições
Corte uma planta num piso pelo nome, deixe o leitor medir e leia os resultados em unidades 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)));
});Um tour guiado num artigo
Defina as paragens uma vez com getCamera() e deixe o tour avançar sozinho ao lado do texto.
// 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));Um vídeo do modelo, a partir da sua página
Gere uma apresentação com uma receita e receba o MP4 — sem gravar o ecrã, sem enviar nada.
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 }));Uma capa para o seu artigo
Gere um pin do Pinterest ou um cartão do LinkedIn do modelo com o seu título e receba o 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 }));Um modelo dentro de um artigo
ui: 'article' é só a tela, enquadrada para o modelo a preencher, com uma roda que deixa o leitor continuar a ler. Escolha o ângulo com 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,
});Uma história que avança com o texto
Fixe o visualizador e deixe cada parágrafo escolher o plano: isolar as paredes, rodar, trazer o sol da tarde.
// <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" } },
]);Um gémeo de cidade em direto na sua página
Abra uma cena publicada, reaja aos seus alertas e cliques, junte a sua própria fonte em direto e partilhe uma ligação para o resultado.
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" });Uma ficha de produto na sua app
Fixe a versão, entregue o ficheiro e mostre o que o fabricante pôs no tipo: coeficiente U, classe de vento, fabricante.
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);Adapte à sua marca
Alinhe o visualizador com o seu produto usando uma única cor de destaque.
new IfcViewer("#viewer", { accent: "#3fa866" });
// …or on the web component:
// <ifc-viewer accent="#3fa866" model="…"></ifc-viewer>Seletor de idioma
Construa um seletor a partir dos idiomas que o visualizador traz.
// build a <select> from the bundled languages
IfcViewer.LANGUAGES.forEach(({ code, label }) => addOption(code, label));
// switch at runtime
viewer.setLanguage("ja");Health Score
Ciclo da validação
validate: true valida cada modelo quando termina de carregar; validate() executa-a a pedido e resolve com o resultado. O que getValidation() devolve em cada momento:
| Quando | getValidation() devolve |
|---|---|
| Antes de qualquer validação | null — getValidationStatus() diz idle. |
| Durante a primeira execução | null — getValidationStatus() diz running, com progresso; validation-started já foi emitido. |
| Após uma execução | { qualityScore, errors, warnings, info, total, status: 'done', modelId }. Com vários modelos, o agregado (modelId: null). |
| Durante uma execução mais recente | Os números anteriores, com status: 'running'. |
| Após uma falha | Os números anteriores com status: 'error' e error — ou null se não havia. validation-failed foi emitido e validate() rejeitou. |
Versões
Fixar uma versão
Desde a 1.18.0, cada versão é também publicada num caminho que nunca muda. Fixe-a em produção; o caminho móvel segue a versão mais recente.
<script type="module">
import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js";
</script>| Caminho | Cache | Uso |
|---|---|---|
/sdk/ifc-viewer.es.js | revalidado em cada carregamento | Para experimentar — sempre a mais recente. |
/sdk/1.18.0/ifc-viewer.es.js | imutável, em cache um ano | Produção — o código testado é o código que corre. |
Que visualizador abre um SDK fixado?
O iframe é o visualizador em direto da mesma origem, não uma cópia congelada: um SDK fixado mantém a sua API e o seu comportamento enquanto o visualizador lá dentro continua a melhorar. O visualizador responde a todos os comandos de qualquer SDK publicado — o build verifica-o —, por isso uma versão fixada antiga continua a funcionar.
Fixe também o conteúdo
versions.json, junto ao SDK, lista o hash Subresource Integrity de cada versão:
<script type="module" src="https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js"
integrity="sha384-WsT4d0DS1oqva4rLWCGBDrx50uZVA3B95IDWbfxaFt8lnjs6NgOrjKNcWQL57/0z" crossorigin="anonymous"></script>Registo de alterações
1.18.0 — objetos de catálogo numa app anfitriã. getElement() devolve o GlobalId e os dados do tipo (typeName, typeProperties com unidades, effectivePropertySets; tipos IFC4 e estilos IFC2x3). validate(), getValidationStatus(), validation-started / validation-failed, e validate: true valida mesmo após cada carregamento. Enquadramento automático após carregar, fit() e frame() com margem para objetos pequenos, frame(elementId), e setView() volta a mover a câmara. ui: 'embed' com toolbar e tools. Versões fixas em /sdk/1.18.0/. findElements(), GlobalIds em getElement() / select() / element-selected, conjuntos de aro e folha de janelas e portas, e os materiais do tipo; em ui: 'embed' o painel de propriedades mantém à vista o objeto escolhido. checkEir('builtin-en14351-1') verifica o desempenho declarado de janelas e portas exteriores segundo a EN 14351-1 — vento, estanquidade à água, acústica, transmissão térmica, g e τv, fabricante e código do produto — em property sets IFC padrão; as regras EIR ganham where (só elementos com um valor de propriedade) e optional.
Ajuda
Resolução de problemas
As poucas coisas em que as pessoas tropeçam.
Não é renderizado nada
Verifique, por esta ordem: se o visualizador chegou a ready, se o modelo carregou (model-loaded em vez de model-error), se o contentor tem altura diferente de zero e se a origem dos bytes é IFC válido. A mensagem de model-error indica a causa.
Recebo um erro de CORS ao carregar um modelo
O CORS só afeta addFromUrl(url). Se controla os bytes, descarregue-os você e chame add(name, bytes) — não se aplica CORS porque entrega os dados diretamente.
O iframe fica em branco ou recusa carregar
A aplicação tem de ser incorporável — sem X-Frame-Options: DENY e sem uma CSP frame-ancestors restritiva no host. A nossa aplicação alojada permite a incorporação.
O meu ArrayBuffer fica vazio depois de add()
add() transfere o buffer para o entregar sem cópia, o que o deixa detached no seu código. Passe uma cópia (bytes.slice(0)) se ainda precisar dos bytes depois.
Os carregamentos ou as consultas expiram
add()/addFromUrl() são rejeitados após loadTimeout (120 s por predefinição; 0 desativa). As consultas são rejeitadas aos 30 s e checkIds aos 120 s. Modelos grandes em dispositivos lentos podem precisar de um loadTimeout maior.
Que navegadores são suportados?
Qualquer navegador moderno com WebAssembly e módulos ES — Chrome, Edge, Firefox, Safari. Internet Explorer não.
Dá 404 no meu servidor de desenvolvimento local
Um servidor de desenvolvimento SPA pode intercetar /sdk/ com o seu fallback HTML. Teste contra um build real ou um alojamento estático, ou importe diretamente o módulo /sdk/ifc-viewer.es.js.