ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/fragmentarium/ui/image-annotation/annotation-tool/automatic-alignment.ts

Summary

Maintainability
A
2 hrs
Test Coverage
A
100%
import Annotation, { Geometry } from 'fragmentarium/domain/annotation'
import _ from 'lodash'
import { AnnotationToken } from 'fragmentarium/domain/annotation-token'

interface Point {
  x: number
  y: number
}

function getCenterPoint(geometry: Geometry): Point {
  return {
    x: geometry.x + geometry.width / 2,
    y: geometry.y + geometry.height / 2,
  }
}

function getNeighbouringAnnotations(
  point: Point,
  annotations: readonly Annotation[]
): Annotation[] {
  return annotations.filter((annotation) => {
    const isHeightInRange =
      annotation.geometry.y < point.y &&
      point.y < annotation.geometry.y + annotation.geometry.height
    const isNeighbouring = point.x < annotation.geometry.x
    return isHeightInRange && isNeighbouring
  })
}

export default function automaticAlignment(
  tokens: readonly (readonly AnnotationToken[])[],
  annotation: Annotation,
  annotations: readonly Annotation[]
): readonly Annotation[] {
  const enabledTokens = tokens.map((tokensRow) =>
    tokensRow.filter((tokenSecond) => tokenSecond.enabled)
  )

  const neighbouringTokens: AnnotationToken[] = []

  for (const row of enabledTokens) {
    let isAnnotationFound = false
    for (const columnElement of row) {
      if (isAnnotationFound) {
        neighbouringTokens.push(columnElement)
      } else if (_.isEqual(annotation.data.path, columnElement.path)) {
        isAnnotationFound = true
      }
    }
    if (isAnnotationFound) {
      break
    }
  }
  const neighbouringAnnotations = getNeighbouringAnnotations(
    getCenterPoint(annotation.geometry),
    annotations
  )

  const mergedAnnotations = neighbouringAnnotations
    .filter((annotation, index) => index < neighbouringTokens.length)
    .map((annotation, index) => {
      const token = neighbouringTokens[index]
      return new Annotation(annotation.geometry, {
        id: annotation.data.id,
        value: token.value,
        type: token.type,
        path: token.path,
        signName: token.sign?.name || '',
      })
    })

  let filteredAnnotations = annotations
  for (const mergedAnnotation of mergedAnnotations) {
    filteredAnnotations = filteredAnnotations.filter(
      (annotation) => annotation.data.id !== mergedAnnotation.data.id
    )
  }
  return [...filteredAnnotations, ...mergedAnnotations]
}