html {
height: 100%;
overflow: hidden;
width: 100%;
}
body {
background-color: #ade0bc;
background-image: url("https://archbee-image-uploads.s3.amazonaws.com/oGjtxJ5MUZmGondFfGBOZ-0l0obCRKymJG7Q9KTtAhl-20240826-204445.png");
background-position: left;
background-repeat: no-repeat;
background-size: cover;
color: #004d47;
font-family: Inter;
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
width: 100%;
}
#console {
display: flex;
flex-direction: column;
height: 100px;
width: 100%;
z-index: 1;
}
#console-copy {
align-items: center;
background-color: #f3f3f5;
border: none;
color: #6e7574;
display: flex;
justify-content: center;
margin-right: 16px;
padding: 5px;
z-index: 1;
}
#console-copy:hover {
color: #212322;
cursor: pointer;
}
#console-copy:active {
color: #c5c7c7;
}
#console-copy #tooltip {
border-radius: 6px;
bottom: 102px;
background-color: black;
color: #fff;
font-size: 12px;
padding: 10px;
position: absolute;
right: 2px;
visibility: hidden;
width: 40px;
}
#console-copy:hover #tooltip {
visibility: visible;
}
#console-header {
align-items: center;
background-color: #f3f3f5;
display: flex;
height: 32px;
justify-content: space-between;
}
#console-header-text {
color: #6e7574;
font-size: 14px;
margin-left: 16px;
user-select: none;
}
#console-text {
background-color: white;
color: black;
flex: 1;
font-family: monospace;
font-size: 14px;
overflow: auto;
padding: 10px 16px 10px 16px;
word-break: break-all;
}
#error {
align-self: center;
bottom: 50px;
display: none;
font-size: 20px;
font-weight: 600;
position: absolute;
text-align: center;
}
#gui {
display: none;
left: 20px;
position: absolute;
top: 20px;
z-index: 1;
}
#logo {
bottom: 15px;
position: absolute;
right: 15px;
}
#logo-image {
width: 200px;
}
#play {
align-items: center;
display: flex;
justify-content: center;
}
#play-button {
align-items: center;
background-color: #004d47;
border-color: transparent;
border-radius: 50%;
color: #ade0bc;
display: flex;
font-size: 26px;
height: 60px;
justify-content: center;
margin-top: 24px;
width: 60px;
}
#play-button:disabled {
cursor: wait;
}
#play-button:hover:enabled {
background-color: #001e1c;
cursor: pointer;
}
#play-icon {
padding-left: 5px;
}
#scene {
display: flex;
flex-direction: column;
height: 100%;
justify-content: flex-end;
width: 100%;
}
#splash {
display: flex;
flex-direction: column;
height: 100%;
min-height: 100%;
}
#splash #title {
display: flex;
flex: 1;
flex-direction: column;
font-size: 36px;
font-weight: 600;
height: 100%;
justify-content: center;
text-align: center;
}
#title-text {
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
}
#viewer-container {
flex: 1;
overflow: hidden;
}
#viewer-3d {
background: linear-gradient(rgb(237, 255, 254) 0px, rgb(255, 255, 255));
height: 100%;
width: 100%;
}
.lil-gui {
--background-color: #30343a;
--focus-color: #00665e;
--hover-color: #005952;
--name-width: 90%;
--number-color: #ade0bc;
--string-color: #ade0bc;
--text-color: #ade0bc;
--title-background-color: #24272b;
--title-text-color: #ade0bc;
--widget-color: #004d47;
--width: 200px;
}
let viewer3d = null;
let gui = null;
const modelId = "my-model-id";
const ElementId = {
CONSOLE_COPY: "console-copy",
CONSOLE_TEXT: "console-text",
ERROR: "error",
GUI: "gui",
LOADING_ICON: "loading-icon",
PLAY_BUTTON: "play-button",
PLAY_ICON: "play-icon",
SPLASH: "splash",
TOOLTIP: "tooltip",
VIEWER_3D: "viewer-3d"
};
const Style = {
FLEX: "flex",
NONE: "none"
};
const consoleCopy = document.getElementById(ElementId.CONSOLE_COPY);
const onCopyLeave = function () {
document.getElementById(ElementId.TOOLTIP).innerHTML = "Copy";
};
consoleCopy.addEventListener("mouseleave", onCopyLeave);
consoleCopy.addEventListener("touchend", onCopyLeave);
const runExample = function () {
const CLICK_EVENT = "Click Event Handler";
const CLIPPING_PLANES_EVENT = "Clipping Planes Event Handler";
const CONTEXT_MENU_EVENT = "Context Menu Event Handler";
const DOUBLE_CLICK_EVENT = "Double Click Event Handler";
const MEASURE_EVENT = "Measure Event Handler";
const SELECT_EVENT = "Select Event Handler";
const VIEWPOINT_EVENT = "Viewpoint Event Handler";
const REMOVE_ALL_EVENTS = "Remove All Events";
hideSpaces();
const eventHandlers = {};
const onClickEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onClick = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener("viewer3d.click", onClick);
eventHandlers[CLICK_EVENT] = handler;
disableGuiProperty(SELECT_EVENT);
enableGuiProperty(REMOVE_ALL_EVENTS);
} else {
eventHandlers[CLICK_EVENT].remove();
delete eventHandlers[CLICK_EVENT];
enableGuiProperty(SELECT_EVENT);
setOutput("");
}
};
const onClippingPlanesEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onClippingPlanesChange = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener(
"viewer3d.clippingplanes",
onClippingPlanesChange
);
eventHandlers[CLIPPING_PLANES_EVENT] = handler;
enableGuiProperty(REMOVE_ALL_EVENTS);
const clippingPlanes = [
{
location: {
x: 0,
y: 0,
z: 6
},
direction: {
x: 0,
y: 0,
z: 1
}
}
];
viewer3d.setClippingPlanes(clippingPlanes);
viewer3d.showClippingPlaneWidget();
} else {
eventHandlers[CLIPPING_PLANES_EVENT].remove();
delete eventHandlers[CLIPPING_PLANES_EVENT];
viewer3d.setClippingPlanes([]);
setOutput("");
}
};
const onContextMenuEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onContextMenu = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener(
"viewer3d.contextmenu",
onContextMenu
);
eventHandlers[CONTEXT_MENU_EVENT] = handler;
enableGuiProperty(REMOVE_ALL_EVENTS);
} else {
eventHandlers[CONTEXT_MENU_EVENT].remove();
delete eventHandlers[CONTEXT_MENU_EVENT];
setOutput("");
}
};
const onDoubleClickEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onDoubleClick = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener(
"viewer3d.dblclick",
onDoubleClick
);
eventHandlers[DOUBLE_CLICK_EVENT] = handler;
enableGuiProperty(REMOVE_ALL_EVENTS);
} else {
eventHandlers[DOUBLE_CLICK_EVENT].remove();
delete eventHandlers[DOUBLE_CLICK_EVENT];
setOutput("");
}
};
const onMeasureEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onMeasurementChange = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener(
"viewer3d.measure",
onMeasurementChange
);
eventHandlers[MEASURE_EVENT] = handler;
enableGuiProperty(REMOVE_ALL_EVENTS);
const options = { mode: "laser" };
viewer3d.enableMeasure(options);
} else {
eventHandlers[MEASURE_EVENT].remove();
delete eventHandlers[MEASURE_EVENT];
viewer3d.clearMeasure();
viewer3d.disableMeasure();
setOutput("");
}
};
const onSelectEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onSelect = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener("viewer3d.select", onSelect);
eventHandlers[SELECT_EVENT] = handler;
disableGuiProperty(CLICK_EVENT);
enableGuiProperty(REMOVE_ALL_EVENTS);
} else {
eventHandlers[SELECT_EVENT].remove();
delete eventHandlers[SELECT_EVENT];
enableGuiProperty(CLICK_EVENT);
setOutput("");
}
};
const onViewpointEventHandlerChange = function (isEnabled) {
if (isEnabled) {
const onViewpointChange = function (event) {
setOutput(event);
};
const handler = viewer3d.addEventListener(
"viewer3d.viewpoint",
onViewpointChange
);
eventHandlers[VIEWPOINT_EVENT] = handler;
enableGuiProperty(REMOVE_ALL_EVENTS);
} else {
eventHandlers[VIEWPOINT_EVENT].remove();
delete eventHandlers[VIEWPOINT_EVENT];
setOutput("");
}
};
const onRemoveAllEventHandlersClick = function () {
Object.values(eventHandlers).forEach((eventHandler) =>
eventHandler.remove()
);
gui.controllers.forEach((controller) => controller.enable());
setOutput("");
viewer3d.clearMeasure();
viewer3d.deselectAll();
viewer3d.disableMeasure();
viewer3d.setClippingPlanes([]);
disableGuiProperty(REMOVE_ALL_EVENTS);
Object.keys(state).forEach((key) => {
if (key !== REMOVE_ALL_EVENTS) {
updateGuiPropertyValue(key, false);
}
});
};
const state = {
[CLICK_EVENT]: false,
[CLIPPING_PLANES_EVENT]: false,
[CONTEXT_MENU_EVENT]: false,
[DOUBLE_CLICK_EVENT]: false,
[MEASURE_EVENT]: false,
[SELECT_EVENT]: false,
[VIEWPOINT_EVENT]: false,
[REMOVE_ALL_EVENTS]: onRemoveAllEventHandlersClick
};
gui.add(state, CLICK_EVENT).onChange(onClickEventHandlerChange);
gui
.add(state, CLIPPING_PLANES_EVENT)
.onChange(onClippingPlanesEventHandlerChange);
gui.add(state, CONTEXT_MENU_EVENT).onChange(onContextMenuEventHandlerChange);
gui.add(state, DOUBLE_CLICK_EVENT).onChange(onDoubleClickEventHandlerChange);
gui.add(state, MEASURE_EVENT).onChange(onMeasureEventHandlerChange);
gui.add(state, SELECT_EVENT).onChange(onSelectEventHandlerChange);
gui.add(state, VIEWPOINT_EVENT).onChange(onViewpointEventHandlerChange);
gui.add(state, REMOVE_ALL_EVENTS).disable();
};
const initialize = async function (onViewerReady) {
showLoadingIndicator();
hideElement(ElementId.ERROR);
gui = new lil.GUI({
container: document.getElementById("gui")
});
gui.title("Options");
const div3d = document.getElementById(ElementId.VIEWER_3D);
const viewerOptions = {
enableTouch: true,
textRenderMode: "dom"
};
viewer3d = new bimsync.viewer3d.Viewer3D(div3d, viewerOptions);
const loadModelOptions = {
modelId: modelId
};
const url3d = await getToken();
viewer3d
.loadModelsFromToken(url3d, loadModelOptions)
.then(function () {
showPlayButton();
hideElement(ElementId.SPLASH);
showElement(ElementId.GUI);
onViewerReady();
})
.catch(function (error) {
showError();
});
};
const start = function () {
initialize(runExample);
};
const copyConsole = async function () {
await navigator.clipboard.writeText(
document.getElementById(ElementId.CONSOLE_TEXT).innerHTML
);
document.getElementById(ElementId.TOOLTIP).innerHTML = "Copied!";
};
const disableGuiProperty = function (propertyName) {
gui
.controllersRecursive()
.find(({ property }) => property === propertyName)
.disable();
};
const enableGuiProperty = function (propertyName) {
gui
.controllersRecursive()
.find(({ property }) => property === propertyName)
.enable();
};
const getToken = async function () {
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const tryCount = 4;
let response, result;
for (let i = 1; i <= tryCount; i++) {
response = await fetch(
"https://documentation-edge.developers.catenda.com/token3d"
);
if (response.ok) {
break;
} else {
if (i < tryCount) {
await delay(i * 1000);
}
continue;
}
}
if (response && response.ok) {
const data = await response.json();
result = data.url;
} else {
showError();
}
return result;
};
const hideElement = function (elementId) {
document.getElementById(elementId).style.display = Style.NONE;
};
const hideSpaces = function () {
const spaceObjectIds = viewer3d
.getProducts()
.filter((product) => product.ifcType === "IfcSpace")
.map((space) => space.objectId);
viewer3d.hide(spaceObjectIds);
};
const setOutput = function (value) {
const text = value instanceof Object ? JSON.stringify(value) : value;
document.getElementById(ElementId.CONSOLE_TEXT).innerHTML = text;
};
const showElement = function (elementId) {
document.getElementById(elementId).style.display = Style.FLEX;
};
const showError = function () {
viewer3d.dispose();
gui.destroy();
showPlayButton();
showElement(ElementId.ERROR);
};
const showLoadingIndicator = function () {
const playButton = document.getElementById(ElementId.PLAY_BUTTON);
playButton.disabled = true;
const playIconElement = document.getElementById(ElementId.PLAY_ICON);
let loadingIcon = document.createElement("i");
loadingIcon.id = ElementId.LOADING_ICON;
loadingIcon.classList.add("fa", "fa-spinner", "fa-spin");
playIconElement.replaceWith(loadingIcon);
};
const showPlayButton = function () {
const playButton = document.getElementById(ElementId.PLAY_BUTTON);
playButton.disabled = false;
const loadingIconElement = document.getElementById(ElementId.LOADING_ICON);
let playIcon = document.createElement("i");
playIcon.id = ElementId.PLAY_ICON;
playIcon.classList.add("fa", "fa-play");
loadingIconElement.replaceWith(playIcon);
};
const updateGuiPropertyValue = function (propertyName, value) {
gui
.controllersRecursive()
.find(({ property }) => property === propertyName)
.setValue(value);
};