How to enable model comparison
Overview
This page demonstrates how to enable side-by-side model comparison of two different models in the IafViewer. To achieve this, it uses two instances of IafViewerDBM wrapped in a CompareView component.
Note: The material on this page serves as examples of how model comparison can be implemented by an application. Components such as
CompareVieware not officially part of the IafViewer. Similarly, the modes listed are also just examples. The examples shown demonstrate particular routes to implementing model comparison but there are other ways to implement this feature.
Main requirements for model comparison
Model comparison requires the following:
CompareViewcomponent- Two
IafViewerDBMinstances - Several required key props
- Use one of the two available patterns of IafViewer behaviour
CompareView component
This component is required as it does the following:
- Provides layout (either parallel or overlay)
- Manages two child components
- Handles image orientation (horizontal or vertical)
IafViewerDBM instances
The ViewerDBM instances do the following:
- Each loads a different model
- Share common configuration via spread props
- Has unique props:
model,modelVersionId,title
Required key props
The following props are required:
serverUrl- Graphics service URLmodel- Model object with_idand_namemodelVersionId- Specific version to loadview3d,view2d,gid- view configurations
Modes
These modes of IafViewer behaviour are available:
- Independent viewers - Each camera is independent of the other
- Synchronized cameras - Cameras have a shared state
For more information, refer to the section Independent and sychronized modes.
Core components
You need to set up a layout wrapped component which manages the visual arrangement of the two viewers.
Refer to the code samples below.
Props
{
mode: 'parallel' | 'overlay', // Display mode
orientation: 'horizontal' | 'vertical', // Layout direction
children: ReactNode[] // Two child components (viewers)
}
Display modes
Parallel Mode - Side-by-side views with 50/50 split
<CompareView mode="parallel" orientation="horizontal">
<ViewerA />
<ViewerB />
</CompareView>
<CompareView mode="overlay" orientation="horizontal">
<ViewerA /> {/* Bottom layer /}
<ViewerB /> {/ Top layer with clipping */}
</CompareView>
IafViewerDBM
You also require a database-managed 3D/2D viewer component which loads and displays BIM models. See example code below.
Key Props for comparison
{
model: { // Model object
_id: string, // Model ID
_name: string, // Display name
_namespaces: object // Namespaces config
},
modelVersionId: string, // Specific version to load
serverUri: string, // Graphics service URL
title: string, // Viewer title (unique identifier)
// View configuration
view3d: object, // 3D view settings
view2d: object, // 2D view settings
gis: object, // GIS settings
}
Basic implementation
For a basic implementation of model comparison, do the following:
- Format models.
- Configure shared props.
- Set up render comparison.
Refer to the sections below for more information:
Format models
Refer to the same code below.
const formattedModels = currentModels.map(item => ({
_id: item.model.model,
_name: item.bimpk.filename.replace(/\.[^/.]+$/, ''),
_versionId: item.model.modelVersionId,
_namespaces: item.model?._namespaces
}));
Configure shared props
Refer to the same code below.
const viewerProps = {
serverUri: 'https://your-server.com',
view3d: {
enable: true,
showToolbar: false
},
view2d: {
enable: false
},
gis: {
enable: true,
showToolbar: false
},
enablePersistence: true
};
Set up render comparison
<CompareView mode="parallel" orientation="horizontal">
{/* First Viewer */}
<div style={{ height: '100%', width: '100%' }}>
<IafViewerDBM
{...viewerProps}
model={formattedModels[0]}
modelVersionId={formattedModels[0]._versionId}
title="Model A"
/>
</div>
{/* Second Viewer */}
<div style={{ height: '100%', width: '100%' }}>
<IafViewerDBM
{...viewerProps}
model={formattedModels[1]}
modelVersionId={formattedModels[1]._versionId}
title="Model B"
/>
</div>
</CompareView>
Complete example
For a complete example, refer to the code below.
import React from 'react';
import IafViewerDBM from '@invicara/iaf-viewer';
import CompareView from './comparison/CompareView.jsx';
import { IafProj } from '@dtplatform/platform-api';
class ModelComparison extends React.Component {
constructor(props) {
super(props);
this.state = {
view3d: { enable: true, showToolbar: false },
view2d: { enable: false }
};
}
render() {
// Get models from project
const currentProject = IafProj.getCurrent();
const currentModels = currentProject?._userAttributes?.currentModels || [];
// Format for IafViewerDBM
const formattedModels = currentModels.map(item => ({
_id: item.model.model,
_name: item.bimpk.filename.replace(/\\.[^/.]+$/, ''),
_versionId: item.model.modelVersionId,
_namespaces: item.model?._namespaces
}));
// Shared configuration
const viewerProps = {
serverUri: this.props.serverUri,
view3d: this.state.view3d,
view2d: this.state.view2d,
gis: { enable: true, showToolbar: false },
enablePersistence: true
};
// Render comparison
if (formattedModels.length >= 2) {
return (
<div style={{ width: '100%', height: '100%' }}>
<CompareView mode="parallel" orientation="horizontal">
<div style={{ height: '100%', width: '100%' }}>
<IafViewerDBM
{...viewerProps}
model={formattedModels[0]}
modelVersionId={formattedModels[0]._versionId}
title="Model A"
/>
</div>
<div style={{ height: '100%', width: '100%' }}>
<IafViewerDBM
{...viewerProps}
model={formattedModels[1]}
modelVersionId={formattedModels[1]._versionId}
title="Model B"
/>
</div>
</CompareView>
</div>
);
}
// Single viewer fallback
return (
<div style={{ height: '100%', width: '100%' }}>
<IafViewerDBM {...viewerProps} model={formattedModels[0]} />
</div>
);
}
}
export default ModelComparison;
Useful concepts
Shared props versus unique props
Note the differences between using shared props and unique props. Shared props will apply to both viewers whereas unique props are specific to each viewer.
Shared props (applies to both viewers)
Shared props include:
serverURL- graphics service endpointview3d,view2d,gis- view configurationsenablePersistence- settings persistencesaveSettings- settings save callback
Unique props (specific to both viewer)
Unique props include:
model- different model object for each viewermodelVersionId- specific version identifiertitle- unique totle for each instance
Independent and synchonized modes
Note the different modes of behavior for the camera in the IafViewer:
- Independent - This is the default behavior
- Synchronized - This is available via a custom implementation
See the different features of each mode, below.
Independent
The independent mode offers the following features:
- Each viewer maintains its own camera position
- Users interact with each view separately
- This mode is useful for comparing different aspects of a model
Synchronized
The synchronized mode offers the following features:
- Can share camera state between viewers
- Good for direct visual comparison with aligned viewpoints
Refer to the sample code for synchronized camera below.
// Synchronized camera example
const [sharedCamera, setSharedCamera] = useState(undefined);
view3d: {
enable: true,
camera: sharedCamera, // Same camera for both
onCameraUpdate: {
callback: (camera) => setSharedCamera(camera)
}
Modes comparison
It is useful to compare different usage examples of independent viewers and synchronized cameras.
Mode 1: Independent viewers
In this example, the viewer operates independently with separate camera positions.
<CompareView mode="parallel" orientation="horizontal">
<IafViewerDBM
model={modelA}
view3d={{ enable: true, showToolbar: false }}
/>
<IafViewerDBM
model={modelB}
view3d={{ enable: true, showToolbar: false }}
/>
</CompareView>
Mode 2: Synchonized camera
In this example, both viewers maintain the same camera position.
const [sharedCamera, setSharedCamera] = useState(undefined);
const viewConfig = {
view3d: {
enable: true,
camera: sharedCamera,
onCameraUpdate: {
delay: 300,
callback: (camera) => setSharedCamera(camera)
}
}
};
<CompareView mode="parallel" orientation="horizontal">
<IafViewerDBM model={modelA} view3d={viewConfig.view3d} />
<IafViewerDBM model={modelB} view3d={viewConfig.view3d} />
</CompareView>