label-studio icon indicating copy to clipboard operation
label-studio copied to clipboard

Brush tool cursor size doesn't match actual stroke width due to incomplete scaling calculation

Open jjhhyyg opened this issue 7 months ago • 2 comments

Describe the bug

The brush and eraser tools create large circular strokes even when the stroke width is set to the minimum value. The visual cursor appears to show the correct size, but the actual drawing produces much larger strokes than expected, making precise annotation impossible at small brush sizes.

To Reproduce

Steps to reproduce the behavior:

  1. Open Label Studio and navigate to an image annotation task
  2. Select the Brush tool (shortcut: 'B')
  3. Set the brush size to minimum (1px) using the vertical slider control or press '[' repeatedly
  4. Observe the cursor size appears small and correct
  5. Draw/paint on the canvas
  6. See error: The actual stroke drawn is much larger than the cursor indicated

Alternative reproduction:

  • Use keyboard shortcuts '[' and ']' to decrease/increase brush size
  • Even at minimum settings, strokes remain large

Expected behavior

  • The brush stroke should match the cursor size exactly
  • When set to minimum size (1px), the brush should produce thin, precise lines
  • The stroke width should scale proportionally with the slider value
  • Cursor size should accurately represent the actual drawing size

Screenshots

[Add screenshots showing the small cursor vs large actual stroke if available]

Environment (please complete the following information):

  • OS: Ubuntu 22.04 amd64
  • Browser: Chrome
  • Label Studio Version: [current version]

Additional context

Technical Analysis: The issue appears to be in the cursor scaling calculation in BrushCursorMixin.cursorStyleRule at line 233 in Brush.jsx:

get cursorStyleRule() {
  const val = self.strokeWidth * (self.obj?.stageScale || 1);
  return Canvas.createBrushSizeCircleCursor(val);
}

Root cause: The cursor calculation only accounts for stageScale but the actual drawing in BrushRegion.preDraw uses additional scaling factors:

ctx.lineWidth = pathPoints.strokeWidth * self.scaleX * self.parent.stageScale;

The mismatch between cursor size calculation (using only stageScale) and actual drawing size (using both scaleX and stageScale) causes the visual inconsistency.

Impact: This bug severely affects user experience for detailed annotation work, making it impossible to create precise brush strokes for fine-grained segmentation tasks.

Potential fix: Update the cursor calculation to match the actual drawing scaling:

get cursorStyleRule() {
  const scaleX = self.obj?.scaleX || 1;
  const stageScale = self.obj?.stageScale || 1;
  const val = self.strokeWidth * scaleX * stageScale;
  return Canvas.createBrushSizeCircleCursor(val);
}

This issue affects both brush and eraser tools as they share the same cursor mixin implementation.

jjhhyyg avatar Jun 09 '25 07:06 jjhhyyg

current version is 1.18.0

jjhhyyg avatar Jun 09 '25 07:06 jjhhyyg

Image Looking the screenshot, the eraser size is far smaller than the original swipe area.

jjhhyyg avatar Jun 09 '25 07:06 jjhhyyg

Hello,

Do you mind checking this in 1.19.0 please?

Thank you, Abu

Comment by Abubakar Saad Workflow Run

heidi-humansignal avatar Jun 17 '25 17:06 heidi-humansignal

Thanks for your contribution! It's working fine now.

jjhhyyg avatar Jun 30 '25 06:06 jjhhyyg

Great News! Please feel free to reach out if you have more questions.

Best Regards!

Comment by Oussama Assili Workflow Run

heidi-humansignal avatar Jun 30 '25 14:06 heidi-humansignal