<commit_before>import {Map2DView} from "../map2d";
import {IProjection} from "../cameras";
import {InputControl} from "./InputControl";

interface PanState {

  pointerType: string,

  panX: number;
  panY: number;

  lastX: number;
  lastY: number;

}

const $pointersDown = Symbol('pointersDown');

const mergePan = (states: PanState[]) => states.reduce(({panX, panY}, state) => {
  panX += state.panX;
  panY += state.panY;

  state.panX = 0;
  state.panY = 0;

  return { panX, panY };
}, {
  panX: 0,
  panY: 0,
});

export class Map2DPanControl extends InputControl {

  map2dView: Map2DView;

  projection: IProjection;

  pixelsPerSecond = 0;

  speedNorth = 0;
  speedEast = 0;
  speedSouth = 0;
  speedWest = 0;

  private [$pointersDown]: Map<number, PanState> = new Map();

  private prevCursorStyle = '';

  /**
   * @param speed pixels per seconds
   */
  constructor(map2dView: Map2DView, projection: IProjection, speed: number = 100) {
    super();

    this.map2dView = map2dView;
    this.projection = projection;
    this.pixelsPerSecond = speed;

    this.start();
  }

  /**
   * @param t delta time since last `update()` call in seconds
   */
  update(t: number) {
    const { map2dView: view, projection } = this;

    view.centerY -= this.speedNorth * t;
    view.centerY += this.speedSouth * t;

    view.centerX += this.speedEast * t;
    view.centerX -= this.speedWest * t;

    const { panX, panY } = mergePan(Array.from(this[$pointersDown].values()));
    const { pixelRatioH, pixelRatioV } = projection;

    view.centerX -= panX / pixelRatioH;
    view.centerY -= panY / pixelRatioV;

    projection.origin.set(view.centerX, view.centerY);

    view.width = projection.width;
    view.height = projection.height;
  }

  start() {
    const {subscribe} = this;

    subscribe(document, 'keydown', this.onKeyDown);
    subscribe(document, 'keyup', this.onKeyUp);
    subscribe(document, 'pointerdown', this.onPointerDown);
    subscribe(document, 'pointerup', this.onPointerUp);
    subscribe(document, 'pointermove', this.onPointerMove);
  }

  private isPanPointer(event: PointerEvent) {
    if (event.isPrimary) {
      if (event.type !== 'pointerup' && event.pointerType === 'mouse') {
        return event.buttons & 1;
      }
      return true;
    }
    return false;
  }

  onPointerDown = (event: PointerEvent) => {
    if (this.isPanPointer(event)) {
      const pointersDown = this[$pointersDown];
      if (!pointersDown.has(event.pointerId)) {
        const {x:lastX, y:lastY} = this.toRelativeCoords(event);
        pointersDown.set(event.pointerId, {

          pointerType: event.pointerType,

          lastX,
          lastY,

          panX: 0,
          panY: 0,

        });
      }
      if (event.pointerType === 'mouse') {
        const el = (event.target as HTMLElement);
        this.prevCursorStyle = el.style.cursor;
        el.style.cursor = 'none';
      }
    }
  }

  onPointerUp = (event: PointerEvent) => {
    const pointersDown = this[$pointersDown];
    if (this.isPanPointer(event)) {
      const state = pointersDown.get(event.pointerId);
      if (state) {
        this.updatePanState(event, state);
        pointersDown.delete(event.pointerId);
      }
    }
    if (!Array.from(pointersDown.values()).find(state => state.pointerType === 'mouse')) {
      const el = (event.target as HTMLElement);
      el.style.cursor = this.prevCursorStyle;
    }
  }

  onPointerMove = (event: PointerEvent) => {
    if (this.isPanPointer(event)) {
      const state = this[$pointersDown].get(event.pointerId);
      if (state) {
        this.updatePanState(event, state);
      }
    }
  }<commit_msg>fix hide cursor feature<commit_after>import {Map2DView} from "../map2d";
import {IProjection} from "../cameras";
import {InputControl} from "./InputControl";

interface PanState {

  pointerType: string,

  panX: number;
  panY: number;

  lastX: number;
  lastY: number;

}

const $pointersDown = Symbol('pointersDown');

const mergePan = (states: PanState[]) => states.reduce(({panX, panY}, state) => {
  panX += state.panX;
  panY += state.panY;

  state.panX = 0;
  state.panY = 0;

  return { panX, panY };
}, {
  panX: 0,
  panY: 0,
});

const MOUSE = 'mouse';

export class Map2DPanControl extends InputControl {

  map2dView: Map2DView;

  projection: IProjection;

  pixelsPerSecond = 0;

  speedNorth = 0;
  speedEast = 0;
  speedSouth = 0;
  speedWest = 0;

  private [$pointersDown]: Map<number, PanState> = new Map();

  cursorStyle: string;

  /**
   * @param speed pixels per seconds
   */
  constructor(map2dView: Map2DView, projection: IProjection, speed: number = 100, cursorStyle = '') {
    super();

    this.map2dView = map2dView;
    this.projection = projection;
    this.pixelsPerSecond = speed;
    this.cursorStyle = cursorStyle;

    this.start();
  }

  /**
   * @param t delta time since last `update()` call in seconds
   */
  update(t: number) {
    const { map2dView: view, projection } = this;

    view.centerY -= this.speedNorth * t;
    view.centerY += this.speedSouth * t;

    view.centerX += this.speedEast * t;
    view.centerX -= this.speedWest * t;

    const { panX, panY } = mergePan(Array.from(this[$pointersDown].values()));
    const { pixelRatioH, pixelRatioV } = projection;

    view.centerX -= panX / pixelRatioH;
    view.centerY -= panY / pixelRatioV;

    projection.origin.set(view.centerX, view.centerY);

    view.width = projection.width;
    view.height = projection.height;
  }

  start() {
    const {subscribe} = this;

    subscribe(document, 'keydown', this.onKeyDown);
    subscribe(document, 'keyup', this.onKeyUp);
    subscribe(document, 'pointerdown', this.onPointerDown);
    subscribe(document, 'pointerup', this.onPointerUp);
    subscribe(document, 'pointermove', this.onPointerMove);
  }

  private isPanPointer(event: PointerEvent) {
    if (event.isPrimary) {
      if (event.type !== 'pointerup' && event.pointerType === MOUSE) {
        return event.buttons & 1;
      }
      return true;
    }
    return false;
  }

  onPointerDown = (event: PointerEvent) => {
    if (this.isPanPointer(event)) {
      const pointersDown = this[$pointersDown];
      if (!pointersDown.has(event.pointerId)) {
        const {x:lastX, y:lastY} = this.toRelativeCoords(event);
        pointersDown.set(event.pointerId, {

          pointerType: event.pointerType,

          lastX,
          lastY,

          panX: 0,
          panY: 0,

        });
      }
      if (event.pointerType === 'mouse') {
        this.hideCursor(event);
      }
    }
  }

  private hideCursor(event: PointerEvent) {
    const el = (event.target as HTMLElement);
    el.style.cursor = 'none';
  }

  onPointerUp = (event: PointerEvent) => {
    const pointersDown = this[$pointersDown];
    if (this.isPanPointer(event)) {
      const state = pointersDown.get(event.pointerId);
      if (state) {
        this.updatePanState(event, state);
        pointersDown.delete(event.pointerId);
      }
    }
    if (event.pointerType === 'mouse') {
      if (!Array.from(pointersDown.values()).find(state => state.pointerType === MOUSE)) {
        this.restoreCursorStyle(event);
      }
    }
  }

  private restoreCursorStyle(event: PointerEvent) {
    const el = (event.target as HTMLElement);
    const {cursorStyle} = this;
    if (el.style.cursor !== cursorStyle) {
      el.style.cursor = cursorStyle;
    }
  }

  onPointerMove = (event: PointerEvent) => {
    if (this.isPanPointer(event)) {
      const state = this[$pointersDown].get(event.pointerId);
      if (state) {
        this.updatePanState(event, state);
      }
    }
    if (event.pointerType === MOUSE && event.buttons === 0) {
      this.restoreCursorStyle(event);
    }
  }
{'fraction_non_alphanumeric': 0.1407904934125549, 'fraction_numerical': 0.006587445104624128, 'mean_word_length': 2.8715, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '126852', 'n_tokens_mistral': 2629, 'n_tokens_neox': 2516, 'n_words': 540}