-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor geometry code out of getNearestBlockIndex (#24715)
- Loading branch information
Showing
3 changed files
with
240 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/** | ||
* A string representing the name of an edge. | ||
* | ||
* @typedef {'top'|'right'|'bottom'|'left'} WPEdgeName | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} WPPoint | ||
* @property {number} x The horizontal position. | ||
* @property {number} y The vertical position. | ||
*/ | ||
|
||
/** | ||
* Given a point, a DOMRect and the name of an edge, returns the distance to | ||
* that edge of the rect. | ||
* | ||
* This function works for edges that are horizontal or vertical (e.g. not | ||
* rotated), the following terms are used so that the function works in both | ||
* orientations: | ||
* | ||
* - Forward, meaning the axis running horizontally when an edge is vertical | ||
* and vertically when an edge is horizontal. | ||
* - Lateral, meaning the axis running vertically when an edge is vertical | ||
* and horizontally when an edge is horizontal. | ||
* | ||
* @param {WPPoint} point The point to measure distance from. | ||
* @param {DOMRect} rect A DOM Rect containing edge positions. | ||
* @param {WPEdgeName} edge The edge to measure to. | ||
*/ | ||
export function getDistanceFromPointToEdge( point, rect, edge ) { | ||
const isHorizontal = edge === 'top' || edge === 'bottom'; | ||
const { x, y } = point; | ||
const pointLateralPosition = isHorizontal ? x : y; | ||
const pointForwardPosition = isHorizontal ? y : x; | ||
const edgeStart = isHorizontal ? rect.left : rect.top; | ||
const edgeEnd = isHorizontal ? rect.right : rect.bottom; | ||
const edgeForwardPosition = rect[ edge ]; | ||
|
||
// Measure the straight line distance to the edge of the rect, when the | ||
// point is adjacent to the edge. | ||
// Else, if the point is positioned diagonally to the edge of the rect, | ||
// measure diagonally to the nearest corner that the edge meets. | ||
let edgeLateralPosition; | ||
if ( | ||
pointLateralPosition >= edgeStart && | ||
pointLateralPosition <= edgeEnd | ||
) { | ||
edgeLateralPosition = pointLateralPosition; | ||
} else if ( pointLateralPosition < edgeEnd ) { | ||
edgeLateralPosition = edgeStart; | ||
} else { | ||
edgeLateralPosition = edgeEnd; | ||
} | ||
|
||
return Math.sqrt( | ||
( pointLateralPosition - edgeLateralPosition ) ** 2 + | ||
( pointForwardPosition - edgeForwardPosition ) ** 2 | ||
); | ||
} | ||
|
||
/** | ||
* Given a point, a DOMRect and a list of allowed edges returns the name of and | ||
* distance to the nearest edge. | ||
* | ||
* @param {WPPoint} point The point to measure distance from. | ||
* @param {DOMRect} rect A DOM Rect containing edge positions. | ||
* @param {WPEdgeName[]} allowedEdges A list of the edges included in the | ||
* calculation. Defaults to all edges. | ||
* | ||
* @return {[number, string]} An array where the first value is the distance | ||
* and a second is the edge name. | ||
*/ | ||
export function getDistanceToNearestEdge( | ||
point, | ||
rect, | ||
allowedEdges = [ 'top', 'bottom', 'left', 'right' ] | ||
) { | ||
let candidateDistance; | ||
let candidateEdge; | ||
|
||
allowedEdges.forEach( ( edge ) => { | ||
const distance = getDistanceFromPointToEdge( point, rect, edge ); | ||
|
||
if ( candidateDistance === undefined || distance < candidateDistance ) { | ||
candidateDistance = distance; | ||
candidateEdge = edge; | ||
} | ||
} ); | ||
|
||
return [ candidateDistance, candidateEdge ]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { getDistanceFromPointToEdge, getDistanceToNearestEdge } from '../math'; | ||
|
||
describe( 'getDistanceFromPointToEdge', () => { | ||
it( 'calculates the horizontal straight line distance when the point is adjacent to a vertical edge', () => { | ||
const point = { x: 0, y: 2 }; | ||
const rect = { | ||
top: 0, | ||
bottom: 4, | ||
left: 2, | ||
}; | ||
expect( getDistanceFromPointToEdge( point, rect, 'left' ) ).toBe( 2 ); | ||
} ); | ||
|
||
it( 'calculates the vertical straight line distance when the point is adjacent to a horizontal edge', () => { | ||
const point = { x: 2, y: 0 }; | ||
const rect = { | ||
top: 2, | ||
left: 0, | ||
right: 4, | ||
}; | ||
expect( getDistanceFromPointToEdge( point, rect, 'top' ) ).toBe( 2 ); | ||
} ); | ||
|
||
it( 'calculates the distance to the nearest corner that the edge forms when the point is not adjacent to a horizontal edge', () => { | ||
const point = { x: 0, y: 0 }; | ||
const rect = { | ||
top: 1, | ||
left: 1, | ||
bottom: 4, | ||
}; | ||
const distance = getDistanceFromPointToEdge( point, rect, 'left' ); | ||
const fixedDistance = distance.toFixed( 2 ); | ||
expect( fixedDistance ).toBe( '1.41' ); | ||
} ); | ||
|
||
it( 'calculates the distance to the nearest corner that the edge forms when the point is not adjacent to a vertical edge', () => { | ||
const point = { x: 0, y: 0 }; | ||
const rect = { | ||
top: 1, | ||
left: 1, | ||
right: 4, | ||
}; | ||
const distance = getDistanceFromPointToEdge( point, rect, 'top' ); | ||
const fixedDistance = distance.toFixed( 2 ); | ||
expect( fixedDistance ).toBe( '1.41' ); | ||
} ); | ||
} ); | ||
|
||
describe( 'getDistanceToNearestEdge', () => { | ||
it( 'returns the correct distance to the top edge, when it is the closest edge', () => { | ||
const point = { x: 3, y: 0 }; | ||
const rect = { | ||
top: 2, | ||
right: 4, | ||
bottom: 4, | ||
left: 2, | ||
}; | ||
expect( getDistanceToNearestEdge( point, rect ) ).toEqual( [ | ||
2, | ||
'top', | ||
] ); | ||
} ); | ||
|
||
it( 'returns the correct distance to the left edge, when it is the closest edge', () => { | ||
const point = { x: 0, y: 3 }; | ||
const rect = { | ||
top: 2, | ||
right: 4, | ||
bottom: 4, | ||
left: 2, | ||
}; | ||
expect( getDistanceToNearestEdge( point, rect ) ).toEqual( [ | ||
2, | ||
'left', | ||
] ); | ||
} ); | ||
|
||
it( 'returns the correct distance to the right edge, when it is the closest edge', () => { | ||
const point = { x: 6, y: 3 }; | ||
const rect = { | ||
top: 2, | ||
right: 4, | ||
bottom: 4, | ||
left: 2, | ||
}; | ||
expect( getDistanceToNearestEdge( point, rect ) ).toEqual( [ | ||
2, | ||
'right', | ||
] ); | ||
} ); | ||
|
||
it( 'returns the correct distance to the bottom edge, when it is the closest edge', () => { | ||
const point = { x: 3, y: 6 }; | ||
const rect = { | ||
top: 2, | ||
right: 4, | ||
bottom: 4, | ||
left: 2, | ||
}; | ||
expect( getDistanceToNearestEdge( point, rect ) ).toEqual( [ | ||
2, | ||
'bottom', | ||
] ); | ||
} ); | ||
|
||
it( 'allows a list of edges to be provided as the third argument', () => { | ||
// Position is closer to right edge, but right edge is not an allowed edge. | ||
const point = { x: 4, y: 2.5 }; | ||
const rect = { | ||
top: 2, | ||
right: 4, | ||
bottom: 4, | ||
left: 2, | ||
}; | ||
expect( | ||
getDistanceToNearestEdge( point, rect, [ 'top', 'bottom' ] ) | ||
).toEqual( [ 0.5, 'top' ] ); | ||
} ); | ||
} ); |