Skip to content

[Interactivity] Selection #107

@luucvanderzee

Description

@luucvanderzee

To support selection, I propose the following syntax:

<script>
  import { Graphic, Section, Point, Rectangle } from '@snlab/florence'

  const pointData = [ ... ]
  const selectedPoints = {}
  let section
  let selectRectangle

  const initSelection = event => {
    section.resetSelection()

    const { x, y } = event.screenCoordinates 
    selectRectangle = { x1: x, x2: x, y1: y, y2: y }
  }

  const updateSelection = event => {
    const { x, y } = event.screenCoordinates 
    selectRectangle.x2 = x
    selectRectangle.y2 = y

    section.updateSelection(selectRectangle)
  }
</script>

<Graphic width={500} height={500}>

  <Section
    bind:this={section}
    onMousedown={initSelection}
    onDrag={updateSelection}
  >

    {#each pointData as point, i (i)}
      <Point
        x={point.x}
        y={point.y}
        onSelect={() => { selectedPoints[i] = true }}
        onDeselect={() => { delete selectionPoints[i] }}
        fill={selectedPoints[i] ? 'red' : 'black' }
      />
    {/each}

  </Section>

  {#if selectRectangle !== undefined}
    <Rectangle {...selectRectangle} fill="green" opacity={0.2} />
  {/if}

</Graphic>

The advantages of this method over the old vue-gg syntax are:

  1. The user has more control over when the selection fires and when it is 'reset'. This makes it easier in the future to implement things like brushing.
  2. In this example I am using a selection-rectangle, but any sort of polygon would work with the same syntax.
  3. Selections can be triggered programmatically- so you can just call section.updateSelection with your own rectangle or polygon. This way you can, for example, create a map with points and polygons and easily light up all points that are in a polygon without using any of the mouse interactions.
  4. The initSelection and updateSelection functions could be abstracted away and made customizable with helpers functions similar to createZoomHandler and createPanHandler.

The downside is that this is maybe a bit more boilerplate-y.

Some more ideas:

  1. In vue-gg, the callbacks given to onSelect and onDeselect were fired when the Mark's centroid would be within the selection rectangle/polygon. However, in some cases it might be preferable to fire when the selection rectangle/polygon intersects the mark instead. I suggest we add a prop on the mark/layer saying selectionFires="intersectsPolygon" or something like that, where the default would be selectionFires="centroidInPolygon".
  2. We have to think about supporting this stuff for polar coordinates too. The implementation described here would be using screen coordinates, which would work in a polar coordinate system, but it would also be cool to be able to drag clockwise or counterclockwise in the polarcoordinate system and have a pie-slice-shaped selection polygon (if that makes any sense). I am not sure yet how we should implement that but I feel like there should be a way.

Thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions