IFC Viewer SDK v1.18.0 ビューアを開く
日本語

IFC Viewer SDK · v1.18.0

IFC ビューアを自社プロダクトに組み込む

同じブラウザビューアを CDE、デジタルツイン、社内のプロジェクトツールに組み込みます。各モデルはクライアント側で解析され、IFC データを自社アプリから直接読み込みます。

3 行で最初の描画まで

JavaScript
const viewer = new IfcViewer("#viewer");

await viewer.add("project.ifc", ifcBytes);
// → rendered, validated, all on the client

セットアップ

クイックスタート

使っているスタックを選んでください。コピーして貼り付ければ描画されます — ビルド手順も API キーも不要です。

HTML
<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>

ホスティング版とセルフホスト

各スニペットは当方の CDN から読み込みます。どのオリジンからでも動作します。セルフホストする場合は ifc-viewer.es.js を自社ドメインから配信してください — SDK はスクリプトからの相対位置でアプリの URL を自動的に解決します。アプリを別の場所に置いている場合は baseUrl オプションを指定してください。

npm パッケージはまだありません。URL でモジュールを読み込む(上記)か、ifc-viewer.es.js を自分のアプリに同梱してください。

実証

ライブデモ

このページは SDK を実行しています。コントロールをクリックするとサンプル IFC を取得してビューアを操作します — 各ボタンは呼び出すメソッドを表示します。

待機中
直近の呼び出し —

アーキテクチャ

仕組み

ページ側にはごく小さなラッパー、サンドボックス化された iframe には完全なビューア、その間をあなたのバイト列が渡り — データはもともとある場所で解析されます。

閲覧者のブラウザ — 何もアップロードされませんあなたのアプリSDK · 約 6 KBサンドボックス iframethree.js · web-ifc · WASMIFC バイト列postMessage

あなたのアプリ

iframe と型付き JS API を生成する約 6 KB の ES モジュール。3D や解析の重量はいっさいあなたのバンドルに入りません。

postMessage ブリッジ

IFC のバイト列は ArrayBuffer として iframe へ転送されます(ゼロコピー)。コマンドとクエリは対応付けられたリクエスト/レスポンスのメッセージです。

ビューア

three.js と WebAssembly にコンパイルされた web-ifc が、iframe の中でモデルを解析・描画します — 閲覧者のマシン上で。

なぜプライベートなのか

モデルがサーバーに届くことはありません。守るべきアップロード経路も、データ所在地の論点も、ファイルサイズ待ちの列もありません。

なぜスケールするのか

計算は閲覧者それぞれの端末で走ります。200 MB のモデルを配信するコストは 2 MB のものと同じ — ただの静的ファイルだからです。

コンセプト

基本コンセプト

作り始める前に押さえておきたい 4 点。

マウントと準備完了

new IfcViewer(target) は即座にマウントし、ビューアの準備ができるまでコマンドをキューに入れます。await IfcViewer.create(target)(または whenReady())ならすぐに使えるインスタンスが得られます。

JavaScript
// queues commands until ready
const viewer = new IfcViewer("#viewer");

// …or await an instance that is ready to use
const viewer = await IfcViewer.create("#viewer");

バイト列か URL か

add(name, bytes) は手元にあるバイト列を渡します — CORS は不要で、何もアップロードされません。バッファは転送される(detached になる)ため、後でも必要ならコピーを渡してください。addFromUrl(url) は CORS 対応の公開 URL を取得します。

JavaScript
// 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");

イベント

on(event, cb) で購読すると、購読解除用の関数が返ります。ready、model-progress、model-loaded、validation-completed、model-error、element-selected が利用できます。

JavaScript
const off = viewer.on("model-loaded", (m) => {
  console.log(m.fileName, m.elementCount);
});
// later: off();

クエリ

必要なときにデータを取得します:getModels、getElement、getValidation、getStats、getIssues、checkIds、checkEir、screenshot。いずれもリクエスト/レスポンス方式で、タイムアウト後に reject します(30 秒。IDS / EIR は最大 120 秒)。

JavaScript
const models = await viewer.getModels();
const { qualityScore } = await viewer.getValidation();
const png = await viewer.screenshot();

リファレンス

API リファレンス

すべてのメソッドをグループ別に — シグネチャと戻り値の型つきで。続いてコンストラクタのオプション、イベント、<ifc-viewer> Web component を掲載します。

コンストラクタ

new IfcViewer(target, options?)IfcViewer

CSS セレクタまたは要素にマウント。下のオプションを参照。

IfcViewer.create(target, options?)Promise<IfcViewer>

ビューアを生成し、コマンドを受け付けられる状態になったら解決します。

モデルの読み込み

add(name, bytes)Promise<ModelLoadedEvent>

自社アプリから IFC バイト(ArrayBuffer/Uint8Array)を読み込みます。バッファはゼロコピーで転送されます。

addFromUrl(url, name?)Promise<ModelLoadedEvent>

CORS 対応の公開 IFC URL を読み込みます。

removeModel(modelId)void

id でモデルをアンロードします。

clear()void

読み込み済みのモデルをすべて削除します。

カメラと選択

select(expressId | globalId, modelId?)void

要素を選択して収めます — IFC の expressID、または GlobalId で(GlobalId ならモデルも特定されます)。

setView(view)void

シーンの名前付きビュー(iso、front、back、left、right、top、bottom)へ移動し、シーン全体を収めます。応答なし(1.14〜1.17 ではビューアが無視していました)。

frame({ view?, fill?, azimuth?, elevation?, animate? })Promise<{ scope }>

プレゼン用の構図:モデルが画面の `fill`(0.2–0.98、既定 0.85)を占めるよう、外接球ではなく角に合わせてフィットします。azimuth / elevation で任意の角度から。

frame(elementId, modelId?, options?)Promise<{ scope: 'element' }>

1 つの要素(expressID)を収めます。ビューを指定しない限り現在の角度のまま。モデルにない場合は拒否します。

fit() · reset()void

現在の角度から余白付きでアクティブなモデルを収める / カメラをリセット。

setCamera(position, direction)void

カメラを指定位置に置き、指定方向を向かせます。

getCamera()Promise<CameraState | null>

カメラの位置と注視点——保存して lookAt で復元できます。

lookAt(position, target, animate?)Promise<void>

カメラを指定位置へ移動し、ターゲットを注視(シーン単位メートル、Y が上)。

setWalkMode(enabled, { speed? })Promise<WalkState>

一人称のウォークモード(WASD で移動、ドラッグで見回し、Esc で終了)。walk-changed を発行。

getWalkState()Promise<WalkState>

ウォークモードが有効か、その速度。

表示・非表示

isolate(ifcType?)void

カテゴリを分離(例: "IfcWall")。省略でクリア。

showAll()void

表示を全復元(非表示・分離を解除)。

hideElements(ids, modelId?) · showElements(ids, modelId?)void

expressID で要素の集合を非表示 / 表示します。

setModelVisible(modelId, visible)Promise<void>

統合シーン内のモデルをアンロードせずに表示/非表示。

setModelOpacity(opacity, modelId?)Promise<void>

モデルを半透明に(0.05–1)——例:設備の周りの意匠。

isolateModel(modelId | null)Promise<void>

このモデルのみ表示。null ですべて再表示。

外観と表示

setBackground(background)Promise<BackgroundState>

シーン背景を変更:プリセット('white'、'paper'、'blueprint'、'sky'、'studio')、'#rrggbb'、'#上,#下'、または { top, bottom }。訪問者自身の設定としては保存されません。

getBackground()Promise<BackgroundState>

現在のシーン背景。

setAccent(color)Promise<void>

実行時に UI のアクセントカラーを変更(#rrggbb)。

setClientMode(enabled)Promise<void>

クライアント表示のオン/オフ——関係者向けの表示で、ui: 'client' と同じです。

setRenderQuality(quality)Promise<void>

'quality' はメインビジュアル用の重いレンダリングを有効化。普段は 'standard'。

日照スタディ

setSolar(opts?)Promise<SolarState>

敷地の現地日時でのリアルな影:{ date: '06-21', time: '18:00' }。敷地は IFC のジオリファレンスから取得。ない場合は location を渡してください——既定の都市を黙って使うことはありません。

getSolar()Promise<SolarState>

スタディの日付・時刻・タイムゾーンと敷地の位置。

マップモード

setSiteContext(opts?)Promise<SiteContextState>

モデルを地形と OpenStreetMap の周辺環境とともに地図上に配置。呼び出しは、あなたのページが訪問者に代わってタイル利用に同意することを意味します。帰属表示を表示してください。

getSiteContext()Promise<SiteContextState>

マップモードの状態、配置、表示が必要な帰属表示。

シーン

openScene(url | SceneDocument)Promise<void>

別のシーンを開く:.scene.json の URL、またはシーン文書(フレームのアドレスに圧縮して渡すため、リンクに収まる必要があります)。ビューアーはそのシーンで再読み込みし、待機中の呼び出しは拒否され、ready が再度発火します。

exportScene({ title?, description?, camera? })Promise<SceneExport>

画面上のシーンをシーン文書として取得 — URL によるモデル、レイヤー、ツイン、地図、カメラ — クレジットすべきソースと、それを含むリンク付き。

packScene(doc)Promise<string | null>

シーン文書を #scene= リンク用に自分で圧縮する(リンクに収まらないときは null)。

データレイヤーとツイン

getLayerPresets()Promise<LayerPreset[]>

公開ライブソースのカタログ。ビューアーの言語で名前が付き、ブラウザーで必要なもの(キー、プロキシ)と、敷地の近くにデータがあるかを示します。

addLayer({ preset } | { url, live? } | { geojson })Promise<DataLayerInfo>

レイヤーを追加:カタログのソース({ preset })、URL 上の GeoJSON またはライブフィード({ url, live })、手元の GeoJSON({ geojson })。セッション限定で、訪問者のレイヤーには保存されません。

getLayers()Promise<DataLayerInfo[]>

すべてのデータレイヤー:状態、フィーチャー数、範囲、更新、帰属表示。

setLayerVisible(id, visible)Promise<DataLayerInfo>

レイヤーの表示・非表示。

frameLayer(id)Promise<void>

レイヤーのフィーチャーへカメラを移動。

removeLayer(id)Promise<void>

レイヤーを削除。

getTwin()Promise<TwinState>

現在の運用ツイン:デバイスソース(状態、最新の読み取り)と、バインディングごとの該当ルール、その色、ラベル値、アラート中かどうか。

断面

addSection({ axis?, offset?, level?, flip? })Promise<SectionsState & { id }>

断面を追加。{ level: '1階' } はその階の平面図、{ axis: 'x', offset: 4.5 } は 4.5 m の断面。

updateSection(id, { offset?, enabled?, flipped? })Promise<SectionsState>

断面の移動、有効/無効、反転。

removeSection(id?)Promise<SectionsState>

断面を 1 つ削除。id を省略するとすべての切断を削除。

setSectionBox('model' | 'selection' | false)Promise<SectionsState>

モデルまたは選択範囲を囲むセクションボックス。false で削除。

getSections()Promise<SectionsState>

すべての断面、ボックス、階ごとの切断に使える階の一覧。

ツアー

startTour(template?, { autoplay?, title? })Promise<TourState>

組み込みツアーを開始:'social'、'client-walkthrough'、'technical-review'(検証の指摘を順に確認)。ビューア自身のツアーバーで再生されます。

playTour({ title?, steps }, { startAt?, autoplay? })Promise<TourState>

自作のツアーを再生:キャプション、ハイライト、分離するクラス付きのカメラ停留点。autoplay は true または停留点ごとの ms。

nextTourStep() · prevTourStep() · goToTourStep(i)Promise<TourState>

停留点を移動。

setTourAutoplay(autoplay)Promise<TourState>

再生中のツアーの自動送りをオン/オフ。

stopTour()Promise<TourState>

ツアーを停止してカメラを戻します。

getTour()Promise<TourState>

ツアーとその停留点(playTour() と同じ形式)——保存して後で再生できます。

プレゼンテーション・ディレクター

getPresentationRecipes()Promise<PresentationRecipe[]>

組み込みのディレクターレシピ(会議デモ、リール、LinkedIn ティーザー、ローンチ、エディトリアルなど)。

createPresentation(recipe?, options?)Promise<PresentationState>

レシピからモデルを編集済み動画に:IFC から計画したショット、キャプション、音楽。結果を Clip Studio で開きます。進捗は presentation-progress で。

exportPresentation({ resolution?, music? })Promise<PresentationVideo>

訪問者のブラウザでプレゼンテーションをエンコードし、MP4 のバイトを返します——アップロード、添付、<video> での再生に。

closePresentation()Promise<void>

Clip Studio を閉じます。

カバースタジオ

getCoverOptions()Promise<CoverCatalog>

カバースタジオのレシピ、テンプレート、フォーマット、パレット。

createCover({ recipe?, template?, format?, palette?, text? })Promise<CoverState>

カバーを作成:レシピを実行(必要なビューを撮影)してから、テンプレート・フォーマット・パレット・テキストを設定。カバースタジオが開き、引き続き編集できます。

getCover()Promise<CoverState | null>

現在のカバー。カバースタジオが閉じていれば null。

exportCover({ type?, slide? })Promise<CoverFile>

エクスポート:png / jpeg(1 ページ)、pdf / pptx(文書全体)、zip(全ページ)。バイトを返します。

closeCover()Promise<void>

カバースタジオを閉じます。

記事キット

activate()void

遅延ビューアを今すぐ起動します(ポスターのボタンと同じ)。それまでの呼び出しはキューで待機。

bindSteps(steps, { rootMargin? })() => void

スクロールテリング:各段落が画面中央を通るとき、構図・分離・太陽の移動・独自コードを実行。解除関数を返します。

setTurntable(enabled | degPerSec)Promise<{ active, speed }>

アイドル時のゆっくりした回転(度/秒、false で停止)。最初の操作で止まり、動きを減らす設定では動きません。

toggleFullscreen()Promise<void>

図を全画面に拡大、または元に戻します。

setPaused(paused)Promise<{ paused }>

描画を停止・再開します。pauseOffscreen が自動で行います。

シーングループ

getGroups()Promise<SceneGroupsState>

シーン内のすべてのグループ——IFC から推定(プロジェクト、敷地、位置)されたものとユーザーが作成したもの。

createGroup(name, modelIds?)Promise<string>

グループを作成(モデルを入れることも可)。id を返します。

renameGroup(id, name) · deleteGroup(id)Promise<void>

ユーザーグループの名前変更・削除。自動グループは編集できません。

assignToGroup(itemId, groupId | null | 'loose')Promise<void>

モデルまたは点群をグループへ移動。null で自動グループ化に戻し、'loose' でどのグループにも入れません。

setGroupVisible(id, visible) · isolateGroup(id | null)Promise<void>

グループ全体の表示/非表示、または 1 つだけ表示。

frameGroup(id)Promise<void>

グループにカメラを合わせます(非表示のメンバーも含む)。

計測

setMeasureTool(tool)Promise<void>

訪問者向けに計測ツールを有効化(計測パネルを開きます)、または 'none'。結果は measurements-changed で届きます。

getMeasurements()Promise<MeasurementsState>

画面上のすべての計測。値は常に SI 単位——m、m²、度——表示とは無関係です。

clearMeasurements(id?)Promise<MeasurementsState>

計測を 1 つ、またはすべて削除。

クエリ

getModels()Promise<ModelSummary[]>

読み込み済みモデルを列挙。Promise を返します。

findElements({ ifcClass?, globalId?, name?, modelId?, limit? })Promise<ElementSummary[]>

読み込んだモデルから IFC クラス、GlobalId、名前で要素を探します。各結果には getElement()、select()、frame() に渡す情報が含まれます。

getElement(expressId | globalId, modelId?)Promise<IfcElementData | null>

expressID または GlobalId で。要素の IFC データ:GlobalId を含む属性、プロパティセットと数量、そしてタイプのデータ(typeName、typeProperties)を単位付きで。effectivePropertySets は両者を統合し、要素自身の値が優先されます。

validate(modelId?, { force? })Promise<ValidationRunResult>

モデルを今すぐ検証し(既定はアクティブなモデル)、その Health Score と問題数で解決します。失敗時は理由付きで拒否。

getValidation()Promise<ValidationSummary | null>

画面上の検証結果。まだ結果がなければ null。「検証のライフサイクル」を参照。

getValidationStatus()Promise<ValidationStatus>

検証の現在の状態:idle、running(進捗付き)、done、error。

getStats()Promise<StatsResult>

モデルごとのカテゴリ別要素数 — ダッシュボードのグラフ用。Promise を返します。

getIssues({ severity?, limit?, modelId? })Promise<IssuesResult>

テーブル用の検証問題(重大度 / 件数でフィルタ)。Promise を返します。

screenshot()Promise<string>

現在の 3D ビューを PNG data URL として取得。Promise を返します。

compare({ base, head, baseLabel?, headLabel? })Promise<{ changes }>

URL で 2 つの納品を(GlobalId で)比較し、結果を比較画面で開きます。

パネル

openPanel(panel | null) · closePanel()void

ツールパネルを開きます。null を渡すと開いているパネルを閉じます。利用できないパネル(クロームが隠している、または対象となる読み込み済みデータがない)は何も起こらず、エラーにもなりません。

getPanels()Promise<PanelsResult>

現在開いているパネルと、この時点で提供されているパネル。`available` にないツールは開けません。単に無効化されているのではありません。

setPanels(panels)void

実行時にレールをこれらのパネルに限定します。URL パラメータ `panels=` と同じ語彙で、そちらより優先されます。ビューアが提供するものを絞るだけで追加はしません。空配列はレールなしを意味します。

点群

addPointCloud(fileName, bytes)Promise<string>

バイト列からスキャンを読み込みます——LAS、LAZ、COPC、PLY、PCD、テキスト。バッファーはコピーではなく転送され、点群 id を返します。

addPointCloudFromUrl(url, fileName?)Promise<string>

ビューアー自身が取得するスキャンを読み込みます。URL は CORS を許可している必要があります。

listPointClouds()Promise<PointCloudInfo[]>

読み込み済みのすべてのスキャン。`pointCount` は常駐している数、`declaredCount` はファイルが持つ数で、予算が解析を打ち切ったときに食い違います。

removePointCloud(cloudId) · clearPointClouds()Promise<void>

スキャンを 1 つ解放して GPU バッファーを返すか、すべて解放します。

setPointCloudVisible(cloudId, visible)Promise<void>

スキャンを解放せずに表示・非表示を切り替えます。

fitPointCloud(cloudId?)Promise<void>

カメラをスキャンに、または最初に読み込んだものに合わせます。

setPointCloudDisplay(display, renderBudget?)Promise<void>

点サイズ、不透明度、配色、密度、信頼度しきい値——すべてのスキャンで共通です。いずれもシェーダーの uniform なので、2000 万点でも即座に反映されます。

inspectPointCloud(enabled?)Promise<void>

クリックで読み取るモードを有効にします。有効な間、点をクリックすると `pointcloud-picked` がシーン座標とファイル内の座標の両方を伴って発火します。

setPointCloudPlacement(placement, cloudId?)Promise<void>

スキャンを手で微調整します——位置、ヨー、水平出し、スケール。部分指定でき、値はクランプされます。導出された位置合わせを置き換えず、その上に乗ります。

setPointCloudUpAxis(axis, cloudId?)Promise<void>

スキャンがどの軸を上とみなすかを修正します。PLY、PCD、テキストは方向を宣言しないためビューアーが推定します——`upAxisSource` を確認してください。

Imported 3D models

addMesh(files)Promise<string>

バイト列からモデルを取り込みます。リストを受け取ります:.gltf には .bin とテクスチャー、.obj には .mtl が必要で、入口ファイルだけではグレーのジオメトリーになります。

addMeshFromUrl(urls)Promise<string>

ビューアーが取得するモデルを取り込みます。必要な URL をすべて渡してください。並列に取得され、すべて CORS を許可している必要があります。

listMeshes()Promise<MeshInfo[]>

取り込み済みのすべてのモデルと、その三角形数・テクスチャー数。隣の値を信頼する前に `unitSource` と `upAxisSource` を読んでください。

removeMesh(meshId?) · clearMeshes()Promise<void>

取り込みを 1 つ削除してジオメトリー・マテリアル・テクスチャーを解放するか、すべて削除します。

setMeshVisible(visible, meshId?)Promise<void>

取り込みを解放せずに表示・非表示を切り替えます。

fitMesh(meshId?)Promise<void>

カメラを 1 つの取り込みに、またはすべてに合わせます。

setMeshPlacement(placement, meshId?)Promise<void>

取り込みを手で配置します——位置、ヨー、水平出し、スケール。部分指定でき、クランプされます。取り込みは IFC の中央、その床の上から始まります。

setMeshUpAxis(axis, meshId?)Promise<void>

ソースの上方向の軸を修正します。意味があるのは OBJ だけです——glTF は仕様で Y を上と定めているため `upAxisSource: 'declared'` を返します。

setMeshUnit(unitScale, meshId?)Promise<void>

ソースの単位を修正します:1 メートル、0.01 センチメートル、0.001 ミリメートル、0.3048 フィート。どの形式も単位を記録しないため、ビューアーがモデルの大きさから推定します。

IDS / EIR 適合性

checkIds(idsXml)Promise<IdsResult>

モデルを buildingSMART の IDS(.ids XML)と照合します。ワーカー内で実行され、仕様ごとの合否を返します。

checkEir(profile)Promise<IdsResult>

EIR / BIM 検証プロファイルでモデルをチェックします。オブジェクト、その JSON(省略形も可)、または 'builtin-en14351-1' のような組み込みプロファイルの id を渡せます。IDS にコンパイルされ同じエンジンで実行されるため、checkIds と同じ IdsResult を返します。

言語

setLanguage(lang)void

実行時に UI 言語を変更します。

getLanguages()string[]

対応言語コードを列挙(ラベルは IfcViewer.LANGUAGES)。

ライフサイクルとイベント

whenReady()Promise<void>

ビューアがコマンドを受け付けられる状態になったら解決します。

isReadyboolean

ゲッター — ビューアが準備完了を通知した時点で true になります。

on(event, cb)() => void

イベントを購読。解除用の関数を返します。

off(event, cb)void

以前に追加したイベントリスナーを削除します。

dispose()void

ビューアを破棄して iframe を削除します。

同時に呼ばれた add() / addFromUrl() は内部で直列化され、各 Promise はそれぞれの読み込みに対応づけられます — アプリ側で始まった読み込み(URL パラメータ、iframe 内でのアップロード)があなたの add() の Promise を解決することはありません。

コンストラクタのオプション

オプション型既定値説明
ui'minimal' | 'full' | 'kiosk' | 'client' | 'article' | 'embed''minimal'UI プリセット。'embed':ファイルを自ら渡すホストアプリ向け — 3D、選択時のプロパティ、検証と計測だけのコンパクトなツールバー。開く・共有・アカウント・言語はなし。
toolbarbooleanpresetツールバーを表示(既定はプリセットに従う)。false で 3D ビューとパネルのみ。
tools('validate' | 'measure')[]['validate', 'measure']ui: 'embed' のコンパクトなツールバーのツール — 'validate'、'measure'。[] でなし。
autoFramebooleantrue読み込みが落ち着くたびに、シーン全体を iso から余白付きで収めます。false ならカメラはあなた次第。
validatebooleantrue読み込み時に検証を実行します(ヘルススコアの元になります)。
panelbooleanfalse検証パネルを自動的に開きます。
panelsPanelName[]—最初のフレームからツールレールをこれらのパネルに限定します — `['scene','map']`、レールなしは `[]`。setPanels および URL パラメータ `panels=` と同じ名前です。読み込み後に setPanels を呼ぶより望ましく、後者は完全なレールを表示してからツールを取り除きます。
langstringautoUI の言語を固定します(例:en、es、ja)。
accent#rrggbbbrandダッシュボードに合わせてビューアを色づけします。
modelstring—準備完了後にこの公開 IFC URL を自動で読み込みます。
backgroundBackgroundSpec—最初のフレームからのシーン背景——プリセット、'#rrggbb'、'#上,#下'。
mapboolean | ('terrain' | 'buildings' | 'showcase')[]—読み込み後にモデルを地図上へ:true、またはレイヤー ['terrain','buildings','showcase']。
solar · moon'MM-DDTHH:MM' · boolean—敷地の現地時刻で日照スタディを開く:'06-21T18:00'。
scansstring[]—モデルと一緒に読み込む点群の URL。
layersstring—開くデータレイヤー設定:データレイヤーパネルから書き出した JSON の URL(ライブソース、スタイル、アラート)。1.16 以降。
scenestring—開くシーン:シーン文書の URL(モデル、データレイヤー、ライブデバイス、地図、カメラ)。1.17 以降。
view · fillCameraView · number—すべてのモデルの読み込み後、このビューからぴったり収めます。ui: 'article' は 'iso' を含みます。
wheel'always' | 'ctrl''always''ctrl':ホイールはページをスクロールし、Ctrl/⌘ を押したときだけズーム。埋め込み地図と同じ動き。ui: 'article' で有効。
lazyboolean | 'visible'false必要になるまで読み込まない:true はボタン付きポスター、'visible' は図が画面に近づくと起動。
poster · posterTitle · posterText · launchLabelstring—ビューアの準備ができるまで表示するポスター画像・タイトル・テキスト・ボタン名。
aspectRatiostring ('16/10')—比率でサイズ指定('16/10'):ポスターとビューアが同じ枠を共有し、ページが動きません。
turntableboolean | numberfalse読み込み後のゆっくりした回転(true = 6°/秒、または度/秒)。
pauseOffscreenbooleanui === 'article'画面外では描画を停止。ui: 'article' では既定で true。
fullscreenButtonbooleanfalse隅に小さな拡大ボタン。
height · widthnumber | string'100%'iframe のサイズ(数値 → px)。
baseUrlstringautoアプリのベース URL。スクリプトの URL から自動的に導出されます。
loadTimeoutnumber120000N ミリ秒後に add()/addFromUrl() を reject します(0 で無効)。
className · titlestring—追加の class / iframe の title。
on* callbacksfunction—簡易コールバック — .on(...) と同じです。

イベント

イベントPayload説明
ready{ languages }ビューアがマウントされ準備完了。
model-progress{ percent, phase }読み込みの進捗(ダウンロード → 解析 → 描画)。
model-loaded{ modelId, fileName, elementCount, fromCache }モデルの読み込みが完了。
validation-started{ modelId }モデルの検証が始まりました — 読み込み後(validate: true)または validate() から。
validation-completed{ modelId, qualityScore, errors, warnings, info, total }検証完了:Health Score と件数、および対象のモデル(統合シーンの集計では null)。
validation-failed{ modelId, message }検証を実行できなかった、または完了しなかった — 理由付き。
model-error{ message, url?, name? }読み込みに失敗。
element-selected{ expressId, modelId, ifcType, name, globalId }ユーザーが要素を選択しました — 自社データと照合できるよう GlobalId 付き。
pointcloud-picked{ cloudId, position, sourcePosition, classification, intensity, distance }スキャンから点を取得。シーンではなくファイル自身の座標です。
map-feature-picked{ id, name?, label?, featureKind, heightM?, heightEstimated }OpenStreetMap の周辺建物をクリック。文脈であってモデルではありません。
walk-changed{ active, speed }ウォークモードがオン/オフになった——訪問者またはホストによって。
measurements-changed{ tool, units, items }計測が追加・削除・名前変更された。リスト全体を含みます。
tour-started{ title, total, template }ツアーが始まった。
tour-step{ index, total, caption }ツアーが別の停留点へ移動した。
tour-ended{ completed }ツアーが停止した。最後の停留点に達していれば completed。
presentation-progress{ stage, label?, progress }ディレクターがプレゼンテーションを生成またはエクスポート中。
layer-feature-picked{ layerId, layer, featureId, geometry, lonLat, properties }データレイヤーのフィーチャーがクリックされた:レイヤー、プロパティ、マーカー用の地点。
alert{ kind, from, id, name, rule, count, sample }データレイヤーまたはツインのルールがアラートを開始・終了した。

静的メンバー

IfcViewer.LANGUAGES({ code, label }[]、母語表記)と IfcViewer.SUPPORTED_LANGUAGES(コード)— ビューアの準備が整う前に言語ピッカーを作れます。

Web component

JavaScript は不要 — タグを任意のページやダッシュボードに置き、属性を設定するだけです。

属性: modeluilangaccentvalidatepanelbase-urltoolbartoolsauto-frame

イベントは ifcviewer:<type> という名前の DOM CustomEvent として再送出されます(detail = payload)。内部の IfcViewer は要素の .viewer にあり、getStats / getIssues / screenshot はそこへ委譲されます。

JavaScript
const el = document.querySelector("ifc-viewer");
el.addEventListener("ifcviewer:validation-completed", (e) => {
  updateScore(e.detail.qualityScore);
});
const stats = await el.getStats();

ガイド

レシピ

実際にチームが作るものを、コピー&ペーストできる形で。

要素インスペクタ

3D 上でクリックし、IFC のプロパティセットを自前のパネルに読み込みます。

JavaScript
viewer.on("element-selected", async (e) => {
  const data = await viewer.getElement(e.expressId, e.modelId);
  renderPanel(data.name, data.globalId, data.propertySets);
});

ヘルススコア ウィジェット

モデルの品質スコアと指摘件数をダッシュボードに表示します。

JavaScript
viewer.on("validation-completed", (v) => {
  setScore(v.qualityScore);                 // 0–100, or null
  setCounts(v.errors, v.warnings, v.info);
});

IDS 適合性チェック

buildingSMART の IDS に対して検証し、仕様ごとに合否を一覧表示します。

JavaScript
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)));
});

EIR / BIM 検証

編集可能な EIR プロファイル(ISO 19650 スタイル)を実行します — .ids ファイルは不要。失敗には要素の GlobalId が付きます。

JavaScript
// 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(" · ")));
});

ブログ記事向けのシーン

白背景、敷地に置かれたモデル、夏至の夕方の影——事前に宣言、クリック不要。

JavaScript
// 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 these

平面図と計測

階名で平面を切断し、読者に計測してもらい、結果を SI 単位で読み取ります。

JavaScript
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)));
});

記事の中のガイドツアー

getCamera() で停留点を一度定義し、本文の横でツアーを自動再生させます。

JavaScript
// 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));

ページからモデルの動画を

レシピでプレゼンテーションを生成し MP4 を受け取ります——画面録画もアップロードも不要。

JavaScript
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 }));

投稿用のカバー

タイトル入りのモデルの Pinterest ピンや LinkedIn カードを生成し、PNG を受け取ります。

JavaScript
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 }));

記事の中のモデル

ui: 'article' はキャンバスだけ。モデルが画面いっぱいに収まり、ホイールでは読者がそのまま読み進められます。角度は frame() で。

JavaScript
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,
});

本文とともに進むストーリー

ビューアを固定し、各段落でショットを決めます:壁だけを表示、回り込み、夕方の太陽。

JavaScript
// <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" } },
]);

ページに組み込むライブ都市ツイン

公開済みのシーンを開き、アラートやクリックに反応し、自分のライブソースを横に追加して、結果へのリンクを共有します。

JavaScript
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" });

アプリ内の製品シート

バージョンを固定し、ファイルを渡し、メーカーがタイプに記載した内容(熱貫流率、耐風圧等級、メーカー)を表示します。

JavaScript
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);

自社ブランドに合わせる

アクセントカラー 1 色でビューアを自社プロダクトに揃えます。

JavaScript
new IfcViewer("#viewer", { accent: "#3fa866" });

// …or on the web component:
// <ifc-viewer accent="#3fa866" model="…"></ifc-viewer>

言語ピッカー

ビューアに同梱された言語からセレクタを組み立てます。

JavaScript
// build a <select> from the bundled languages
IfcViewer.LANGUAGES.forEach(({ code, label }) => addOption(code, label));

// switch at runtime
viewer.setLanguage("ja");

Health Score

検証のライフサイクル

validate: true は各モデルの読み込み後に検証し、validate() は必要なときに実行して結果で解決します。getValidation() が各時点で返すもの:

タイミングgetValidation() の戻り値
検証前null — getValidationStatus() は idle。
最初の実行中null — getValidationStatus() は進捗付きの running。validation-started は発火済み。
実行後{ qualityScore, errors, warnings, info, total, status: 'done', modelId }。複数モデルでは集計(modelId: null)。
新しい実行の最中前回の数値、status: 'running'。
失敗後前回の数値に status: 'error' と error — 前回がなければ null。validation-failed が発火し、validate() は拒否済み。

バージョン

バージョンを固定する

1.18.0 以降、各リリースは決して変わらないパスにも公開されます。本番では固定してください。可変パスは常に最新リリースを指します。

HTML
<script type="module">
  import { IfcViewer } from "https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js";
</script>
パスキャッシュ用途
/sdk/ifc-viewer.es.js読み込みごとに再検証試用 — 常に最新。
/sdk/1.18.0/ifc-viewer.es.js不変、1 年間キャッシュ本番 — テストしたコードがそのまま動きます。

固定した SDK はどのビューアを開く?

iframe は同一オリジンのライブなビューアで、凍結されたコピーではありません。固定した SDK は自身の API と挙動を保ち、中のビューアは改善を続けます。ビューアは公開済みのどの SDK が送るコマンドにも応答し(ビルドで検査)、古い固定版も動き続けます。

内容も固定する

SDK の隣にある versions.json に各バージョンの Subresource Integrity ハッシュがあります:

HTML
<script type="module" src="https://www.ifcvieweronline.eu/sdk/1.18.0/ifc-viewer.es.js"
        integrity="sha384-WsT4d0DS1oqva4rLWCGBDrx50uZVA3B95IDWbfxaFt8lnjs6NgOrjKNcWQL57/0z" crossorigin="anonymous"></script>

変更履歴

1.18.0 — ホストアプリ内のカタログオブジェクト。getElement() が GlobalId とタイプのデータ(typeName、単位付き typeProperties、effectivePropertySets。IFC4 のタイプと IFC2x3 のスタイル)を返します。validate()、getValidationStatus()、validation-started / validation-failed を追加し、validate: true は読み込みごとに確実に検証します。読み込み後の自動フレーミング、小さな物体にも余白をとる fit() と frame()、frame(elementId)、setView() が再びカメラを動かします。ui: 'embed' と toolbar・tools。固定ビルドは /sdk/1.18.0/。findElements()、getElement() / select() / element-selected での GlobalId、窓とドアの枠・パネルのプロパティセット、タイプの材料を追加。ui: 'embed' ではプロパティパネルが選択したオブジェクトを隠しません。 checkEir('builtin-en14351-1') は EN 14351-1 に基づく窓と外部ドアの宣言性能(耐風圧、水密性、遮音、熱貫流率、g と τv、製造者と製品型式コード)を標準 IFC プロパティセットでチェックします。EIR ルールに where(特定のプロパティ値を持つ要素のみ)と optional が加わりました。

ヘルプ

トラブルシューティング

つまずきやすいのは、ほんの数点です。

何も描画されません

順に確認してください:ビューアが ready に到達したか、モデルが読み込まれたか(model-error ではなく model-loaded か)、コンテナの高さがゼロでないか、バイト列が正しい IFC か。原因は model-error のメッセージに示されます。

モデルの読み込みで CORS エラーになります

CORS が関係するのは addFromUrl(url) だけです。バイト列を自分で扱えるなら、自分で取得して add(name, bytes) を呼んでください — データを直接渡すので CORS は関係しません。

iframe が真っ白、または読み込まれません

アプリが埋め込み可能である必要があります — ホスト側に X-Frame-Options: DENY や厳しい frame-ancestors の CSP があってはいけません。当方のホスティング版は埋め込みを許可しています。

add() のあと ArrayBuffer が空になります

add() はゼロコピーで受け渡すためにバッファを転送し、その結果あなたのコード側では detached になります。あとでもバイト列が必要ならコピー(bytes.slice(0))を渡してください。

読み込みやクエリがタイムアウトします

add()/addFromUrl() は loadTimeout 後に reject します(既定 120 秒。0 で無効)。クエリは 30 秒、checkIds は 120 秒です。低速な端末で大きなモデルを扱う場合は loadTimeout を大きくしてください。

対応ブラウザは?

WebAssembly と ES モジュールに対応した最新のブラウザであれば動作します — Chrome、Edge、Firefox、Safari。Internet Explorer は対象外です。

ローカルの開発サーバーで 404 になります

SPA の開発サーバーが HTML フォールバックで /sdk/ を横取りすることがあります。実際のビルドや静的ホスティングで試すか、/sdk/ifc-viewer.es.js モジュールを直接 import してください。