2022-01-13 13:21:57 +00:00
import { OccupiedSpace } from "constants/CanvasEditorConstants" ;
import { GridDefaults } from "constants/WidgetConstants" ;
import { isEmpty } from "lodash" ;
import {
CollidingSpace ,
CollisionAccessors ,
2022-04-01 14:57:03 +00:00
CollisionMap ,
2022-01-13 13:21:57 +00:00
CollisionTree ,
2022-04-01 14:57:03 +00:00
CollisionTreeCache ,
2022-01-13 13:21:57 +00:00
Delta ,
DirectionalMovement ,
2022-04-01 14:57:03 +00:00
DirectionalVariables ,
2022-01-13 13:21:57 +00:00
GridProps ,
HORIZONTAL_RESIZE_LIMIT ,
2022-04-01 14:57:03 +00:00
PrevReflowState ,
2022-01-13 13:21:57 +00:00
ReflowDirection ,
ReflowedSpaceMap ,
2022-04-01 14:57:03 +00:00
SecondOrderCollisionMap ,
SpaceMap ,
SpaceMovementMap ,
2022-01-13 13:21:57 +00:00
VERTICAL_RESIZE_LIMIT ,
} from "./reflowTypes" ;
import {
2022-04-01 14:57:03 +00:00
checkReCollisionWithOtherNewSpacePositions ,
filterCommonSpaces ,
buildArrayToCollisionMap ,
2022-01-13 13:21:57 +00:00
getAccessor ,
getCollidingSpacesInDirection ,
getMaxX ,
getMaxY ,
2022-04-01 14:57:03 +00:00
getModifiedOccupiedSpacesMap ,
2022-01-13 13:21:57 +00:00
getReflowDistance ,
2022-04-01 14:57:03 +00:00
getReflowedDimension ,
2022-01-13 13:21:57 +00:00
getResizedDimensions ,
shouldReplaceOldMovement ,
sortCollidingSpacesByDistance ,
2022-04-01 14:57:03 +00:00
getModifiedCollidingSpace ,
2022-04-20 07:26:35 +00:00
checkProcessNodeForTree ,
getRelativeCollidingValue ,
2022-01-13 13:21:57 +00:00
} from "./reflowUtils" ;
/ * *
* returns movement map of all the cascading colliding spaces
2022-04-01 14:57:03 +00:00
* Movement map has X , Y , width and height of each reflowed spaces in absolute values
*
* @param newSpacePositions new / c u r r e n t p o s i t i o n s a r r a y o f t h e s p a c e / b l o c k
* @param newSpacePositionsMap new / c u r r e n t p o s i t i o n s m a p o f t h e s p a c e / b l o c k
2022-01-13 13:21:57 +00:00
* @param occupiedSpaces array of all the occupied spaces on the canvas
2022-04-01 14:57:03 +00:00
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
* @param collidingSpaces array of Colliding spaces of the dragging / resizing space
2022-01-13 13:21:57 +00:00
* @param collidingSpaceMap Map of Colliding spaces of the dragging / resizing space
* @param gridProps properties of the canvas ' s grid
* @param delta X and Y coordinate displacement of the newPosition from the original position
* @param shouldResize boolean to indicate if colliding spaces should resize
2022-04-01 14:57:03 +00:00
* @param globalDirection ReflowDirection , direction of reflow
* @param globalIsHorizontal boolean to identify if the current orientation is horizontal
* @param prevReflowState this contains a map of reference to the key values of previous reflow method call to back trace widget movements
* @param primaryMovementMap movement map / information from previous run of the algorithm
2022-01-13 13:21:57 +00:00
* @returns movement map of all the cascading colliding spaces
* /
export function getMovementMap (
2022-04-01 14:57:03 +00:00
newSpacePositions : OccupiedSpace [ ] ,
newSpacePositionsMap : SpaceMap ,
2022-01-13 13:21:57 +00:00
occupiedSpaces : OccupiedSpace [ ] ,
2022-04-01 14:57:03 +00:00
occupiedSpacesMap : SpaceMap ,
OGOccupiedSpacesMap : SpaceMap ,
collidingSpaces : CollidingSpace [ ] ,
collidingSpaceMap : CollisionMap ,
2022-01-13 13:21:57 +00:00
gridProps : GridProps ,
delta = { X : 0 , Y : 0 } ,
shouldResize = true ,
2022-04-01 14:57:03 +00:00
globalDirection : ReflowDirection ,
globalIsHorizontal : boolean ,
prevReflowState : PrevReflowState ,
primaryMovementMap? : ReflowedSpaceMap ,
primarySecondOrderCollisionMap? : SecondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
) {
2022-04-01 14:57:03 +00:00
const movementMap : ReflowedSpaceMap = { . . . primaryMovementMap } ;
//create a tree structure of collision
const { collisionTrees , secondOrderCollisionMap } = getCollisionTree (
newSpacePositions ,
2022-01-13 13:21:57 +00:00
occupiedSpaces ,
2022-04-01 14:57:03 +00:00
occupiedSpacesMap ,
OGOccupiedSpacesMap ,
collidingSpaces ,
globalDirection ,
globalIsHorizontal ,
2022-01-13 13:21:57 +00:00
collidingSpaceMap ,
2022-04-01 14:57:03 +00:00
gridProps ,
primaryMovementMap || prevReflowState . prevMovementMap ,
prevReflowState ,
! ! primaryMovementMap ,
primarySecondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
) ;
2022-04-01 14:57:03 +00:00
if ( ! collisionTrees || collisionTrees . length <= 0 ) {
2022-01-13 13:21:57 +00:00
return { } ;
}
2022-04-01 14:57:03 +00:00
const directionalVariables : DirectionalVariables = { } ;
2022-01-13 13:21:57 +00:00
2022-04-20 07:26:35 +00:00
//globalProcessedNodes are the global cache to not generate the collision tree for spaces processed unnecessarily
//this is done for the sake of performance
const globalProcessedNodes : CollisionTreeCache = { } ;
2022-04-01 14:57:03 +00:00
//solve the tree structure to get the movement values
for ( let i = 0 ; i < collisionTrees . length ; i ++ ) {
const childNode = collisionTrees [ i ] ;
const childDirection = childNode . direction ;
2022-01-13 13:21:57 +00:00
const directionalAccessors = getAccessor ( childDirection ) ;
const distanceBeforeCollision =
childNode [ directionalAccessors . oppositeDirection ] -
2022-04-01 14:57:03 +00:00
childNode . collidingValue ;
2022-01-13 13:21:57 +00:00
2022-11-23 09:48:23 +00:00
const { occupiedLength , occupiedSpace } = getMovementMapHelper (
2022-01-13 13:21:57 +00:00
childNode ,
movementMap ,
delta ,
gridProps ,
childDirection ,
2022-04-01 14:57:03 +00:00
directionalAccessors ,
2022-01-13 13:21:57 +00:00
childNode [ directionalAccessors . direction ] ,
distanceBeforeCollision ,
2022-04-01 14:57:03 +00:00
collisionTrees ,
i ,
0 ,
2022-01-13 13:21:57 +00:00
shouldResize ,
2022-04-20 07:26:35 +00:00
globalProcessedNodes ,
2022-01-13 13:21:57 +00:00
) ;
2022-11-23 09:48:23 +00:00
let staticOccupiedLength = 0 ,
2022-01-13 13:21:57 +00:00
maxOccupiedSpace = 0 ;
2022-04-01 14:57:03 +00:00
if ( ! directionalVariables [ childNode . collidingId ] ) {
directionalVariables [ childNode . collidingId ] = { } ;
}
if ( directionalVariables [ childNode . collidingId ] [ childDirection ] ) {
2022-11-23 09:48:23 +00:00
[ staticOccupiedLength , maxOccupiedSpace ] = directionalVariables [
2022-04-01 14:57:03 +00:00
childNode . collidingId
] [ childDirection ] ;
2022-01-13 13:21:57 +00:00
}
2022-11-23 09:48:23 +00:00
staticOccupiedLength = Math . max ( staticOccupiedLength , occupiedLength ) ;
2022-01-13 13:21:57 +00:00
maxOccupiedSpace = Math . max ( maxOccupiedSpace , occupiedSpace ) ;
2022-04-01 14:57:03 +00:00
directionalVariables [ childNode . collidingId ] [ childDirection ] = [
2022-11-23 09:48:23 +00:00
staticOccupiedLength ,
2022-01-13 13:21:57 +00:00
maxOccupiedSpace ,
directionalAccessors ,
childDirection ,
] ;
}
2022-11-23 09:48:23 +00:00
//based on the movement values and the occupiedLength of the dragging space from the borders, movement limits are calculated
2022-04-01 14:57:03 +00:00
const movementVariablesMap = getMovementVariables (
newSpacePositionsMap ,
directionalVariables ,
delta ,
gridProps ,
shouldResize ,
) ;
2022-01-13 13:21:57 +00:00
return {
2022-04-01 14:57:03 +00:00
movementVariablesMap ,
2022-01-13 13:21:57 +00:00
movementMap ,
2022-04-01 14:57:03 +00:00
secondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
} ;
}
/ * *
2022-04-01 14:57:03 +00:00
* Collision tree is an Object containing the spaces and it ' s colliding spaces in a tree form
* eg , if a spaces are colliding with root space , then they are added as children of root space .
* children spaces further check the spaces colliding with them and then add them as their children , so on
*
* @param newSpacePositions new / c u r r e n t p o s i t i o n s a r r a y o f t h e s p a c e / b l o c k
2022-01-13 13:21:57 +00:00
* @param occupiedSpaces array of all the occupied spaces on the canvas
2022-04-01 14:57:03 +00:00
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
* @param collidingSpaces array of Colliding spaces of the dragging / resizing space
* @param globalDirection ReflowDirection , direction of reflow
* @param globalIsHorizontal boolean to identify if the current orientation is horizontal
* @param gridProps properties of the canvas ' s grid
* @param prevMovementMap is previous run 's movement map if this is the first orientation, or is primary orientation' s movement map if it is the second orientation run
* @param prevReflowState this contains a map of reference to the key values of previous reflow method call to back trace widget movements
* @param isSecondRun boolean to indicate if it is being run for the second time
* @returns array of collision Tree
2022-01-13 13:21:57 +00:00
* /
2022-04-01 14:57:03 +00:00
export function getCollisionTree (
newSpacePositions : OccupiedSpace [ ] ,
2022-01-13 13:21:57 +00:00
occupiedSpaces : OccupiedSpace [ ] ,
2022-04-01 14:57:03 +00:00
occupiedSpacesMap : SpaceMap ,
OGOccupiedSpacesMap : SpaceMap ,
collidingSpaces : CollidingSpace [ ] ,
globalDirection : ReflowDirection ,
globalIsHorizontal : boolean ,
collidingSpaceMap : CollisionMap ,
gridProps : GridProps ,
prevMovementMap : ReflowedSpaceMap ,
prevReflowState : PrevReflowState ,
isSecondRun : boolean ,
primarySecondOrderCollisionMap? : SecondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
) {
2022-04-01 14:57:03 +00:00
//To calculate the tree, you iterate over the directly colliding spaces
const collisionTrees : CollisionTree [ ] = [ ] ;
const secondOrderCollisionMap : SecondOrderCollisionMap = {
. . . primarySecondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
} ;
2022-04-01 14:57:03 +00:00
//globalProcessedNodes are the global cache to not generate the collision tree for spaces processed unnecessarily
//this is done for the sake of performance
const globalProcessedNodes : CollisionTreeCache = { } ;
2022-01-13 13:21:57 +00:00
2022-04-01 14:57:03 +00:00
for ( let i = 0 ; i < collidingSpaces . length ; i ++ ) {
const collidingSpace = collidingSpaces [ i ] ;
2022-04-20 07:26:35 +00:00
if (
checkProcessNodeForTree ( collidingSpace , globalProcessedNodes )
. shouldProcessNode
) {
2022-04-01 14:57:03 +00:00
// This is required if we suddenly switch orientations, like from horizontal to vertical or vice versa
const {
currentAccessors ,
currentCollidingSpace ,
currentDirection ,
currentOccSpaces ,
currentOccSpacesMap ,
} = getModifiedArgumentsForCollisionTree (
collidingSpace ,
occupiedSpaces ,
occupiedSpacesMap ,
OGOccupiedSpacesMap ,
ReflowDirection . UNSET ,
globalIsHorizontal ,
prevMovementMap ,
gridProps ,
) ;
2022-01-13 13:21:57 +00:00
2022-04-01 14:57:03 +00:00
// this method recursively builds the tree structure
2022-04-20 07:26:35 +00:00
const {
collisionTree : currentCollisionTree ,
2022-11-23 09:48:23 +00:00
occupiedLength ,
2022-04-20 07:26:35 +00:00
} = getCollisionTreeHelper (
2022-04-01 14:57:03 +00:00
newSpacePositions ,
currentOccSpaces ,
currentOccSpacesMap ,
OGOccupiedSpacesMap ,
currentCollidingSpace ,
globalDirection ,
currentDirection ,
currentAccessors ,
collidingSpaces ,
collidingSpaceMap ,
gridProps ,
i ,
prevMovementMap ,
prevReflowState ,
true ,
isSecondRun ,
globalProcessedNodes ,
secondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
) ;
2022-04-20 07:26:35 +00:00
//To get colliding Value of the space relative to the Canvas edges
const relativeCollidingValue = getRelativeCollidingValue (
currentAccessors ,
currentCollidingSpace . collidingValue ,
currentDirection ,
gridProps ,
2022-11-23 09:48:23 +00:00
occupiedLength ,
2022-04-20 07:26:35 +00:00
) ;
2022-04-01 14:57:03 +00:00
if ( currentCollisionTree ) {
collisionTrees . push ( {
. . . currentCollisionTree ,
2022-04-20 07:26:35 +00:00
collidingValue : relativeCollidingValue ,
2022-04-01 14:57:03 +00:00
collidingId : currentCollidingSpace.collidingId ,
} ) ;
//initialize if undefined
if ( ! globalProcessedNodes [ currentCollidingSpace . id ] ) {
globalProcessedNodes [ currentCollidingSpace . id ] = { } ;
}
//add value to cache
globalProcessedNodes [ currentCollidingSpace . id ] [
currentCollidingSpace . direction
2022-04-20 07:26:35 +00:00
] = {
value : relativeCollidingValue ,
childNode : {
. . . currentCollisionTree ,
collidingValue : relativeCollidingValue ,
collidingId : currentCollidingSpace.collidingId ,
} ,
} ;
2022-01-13 13:21:57 +00:00
}
}
}
2022-04-01 14:57:03 +00:00
return { collisionTrees , secondOrderCollisionMap } ;
2022-01-13 13:21:57 +00:00
}
2022-04-01 14:57:03 +00:00
/ * *
* generates a collision tree recursively
* if a occupiedSpaces are colliding with collidingSpace , then they are added as children of collidingSpace space .
* children spaces further check the spaces colliding with them and then add them as their children , so on
* This is done recursively
*
* @param newSpacePositions new / c u r r e n t p o s i t i o n s a r r a y o f t h e s p a c e / b l o c k
* @param occupiedSpaces array of all the occupied spaces on the canvas
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
* @param collidingSpace current colliding space of which collision tree is returned
* @param globalDirection ReflowDirection , global direction of reflow
* @param direction ReflowDirection , direction of reflow of the colliding space
* @param accessors accessors to access dimensions of spaces in a direction
* @param globalCollidingSpaces array of initial colliding widgets
* @param collidingSpaceMap Map of Colliding spaces of the dragging / resizing space
* @param gridProps properties of the canvas ' s grid
* @param insertionIndex current index at which any new direct collision of new space positions will be added
* @param prevMovementMap is previous run 's movement map if this is the first orientation, or is primary orientation' s movement map if it is the second orientation run
* @param prevReflowState this contains a map of reference to the key values of previous reflow method call to back trace widget movements
* @param isDirectCollidingSpace boolean if the space is direct collision of the new space positions
* @param isSecondRun boolean to indicate if it is being run for the second time
* @param globalProcessedNodes cache to make sure to not generate a tree for the same space in the getCollision Tree
* @param secondOrderCollisionMap collision map of the direct collisions of the initial direct collisions
* @param processedNodes cache to make sure to not generate a tree for the same space of all the widgets below the colliding space
* @returns collision tree of a particular in a direction
* /
2022-01-13 13:21:57 +00:00
function getCollisionTreeHelper (
2022-04-01 14:57:03 +00:00
newSpacePositions : OccupiedSpace [ ] ,
2022-01-13 13:21:57 +00:00
occupiedSpaces : OccupiedSpace [ ] ,
2022-04-01 14:57:03 +00:00
occupiedSpacesMap : SpaceMap ,
OGOccupiedSpacesMap : SpaceMap ,
2022-01-13 13:21:57 +00:00
collidingSpace : CollidingSpace ,
2022-04-01 14:57:03 +00:00
globalDirection : ReflowDirection ,
2022-01-13 13:21:57 +00:00
direction : ReflowDirection ,
2022-04-01 14:57:03 +00:00
accessors : CollisionAccessors ,
globalCollidingSpaces : CollidingSpace [ ] ,
collidingSpaceMap : CollisionMap ,
gridProps : GridProps ,
insertionIndex : number ,
prevMovementMap : ReflowedSpaceMap ,
prevReflowState : PrevReflowState ,
isDirectCollidingSpace : boolean ,
isSecondRun : boolean ,
globalProcessedNodes : CollisionTreeCache ,
secondOrderCollisionMap? : SecondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
) {
2022-04-20 07:26:35 +00:00
if ( ! collidingSpace ) return { } ;
2022-11-23 09:48:23 +00:00
let occupiedLength = 0 ;
2022-01-13 13:21:57 +00:00
const collisionTree : CollisionTree = { . . . collidingSpace , children : { } } ;
2022-04-01 14:57:03 +00:00
// we resize the space to either increase the width or height based on movement
// for the sake of finding all the colliding spaces
//for example, if a space is moved by 2 rows in the BOTTOM direction,
//then the space's bottom dimension is increased by 2 rows
const resizedDimensions = getResizedDimensions ( collisionTree , accessors ) ;
const filteredNewSpacePositions = newSpacePositions . filter (
( a ) = > a . id !== collidingSpace . collidingId ,
2022-01-13 13:21:57 +00:00
) ;
2022-04-01 14:57:03 +00:00
// check if the space again collides with other dragging spaces in group widget scenario
if (
checkReCollisionWithOtherNewSpacePositions (
resizedDimensions ,
collidingSpace ,
globalDirection ,
direction ,
filteredNewSpacePositions ,
globalCollidingSpaces ,
insertionIndex ,
globalProcessedNodes ,
collidingSpaceMap ,
prevReflowState ,
isSecondRun ,
)
)
2022-04-20 07:26:35 +00:00
return { } ;
2022-04-01 14:57:03 +00:00
// to get it's colliding spaces
2022-01-13 13:21:57 +00:00
const {
collidingSpaces ,
occupiedSpacesInDirection ,
} = getCollidingSpacesInDirection (
resizedDimensions ,
2022-04-01 14:57:03 +00:00
collidingSpace ,
globalDirection ,
2022-01-13 13:21:57 +00:00
direction ,
2022-04-01 14:57:03 +00:00
gridProps ,
prevReflowState ,
collidingSpaceMap ,
2022-01-13 13:21:57 +00:00
occupiedSpaces ,
2022-04-01 14:57:03 +00:00
isDirectCollidingSpace ,
2022-01-13 13:21:57 +00:00
) ;
2022-04-01 14:57:03 +00:00
if ( isDirectCollidingSpace && secondOrderCollisionMap ) {
//initialize if undefined
if ( ! secondOrderCollisionMap [ collidingSpace . id ] ) {
secondOrderCollisionMap [ collidingSpace . id ] = {
. . . occupiedSpacesMap [ collidingSpace . id ] ,
children : { } ,
} ;
}
secondOrderCollisionMap [ collidingSpace . id ] . children = {
. . . secondOrderCollisionMap [ collidingSpace . id ] . children ,
. . . buildArrayToCollisionMap ( collidingSpaces ) ,
} ;
}
sortCollidingSpacesByDistance ( collidingSpaces ) ;
2022-01-13 13:21:57 +00:00
for ( const currentCollidingSpace of collidingSpaces ) {
2022-04-01 14:57:03 +00:00
// If in case it changes orientation
const {
currentAccessors ,
currentCollidingSpace : modifiedCollidingSpace ,
currentDirection ,
currentOccSpaces ,
currentOccSpacesMap ,
} = getModifiedArgumentsForCollisionTree (
currentCollidingSpace ,
occupiedSpacesInDirection ,
occupiedSpacesMap ,
OGOccupiedSpacesMap ,
direction ,
accessors . isHorizontal ,
prevMovementMap ,
gridProps ,
) ;
2022-04-20 07:26:35 +00:00
const { currentChildNode , shouldProcessNode } = checkProcessNodeForTree (
modifiedCollidingSpace ,
globalProcessedNodes ,
) ;
if ( shouldProcessNode ) {
2022-04-01 14:57:03 +00:00
//Recursively call to build the tree
2022-04-20 07:26:35 +00:00
const {
collisionTree : currentCollisionTree ,
2022-11-23 09:48:23 +00:00
occupiedLength : currentOccupiedLength ,
2022-04-20 07:26:35 +00:00
} = getCollisionTreeHelper (
2022-04-01 14:57:03 +00:00
filteredNewSpacePositions ,
currentOccSpaces ,
currentOccSpacesMap ,
OGOccupiedSpacesMap ,
modifiedCollidingSpace ,
globalDirection ,
currentDirection ,
currentAccessors ,
globalCollidingSpaces ,
collidingSpaceMap ,
gridProps ,
insertionIndex ,
prevMovementMap ,
prevReflowState ,
false ,
isSecondRun ,
2022-01-13 13:21:57 +00:00
globalProcessedNodes ,
2022-04-01 14:57:03 +00:00
secondOrderCollisionMap ,
2022-01-13 13:21:57 +00:00
) ;
2022-04-01 14:57:03 +00:00
//initialize if undefined
if ( ! globalProcessedNodes [ modifiedCollidingSpace . id ] ) {
globalProcessedNodes [ modifiedCollidingSpace . id ] = { } ;
}
2022-01-13 13:21:57 +00:00
2022-04-20 07:26:35 +00:00
if ( currentCollisionTree ) {
//To get colliding Value of the space relative to the Canvas edges
const relativeCollidingValue = getRelativeCollidingValue (
currentAccessors ,
modifiedCollidingSpace . collidingValue ,
currentDirection ,
gridProps ,
2022-11-23 09:48:23 +00:00
currentOccupiedLength ,
2022-04-20 07:26:35 +00:00
) ;
//add value to cache
globalProcessedNodes [ modifiedCollidingSpace . id ] [
modifiedCollidingSpace . direction
] = {
value : relativeCollidingValue ,
childNode : {
. . . currentCollisionTree ,
} ,
2022-01-13 13:21:57 +00:00
} ;
2022-04-20 07:26:35 +00:00
if ( collisionTree . children ) {
collisionTree . children [ currentCollidingSpace . id ] = {
. . . currentCollisionTree ,
} ;
}
2022-01-13 13:21:57 +00:00
}
2022-11-23 09:48:23 +00:00
//store overall maximum travel
if ( currentOccupiedLength )
occupiedLength = Math . max ( occupiedLength , currentOccupiedLength ) ;
2022-04-20 07:26:35 +00:00
} else if ( currentChildNode && collisionTree . children ) {
collisionTree . children [ currentChildNode . id ] = {
. . . currentChildNode ,
} ;
2022-01-13 13:21:57 +00:00
}
}
2022-11-23 09:48:23 +00:00
return {
collisionTree ,
occupiedLength :
occupiedLength +
( accessors . isHorizontal
? HORIZONTAL_RESIZE_LIMIT
: collidingSpace . fixedHeight && accessors . directionIndicator < 0
? collidingSpace . fixedHeight
: VERTICAL_RESIZE_LIMIT ) ,
} ;
2022-01-13 13:21:57 +00:00
}
2022-04-01 14:57:03 +00:00
/ * *
* to get modified arguments of spaces is opposite orientation than the current orientation
* If while recursing through the collision tree and children is of different direction or orientation than the parent node ,
* then the occupied spaces need to be modified to suit that orientation
*
* @param collidingSpace current colliding space of which collision tree is returned
* @param occupiedSpaces array of all the occupied spaces on the canvas
* @param occupiedSpacesMap map of all the occupied spaces on the canvas
* @param OGOccupiedSpacesMap map of all the original occupied spaces on the canvas before modification based on orientation
* @param direction ReflowDirection , direction of reflow of the colliding space
* @param isHorizontal boolean indicating if the current orientation is horizontal
* @param prevMovementMap movement map generate during the previous run
* @param gridProps properties of the canvas ' s grid
* @param globalProcessedNodes cache to make sure to not generate a tree for the same space in the getCollision Tree
* @param currentProcessedNodes cache to make sure to not generate a tree for the same space of all the widgets below the colliding space
* @returns collision tree of a particular in a direction
* /
export function getModifiedArgumentsForCollisionTree (
collidingSpace : CollidingSpace ,
occupiedSpaces : OccupiedSpace [ ] ,
occupiedSpacesMap : SpaceMap ,
OGOccupiedSpacesMap : SpaceMap ,
direction : ReflowDirection ,
isHorizontal : boolean ,
prevMovementMap : ReflowedSpaceMap ,
gridProps : GridProps ,
) {
const currentDirection = collidingSpace . direction ;
const currentAccessors = getAccessor ( currentDirection ) ;
let currentOccSpaces = occupiedSpaces ;
let currentOccSpacesMap = { . . . occupiedSpacesMap } ;
// modify the collidingSpace position's values to be in the other orientation
let currentCollidingSpace = getModifiedCollidingSpace (
collidingSpace ,
OGOccupiedSpacesMap ,
prevMovementMap ,
currentAccessors . isHorizontal ,
gridProps ,
currentAccessors . perpendicularMax ,
currentAccessors . perpendicularMin ,
) ;
//modify the occupied spaces to be in the other orientation,
// if the current orientation of the colliding space is different from the parent space
if ( collidingSpace . direction !== direction ) {
if ( currentAccessors . isHorizontal !== isHorizontal ) {
currentOccSpacesMap = getModifiedOccupiedSpacesMap (
OGOccupiedSpacesMap ,
prevMovementMap ,
currentAccessors . isHorizontal ,
gridProps ,
currentAccessors . perpendicularMax ,
currentAccessors . perpendicularMin ,
) ;
currentCollidingSpace = {
. . . currentCollidingSpace ,
. . . currentOccSpacesMap [ collidingSpace . id ] ,
} ;
}
filterCommonSpaces (
{
[ collidingSpace . collidingId ] : true ,
} ,
currentOccSpacesMap ,
) ;
currentOccSpaces = Object . values ( currentOccSpacesMap ) ;
currentOccSpaces . sort ( ( a , b ) = > {
return a [ currentAccessors . direction ] - b [ currentAccessors . direction ] ;
} ) ;
}
return {
currentOccSpacesMap ,
currentAccessors ,
currentDirection ,
currentOccSpaces ,
currentCollidingSpace ,
} ;
}
/ * *
* Helper method to generate movement map by recursively going over the collision tree
* This method recursively traverses the collision tree , to calculate from the ends of the tree making it ' s way to the roots
* This is calculated in that way to check if the ends of branches are colliding with the boundaries of the canvas .
*
* @param collisionTree space and it ' s colliding spaces in a tree structure
* @param movementMap map containing reflowed X , Y , width and height of spaces
* @param gridProps properties of the canvas ' s grid
* @param direction ReflowDirection , direction of reflow of the colliding space
* @param accessors accessors to access dimensions of spaces in a direction
* @param prevWidgetDistance dimension of the previous colliding widget in the direction
* @param distanceBeforeCollision point of collision from the previous widget
* @param globalCollisionTrees Array of collision trees of direct colliding spaces
* @param index index of insertion if a collision node is of different direction from it ' s parent
* @param emptySpaces current number of emptySpaces it ' s parent ancestors encountered while reflowed
* @param shouldResize if the reflowed widgets can be reflowed
2022-04-20 07:26:35 +00:00
* @param globalProcessedNodes cache to make sure to not generate a tree for the same space in the getCollision Tree
2022-04-01 14:57:03 +00:00
* @returns movement map of current collision tree node
* /
2022-01-13 13:21:57 +00:00
function getMovementMapHelper (
collisionTree : CollisionTree ,
movementMap : ReflowedSpaceMap ,
dimensions = { X : 0 , Y : 0 } ,
gridProps : GridProps ,
direction : ReflowDirection ,
2022-04-01 14:57:03 +00:00
accessors : CollisionAccessors ,
prevWidgetDistance : number ,
2022-01-13 13:21:57 +00:00
distanceBeforeCollision = 0 ,
2022-04-01 14:57:03 +00:00
globalCollisionTrees : CollisionTree [ ] ,
index : number ,
emptySpaces = 0 ,
2022-01-13 13:21:57 +00:00
shouldResize : boolean ,
2022-04-20 07:26:35 +00:00
globalProcessedNodes : CollisionTreeCache ,
2022-01-13 13:21:57 +00:00
) {
let maxOccupiedSpace = 0 ,
2022-11-23 09:48:23 +00:00
occupiedLength = 0 ,
2022-04-01 14:57:03 +00:00
currentEmptySpaces = emptySpaces ;
2022-01-13 13:21:57 +00:00
if ( collisionTree . children && ! isEmpty ( collisionTree . children ) ) {
const childNodes = Object . values ( collisionTree . children ) ;
for ( const childNode of childNodes ) {
2022-04-01 14:57:03 +00:00
if ( childNode . direction !== direction ) {
globalCollisionTrees . splice ( index + 1 , 0 , childNode ) ;
continue ;
}
2022-01-13 13:21:57 +00:00
const nextEmptySpaces =
emptySpaces +
2022-04-01 14:57:03 +00:00
Math . abs ( prevWidgetDistance - childNode [ accessors . oppositeDirection ] ) ;
2022-01-13 13:21:57 +00:00
2022-04-20 07:26:35 +00:00
let {
2022-01-13 13:21:57 +00:00
currentEmptySpaces : childEmptySpaces ,
2022-11-23 09:48:23 +00:00
occupiedLength : currentOccupiedLength ,
2022-01-13 13:21:57 +00:00
occupiedSpace ,
2022-04-20 07:26:35 +00:00
shouldProcessNode ,
} = checkProcessNodeForTree ( childNode , globalProcessedNodes ) ;
//process the nodes if either one is undefined
if (
shouldProcessNode ||
2022-11-23 09:48:23 +00:00
currentOccupiedLength === undefined ||
2022-04-20 07:26:35 +00:00
occupiedSpace === undefined ||
childEmptySpaces === undefined
) {
const movementVariables = getMovementMapHelper (
childNode ,
movementMap ,
dimensions ,
gridProps ,
direction ,
accessors ,
childNode [ accessors . direction ] ,
distanceBeforeCollision ,
globalCollisionTrees ,
index ,
nextEmptySpaces ,
shouldResize ,
globalProcessedNodes ,
) ;
//initialize if undefined
if ( ! globalProcessedNodes [ childNode . id ] ) {
globalProcessedNodes [ childNode . id ] = { } ;
}
//add value to cache
globalProcessedNodes [ childNode . id ] [ childNode . direction ] = {
value : childNode.collidingValue ,
2022-11-23 09:48:23 +00:00
occupiedLength : movementVariables.occupiedLength ,
2022-04-20 07:26:35 +00:00
occupiedSpace : movementVariables.occupiedSpace ,
currentEmptySpaces : movementVariables.currentEmptySpaces ,
} ;
//set current values
shouldProcessNode = false ;
2022-11-23 09:48:23 +00:00
currentOccupiedLength = movementVariables . occupiedLength ;
2022-04-20 07:26:35 +00:00
occupiedSpace = movementVariables . occupiedSpace ;
childEmptySpaces = movementVariables . currentEmptySpaces ;
}
2022-01-13 13:21:57 +00:00
if ( maxOccupiedSpace < occupiedSpace ) {
currentEmptySpaces = childEmptySpaces ;
}
2022-04-01 14:57:03 +00:00
//maxOccupiedSpace is the maximum dimension that is occupied by all the spaces above it in the tree
2022-01-13 13:21:57 +00:00
maxOccupiedSpace = Math . max ( maxOccupiedSpace , occupiedSpace || 0 ) ;
2022-11-23 09:48:23 +00:00
// occupiedLength is the sum of minimum occupied lengths of all spaces between collidingSpace and the edge of canvas,
2022-04-01 14:57:03 +00:00
//useful to calculate resized dimensions for spaces colliding with boundaries
2022-11-23 09:48:23 +00:00
occupiedLength = Math . max ( occupiedLength , currentOccupiedLength ) ;
2022-01-13 13:21:57 +00:00
}
} else {
if ( direction === ReflowDirection . RIGHT ) {
currentEmptySpaces +=
GridDefaults . DEFAULT_GRID_COLUMNS - collisionTree . right ;
} else if ( direction !== ReflowDirection . BOTTOM ) {
currentEmptySpaces += collisionTree [ accessors . direction ] ;
}
}
2022-04-01 14:57:03 +00:00
//It calculates mainly the X, Y, width, height of the spaces to transform the existing spaces
2022-01-13 13:21:57 +00:00
const getSpaceMovement = accessors . isHorizontal
? getHorizontalSpaceMovement
: getVerticalSpaceMovement ;
const movementObj = getSpaceMovement (
collisionTree ,
gridProps ,
direction ,
maxOccupiedSpace ,
2022-11-23 09:48:23 +00:00
occupiedLength ,
2022-01-13 13:21:57 +00:00
distanceBeforeCollision ,
emptySpaces ,
currentEmptySpaces ,
dimensions ,
shouldResize ,
) ;
if (
2022-04-01 14:57:03 +00:00
// If a value already exists in the movement map,
// this method is used to check if the current one should override the existing movement values of the space
2022-01-13 13:21:57 +00:00
! shouldReplaceOldMovement (
movementMap [ collisionTree . id ] ,
movementObj ,
direction ,
)
) {
2022-04-01 14:57:03 +00:00
const { isHorizontal } = getAccessor ( direction ) ;
return isHorizontal
? {
occupiedSpace :
( movementMap [ collisionTree . id ] . horizontalMaxOccupiedSpace || 0 ) +
collisionTree [ accessors . parallelMax ] -
collisionTree [ accessors . parallelMin ] ,
2022-11-23 09:48:23 +00:00
occupiedLength :
2022-11-27 17:12:00 +00:00
( movementMap [ collisionTree . id ] . horizontalOccupiedLength || 0 ) +
HORIZONTAL_RESIZE_LIMIT ,
2022-04-01 14:57:03 +00:00
currentEmptySpaces :
( movementMap [ collisionTree . id ] . horizontalEmptySpaces as number ) ||
0 ,
}
: {
occupiedSpace :
( movementMap [ collisionTree . id ] . verticalMaxOccupiedSpace || 0 ) +
collisionTree [ accessors . parallelMax ] -
collisionTree [ accessors . parallelMin ] ,
2022-11-23 09:48:23 +00:00
occupiedLength :
2022-11-27 17:12:00 +00:00
( movementMap [ collisionTree . id ] . verticalOccupiedLength || 0 ) +
( collisionTree . fixedHeight && accessors . directionIndicator < 0
? collisionTree . fixedHeight
: VERTICAL_RESIZE_LIMIT ) ,
2022-04-01 14:57:03 +00:00
currentEmptySpaces :
( movementMap [ collisionTree . id ] . verticalEmptySpaces as number ) || 0 ,
} ;
2022-01-13 13:21:57 +00:00
}
2022-04-01 14:57:03 +00:00
movementMap [ collisionTree . id ] = {
. . . movementMap [ collisionTree . id ] ,
. . . movementObj ,
} ;
2022-01-13 13:21:57 +00:00
return {
occupiedSpace :
maxOccupiedSpace +
collisionTree [ accessors . parallelMax ] -
collisionTree [ accessors . parallelMin ] ,
2022-11-23 09:48:23 +00:00
occupiedLength :
occupiedLength +
( accessors . isHorizontal
? HORIZONTAL_RESIZE_LIMIT
: collisionTree . fixedHeight && accessors . directionIndicator < 0
? collisionTree . fixedHeight
: VERTICAL_RESIZE_LIMIT ) ,
2022-01-13 13:21:57 +00:00
currentEmptySpaces ,
} ;
}
2022-04-01 14:57:03 +00:00
/ * *
* get movement of the collision tree node in horizontal orientation
* @param collisionTree space and it ' s colliding spaces in a tree structure
* @param gridProps properties of the canvas ' s grid
* @param direction ReflowDirection , direction of reflow of the colliding space
* @param maxOccupiedSpace dimension of all the spaces that were occupied
2022-11-23 09:48:23 +00:00
* @param occupiedLength is the sum of minimum occupied lengths of all spaces between collidingSpace and the edge of canvas
2022-04-01 14:57:03 +00:00
* @param distanceBeforeCollision point of collision from the previous widget
* @param emptySpaces total number of emptySpaces it ' s parent ancestors encountered while reflowed
* @param currentEmptySpaces current number of emptySpaces this node encountered
* @param shouldResize if the reflowed widgets can be reflowed
* @param delta X and Y distance from the original new space positions
* @param shouldResize if the reflowed widgets can be reflowed
* @returns movement of the collision tree node in horizontal orientation
* /
export function getHorizontalSpaceMovement (
2022-01-13 13:21:57 +00:00
collisionTree : CollisionTree ,
gridProps : GridProps ,
direction : ReflowDirection ,
maxOccupiedSpace : number ,
2022-11-23 09:48:23 +00:00
occupiedLength : number ,
2022-01-13 13:21:57 +00:00
distanceBeforeCollision : number ,
emptySpaces : number ,
currentEmptySpaces : number ,
{ X } : Delta ,
shouldResize : boolean ,
) {
2022-04-01 14:57:03 +00:00
//maxX is the maximum leeway left for the space before it cannot move anymore in the X axis
2022-01-13 13:21:57 +00:00
const maxX = getMaxX (
collisionTree ,
gridProps ,
direction ,
2022-11-23 09:48:23 +00:00
occupiedLength ,
2022-01-13 13:21:57 +00:00
maxOccupiedSpace ,
shouldResize ,
) ;
2022-04-01 14:57:03 +00:00
const width = getReflowedDimension (
2022-01-13 13:21:57 +00:00
collisionTree ,
direction ,
X ,
maxX ,
distanceBeforeCollision ,
gridProps . parentColumnSpace ,
emptySpaces ,
HORIZONTAL_RESIZE_LIMIT ,
shouldResize ,
) ;
const spaceMovement = {
X : getReflowDistance (
collisionTree ,
direction ,
maxX ,
distanceBeforeCollision ,
width ,
emptySpaces ,
gridProps . parentColumnSpace ,
) ,
2022-04-01 14:57:03 +00:00
dimensionXBeforeCollision : distanceBeforeCollision ,
directionX : direction ,
2022-01-13 13:21:57 +00:00
maxX ,
width ,
2022-04-01 14:57:03 +00:00
horizontalEmptySpaces : currentEmptySpaces ,
2022-11-23 09:48:23 +00:00
horizontalOccupiedLength : occupiedLength ,
2022-04-01 14:57:03 +00:00
horizontalMaxOccupiedSpace : maxOccupiedSpace ,
2022-01-13 13:21:57 +00:00
} ;
return spaceMovement ;
}
2022-04-01 14:57:03 +00:00
/ * *
* get movement of the collision tree node in vertical orientation
* @param collisionTree space and it ' s colliding spaces in a tree structure
* @param gridProps properties of the canvas ' s grid
* @param direction ReflowDirection , direction of reflow of the colliding space
* @param maxOccupiedSpace dimension of all the spaces that were occupied
2022-11-23 09:48:23 +00:00
* @param occupiedLength is the sum of minimum occupied lengths of all spaces between collidingSpace and the edge of canvas
2022-04-01 14:57:03 +00:00
* @param distanceBeforeCollision point of collision from the previous widget
* @param emptySpaces total number of emptySpaces it ' s parent ancestors encountered while reflowed
* @param currentEmptySpaces current number of emptySpaces this node encountered
* @param shouldResize if the reflowed widgets can be reflowed
* @param delta X and Y distance from the original new space positions
* @param shouldResize if the reflowed widgets can be reflowed
* @returns movement of the collision tree node in vertical orientation
* /
export function getVerticalSpaceMovement (
2022-01-13 13:21:57 +00:00
collisionTree : CollisionTree ,
gridProps : GridProps ,
direction : ReflowDirection ,
maxOccupiedSpace : number ,
2022-11-23 09:48:23 +00:00
occupiedLength : number ,
2022-01-13 13:21:57 +00:00
distanceBeforeCollision : number ,
emptySpaces : number ,
currentEmptySpaces : number ,
{ Y } : Delta ,
shouldResize : boolean ,
) {
2022-04-01 14:57:03 +00:00
//maxY is the maximum leeway left for the space before it cannot move anymore in the Y axis
2022-01-13 13:21:57 +00:00
const maxY = getMaxY (
collisionTree ,
gridProps ,
direction ,
2022-11-23 09:48:23 +00:00
occupiedLength ,
2022-01-13 13:21:57 +00:00
maxOccupiedSpace ,
shouldResize ,
) ;
2022-04-01 14:57:03 +00:00
const height = getReflowedDimension (
2022-01-13 13:21:57 +00:00
collisionTree ,
direction ,
Y ,
maxY ,
distanceBeforeCollision ,
gridProps . parentRowSpace ,
emptySpaces ,
VERTICAL_RESIZE_LIMIT ,
shouldResize ,
) ;
const spaceMovement = {
Y : getReflowDistance (
collisionTree ,
direction ,
maxY ,
distanceBeforeCollision ,
height ,
emptySpaces ,
gridProps . parentRowSpace ,
true ,
) ,
2022-04-01 14:57:03 +00:00
dimensionYBeforeCollision : distanceBeforeCollision ,
directionY : direction ,
2022-01-13 13:21:57 +00:00
maxY ,
height ,
2022-04-01 14:57:03 +00:00
verticalEmptySpaces : currentEmptySpaces ,
2022-11-23 09:48:23 +00:00
verticalOccupiedLength : occupiedLength ,
2022-04-01 14:57:03 +00:00
verticalMaxOccupiedSpace : maxOccupiedSpace ,
2022-01-13 13:21:57 +00:00
} ;
return spaceMovement ;
}
2022-04-01 14:57:03 +00:00
/ * *
* to get movement variable to determine the limit of all the new Space Positions ,
* MovementVariables are intermediatory variables to calculate the actual movement Limits of each dragging / resizing space
*
* @param newSpacePositionsMap new / c u r r e n t p o s i t i o n s m a p o f t h e s p a c e / b l o c k
2022-11-23 09:48:23 +00:00
* @param directionalVariables information required to calculate limits such as occupiedLength , emptySpaces of new space positions
2022-04-01 14:57:03 +00:00
* @param delta X and Y distance from original positions
* @param gridProps properties of the canvas ' s grid
* @param shouldResize boolean to indicate if colliding spaces should resize
* @returns movement variable to determine the limit of all the new Space Positions
* /
function getMovementVariables (
newSpacePositionsMap : SpaceMap ,
directionalVariables : DirectionalVariables ,
delta : Delta ,
gridProps : GridProps ,
shouldResize : boolean ,
) {
const newSpacePositionIds = Object . keys ( directionalVariables ) ;
const movementVariablesMap : SpaceMovementMap = { } ;
for ( const newSpacePositionId of newSpacePositionIds ) {
if ( ! newSpacePositionsMap [ newSpacePositionId ] ) continue ;
const movementVariables = directionalVariables [ newSpacePositionId ] ;
const directionalKeys = Object . keys ( movementVariables ) ;
const directionalMovements : DirectionalMovement [ ] = [ ] ;
for ( const directionKey of directionalKeys ) {
const [
2022-11-23 09:48:23 +00:00
staticOccupiedLength ,
2022-04-01 14:57:03 +00:00
maxOccupiedSpace ,
accessors ,
reflowDirection ,
] = movementVariables [ directionKey ] ;
const maxMethod = accessors . isHorizontal ? getMaxX : getMaxY ;
const gridDistance = accessors . isHorizontal
? gridProps . parentColumnSpace
: gridProps . parentRowSpace ;
const coordinateKey = accessors . isHorizontal ? "X" : "Y" ;
const maxMovement =
maxMethod (
newSpacePositionsMap [ newSpacePositionId ] as CollisionTree ,
gridProps ,
reflowDirection ,
2022-11-23 09:48:23 +00:00
staticOccupiedLength ,
2022-04-01 14:57:03 +00:00
maxOccupiedSpace ,
shouldResize ,
) +
delta [ coordinateKey ] +
accessors . directionIndicator * gridDistance ;
directionalMovements . push ( {
maxMovement ,
directionalIndicator : accessors.directionIndicator ,
coordinateKey ,
isHorizontal : accessors.isHorizontal ,
} ) ;
}
movementVariablesMap [ newSpacePositionId ] = directionalMovements ;
}
return movementVariablesMap ;
}