Files
ueblueprint/js/selection/FastSelectionModel.js
barsdeveloper 7469d55518 Replace parsing and test libraries
* WIP

* WIP

* wip

* WIP

* Several fixes

* Tests wip port to playwright

* WIP

* Fix more tests

* Serialization tests fixed

* Several fixes for tests

* Input options types

* Type adjustments

* Fix object reference parser

* Tests fixes

* More tests fixes
2024-02-14 00:40:42 +01:00

161 lines
7.5 KiB
JavaScript
Executable File

import OrderedIndexArray from "./OrderedIndexArray.js"
/**
* @typedef {typeof import("../Blueprint.js").default.nodeBoundariesSupplier} BoundariesFunction
* @typedef {typeof import("../Blueprint.js").default.nodeSelectToggleFunction} SelectionFunction
* @typedef {{
* primaryBoundary: Number,
* secondaryBoundary: Number,
* insertionPosition?: Number,
* rectangle: Number
* onSecondaryAxis: Boolean
* }} Metadata
*/
export default class FastSelectionModel {
/**
* @param {Coordinates} initialPosition
* @param {NodeElement[]} rectangles
* @param {BoundariesFunction} boundariesFunc
* @param {SelectionFunction} selectFunc
*/
constructor(initialPosition, rectangles, boundariesFunc, selectFunc) {
this.initialPosition = initialPosition
this.finalPosition = initialPosition
/** @type {Metadata[]} */
this.metadata = new Array(rectangles.length)
this.primaryOrder = new OrderedIndexArray((element) => this.metadata[element].primaryBoundary)
this.secondaryOrder = new OrderedIndexArray((element) => this.metadata[element].secondaryBoundary)
this.selectFunc = selectFunc
this.rectangles = rectangles
this.primaryOrder.reserve(this.rectangles.length)
this.secondaryOrder.reserve(this.rectangles.length)
rectangles.forEach((rect, index) => {
/** @type {Metadata} */
let rectangleMetadata = {
primaryBoundary: this.initialPosition[0],
secondaryBoundary: this.initialPosition[1],
rectangle: index,
onSecondaryAxis: false,
}
this.metadata[index] = rectangleMetadata
selectFunc(rect, false) // Initially deselected (Eventually)
const rectangleBoundaries = boundariesFunc(rect)
// Secondary axis first because it may be inserted in this.secondaryOrder during the primary axis check
if (this.initialPosition[1] < rectangleBoundaries.secondaryInf) { // Initial position is before the rectangle
rectangleMetadata.secondaryBoundary = rectangleBoundaries.secondaryInf
} else if (rectangleBoundaries.secondarySup < this.initialPosition[1]) { // Initial position is after the rectangle
rectangleMetadata.secondaryBoundary = rectangleBoundaries.secondarySup
} else {
rectangleMetadata.onSecondaryAxis = true
}
if (this.initialPosition[0] < rectangleBoundaries.primaryInf) { // Initial position is before the rectangle
rectangleMetadata.primaryBoundary = rectangleBoundaries.primaryInf
this.primaryOrder.insert(index)
} else if (rectangleBoundaries.primarySup < this.initialPosition[0]) { // Initial position is after the rectangle
rectangleMetadata.primaryBoundary = rectangleBoundaries.primarySup
this.primaryOrder.insert(index)
} else { // Initial lays inside the rectangle (considering just this axis)
// Secondary order depends on primary order, if primary boundaries are not satisfied, the element is not watched for secondary ones
if (rectangleBoundaries.secondarySup < this.initialPosition[1] || this.initialPosition[1] < rectangleBoundaries.secondaryInf) {
this.secondaryOrder.insert(index)
} else {
selectFunc(rect, true)
}
}
})
this.primaryOrder.currentPosition = this.primaryOrder.getPosition(this.initialPosition[0])
this.secondaryOrder.currentPosition = this.secondaryOrder.getPosition(this.initialPosition[1])
this.computeBoundaries()
}
computeBoundaries() {
this.boundaries = {
// Primary axis negative expanding
primaryN: {
v: this.primaryOrder.getPrevValue(),
i: this.primaryOrder.getPrev()
},
primaryP: {
v: this.primaryOrder.getNextValue(),
i: this.primaryOrder.getNext()
},
// Secondary axis negative expanding
secondaryN: {
v: this.secondaryOrder.getPrevValue(),
i: this.secondaryOrder.getPrev()
},
// Secondary axis positive expanding
secondaryP: {
v: this.secondaryOrder.getNextValue(),
i: this.secondaryOrder.getNext()
}
}
}
selectTo(finalPosition) {
const direction = [
Math.sign(finalPosition[0] - this.initialPosition[0]),
Math.sign(finalPosition[1] - this.initialPosition[1])
]
const primaryBoundaryCrossed = (index, added) => {
if (this.metadata[index].onSecondaryAxis) {
this.selectFunc(this.rectangles[index], added)
} else {
if (added) {
this.secondaryOrder.insert(index, finalPosition[1])
const secondaryBoundary = this.metadata[index].secondaryBoundary
if (
// If inserted before the current position
Math.sign(finalPosition[1] - secondaryBoundary) == direction[1]
// And after initial position
&& Math.sign(secondaryBoundary - this.initialPosition[1]) == direction[1]
) {
// Secondary axis is already satisfied then
this.selectFunc(this.rectangles[index], true)
}
} else {
this.selectFunc(this.rectangles[index], false)
this.secondaryOrder.remove(index)
}
}
this.computeBoundaries()
this.selectTo(finalPosition)
}
if (finalPosition[0] < this.boundaries.primaryN.v) {
--this.primaryOrder.currentPosition
primaryBoundaryCrossed(
this.boundaries.primaryN.i,
this.initialPosition[0] > this.boundaries.primaryN.v && finalPosition[0] < this.initialPosition[0])
} else if (finalPosition[0] > this.boundaries.primaryP.v) {
++this.primaryOrder.currentPosition
primaryBoundaryCrossed(
this.boundaries.primaryP.i,
this.initialPosition[0] < this.boundaries.primaryP.v && this.initialPosition[0] < finalPosition[0])
}
const secondaryBoundaryCrossed = (index, added) => {
this.selectFunc(this.rectangles[index], added)
this.computeBoundaries()
this.selectTo(finalPosition)
}
if (finalPosition[1] < this.boundaries.secondaryN.v) {
--this.secondaryOrder.currentPosition
secondaryBoundaryCrossed(
this.boundaries.secondaryN.i,
this.initialPosition[1] > this.boundaries.secondaryN.v && finalPosition[1] < this.initialPosition[1])
} else if (finalPosition[1] > this.boundaries.secondaryP.v) {
++this.secondaryOrder.currentPosition
secondaryBoundaryCrossed(
this.boundaries.secondaryP.i,
this.initialPosition[1] < this.boundaries.secondaryP.v && this.initialPosition[1] < finalPosition[1])
}
this.finalPosition = finalPosition
}
}