IFC Viewer SDK v1.18.0 เปิดตัวแสดงผล
ไทย

IFC Viewer SDK · v1.18.0

ฝังตัวแสดงผล IFC ลงในผลิตภัณฑ์ของคุณเอง

แทรกตัวแสดงผลบนเบราว์เซอร์เดียวกันลงใน CDE ดิจิทัลทวิน หรือเครื่องมือภายในของโครงการ โหลดข้อมูล IFC จากแอปของคุณ โดยแต่ละโมเดลถูกประมวลผลที่ฝั่งไคลเอนต์

สามบรรทัดถึงการเรนเดอร์ครั้งแรก

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

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

การติดตั้ง

เริ่มต้นอย่างรวดเร็ว

เลือกสแตกของคุณ คัดลอก วาง แล้วเรนเดอร์ — ไม่ต้องมีขั้นตอน build ไม่ต้องใช้ API key

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 KBiframe ที่แยกออกมาthree.js · web-ifc · WASMไบต์ IFCpostMessage

แอปของคุณ

โมดูล ES ขนาด ~6 KB ที่สร้าง iframe และ JS API แบบมี type น้ำหนักของ 3D และการ parse ไม่แตะบันเดิลของคุณเลย

สะพาน postMessage

ไบต์ IFC ถูกโอนไปยัง iframe ในรูป ArrayBuffer (ไม่มีการคัดลอก) คำสั่งและคำค้นเป็นข้อความแบบ request/response ที่จับคู่กัน

ตัวแสดงผล

three.js และ web-ifc ที่คอมไพล์เป็น WebAssembly ทำการ parse และเรนเดอร์โมเดลภายใน iframe — บนเครื่องของผู้เข้าชม

ทำไมจึงเป็นส่วนตัว

โมเดลไม่เคยไปถึงเซิร์ฟเวอร์ ไม่มีสายการอัปโหลดให้ต้องดูแลความปลอดภัย ไม่มีคำถามเรื่องถิ่นที่อยู่ของข้อมูล ไม่มีคิวตามขนาดไฟล์

ทำไมจึงขยายตัวได้

การคำนวณเกิดขึ้นบนอุปกรณ์ของผู้เข้าชมแต่ละคน การเสิร์ฟโมเดล 200 MB มีต้นทุนเท่ากับ 2 MB — เพราะมันคือไฟล์สแตติก

แนวคิด

แนวคิดหลัก

สี่เรื่องที่ควรรู้ก่อนลงมือสร้าง

การ mount และความพร้อม

new IfcViewer(target) จะ mount ทันทีและเก็บคำสั่งไว้ในคิวจนกว่าตัวแสดงผลจะพร้อม ส่วน 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) จะดึงจาก URL สาธารณะที่เปิด CORS

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 แต่ละตัวเป็นแบบ request/response และจะ reject เมื่อหมดเวลา (30 วินาที; IDS / EIR สูงสุด 120 วินาที)

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

อ้างอิง

อ้างอิง API

ทุกเมธอด จัดเป็นกลุ่ม — พร้อมลายเซ็นและชนิดค่าที่คืน ถัดไปคือตัวเลือกของ constructor อีเวนต์ และ web component <ifc-viewer>

Constructor

new IfcViewer(target, options?)IfcViewer

ติดตั้งในตัวเลือก CSS หรืออิลิเมนต์ ดูออปชันด้านล่าง

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

สร้างตัวแสดงผลและ resolve เมื่อพร้อมรับคำสั่ง

การโหลดโมเดล

add(name, bytes)Promise<ModelLoadedEvent>

โหลดไบต์ IFC (ArrayBuffer/Uint8Array) จากแอปของคุณ บัฟเฟอร์ถูกถ่ายโอนแบบไม่คัดลอก

addFromUrl(url, name?)Promise<ModelLoadedEvent>

โหลด URL IFC สาธารณะที่เปิด CORS

removeModel(modelId)void

ยกเลิกการโหลดโมเดลตาม id

clear()void

ลบโมเดลที่โหลดไว้ทั้งหมด

กล้องและการเลือก

select(expressId | globalId, modelId?)void

เลือกและจัดเฟรมชิ้นส่วน — ด้วย expressID ของ IFC หรือด้วย 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' }>

จัดเฟรมชิ้นส่วนเดียว (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>

เปิดฉากอื่น: URL ของไฟล์ .scene.json หรือเอกสารฉาก (ถูกบีบอัดไปกับที่อยู่ของเฟรม จึงต้องใส่ในลิงก์ได้) ตัวแสดงผลจะโหลดใหม่ การเรียกที่รออยู่จะถูกปฏิเสธ และ 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 }) GeoJSON หรือฟีดสดที่ URL ({ 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 ม.

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

เลื่อน เปิด/ปิด หรือกลับด้านระนาบ

removeSection(id?)Promise<SectionsState>

ลบระนาบหนึ่ง หรือลบการตัดทั้งหมดเมื่อไม่ระบุ 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 (หนึ่งหน้า), pdf / pptx (ทั้งเอกสาร), zip (ทุกหน้า) ส่งไบต์กลับมา

closeCover()Promise<void>

ปิดสตูดิโอหน้าปก

ชุดสำหรับบทความ

activate()void

เริ่มตัวดูที่โหลดภายหลังทันที (เหมือนปุ่มบนโปสเตอร์) คำสั่งก่อนหน้านี้จะรอในคิว

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

Scrollytelling: ขณะแต่ละย่อหน้าผ่านกลางจอ จะจัดเฟรม แยกองค์ประกอบ ขยับดวงอาทิตย์ หรือรันโค้ดของคุณ คืนค่าฟังก์ชันสำหรับยกเลิก

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>

แสดงหรือซ่อนทั้งกลุ่ม หรือแสดงเพียงกลุ่มเดียว

frameGroup(id)Promise<void>

จัดกล้องให้พอดีกับกลุ่ม รวมสมาชิกที่ซ่อนอยู่

การวัด

setMeasureTool(tool)Promise<void>

เปิดเครื่องมือวัดให้ผู้เข้าชม (เปิดแผงวัด) หรือ 'none' ผลลัพธ์มาทาง measurements-changed

getMeasurements()Promise<MeasurementsState>

การวัดทั้งหมดบนหน้าจอ ค่าเป็นหน่วย SI เสมอ — เมตร, ตร.ม., องศา — ไม่ว่าจะแสดงอย่างไร

clearMeasurements(id?)Promise<MeasurementsState>

ลบการวัดหนึ่งรายการ หรือทั้งหมด

การสืบค้น

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 (ตาม 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>

เอาไฟล์สแกนหนึ่งชิ้นออกและคืนบัฟเฟอร์ GPU หรือเอาออกทั้งหมด

setPointCloudVisible(cloudId, visible)Promise<void>

แสดงหรือซ่อนไฟล์สแกนโดยไม่ต้องเอาออกจากหน่วยความจำ

fitPointCloud(cloudId?)Promise<void>

จัดกล้องให้พอดีกับไฟล์สแกนหนึ่งชิ้น หรือชิ้นแรกที่โหลด

setPointCloudDisplay(display, renderBudget?)Promise<void>

ขนาดจุด ความทึบ โหมดสี ความหนาแน่น และเกณฑ์ความเชื่อมั่น — ใช้ร่วมกันทุกไฟล์ ทุกค่าเป็น uniform ของเชเดอร์ จึงมีผลทันทีแม้ที่ 20 ล้านจุด

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>

เอาโมเดลที่นำเข้าออกหนึ่งชิ้นและคืนเรขาคณิต วัสดุ และเท็กซ์เจอร์ หรือเอาออกทั้งหมด

setMeshVisible(visible, meshId?)Promise<void>

แสดงหรือซ่อนโมเดลที่นำเข้าโดยไม่ต้องเอาออกจากหน่วยความจำ

fitMesh(meshId?)Promise<void>

จัดกล้องให้พอดีกับโมเดลที่นำเข้าหนึ่งชิ้น หรือทั้งหมด

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>

ตรวจโมเดลกับ IDS ของ buildingSMART (ไฟล์ XML .ids) ทำงานใน worker และคืนผลผ่าน/ไม่ผ่านแยกตามข้อกำหนด

checkEir(profile)Promise<IdsResult>

ตรวจแบบจำลองตามโปรไฟล์การตรวจสอบ EIR / BIM — เป็นออบเจกต์ JSON ของมัน (รวมรูปแบบย่อ) หรือ id ของโปรไฟล์ในตัว เช่น 'builtin-en14351-1' คอมไพล์เป็น IDS และทำงานบนเอนจินเดียวกัน: ได้ IdsResult รูปแบบเดียวกับ checkIds

ภาษา

setLanguage(lang)void

เปลี่ยนภาษาอินเทอร์เฟซขณะทำงาน

getLanguages()string[]

แสดงรายการรหัสภาษาที่รองรับ (ป้ายกำกับดูที่ IfcViewer.LANGUAGES)

วงจรชีวิตและอีเวนต์

whenReady()Promise<void>

Resolve เมื่อตัวแสดงผลพร้อมรับคำสั่ง

isReadyboolean

Getter — เป็น true ทันทีที่ตัวแสดงผลแจ้งว่าพร้อมแล้ว

on(event, cb)() => void

สมัครรับอีเวนต์ คืนค่าฟังก์ชันสำหรับยกเลิก

off(event, cb)void

ลบตัวรับอีเวนต์ที่เพิ่มไว้ก่อนหน้า

dispose()void

รื้อตัวแสดงผลและลบ iframe

การเรียก add() / addFromUrl() พร้อมกันจะถูกทำให้เป็นลำดับภายใน และแต่ละ promise จะจับคู่กับการโหลดของตัวเอง — การโหลดที่เริ่มโดยแอป (พารามิเตอร์ URL หรือการอัปโหลดภายใน iframe) จะไม่มีวัน resolve promise ของ add() ที่คุณเรียก

ตัวเลือกของ constructor

ตัวเลือกชนิดค่าเริ่มต้นคำอธิบาย
ui'minimal' | 'full' | 'kiosk' | 'client' | 'article' | 'embed''minimal'พรีเซ็ตอินเทอร์เฟซ 'embed': สำหรับแอปโฮสต์ที่ส่งไฟล์เอง — 3D แสดงคุณสมบัติเมื่อเลือก แถบเครื่องมือแบบกะทัดรัดที่มี ตรวจสอบ และ วัด ไม่มี เปิด แชร์ บัญชี หรือภาษา
toolbarbooleanpresetแสดงแถบเครื่องมือ (ค่าเริ่มต้นตามพรีเซ็ต) false จะเหลือเพียงมุมมอง 3D และแผง
tools('validate' | 'measure')[]['validate', 'measure']เครื่องมือบนแถบกะทัดรัดของ ui: 'embed' — 'validate', 'measure' [] หมายถึงไม่มี
autoFramebooleantrueจัดเฟรมทั้งฉากจากมุม iso พร้อมระยะขอบทุกครั้งที่การโหลดเสร็จ false ให้คุณควบคุมกล้องเอง
validatebooleantrueรันการตรวจสอบตอนโหลด (เป็นที่มาของ Health Score)
panelbooleanfalseเปิดแผงการตรวจสอบโดยอัตโนมัติ
panelsPanelName[]—จำกัดแถบเครื่องมือให้เหลือเฉพาะแผงเหล่านี้ตั้งแต่เฟรมแรก — `['scene','map']` หรือ `[]` เพื่อไม่ให้มีแถบ ใช้ชื่อเดียวกับ setPanels และพารามิเตอร์ URL `panels=` ดีกว่าการเรียก setPanels หลังโหลด ซึ่งจะแสดงแถบเต็มก่อนแล้วค่อยเอาเครื่องมือออก
langstringautoบังคับภาษาของอินเทอร์เฟซ (เช่น en, es, ja)
accent#rrggbbbrandปรับสีตัวแสดงผลให้เข้ากับแดชบอร์ดของคุณ
modelstring—โหลด URL สาธารณะของ IFC นี้โดยอัตโนมัติเมื่อพร้อม
backgroundBackgroundSpec—พื้นหลังฉากตั้งแต่เฟรมแรก — พรีเซ็ต, '#rrggbb' หรือ '#บน,#ล่าง'
mapboolean | ('terrain' | 'buildings' | 'showcase')[]—วางโมเดลบนแผนที่เมื่อโหลดเสร็จ: true หรือเลเยอร์ ['terrain','buildings','showcase']
solar · moon'MM-DDTHH:MM' · boolean—เปิดการศึกษาแสงแดดที่เวลาท้องถิ่นของไซต์: '06-21T18:00'
scansstring[]—URL ของพอยต์คลาวด์ที่จะโหลดพร้อมโมเดล
layersstring—การตั้งค่าชั้นข้อมูลที่จะเปิด: URL ของ JSON ที่ส่งออกจากแผงชั้นข้อมูล (แหล่งสด สไตล์ การแจ้งเตือน) ตั้งแต่ 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'หยุดเรนเดอร์เมื่ออยู่นอกจอ ค่าเริ่มต้น true เมื่อใช้ ui: 'article'
fullscreenButtonbooleanfalseปุ่มขยายเล็กๆ ที่มุม
height · widthnumber | string'100%'ขนาดของ iframe (ตัวเลข → px)
baseUrlstringautoURL ฐานของแอป อนุมานจาก URL ของสคริปต์
loadTimeoutnumber120000Reject add()/addFromUrl() หลังผ่านไป N มิลลิวินาที (0 คือปิดใช้งาน)
className · titlestring—คลาสเพิ่มเติม / title ของ iframe
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

อีเวนต์จะถูกส่งซ้ำเป็น CustomEvent ของ DOM ชื่อ ifcviewer:<type> (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();

คู่มือ

สูตรสำเร็จ

รูปแบบที่คัดลอกไปวางได้ สำหรับสิ่งที่ทีมสร้างกันจริง ๆ

ตัวตรวจสอบอิลิเมนต์

คลิกในมุมมอง 3 มิติ แล้วอ่าน property set ของ IFC เข้าสู่แผงของคุณเอง

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

วิดเจ็ต Health Score

แสดงคะแนนคุณภาพของโมเดลและจำนวนปัญหาบนแดชบอร์ดของคุณ

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

ตรวจความสอดคล้องด้วย IDS

ตรวจกับ IDS ของ buildingSMART แล้วแสดงผลผ่าน / ไม่ผ่าน แยกตามข้อกำหนด

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

หน้าข้อมูลสินค้าในแอปของคุณ

ตรึงเวอร์ชัน ส่งไฟล์ และแสดงสิ่งที่ผู้ผลิตใส่ไว้ในประเภท: ค่า U ระดับแรงลม ผู้ผลิต

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

ปรับให้เข้ากับแบรนด์

จับคู่ตัวแสดงผลกับผลิตภัณฑ์ของคุณด้วยสีเน้นเพียงสีเดียว

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ไม่เปลี่ยนแปลง แคชหนึ่งปีโปรดักชัน — โค้ดที่คุณทดสอบคือโค้ดที่รัน

SDK ที่ตรึงไว้เปิดตัวแสดงผลตัวไหน?

iframe คือตัวแสดงผลจริงที่ origin เดียวกัน ไม่ใช่สำเนาที่แช่แข็ง: SDK ที่ตรึงไว้คง API และพฤติกรรมของตัวเอง ขณะที่ตัวแสดงผลด้านในยังพัฒนาต่อ ตัวแสดงผลตอบทุกคำสั่งจาก SDK ทุกเวอร์ชันที่เผยแพร่ — มีการตรวจตอน build — เวอร์ชันเก่าที่ตรึงไว้จึงยังใช้งานได้

ตรึงเนื้อหาด้วย

versions.json ข้าง SDK แสดงแฮช 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(), GlobalId ใน getElement() / select() / element-selected, ชุดคุณสมบัติวงกบและบานของหน้าต่างและประตู และวัสดุของประเภท ใน ui: 'embed' แผงคุณสมบัติจะไม่บังวัตถุที่เลือก checkEir('builtin-en14351-1') ตรวจสมรรถนะที่ประกาศของหน้าต่างและประตูภายนอกตาม EN 14351-1 — แรงลม การกันน้ำ เสียง ค่าการถ่ายเทความร้อน g และ τv ผู้ผลิตและรหัสผลิตภัณฑ์ — บน property set มาตรฐานของ IFC; กฎ EIR เพิ่ม where (เฉพาะองค์ประกอบที่มีค่าคุณสมบัติ) และ optional

ความช่วยเหลือ

การแก้ปัญหา

ไม่กี่เรื่องที่คนมักสะดุด

ไม่มีอะไรถูกเรนเดอร์

ตรวจตามลำดับนี้: ตัวแสดงผลถึงสถานะ ready แล้วหรือไม่, โมเดลโหลดสำเร็จหรือไม่ (model-loaded ไม่ใช่ model-error), คอนเทนเนอร์มีความสูงไม่เป็นศูนย์หรือไม่ และแหล่งไบต์เป็น IFC ที่ถูกต้องหรือไม่ ข้อความของ model-error จะบอกสาเหตุ

เจอข้อผิดพลาด CORS ตอนโหลดโมเดล

CORS มีผลเฉพาะกับ addFromUrl(url) หากคุณควบคุมไบต์ได้เอง ให้ดึงเองแล้วเรียก add(name, bytes) — ไม่มี CORS เข้ามาเกี่ยวข้อง เพราะคุณส่งข้อมูลให้โดยตรง

iframe ว่างเปล่าหรือไม่ยอมโหลด

แอปต้องฝังได้ — ต้องไม่มี X-Frame-Options: DENY และไม่มี CSP frame-ancestors ที่เข้มงวดบนโฮสต์ แอปที่เราโฮสต์อนุญาตให้ฝังได้

ArrayBuffer ของฉันว่างเปล่าหลังเรียก add()

add() จะโอนบัฟเฟอร์เพื่อส่งมอบแบบไม่คัดลอก ซึ่งทำให้มันกลายเป็น detached ในโค้ดของคุณ ให้ส่งสำเนา (bytes.slice(0)) หากยังต้องใช้ไบต์เหล่านั้นภายหลัง

การโหลดหรือการสืบค้นหมดเวลา

add()/addFromUrl() จะ reject หลังครบ loadTimeout (ค่าเริ่มต้น 120 วินาที; 0 คือปิดใช้งาน) การสืบค้นจะ reject ที่ 30 วินาที และ checkIds ที่ 120 วินาที โมเดลขนาดใหญ่บนอุปกรณ์ที่ช้าอาจต้องใช้ loadTimeout ที่สูงขึ้น

รองรับเบราว์เซอร์ใดบ้าง?

เบราว์เซอร์สมัยใหม่ที่อัปเดตต่อเนื่องและรองรับ WebAssembly กับ ES module — Chrome, Edge, Firefox, Safari ไม่รองรับ Internet Explorer

ได้ 404 บนเซิร์ฟเวอร์พัฒนาเครื่องผม

เซิร์ฟเวอร์พัฒนาแบบ SPA อาจดักเส้นทาง /sdk/ ด้วย HTML fallback ให้ทดสอบกับ build จริงหรือโฮสต์แบบสแตติก หรือนำเข้าโมดูล /sdk/ifc-viewer.es.js โดยตรง