Skip to content

Commit cdb8895

Browse files
WIP
1 parent 7a0b26b commit cdb8895

File tree

5 files changed

+154
-26
lines changed

5 files changed

+154
-26
lines changed

app/src/organisms/ErrorRecoveryFlows/shared/TwoColLwInfoAndDeck.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,16 @@ export function TwoColLwInfoAndDeck(
245245
)}
246246
{labwareRenderInfo
247247
.filter(l => l.labwareId !== failedLwId)
248-
.map(({ x, y, labwareDef, labwareId }) => (
249-
// TODO BEFORE MERGE
250-
<g key={labwareId} transform={`translate(${x},${y})`}>
248+
.map(({ labwareOrigin, labwareDef, labwareId }) => (
249+
<g
250+
key={labwareId}
251+
transform={`translate(${labwareOrigin.x},${labwareOrigin.y})`}
252+
>
251253
{labwareDef != null && labwareId !== failedLwId ? (
252-
<LabwareRender definition={labwareDef} />
254+
<LabwareRender
255+
definition={labwareDef}
256+
positioningMode="passThrough"
257+
/>
253258
) : null}
254259
</g>
255260
))}

app/src/organisms/InterventionModal/MoveLabwareInterventionContent.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,17 @@ export function MoveLabwareInterventionContent({
229229
)}
230230
{labwareRenderInfo
231231
.filter(l => l.labwareId !== command.params.labwareId)
232-
.map(({ x, y, labwareDef, labwareId }) => (
233-
// TODO BEFORE MERGE
234-
<g key={labwareId} transform={`translate(${x},${y})`}>
232+
.map(({ labwareOrigin, labwareDef, labwareId }) => (
233+
<g
234+
key={labwareId}
235+
transform={`translate(${labwareOrigin.x},${labwareOrigin.y})`}
236+
>
235237
{labwareDef != null &&
236238
labwareId !== command.params.labwareId ? (
237-
<LabwareRender definition={labwareDef} />
239+
<LabwareRender
240+
definition={labwareDef}
241+
positioningMode="passThrough"
242+
/>
238243
) : null}
239244
</g>
240245
))}

app/src/organisms/InterventionModal/utils/getRunLabwareRenderInfo.ts

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
import {
2+
coordinateTupleToVector3D,
3+
getAddressableAreaFromSlotId,
4+
getDeckSlotOriginToLabwareOrigin,
5+
getLabwareBackLeftBottomToOrigin,
26
getPositionFromSlotId,
3-
getSchema2Dimensions,
47
getSlotHasMatingSurfaceUnitVector,
8+
getVectorSum,
59
} from '@opentrons/shared-data'
610

711
import type { RunData } from '@opentrons/api-client'
812
import type {
913
DeckDefinition,
1014
LabwareDefinition,
1115
LabwareDefinitionsByUri,
16+
Vector3D,
1217
} from '@opentrons/shared-data'
1318

1419
export interface RunLabwareInfo {
15-
x: number
16-
y: number
20+
/**
21+
* The labware origin, in deck coordinates.
22+
* Use this with `<LabwareRender positioningMode="passThrough">`.
23+
*/
24+
labwareOrigin: Vector3D
25+
1726
labwareDef: LabwareDefinition
1827
labwareId: string
1928
}
@@ -44,30 +53,71 @@ export function getRunLabwareRenderInfo(
4453
'addressableAreaName' in location
4554
? location.addressableAreaName
4655
: location.slotName
47-
const slotPosition = getPositionFromSlotId(slotName, deckDef)
56+
57+
const slotAddressableArea = getAddressableAreaFromSlotId(
58+
slotName,
59+
deckDef
60+
)
61+
if (slotAddressableArea == null) {
62+
return acc
63+
}
64+
4865
const slotHasMatingSurfaceVector = getSlotHasMatingSurfaceUnitVector(
4966
deckDef,
5067
slotName
5168
)
69+
if (!slotHasMatingSurfaceVector) {
70+
// todo(mm, 2025-06-25): Are we using slotHasMatingSurfaceVector as an
71+
// approximation for "is a deck slot"? Should we use
72+
// `slotAddressableArea.areaType === "slot"`` instead?
73+
return acc
74+
}
75+
76+
// 0,0,0 default inherited from prior code. I don't think we ever reach it in
77+
// practice, just keeping it to be safe.
78+
const slotOriginTuple = getPositionFromSlotId(slotName, deckDef) ?? [
79+
0,
80+
0,
81+
0,
82+
]
83+
const slotOrigin = coordinateTupleToVector3D(slotOriginTuple)
84+
85+
const slotOriginToLabwareOrigin = getDeckSlotOriginToLabwareOrigin(
86+
slotAddressableArea,
87+
labwareDef
88+
)
89+
const labwareOrigin = getVectorSum(
90+
slotOrigin,
91+
slotOriginToLabwareOrigin
92+
)
5293

53-
return slotHasMatingSurfaceVector
54-
? [
55-
...acc,
56-
{
57-
x: slotPosition?.[0] ?? 0,
58-
y: slotPosition?.[1] ?? 0,
59-
labwareId: labware.id,
60-
labwareDef,
61-
},
62-
]
63-
: acc
94+
return [
95+
...acc,
96+
{
97+
labwareOrigin,
98+
labwareId: labware.id,
99+
labwareDef,
100+
},
101+
]
64102
} else {
65-
const { yDimension } = getSchema2Dimensions(labwareDef)
103+
// Place off-deck labware literally beyond the bounds of the deck.
104+
// It's a little unclear why we do this and whether the exact position matters.
105+
// This roughly maintains prior behavior.
106+
const whereToPutLabwareBackLeftBottom: Vector3D = {
107+
x: 0,
108+
y: deckDef.cornerOffsetFromOrigin[1],
109+
z: 0,
110+
}
111+
112+
const labwareOrigin = getVectorSum(
113+
whereToPutLabwareBackLeftBottom,
114+
getLabwareBackLeftBottomToOrigin(labwareDef)
115+
)
116+
66117
return [
67118
...acc,
68119
{
69-
x: 0,
70-
y: deckDef.cornerOffsetFromOrigin[1] - yDimension,
120+
labwareOrigin,
71121
labwareId: labware.id,
72122
labwareDef,
73123
},

shared-data/js/helpers/__tests__/labwareSchemaShims.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
22

33
import {
44
getDeckSlotOriginToLabwareOrigin,
5+
getLabwareBackLeftBottomToOrigin,
56
getLabwareViewBox,
67
getSchema2Dimensions,
78
} from '../..'
@@ -171,3 +172,47 @@ describe('getDeckSlotOriginToLabwareOrigin()', () => {
171172
expect(result).toStrictEqual(expectedResult)
172173
})
173174
})
175+
176+
describe('getLabwareBackLeftBottomToOrigin()', () => {
177+
it('should handle schema 2 labware definitions', () => {
178+
const labwareDef: Partial<LabwareDefinition2> = {
179+
schemaVersion: 2,
180+
cornerOffsetFromSlot: {
181+
// Should not affect result.
182+
x: 10,
183+
y: 20,
184+
z: 30,
185+
},
186+
dimensions: {
187+
xDimension: 100,
188+
yDimension: 200,
189+
zDimension: 300,
190+
},
191+
}
192+
const result = getLabwareBackLeftBottomToOrigin(
193+
labwareDef as LabwareDefinition2
194+
)
195+
const expectedResult: typeof result = { x: 0, y: -200, z: 0 }
196+
expect(result).toStrictEqual(expectedResult)
197+
})
198+
it('should handle schema 3 labware definitions', () => {
199+
const labwareDef: Partial<LabwareDefinition3> = {
200+
schemaVersion: 3,
201+
extents: {
202+
total: {
203+
backLeftBottom: { x: -10, y: 10, z: 0 },
204+
frontRightTop: { x: 210, y: -110, z: 1000 },
205+
},
206+
},
207+
}
208+
const result = getLabwareBackLeftBottomToOrigin(
209+
labwareDef as LabwareDefinition3
210+
)
211+
const expectedResult: typeof result = {
212+
x: 10,
213+
y: -10,
214+
z: -0,
215+
}
216+
expect(result).toStrictEqual(expectedResult)
217+
})
218+
})

shared-data/js/helpers/labwareSchemaShims.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,29 @@ export function getDeckSlotOriginToLabwareOrigin(
112112
}
113113
}
114114

115+
/**
116+
* Return the offset from a labware's back-left-bottom (-x, +y, -z) corner to its origin.
117+
*
118+
* This is a lower-level helper for the rare cases where we want to position a labware
119+
* specifically by its back-left corner. Typically, you'll want something higher-level,
120+
* like `getLabwareViewBox()` or `getDeckSlotOriginToLabwareOrigin()`, instead.
121+
*/
122+
export function getLabwareBackLeftBottomToOrigin(
123+
definition: LabwareDefinition
124+
): Vector3D {
125+
if (definition.schemaVersion === 2) {
126+
return {
127+
x: 0,
128+
y: -definition.dimensions.yDimension,
129+
z: 0,
130+
}
131+
} else {
132+
const originToBackLeftBottom = definition.extents.total.backLeftBottom
133+
const backLeftBottomToOrigin = getVectorInverse(originToBackLeftBottom)
134+
return backLeftBottomToOrigin
135+
}
136+
}
137+
115138
/**
116139
* Return a definition's cornerOffsetFromSlot in the style of labware schema 2.
117140
*

0 commit comments

Comments
 (0)