Skip to content

Commit

Permalink
Add deleteIfOutOfBounds to markings renderer (#6889)
Browse files Browse the repository at this point in the history
* Add deleteIfOutOfBounds to markings renderer

Store a list of current marking tools in the markings renderer. Check deleteIfOutOfBounds for each tool on every update and delete any tools that are outside the subject.

The subject bounds are calculated from the SVG image, for image subjects, or from the SVG scaled size rectangle otherwise.

* More robust checking in MarkingInitializer
Marks can now vanish mid-drag, so we need to check their existence before using them.

* Rewrite deleteIfOutOfBounds as JS

* Track the array index for new marks
  • Loading branch information
eatyourgreens authored Oct 25, 2023
1 parent c5405ba commit 8fade55
Show file tree
Hide file tree
Showing 20 changed files with 81 additions and 43 deletions.
6 changes: 5 additions & 1 deletion app/classifier/annotation-renderer/svg.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ export default class SVGRenderer extends React.Component {
super(props);
this.getScreenCurrentTransformationMatrix = this.getScreenCurrentTransformationMatrix.bind(this);
this.getEventOffset = this.getEventOffset.bind(this);
this.getSizeRect = this.getSizeRect.bind(this);
this.normalizeDifference = this.normalizeDifference.bind(this);
this.eventCoordsToSVGCoords = this.eventCoordsToSVGCoords.bind(this);
}

getSizeRect() {
const clientRect = this.sizeRect && this.sizeRect.getBoundingClientRect();
const subjectNode = this.subjectImage || this.sizeRect;
const clientRect = subjectNode?.getBoundingClientRect();

if (clientRect) {
const { width, height } = clientRect;
Expand Down Expand Up @@ -98,6 +100,7 @@ export default class SVGRenderer extends React.Component {
annotation: this.props.annotation,
containerRect: this.getSizeRect(),
frame: this.props.frame,
getContainerRect: this.getSizeRect,
getEventOffset: this.getEventOffset,
getScreenCurrentTransformationMatrix: this.getScreenCurrentTransformationMatrix,
naturalWidth: this.props.naturalWidth,
Expand Down Expand Up @@ -181,6 +184,7 @@ export default class SVGRenderer extends React.Component {
{type === 'image' && this.props.naturalWidth && (
<Draggable onDrag={this.props.panEnabled ? this.props.panByDrag : () => {}}>
<SVGImage
ref={(node) => { this.subjectImage = node; }}
className={this.props.panEnabled ? 'pan-active' : ''}
src={src}
width={this.props.naturalWidth}
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/circle.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

MINIMUM_RADIUS = 5
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/column-rectangle.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

DEFAULT_WIDTH = 25
Expand Down
15 changes: 0 additions & 15 deletions app/classifier/drawing-tools/delete-if-out-of-bounds.coffee

This file was deleted.

26 changes: 26 additions & 0 deletions app/classifier/drawing-tools/delete-if-out-of-bounds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import ReactDOM from 'react-dom'

import isInBounds from '../../lib/is-in-bounds'

export default function deleteIfOutOfBounds(tool) {
let containerBounds = null;
try {
containerBounds = tool?.props.getContainerRect();
} catch (e) {
containerBounds = tool?.props.containerRect;
}

try {
const domNode = ReactDOM.findDOMNode(tool);
let {left, top, width, height} = domNode.getBoundingClientRect();
left += pageXOffset;
top += pageYOffset;
const outOfBounds = !isInBounds({left, top, width, height}, containerBounds);
if (outOfBounds) {
tool?.props.onDestroy()
}
} catch (error) {
// uncomment this for debugging
//console.error(error)
}
}
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/ellipse.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

DEFAULT_SQUASH = 1 / 2
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/freehand-line.cjsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'
{svgPathProperties} = require 'svg-path-properties'
{createPathFromCoords, filterDupeCoords, roundCoords} = require './freehand-helpers'
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/freehand-segment-line.cjsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'
{createPathFromCoords, filterDupeCoords, roundCoords} = require './freehand-helpers'

Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/full-height-line.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

GRAB_STROKE_WIDTH = 20
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/full-width-line.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

GRAB_STROKE_WIDTH = 20
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/grid.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

module.exports = createReactClass
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/line.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'
DragHandle = require './drag-handle'

Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/point.cjsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
React = require 'react'
createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
Draggable = require('../../lib/draggable').default
DeleteButton = require './delete-button'
isInBounds = require '../../lib/is-in-bounds'
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/poly-curve.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

FINISHER_RADIUS = 8
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/polygon.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

FINISHER_RADIUS = 8
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/rectangle.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

MINIMUM_SIZE = 5
Expand Down
2 changes: 1 addition & 1 deletion app/classifier/drawing-tools/triangle.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ createReactClass = require 'create-react-class'
DrawingToolRoot = require('./root').default;
DragHandle = require './drag-handle'
Draggable = require('../../lib/draggable').default
deleteIfOutOfBounds = require './delete-if-out-of-bounds'
deleteIfOutOfBounds = require('./delete-if-out-of-bounds').default
DeleteButton = require './delete-button'

MINIMUM_RADIUS = 5
Expand Down
23 changes: 13 additions & 10 deletions app/classifier/tasks/drawing/marking-initializer.cjsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module.exports = createReactClass
contextTypes:
geordi: PropTypes.object

activeMarkIndex: -1

getDefaultProps: ->
annotation: null
annotations: []
Expand Down Expand Up @@ -66,14 +68,15 @@ module.exports = createReactClass
for key, value of initValues
mark[key] = value

this.activeMarkIndex = @props.annotation.value.length - 1
@onChange()

handleInitDrag: (e) ->
taskDescription = @props.workflow.tasks[@props.annotation.task]
mark = @props.annotation.value[@props.annotation.value.length - 1]
MarkComponent = drawingTools[taskDescription.tools[mark.tool].type]
mark = @props.annotation.value[this.activeMarkIndex]
MarkComponent = mark && drawingTools[taskDescription.tools[mark.tool].type]

if MarkComponent.initMove?
if mark and MarkComponent.initMove?
mouseCoords = @props.getEventOffset e
initMoveValues = MarkComponent.initMove mouseCoords, mark, e
for key, value of initMoveValues
Expand All @@ -84,19 +87,18 @@ module.exports = createReactClass
handleInitRelease: (e) ->
pref = @props.preferences?.preferences ? {}
taskDescription = @props.workflow.tasks[@props.annotation.task]
mark = @props.annotation.value[@props.annotation.value.length - 1]
MarkComponent = drawingTools[taskDescription.tools[mark.tool].type]
mark = @props.annotation.value[this.activeMarkIndex]
MarkComponent = mark && drawingTools[taskDescription.tools[mark?.tool].type]

toolName = taskDescription.tools[mark.tool].type
@context.geordi?.logEvent type: "draw-#{toolName}"
toolName = mark && taskDescription.tools[mark.tool].type

if MarkComponent.initRelease?
if mark and MarkComponent.initRelease?
mouseCoords = @props.getEventOffset e
initReleaseValues = MarkComponent.initRelease mouseCoords, mark, e
for key, value of initReleaseValues
mark[key] = value

if MarkComponent.saveState? and pref.activeTemplate
if mark and MarkComponent.saveState? and pref.activeTemplate
multipleMarks = MarkComponent.saveState mark, pref[pref.activeTemplate], pref.activeTemplate
for multiple in multipleMarks
@props.annotation.value.push multiple
Expand All @@ -106,8 +108,9 @@ module.exports = createReactClass

@onChange()

if MarkComponent.initValid?
if mark and MarkComponent.initValid?
unless MarkComponent.initValid mark, @props
this.activeMarkIndex = -1
@destroyMark @props.annotation, mark

destroyMark: (annotation, mark) ->
Expand Down
22 changes: 19 additions & 3 deletions app/classifier/tasks/drawing/markings-renderer.cjsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
React = require 'react'
createReactClass = require 'create-react-class'
drawingTools = require '../../drawing-tools'
deleteIfOutOfBounds = require('../../drawing-tools/delete-if-out-of-bounds').default
strategies = require('../../../features/feedback/shared/strategies').default

module.exports = createReactClass
displayName: 'MarkingsRenderer'

tools: []

getDefaultProps: ->
annotations: []
annotation: null
Expand Down Expand Up @@ -35,9 +38,14 @@ module.exports = createReactClass
@setState oldSetOfMarks: newSetOfMarks
# console.log 'Marks are now', newSetOfMarks

componentDidUpdate: () ->
for tool in this.tools
deleteIfOutOfBounds(tool)

render: ->
skippedMarks = 0
drawingInProgress = false
this.tools = []
<g>
{for annotation in @props.annotations
annotation._key ?= Math.random()
Expand Down Expand Up @@ -83,6 +91,7 @@ module.exports = createReactClass
vertical: 0.001
toolEnv =
containerRect: @props.containerRect
getContainerRect: @props.getContainerRect
scale: scale
disabled: isPriorAnnotation || drawingInProgress
selected: mark is @state.selection and not isPriorAnnotation
Expand Down Expand Up @@ -114,7 +123,13 @@ module.exports = createReactClass
getScreenCurrentTransformationMatrix: @props.getScreenCurrentTransformationMatrix

ToolComponent = drawingTools[toolDescription.type]
<ToolComponent key={mark._key} {...toolProps} {...toolEnv} {...toolMethods} />}
<ToolComponent
ref={(tool) => this.tools.push(tool) if tool}
key={mark._key}
{...toolProps}
{...toolEnv}
{...toolMethods}
/>}
</g>}
</g>

Expand All @@ -136,8 +151,9 @@ module.exports = createReactClass
if mark is @state.selection
@setState selection: null
markIndex = annotation.value.indexOf mark
annotation.value.splice markIndex, 1
@props.onChange annotation
if markIndex > -1
annotation.value.splice markIndex, 1
@props.onChange annotation
if mark.templateID
index = []
for cell in annotation.value
Expand Down
4 changes: 4 additions & 0 deletions app/components/svg-image.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class SVGImage extends React.Component {
return {};
}

getBoundingClientRect() {
return this.refs.image?.getBoundingClientRect();
}

render() {
const imageProps = Object.assign({}, this.props);
delete imageProps.modification;
Expand Down

0 comments on commit 8fade55

Please sign in to comment.