# model/StrokeComponent.js

    import { modelLogger as logger } from '../configuration/LoggerConfig'

    /**
     * Stroke symbol
     * @typedef {Object} Stroke
     * @property {String} type=stroke Symbol type, 'stroke' for stroke
     * @property {String} pointerType=undefined Pointer type
     * @property {Number} pointerId=undefined Pointer id
     * @property {Array<Number>} x=[] X coordinates
     * @property {Array<Number>} y=[] Y coordinates
     * @property {Array<Number>} t=[] Timestamps matching x,y coordinates
     * @property {Array<Number>} p=[] Pressure
     * @property {Array<Number>} l=[] Length from origin
     * @property {Number} width=0 (for rendering) Pen/brush width
     * @property {String} color=undefined (for rendering) Pen/brush color
     */

    /**
     * pointerEvents symbol
     * @typedef {Object} pointerEvents
     * @property {String} type=pointerEvents Symbol type, 'pointerEvents' for pointerEvents
     * @property {Boolean} processGestures=False indicates if the gestures have to be processed
     * @property {Array<Stroke>} events=[] the events to process
     */

    function computeDistance (x, y, xArray, yArray, lastIndexPoint) {
      const distance = Math.sqrt(Math.pow((y - yArray[lastIndexPoint - 1]), 2) + Math.pow((x - xArray[lastIndexPoint - 1]), 2))
      return isNaN(distance) ? 0 : distance
    }

    function computeLength (x, y, xArray, yArray, lArray, lastIndexPoint) {
      const length = lArray[lastIndexPoint - 1] + computeDistance(x, y, xArray, yArray, lastIndexPoint)
      return isNaN(length) ? 0 : length
    }

    function computePressure (x, y, xArray, yArray, lArray, lastIndexPoint) {
      let ratio = 1.0
      const distance = computeDistance(x, y, xArray, yArray, lastIndexPoint)
      const length = computeLength(x, y, xArray, yArray, lArray, lastIndexPoint)

      if (length === 0) {
        ratio = 0.5
      } else if (distance === length) {
        ratio = 1.0
      } else if (distance < 10) {
        ratio = 0.2 + Math.pow(0.1 * distance, 0.4)
      } else if (distance > length - 10) {
        ratio = 0.2 + Math.pow(0.1 * (length - distance), 0.4)
      }
      const pressure = ratio * Math.max(0.1, 1.0 - (0.1 * Math.sqrt(distance)))
      return isNaN(parseFloat(pressure)) ? 0.5 : pressure
    }

    function filterPointByAcquisitionDelta (x, y, xArray, yArray, width) {
      const delta = (2 + (width / 4))
      let ret = false
      if (xArray.length === 0 || yArray.length === 0 || Math.abs(xArray[xArray.length - 1] - x) >= delta || Math.abs(yArray[yArray.length - 1] - y) >= delta) {
        ret = true
      }
      return ret
    }

    /**
     * Create a new stroke
     * @param {Object} properties Properties to be applied to the stroke.
     * @return {Stroke} New stroke with properties for quadratics draw
     */
    export function createStrokeComponent (properties) {
      const defaultStroke = {
        type: 'stroke',
        x: [],
        y: [],
        t: [],
        p: [],
        l: [],
        width: 0
      }
      return Object.assign({}, defaultStroke, properties)
    }

    /**
     * Get a JSON copy of a stroke by filtering its properties
     * @param {Stroke} stroke Current stroke
     * @return {{x: Array<Number>, y: Array<Number>, t: Array<Number>}} Simplified stroke object
     */
    export function toJSON (stroke) {
      return { x: stroke.x, y: stroke.y, t: stroke.t }
    }

    /**
     * Mutate a stroke by adding a point to it.
     * @param {Stroke} stroke Current stroke
     * @param {{x: Number, y: Number, t: Number}} point Point to add
     * @return {Stroke} Updated stroke
     */
    export function addPoint (stroke, point) {
      const strokeReference = stroke
      if (filterPointByAcquisitionDelta(point.x, point.y, strokeReference.x, strokeReference.y, strokeReference.width)) {
        strokeReference.x.push(point.x)
        strokeReference.y.push(point.y)
        strokeReference.t.push(point.t)
        strokeReference.p.push(computePressure(point.x, point.y, strokeReference.x, strokeReference.y, strokeReference.l, strokeReference.x.length - 1))
        strokeReference.l.push(computeLength(point.x, point.y, strokeReference.x, strokeReference.y, strokeReference.l, strokeReference.x.length - 1))
      } else {
        logger.trace('ignore filtered point', point)
      }
      return strokeReference
    }

    /**
     * Slice a stroke and return the sliced part of it
     * @param {Stroke} stroke Current stroke
     * @param {Number} [start=0] Zero-based index at which to begin extraction
     * @param {Number} [end=length] Zero-based index at which to end extraction
     * @return {Stroke} Sliced stroke
     */
    export function slice (stroke, start = 0, end = stroke.x.length) {
      const slicedStroke = createStrokeComponent({ color: stroke.color, width: stroke.width })
      for (let i = start; i < end; i++) {
        addPoint(slicedStroke, {
          x: stroke.x[i],
          y: stroke.y[i],
          t: stroke.t[i]
        })
      }
      return slicedStroke
    }

    /**
     * Extract point by index
     * @param {Stroke} stroke Current stroke
     * @param {Number} index Zero-based index
     * @return {{x: Number, y: Number, t: Number, p: Number, l: Number}} Point with properties for quadratics draw
     */
    export function getPointByIndex (stroke, index) {
      let point
      if (index !== undefined && index >= 0 && index < stroke.x.length) {
        point = {
          x: stroke.x[index],
          y: stroke.y[index],
          t: stroke.t[index],
          p: stroke.p[index],
          l: stroke.l[index]
        }
      }
      return point
    }