Enabling 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 exampleconst [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>