Skip to content

Commit

Permalink
Matrix api load model with custom colors (#795)
Browse files Browse the repository at this point in the history
* customViewSettings support for global element Ids

* store view, handle api event

* Update CadView.jsx

* Fixup useModelStore refs from merge.  Fix a div/p bug in About.

---------

Signed-off-by: Pablo Mayrgundter <pablo.mayrgundter@gmail.com>
Co-authored-by: aozien <a.osamamail@gmail.com>
  • Loading branch information
pablo-mayrgundter and aozien committed Sep 30, 2023
1 parent 66d9151 commit 0b2192d
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 21 deletions.
28 changes: 28 additions & 0 deletions cypress/e2e/integration/bldrs-inside-iframe.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,32 @@ describe('bldrs inside iframe', () => {
assert.equal(msg.data['current'].length, 0)
})
})

it('should set defaultColor to gray, and color one element blue by view settings', () => {
cy.get('@iframe').trigger('keydown', {keyCode: KEYCODE_ESC})
cy.get('#lastMessageReceivedAction').contains(/ModelLoaded/i)
const defaultGrayColor = {
x: 0.85,
y: 0.85,
z: 0.85,
w: 1,
}
const colorBlue = {
x: 0.2,
y: 0.3,
z: 0.9,
w: 1,
}
cy.get('#txtSendMessageType').clear().type('ai.bldrs-share.ChangeViewSettings')
const msg = {
customViewSettings: {
defaultColor: defaultGrayColor,
globalIdsToColorMap: {
'3qoAS2W2r7m9vxQ0sGR5Rc': colorBlue,
},
},
}
cy.get('#txtSendMessagePayload').clear().type(JSON.stringify(msg), {parseSpecialCharSequences: false})
cy.get('#btnSendMessage').click()
})
})
14 changes: 5 additions & 9 deletions src/Components/About/AboutControl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,15 @@ function AboutContent({setIsDialogDisplayed}) {
</Stack>
<Box sx={{padding: '0px 10px', textAlign: 'left'}} elevation={0}>
<Typography variant={'body1'}>
<Box variant='span'>
Welcome to Share.<br/>
Upload your IFC model,
position the camera, select elements and crop the model using section planes;
share the exact view using generated link.
With Share everyone has access to the same context in model space.
</Box>
<Box variant='span'>
You can reach us on{' '}
<Link href='https://discord.com/channels/853953158560743424/853953158560743426' color='inherit' variant='overline'>
discord
</Link>.
</Box>
With Share everyone has access to the same context in model space.<br/>
You can reach us on{' '}
<Link href='https://discord.com/channels/853953158560743424/853953158560743426' color='inherit' variant='overline'>
discord
</Link>.
</Typography>
</Box>
<PrivacyControl/>
Expand Down
2 changes: 1 addition & 1 deletion src/Components/CutPlaneMenu.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('CutPlaneMenu', () => {
const viewer = __getIfcViewerAPIExtendedMockSingleton()
await act(() => {
result.current.setViewer(viewer)
result.current.setModel(model)
result.current.setModelStore(model)
})
render(
<ShareMock
Expand Down
2 changes: 1 addition & 1 deletion src/Components/ItemProperties/ItemProperties.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test('ItemProperties for single element', async () => {
const {result} = renderHook(() => useStore((state) => state))
await act(() => {
result.current.setSelectedElement({expressID: 10})
result.current.setModel(new MockModel)
result.current.setModelStore(new MockModel)
})

const {getByText} = render(
Expand Down
8 changes: 5 additions & 3 deletions src/Containers/CadView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ export default function CadView({
const [model, setModel] = useState(null)
const viewer = useStore((state) => state.viewer)
const setViewer = useStore((state) => state.setViewer)
const customViewSettings = useStore((state) => state.customViewSettings)
// setModelStore instead of setModel since there's already a state var with this name
const setModelStore = useStore((state) => state.setModel)
const setModelStore = useStore((state) => state.setModelStore)
const isNavPanelOpen = useStore((state) => state.isNavPanelOpen)
const isDrawerOpen = useStore((state) => state.isDrawerOpen)
const setCutPlaneDirections = useStore((state) => state.setCutPlaneDirections)
Expand Down Expand Up @@ -111,7 +112,7 @@ export default function CadView({
useEffect(() => {
debug().log('CadView#useEffect1[modelPath], calling onModelPath...')
onModelPath()
}, [modelPath])
}, [modelPath, customViewSettings])


// Viewer changes in onModelPath (above)
Expand Down Expand Up @@ -310,7 +311,8 @@ export default function CadView({
// TODO(pablo): error modal.
setIsLoading(false)
setAlertMessage(`Could not load file: ${filepath}`)
})
}, customViewSettings)

await viewer.isolator.setModel(loadedModel)

Privacy.recordEvent('select_content', {
Expand Down
34 changes: 30 additions & 4 deletions src/Infrastructure/IfcCustomViewSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,39 @@ import IfcColor from './IfcColor'
*
* @param {IfcColor} defaultColor the color to be used if the id wasn't found in the provided map,
* if undefined the original element color will be used instead
* @param {object} idsToColorMap an object containing pairs of {[expressId]:[IfcColor]} to be used for those elements
* @param {object} expressIdsToColorMap an object containing pairs of {[expressId]:[IfcColor]} to be used for those elements
* @param {object} globalIdsToColorMap an object containing pairs of {[globalId]:[IfcColor]} to be used for those elements
*/
export default function IfcCustomViewSettings(defaultColor, idsToColorMap = {}) {
export default function IfcCustomViewSettings(defaultColor, expressIdsToColorMap = {}, globalIdsToColorMap = {}) {
this.defaultColor = defaultColor
this.idsToColorMap = idsToColorMap
this.expressIdsToColorMap = expressIdsToColorMap
this.globalIdsToColorMap = globalIdsToColorMap


this.getElementColor = (expressId) => {
return this.idsToColorMap[expressId] ? this.idsToColorMap[expressId] : this.defaultColor
return this.expressIdsToColorMap[expressId] ? this.expressIdsToColorMap[expressId] : this.defaultColor
}


/**
* Convert the color mapping for [globalId]:[color] to be [expressId]:[color] as well
*
* @param {object} api
* @param {number} modelID
* @param {IfcCustomViewSettings} customViewSettings
*/
this.normalizeGlobalIdSettings = (api, modelID) => {
const hasGlobalIdSettings = Object.keys(this.globalIdsToColorMap ?? []).length !== 0
if (hasGlobalIdSettings) {
const globalIds = Object.entries(this.globalIdsToColorMap)
// eslint-disable-next-line new-cap
api.CreateIfcGuidToExpressIdMapping(modelID)
const mappedToExpressIds = globalIds.map((pair) => [api.ifcGuidMap.get(modelID).get(pair[0]), pair[1]])
mappedToExpressIds.filter((pair) => pair[0]) // Remove global ids that weren't found
.forEach((pair) => {
this.expressIdsToColorMap[pair[0]] = pair[1] // Override the setting in the customViewSettingsObject
})
api.ifcGuidMap.delete(modelID)
}
}
}
2 changes: 2 additions & 0 deletions src/Infrastructure/IfcElementsStyleManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ function newInitializeLoadingStateFunction(parser) {
const viewSettings = await compileViewRules(this.state.api, modelID, this._rules)
this._overrideStyles = viewSettings
}
// Check if _overrideStyles has any setting using global id to be converted to expressId
this._overrideStyles?.normalizeGlobalIdSettings(this.state.api, modelID)
// eslint-disable-next-line new-cap
const shapes = await this.state.api.GetLineIDsWithType(modelID, IFCPRODUCTDEFINITIONSHAPE)
this.loadingState.total = shapes.size()
Expand Down
2 changes: 2 additions & 0 deletions src/WidgetApi/ApiEventsRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ElementSelectionChangedEventDispatcher from './event-dispatchers/ElementS
import ModelLoadedEventDispatcher from './event-dispatchers/ModelLoadedEventDispatcher'
import HiddenElementsEventDispatcher from './event-dispatchers/HiddenElementsEventDispatcher'
import HighlightElementsEventHandler from './event-handlers/HighlightElementsEventHandler'
import ChangeViewSettingsEventHandler from './event-handlers/ChangeViewSettingsEventHandler'


/**
Expand Down Expand Up @@ -48,6 +49,7 @@ class ApiEventsRegistry {
new SuppressAboutDialogHandler(this.apiConnection),
new HideElementsEventHandler(this.apiConnection, this.searchIndex),
new UnhideElementsEventHandler(this.apiConnection, this.searchIndex),
new ChangeViewSettingsEventHandler(this.apiConnection),
]
for (const event of events) {
this.apiConnection.on(`action:${event.name}`, event.handler.bind(event))
Expand Down
43 changes: 43 additions & 0 deletions src/WidgetApi/event-handlers/ChangeViewSettingsEventHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import IfcCustomViewSettings from '../../Infrastructure/IfcCustomViewSettings'
import useStore from '../../store/useStore'
import ApiEventHandler from './ApiEventHandler'

/**
* Select Elements API event handler
*/
class ChangeViewSettingsEventHandler extends ApiEventHandler {
apiConnection = null
name = 'ai.bldrs-share.ChangeViewSettings'

/**
* constructor
*
* @param {object} apiConnection AbstractApiConnection
*/
constructor(apiConnection, navigation) {
super()
this.apiConnection = apiConnection
}

/**
* The handler for this event
*
* @param {object} data the event associated data
* @return {object} the response of the API call
*/
handler(data) {
if (!('customViewSettings' in data)) {
return this.apiConnection.missingArgumentResponse('customViewSettings')
}
const customViewSettings = data.customViewSettings
const customViewSettingsObject = new IfcCustomViewSettings(
customViewSettings.defaultColor,
customViewSettings.expressIdsToColorMap,
customViewSettings.globalIdsToColorMap,
)
useStore.setState({customViewSettings: customViewSettingsObject})
return this.apiConnection.successfulResponse({})
}
}

export default ChangeViewSettingsEventHandler
6 changes: 4 additions & 2 deletions src/store/IFCSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ export default function createIFCSlice(set, get) {
preselectedElementIds: null,
cameraControls: null,
loadedFileInfo: null,
setViewer: (viewer) => set(() => ({viewer: viewer})),
setModel: (model) => set(() => ({model: model})),
customViewSettings: null,
setViewerStore: (viewer) => set(() => ({viewerStore: viewer})),
setModelStore: (model) => set(() => ({model: model})),
setModelPath: (modelPath) => set(() => ({modelPath: modelPath})),
setSelectedElement: (element) => set(() => ({selectedElement: element})),
setSelectedElements: (elements) => set(() => ({selectedElements: elements})),
setElementTypesMap: (map) => set(() => ({elementTypesMap: map})),
setPreselectedElementIds: (elementIds) => set(() => ({preselectedElementIds: elementIds})),
setCameraControls: (cameraControls) => set(() => ({cameraControls: cameraControls})),
setLoadedFileInfo: (loadedFileInfo) => set(() => ({loadedFileInfo: loadedFileInfo})),
setCustomViewSettings: (customViewSettings) => set(() => ({customViewSettings: customViewSettings})),
}
}
2 changes: 1 addition & 1 deletion src/store/useStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('IFC slice', () => {
const {result} = renderHook(() => useStore((state) => state))
const model = {castShadow: false}
act(() => {
result.current.setModel(model)
result.current.setModelStore(model)
})
expect(result.current.model).toEqual(model)
})
Expand Down

0 comments on commit 0b2192d

Please sign in to comment.