mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-03 23:55:04 +08:00
* 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
161 lines
7.5 KiB
JavaScript
Executable File
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
|
|
}
|
|
}
|