Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit eac80ba

Browse files
hlomziknick-skriabinGondragos
authored
fix: LSDV-4684: Fix check for image boundaries (#1294)
* fix: LSDV-4684: Fix check for image boundaries When DEV-3793 FF is on this check is broken. Have to check against 100 instead of real sizes. * Test that regions can't be created outside of the canvas * Adjust tests * Test fine tuning * Fix FF init * Fix ellipses behavior * Change waiting order Co-authored-by: Sergey <[email protected]> * Fix regression and tests --------- Co-authored-by: Nick Skriabin <[email protected]> Co-authored-by: Nick Skriabin <[email protected]> Co-authored-by: Sergey <[email protected]>
1 parent dd0a3b1 commit eac80ba

File tree

3 files changed

+177
-8
lines changed

3 files changed

+177
-8
lines changed

e2e/fragments/LabelStudio.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const { I } = inject();
22
const Helpers = require('../tests/helpers');
3-
const Asserts = require('../utils/asserts');
43

54
module.exports = {
65
init({ events = {}, ...params }) {

e2e/tests/image.test.js

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ const config = `
1313
</RectangleLabels>
1414
</View>`;
1515

16+
const configEllipse = `
17+
<View>
18+
<Image name="img" value="$image"></Image>
19+
<EllipseLabels name="tag" toName="img">
20+
<Label value="Planet"></Label>
21+
<Label value="Moonwalker" background="blue"></Label>
22+
</EllipseLabels>
23+
</View>`;
24+
1625
const perRegionConfig = `
1726
<View>
1827
<Image name="img" value="$image"></Image>
@@ -138,3 +147,150 @@ Scenario('Image with perRegion tags', async function({ I, AtImageView, AtSidebar
138147
assert.strictEqual(result.length, 1);
139148
assert.deepStrictEqual(result[0].value.rectanglelabels, ['Moonwalker']);
140149
});
150+
151+
const outOfBoundsFFs = new DataTable(['FF_DEV_3793'])
152+
153+
outOfBoundsFFs.add([true]);
154+
outOfBoundsFFs.add([false])
155+
156+
Data(outOfBoundsFFs)
157+
.Scenario('Can\'t create rectangles outside of canvas', async ({
158+
I,
159+
AtLabels,
160+
AtSidebar,
161+
AtImageView,
162+
LabelStudio,
163+
current,
164+
}) => {
165+
LabelStudio.setFeatureFlags({
166+
fflag_fix_front_dev_3793_relative_coords_short: current.FF_DEV_3793,
167+
});
168+
169+
I.amOnPage('/');
170+
171+
LabelStudio.init({
172+
config,
173+
data: { image },
174+
task: {
175+
id: 0,
176+
annotations: [{ id: 1001, result: [] }],
177+
predictions: [],
178+
},
179+
});
180+
181+
await AtImageView.waitForImage();
182+
await AtImageView.lookForStage();
183+
184+
const stage = AtImageView.stageBBox();
185+
186+
I.say('Drawing region in the upper left corner');
187+
AtLabels.clickLabel('Planet');
188+
AtImageView.drawByDrag(100, 100, -200, -200);
189+
190+
I.say('Drawing region in the upper right corner');
191+
AtLabels.clickLabel('Planet');
192+
AtImageView.drawByDrag(stage.width - 100, 100, stage.width + 100, -100);
193+
194+
I.say('Drawing region in the bottom left corner');
195+
AtLabels.clickLabel('Planet');
196+
AtImageView.drawByDrag(100, stage.height - 100, -100, stage.height + 100);
197+
198+
I.say('Drawing region in the bottom right corner');
199+
AtLabels.clickLabel('Planet');
200+
AtImageView.drawByDrag(stage.width - 100, stage.height - 100, stage.width + 100, stage.height + 100);
201+
202+
AtSidebar.seeRegions(4);
203+
204+
const result = await LabelStudio.serialize();
205+
206+
const [r1, r2, r3, r4] = result.map(r => r.value);
207+
208+
I.say('First region should be in the corner');
209+
assert.strictEqual(r1.x, 0);
210+
assert.equal(r1.y, 0);
211+
212+
I.say('Second region should be in the corner');
213+
assert.equal(Math.round(r2.x + r2.width), 100);
214+
assert.equal(r2.y, 0);
215+
216+
I.say('Third region should be in the corner');
217+
assert.equal(r3.x, 0);
218+
assert.equal(Math.round(r3.y + r3.height), 100);
219+
220+
I.say('Fourth region should be in the corner');
221+
assert.equal(Math.round(r4.x + r4.width), 100);
222+
assert.equal(Math.round(r4.y + r4.height), 100);
223+
});
224+
225+
Data(outOfBoundsFFs).
226+
Scenario('Can\'t create ellipses outside of canvas', async ({
227+
I,
228+
AtLabels,
229+
AtSidebar,
230+
AtImageView,
231+
LabelStudio,
232+
current,
233+
}) => {
234+
LabelStudio.setFeatureFlags({
235+
fflag_fix_front_dev_3793_relative_coords_short: current.FF_DEV_3793,
236+
});
237+
238+
I.amOnPage('/');
239+
240+
LabelStudio.init({
241+
config: configEllipse,
242+
data: { image },
243+
task: {
244+
id: 0,
245+
annotations: [{ id: 1001, result: [] }],
246+
predictions: [],
247+
},
248+
});
249+
250+
await AtImageView.waitForImage();
251+
await AtImageView.lookForStage();
252+
253+
const stage = AtImageView.stageBBox();
254+
const ellipses = [
255+
// top-left corner
256+
[100, 100, -200, -200],
257+
// top-right corner
258+
[stage.width - 100, 100, stage.width + 100, -100],
259+
// bottom-left corner
260+
[100, stage.height - 100, -100, stage.height + 100],
261+
// bottom-right corner
262+
[stage.width - 100, stage.height - 100, stage.width + 100, stage.height + 100],
263+
];
264+
265+
for (const ellipse of ellipses) {
266+
I.say('Drawing region in the upper left corner');
267+
AtLabels.clickLabel('Planet');
268+
AtImageView.drawByDrag(...ellipse);
269+
}
270+
271+
AtSidebar.seeRegions(4);
272+
273+
const result = await LabelStudio.serialize();
274+
const radiusX = 100 / stage.width * 100;
275+
const radiusY = 100 / stage.height * 100;
276+
277+
for (let i = 0; i < result.length; i++) {
278+
const res = result[i].value;
279+
const region = ellipses[i];
280+
281+
I.say('Make sure ellipse radius is correct (should be same for all)');
282+
// toFixed is to bypass JS floating point precision limitations (f32 sucks)
283+
assert.strictEqual(res.radiusX.toFixed(3), radiusX.toFixed(3));
284+
assert.strictEqual(res.radiusY.toFixed(3), radiusY.toFixed(3));
285+
286+
I.say('Make sure that center is in correct spot');
287+
const [expectedX, expectedY] = [
288+
region[0] / stage.width * 100,
289+
region[1] / stage.height * 100,
290+
];
291+
292+
assert.strictEqual(res.x.toFixed(3), expectedX.toFixed(3));
293+
assert.strictEqual(res.y.toFixed(3), expectedY.toFixed(3));
294+
}
295+
});
296+

src/mixins/DrawingTool.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const DrawingTool = types
1212
unselectRegionOnToolChange: true,
1313
})
1414
.volatile(() => {
15-
return {
15+
return {
1616
currentArea: null,
1717
};
1818
})
@@ -230,16 +230,30 @@ const TwoPointsDrawingTool = DrawingTool.named('TwoPointsDrawingTool')
230230
const shape = self.getCurrentArea();
231231

232232
if (!shape) return;
233-
const { stageWidth, stageHeight } = self.obj;
233+
const isEllipse = shape.type.includes('ellipse');
234+
const maxStageWidth = isFF(FF_DEV_3793) ? 100 : self.obj.stageWidth;
235+
const maxStageHeight = isFF(FF_DEV_3793) ? 100 : self.obj.stageHeight;
234236

235-
let { x1, y1, x2, y2 } = Utils.Image.reverseCoordinates({ x: shape.startX, y: shape.startY }, { x, y });
237+
let { x1, y1, x2, y2 } = isEllipse ? {
238+
x1: shape.startX,
239+
y1: shape.startY,
240+
x2: x,
241+
y2: y,
242+
} : Utils.Image.reverseCoordinates({ x: shape.startX, y: shape.startY }, { x, y });
236243

237244
x1 = Math.max(0, x1);
238245
y1 = Math.max(0, y1);
239-
x2 = Math.min(stageWidth, x2);
240-
y2 = Math.min(stageHeight, y2);
246+
x2 = Math.min(maxStageWidth, x2);
247+
y2 = Math.min(maxStageHeight, y2);
248+
249+
let [distX, distY] = [x2 - x1, y2 - y1].map(Math.abs);
250+
251+
if (isEllipse) {
252+
distX = Math.min(distX, Math.min(x1, maxStageWidth - x1));
253+
distY = Math.min(distY, Math.min(y1, maxStageHeight - y1));
254+
}
241255

242-
shape.setPositionInternal(x1, y1, x2 - x1, y2 - y1, shape.rotation);
256+
shape.setPositionInternal(x1, y1, distX, distY, shape.rotation);
243257
},
244258

245259
finishDrawing(x, y) {
@@ -420,7 +434,7 @@ const MultipleClicksDrawingTool = DrawingTool.named('MultipleClicksMixin')
420434
},
421435

422436
drawDefault() {
423-
const { x,y } = startPoint;
437+
const { x, y } = startPoint;
424438
let dX = self.defaultDimensions.length;
425439
let dY = self.defaultDimensions.length;
426440

0 commit comments

Comments
 (0)