Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.
Merged
6 changes: 6 additions & 0 deletions e2e/fragments/AtSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ module.exports = {
I.dontSee("Regions", this._sideBarLocator);
}
},
seeRelations(count) {
I.see(`Relations (${count})`, this._sideBarLocator);
},
dontSeeRelations() {
I.dontSee(`Relations`, this._sideBarLocator);
},
seeSelectedRegion() {
I.seeElement(this._selectedRegionsLocator);
},
Expand Down
112 changes: 112 additions & 0 deletions e2e/tests/regression-tests/brush-relations.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/* global Feature, DataTable, Data, locate, Scenario */
const assert = require("assert");

Feature("Brush relations").tag("@regress");

const IMAGE = "https://user.fm/files/v2-901310d5cb3fa90e0616ca10590bacb3/spacexmoon-800x501.jpg";

const config = `<View>
<Image name="img" value="$image"></Image>
<BrushLabels name="tag" toName="img">
<Label value="Test" background="orange"></Label>
</BrushLabels>
</View>`;

function getPointAtSpiral(t, v , w) {
return { x: v * t * Math.cos(w * t), y: v * t * Math.sin(w * t) };
}

function generateSpiralPoints(x0, y0, R, v , w) {
let t = 1, x, y;
const points = [];

do {
({ x, y } = getPointAtSpiral(t++, v, w));
points.push([x + x0, y + y0]);
} while (x ** 2 + y ** 2 < R ** 2);
return points;
}

Scenario("Brush relations shouldn't crash everything", async ({ I, LabelStudio, AtImageView, AtSidebar, ErrorsCollector }) => {
const params = {
config,
data: { image: IMAGE },
};

I.amOnPage("/");
await ErrorsCollector.run();
LabelStudio.init(params);
AtImageView.waitForImage();
AtSidebar.seeRegions(0);
await AtImageView.lookForStage();
const canvasSize = await AtImageView.getCanvasSize();
const regionsCentralPoints = [];

// create 4 brush regions
for (let i = 0; i < 4; i++) {
// find start position
const x = canvasSize.width / 4 * ((i % 2) * 2 + 1);
const y = canvasSize.height / 4 * ((Math.floor(i / 2) % 2) * 2 + 1);
// generate points in a spiral
const points = generateSpiralPoints(x, y, Math.min(canvasSize.width / 6, canvasSize.height / 6), .4, Math.PI / 18);

// select the brush label
I.pressKey("1");
// draw a brush region
AtImageView.drawThroughPoints(points);
AtSidebar.seeRegions(i+1);
// unselect the region
I.pressKey("u");
// save the central point
regionsCentralPoints.push({ x, y });
}

// Check that we can create a relation between the brush regions
{
// switch to the move tool for easy region selecting
I.pressKey("v");
// select the first region
AtImageView.clickAt(regionsCentralPoints[0].x, regionsCentralPoints[0].y);
// create relation to the second region
I.pressKey(["alt", "r"]);
AtImageView.clickAt(regionsCentralPoints[1].x, regionsCentralPoints[1].y);
// check that the relation has been created
AtSidebar.seeRelations(1);
}

// Check that relations work fine on a brush restoration (from rle)
{
// export annotation
const annotation = await LabelStudio.serialize();

// reload LS with that datalabel studio logo
LabelStudio.init({
...params,
annotations: [{ id: "imported", result: annotation }],
});

AtImageView.waitForImage();
// Check that relation still exist
AtSidebar.seeRelations(1);

// Try to create new relation with restored regions
{
// switch to the move tool for easy region selecting
I.pressKey("v");
// select the third region
AtImageView.clickAt(regionsCentralPoints[2].x, regionsCentralPoints[2].y);
// create relation to the fourth region
I.pressKey(["alt", "r"]);
AtImageView.clickAt(regionsCentralPoints[3].x, regionsCentralPoints[3].y);
// check that the relation has been created
AtSidebar.seeRelations(2);
}

// Check the we didn't get any errors
const errors = await ErrorsCollector.grabErrors();

if (errors.length) {
assert.fail(`Got an error: ${errors[0]}`);
}
}
});
2 changes: 1 addition & 1 deletion src/common/TimeAgo/TimeAgo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function getNextTick(passedTime = 0) {
}

type TimeAgoProps = React.ComponentPropsWithoutRef<"time"> & {
date: number | string | Date
date: number | string | Date,
}

export const TimeAgo = ({ date, ...rest }: TimeAgoProps) => {
Expand Down
4 changes: 3 additions & 1 deletion src/components/RelationsOverlay/BoundingBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ const _detect = region => {
}
case "brushregion": {
// If there is no imageData we just wait for the next render
return region.imageData && imageRelatedBBox(
const bbox = region.imageData && Geometry.getImageDataBBox(region.imageData.data, region.imageData.width, region.imageData.height);

return bbox && imageRelatedBBox(
region,
Geometry.getImageDataBBox(region.imageData.data, region.imageData.width, region.imageData.height),
);
Expand Down
13 changes: 12 additions & 1 deletion src/regions/BrushRegion.js
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,18 @@ const HtxBrushView = ({ item }) => {
highlightedImageRef.current.src = dataUrl;
done = true;
};
}, [item.touches.length, item.strokeColor, item.parent.stageScale, store.annotationStore.selected?.id, item.parent?.zoomingPositionX, item.parent?.zoomingPositionY, item.parent?.stageWidth, item.parent?.stageHeight]);
}, [
item.touches.length,
item.strokeColor,
item.parent.stageScale,
store.annotationStore.selected?.id,
item.parent?.zoomingPositionX,
item.parent?.zoomingPositionY,
item.parent?.stageWidth,
item.parent?.stageHeight,
item.rle,
image,
]);

if (!item.parent) return null;

Expand Down