@zag-js/rect-utils 0.39.0 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +147 -79
- package/dist/index.d.ts +147 -79
- package/dist/index.js +391 -72
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +381 -72
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/affine-transform.ts +177 -0
- package/src/align.ts +11 -44
- package/src/clamp.ts +26 -0
- package/src/closest.ts +5 -14
- package/src/compass.ts +25 -0
- package/src/constrain.ts +18 -0
- package/src/contains.ts +2 -2
- package/src/distance.ts +4 -3
- package/src/equality.ts +13 -0
- package/src/from-element.ts +3 -1
- package/src/from-points.ts +2 -2
- package/src/from-range.ts +2 -1
- package/src/from-rotation.ts +2 -2
- package/src/from-window.ts +2 -1
- package/src/index.ts +5 -1
- package/src/intersection.ts +2 -2
- package/src/operations.ts +2 -2
- package/src/polygon.ts +15 -1
- package/src/rect.ts +28 -18
- package/src/resize.ts +106 -0
- package/src/types.ts +50 -3
- package/src/union.ts +5 -17
- package/src/get-polygon.ts +0 -15
package/src/intersection.ts
CHANGED
package/src/operations.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createRect
|
|
2
|
-
import type { Point, RectInset, SymmetricRectInset } from "./types"
|
|
1
|
+
import { createRect } from "./rect"
|
|
2
|
+
import type { Point, Rect, RectInset, SymmetricRectInset } from "./types"
|
|
3
3
|
|
|
4
4
|
export const isSymmetric = (v: any): v is SymmetricRectInset => "dx" in v || "dy" in v
|
|
5
5
|
|
package/src/polygon.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createRect, getRectCorners } from "./rect"
|
|
2
|
+
import type { Point, RectInit } from "./types"
|
|
3
|
+
|
|
4
|
+
export function getElementPolygon(rectValue: RectInit, placement: string) {
|
|
5
|
+
const rect = createRect(rectValue)
|
|
6
|
+
const { top, right, left, bottom } = getRectCorners(rect)
|
|
7
|
+
const [base] = placement.split("-")
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
top: [left, top, right, bottom],
|
|
11
|
+
right: [top, right, bottom, left],
|
|
12
|
+
bottom: [top, left, bottom, right],
|
|
13
|
+
left: [right, top, left, bottom],
|
|
14
|
+
}[base]
|
|
15
|
+
}
|
|
2
16
|
|
|
3
17
|
export function isPointInPolygon(polygon: Point[], point: Point) {
|
|
4
18
|
const { x, y } = point
|
package/src/rect.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import type { RectEdge,
|
|
1
|
+
import type { Point, Rect, RectEdge, RectInit } from "./types"
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/* -----------------------------------------------------------------------------
|
|
4
|
+
* Point
|
|
5
|
+
* -----------------------------------------------------------------------------*/
|
|
4
6
|
|
|
5
|
-
export
|
|
7
|
+
export const createPoint = (x: number, y: number) => ({ x, y })
|
|
8
|
+
|
|
9
|
+
export const subtractPoints = (a: Point, b: Point) => createPoint(a.x - b.x, a.y - b.y)
|
|
10
|
+
export const addPoints = (a: Point, b: Point) => createPoint(a.x + b.x, a.y + b.y)
|
|
11
|
+
|
|
12
|
+
export function isPoint(v: any): v is Point {
|
|
13
|
+
return Reflect.has(v, "x") && Reflect.has(v, "y")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* -----------------------------------------------------------------------------
|
|
17
|
+
* Rect
|
|
18
|
+
* -----------------------------------------------------------------------------*/
|
|
19
|
+
|
|
20
|
+
export function createRect(r: RectInit): Rect {
|
|
6
21
|
const { x, y, width, height } = r
|
|
7
22
|
const midX = x + width / 2
|
|
8
23
|
const midY = y + height / 2
|
|
@@ -17,32 +32,27 @@ export function createRect(r: RectValue) {
|
|
|
17
32
|
maxY: y + height,
|
|
18
33
|
midX,
|
|
19
34
|
midY,
|
|
20
|
-
center:
|
|
35
|
+
center: createPoint(midX, midY),
|
|
21
36
|
}
|
|
22
37
|
}
|
|
23
38
|
|
|
24
|
-
export type Rect = ReturnType<typeof createRect>
|
|
25
|
-
|
|
26
|
-
const hasProp = <T extends string>(obj: any, prop: T): obj is Record<T, any> =>
|
|
27
|
-
Object.prototype.hasOwnProperty.call(obj, prop)
|
|
28
|
-
|
|
29
39
|
export function isRect(v: any): v is Rect {
|
|
30
|
-
return
|
|
40
|
+
return Reflect.has(v, "x") && Reflect.has(v, "y") && Reflect.has(v, "width") && Reflect.has(v, "height")
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
export function getRectCenters(v: Rect) {
|
|
34
|
-
const top =
|
|
35
|
-
const right =
|
|
36
|
-
const bottom =
|
|
37
|
-
const left =
|
|
44
|
+
const top = createPoint(v.midX, v.minY)
|
|
45
|
+
const right = createPoint(v.maxX, v.midY)
|
|
46
|
+
const bottom = createPoint(v.midX, v.maxY)
|
|
47
|
+
const left = createPoint(v.minX, v.midY)
|
|
38
48
|
return { top, right, bottom, left }
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
export function getRectCorners(v: Rect) {
|
|
42
|
-
const top =
|
|
43
|
-
const right =
|
|
44
|
-
const bottom =
|
|
45
|
-
const left =
|
|
52
|
+
const top = createPoint(v.minX, v.minY)
|
|
53
|
+
const right = createPoint(v.maxX, v.minY)
|
|
54
|
+
const bottom = createPoint(v.maxX, v.maxY)
|
|
55
|
+
const left = createPoint(v.minX, v.maxY)
|
|
46
56
|
return { top, right, bottom, left }
|
|
47
57
|
}
|
|
48
58
|
|
package/src/resize.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { AffineTransform } from "./affine-transform"
|
|
2
|
+
import { compassDirectionMap, oppositeDirectionMap, type CompassDirection } from "./compass"
|
|
3
|
+
import type { Point, Rect, RectInit, ScalingOptions } from "./types"
|
|
4
|
+
|
|
5
|
+
const { sign, abs, min } = Math
|
|
6
|
+
|
|
7
|
+
function getRectExtentPoint(rect: Rect, direction: CompassDirection) {
|
|
8
|
+
const { minX, minY, maxX, maxY, midX, midY } = rect
|
|
9
|
+
const x = direction.includes("w") ? minX : direction.includes("e") ? maxX : midX
|
|
10
|
+
const y = direction.includes("n") ? minY : direction.includes("s") ? maxY : midY
|
|
11
|
+
return { x, y }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getOppositeDirection(direction: CompassDirection) {
|
|
15
|
+
return oppositeDirectionMap[direction]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resizeRect(rect: Rect, offset: Point, direction: CompassDirection, opts: ScalingOptions) {
|
|
19
|
+
const { scalingOriginMode, lockAspectRatio } = opts
|
|
20
|
+
|
|
21
|
+
const extent = getRectExtentPoint(rect, direction)
|
|
22
|
+
|
|
23
|
+
const oppositeDirection = getOppositeDirection(direction)
|
|
24
|
+
const oppositeExtent = getRectExtentPoint(rect, oppositeDirection)
|
|
25
|
+
|
|
26
|
+
if (scalingOriginMode === "center") {
|
|
27
|
+
offset = { x: offset.x * 2, y: offset.y * 2 }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const newExtent = {
|
|
31
|
+
x: extent.x + offset.x,
|
|
32
|
+
y: extent.y + offset.y,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const multiplier = {
|
|
36
|
+
x: compassDirectionMap[direction].x * 2 - 1,
|
|
37
|
+
y: compassDirectionMap[direction].y * 2 - 1,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const newSize = {
|
|
41
|
+
width: newExtent.x - oppositeExtent.x,
|
|
42
|
+
height: newExtent.y - oppositeExtent.y,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const scaleX = (multiplier.x * newSize.width) / rect.width
|
|
46
|
+
const scaleY = (multiplier.y * newSize.height) / rect.height
|
|
47
|
+
|
|
48
|
+
const largestMagnitude = abs(scaleX) > abs(scaleY) ? scaleX : scaleY
|
|
49
|
+
|
|
50
|
+
const scale = lockAspectRatio
|
|
51
|
+
? { x: largestMagnitude, y: largestMagnitude }
|
|
52
|
+
: {
|
|
53
|
+
x: extent.x === oppositeExtent.x ? 1 : scaleX,
|
|
54
|
+
y: extent.y === oppositeExtent.y ? 1 : scaleY,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (extent.y === oppositeExtent.y) {
|
|
58
|
+
scale.y = abs(scale.y)
|
|
59
|
+
} else if (sign(scale.y) !== sign(scaleY)) {
|
|
60
|
+
scale.y *= -1
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (extent.x === oppositeExtent.x) {
|
|
64
|
+
scale.x = abs(scale.x)
|
|
65
|
+
} else if (sign(scale.x) !== sign(scaleX)) {
|
|
66
|
+
scale.x *= -1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
switch (scalingOriginMode) {
|
|
70
|
+
case "extent":
|
|
71
|
+
return transformRect(rect, AffineTransform.scale(scale.x, scale.y, oppositeExtent), false)
|
|
72
|
+
case "center":
|
|
73
|
+
return transformRect(
|
|
74
|
+
rect,
|
|
75
|
+
AffineTransform.scale(scale.x, scale.y, {
|
|
76
|
+
x: rect.midX,
|
|
77
|
+
y: rect.midY,
|
|
78
|
+
}),
|
|
79
|
+
false,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function createRectFromPoints(initialPoint: Point, finalPoint: Point, normalized: boolean = true): RectInit {
|
|
85
|
+
if (normalized) {
|
|
86
|
+
return {
|
|
87
|
+
x: min(finalPoint.x, initialPoint.x),
|
|
88
|
+
y: min(finalPoint.y, initialPoint.y),
|
|
89
|
+
width: abs(finalPoint.x - initialPoint.x),
|
|
90
|
+
height: abs(finalPoint.y - initialPoint.y),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
x: initialPoint.x,
|
|
96
|
+
y: initialPoint.y,
|
|
97
|
+
width: finalPoint.x - initialPoint.x,
|
|
98
|
+
height: finalPoint.y - initialPoint.y,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function transformRect(rect: Rect, transform: AffineTransform, normalized = true): RectInit {
|
|
103
|
+
const p1 = transform.applyTo({ x: rect.minX, y: rect.minY })
|
|
104
|
+
const p2 = transform.applyTo({ x: rect.maxX, y: rect.maxY })
|
|
105
|
+
return createRectFromPoints(p1, p2, normalized)
|
|
106
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
/* -----------------------------------------------------------------------------
|
|
2
|
+
* Basic
|
|
3
|
+
* -----------------------------------------------------------------------------*/
|
|
2
4
|
|
|
3
|
-
export
|
|
5
|
+
export interface Point {
|
|
4
6
|
x: number
|
|
5
7
|
y: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Size {
|
|
6
11
|
width: number
|
|
7
12
|
height: number
|
|
8
13
|
}
|
|
9
14
|
|
|
15
|
+
export interface Bounds {
|
|
16
|
+
minX: number
|
|
17
|
+
midX: number
|
|
18
|
+
maxX: number
|
|
19
|
+
minY: number
|
|
20
|
+
midY: number
|
|
21
|
+
maxY: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CenterPoint {
|
|
25
|
+
center: Point
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RectInit extends Point, Size {}
|
|
29
|
+
|
|
30
|
+
export interface Rect extends Point, Size, Bounds, CenterPoint {}
|
|
31
|
+
|
|
32
|
+
/* -----------------------------------------------------------------------------
|
|
33
|
+
* Edge and Side
|
|
34
|
+
* -----------------------------------------------------------------------------*/
|
|
35
|
+
|
|
10
36
|
export type RectSide = "top" | "right" | "bottom" | "left"
|
|
11
37
|
|
|
12
38
|
export type RectPoint =
|
|
@@ -42,4 +68,25 @@ export type RectCenters = Record<RectCenter, Point> & {
|
|
|
42
68
|
|
|
43
69
|
export type RectInset = Partial<Record<RectSide, number>>
|
|
44
70
|
|
|
45
|
-
export
|
|
71
|
+
export interface SymmetricRectInset {
|
|
72
|
+
dx?: number
|
|
73
|
+
dy?: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ScalingOptions {
|
|
77
|
+
scalingOriginMode: "center" | "extent"
|
|
78
|
+
lockAspectRatio: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* -----------------------------------------------------------------------------
|
|
82
|
+
* Alignment
|
|
83
|
+
* -----------------------------------------------------------------------------*/
|
|
84
|
+
|
|
85
|
+
export interface AlignOptions {
|
|
86
|
+
h: HAlign
|
|
87
|
+
v: VAlign
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type HAlign = "left-inside" | "left-outside" | "center" | "right-inside" | "right-outside"
|
|
91
|
+
|
|
92
|
+
export type VAlign = "top-inside" | "top-outside" | "center" | "bottom-inside" | "bottom-outside"
|
package/src/union.ts
CHANGED
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
import { getRectFromPoints } from "./from-points"
|
|
2
|
-
import type { Rect } from "./
|
|
2
|
+
import type { Rect } from "./types"
|
|
3
3
|
|
|
4
4
|
const { min, max } = Math
|
|
5
5
|
|
|
6
6
|
export function union(...rs: Rect[]): Rect {
|
|
7
7
|
const pMin = {
|
|
8
|
-
x: min.
|
|
9
|
-
|
|
10
|
-
rs.map((r) => r.minX),
|
|
11
|
-
),
|
|
12
|
-
y: min.apply(
|
|
13
|
-
Math,
|
|
14
|
-
rs.map((r) => r.minY),
|
|
15
|
-
),
|
|
8
|
+
x: min(...rs.map((r) => r.minX)),
|
|
9
|
+
y: min(...rs.map((r) => r.minY)),
|
|
16
10
|
}
|
|
17
11
|
const pMax = {
|
|
18
|
-
x: max.
|
|
19
|
-
|
|
20
|
-
rs.map((r) => r.maxX),
|
|
21
|
-
),
|
|
22
|
-
y: max.apply(
|
|
23
|
-
Math,
|
|
24
|
-
rs.map((r) => r.maxY),
|
|
25
|
-
),
|
|
12
|
+
x: max(...rs.map((r) => r.maxX)),
|
|
13
|
+
y: max(...rs.map((r) => r.maxY)),
|
|
26
14
|
}
|
|
27
15
|
return getRectFromPoints(pMin, pMax)
|
|
28
16
|
}
|
package/src/get-polygon.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { createRect, getRectCorners } from "./rect"
|
|
2
|
-
import type { RectValue } from "./types"
|
|
3
|
-
|
|
4
|
-
export function getElementPolygon(rectValue: RectValue, placement: string) {
|
|
5
|
-
const rect = createRect(rectValue)
|
|
6
|
-
const { top, right, left, bottom } = getRectCorners(rect)
|
|
7
|
-
const [base] = placement.split("-")
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
top: [left, top, right, bottom],
|
|
11
|
-
right: [top, right, bottom, left],
|
|
12
|
-
bottom: [top, left, bottom, right],
|
|
13
|
-
left: [right, top, left, bottom],
|
|
14
|
-
}[base]
|
|
15
|
-
}
|