Merge pull request #86 from tldraw/utils-refactor
Split vectors and intersections into new packages
This commit is contained in:
commit
9f309bb485
58 changed files with 1801 additions and 880 deletions
17
.vscode/tasks.json
vendored
17
.vscode/tasks.json
vendored
|
@ -2,17 +2,12 @@
|
|||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "tsc",
|
||||
"type": "shell",
|
||||
"command": "./node_modules/.bin/tsc",
|
||||
"args": ["--noEmit"],
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"echo": false,
|
||||
"focus": false,
|
||||
"panel": "dedicated"
|
||||
},
|
||||
"problemMatcher": "$tsc-watch"
|
||||
"label": "Check for type errors",
|
||||
"type": "typescript",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"option": "watch",
|
||||
"problemMatcher": ["$tsc-watch"],
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
10
package.json
10
package.json
|
@ -13,7 +13,9 @@
|
|||
"packages/core",
|
||||
"packages/tldraw",
|
||||
"packages/dev",
|
||||
"packages/www"
|
||||
"packages/www",
|
||||
"packages/vec",
|
||||
"packages/intersect"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
|
@ -22,7 +24,7 @@
|
|||
"start": "lerna run start:pre && lerna run start --stream --parallel",
|
||||
"start:www": "yarn build:packages && lerna run start --parallel & cd packages/www && yarn dev",
|
||||
"build": "yarn build:packages && cd packages/www && yarn build",
|
||||
"build:packages": "cd packages/core && yarn build && cd ../tldraw && yarn build",
|
||||
"build:packages": "cd packages/vec && yarn build && cd ../intersect && yarn build && cd ../core && yarn build && cd ../tldraw && yarn build",
|
||||
"publish:patch": "yarn build:packages && lerna publish patch",
|
||||
"docs": "lerna run docs --stream",
|
||||
"docs:watch": "lerna run docs:watch --stream"
|
||||
|
@ -105,11 +107,15 @@
|
|||
},
|
||||
"testEnvironment": "jsdom",
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/packages/vec/dist/",
|
||||
"<rootDir>/packages/intersect/dist/",
|
||||
"<rootDir>/packages/core/dist/",
|
||||
"<rootDir>/packages/tldraw/dist/",
|
||||
"<rootDir>/packages/tldraw/test-utils/"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"@tldraw/vec": "<rootDir>/packages/vec/src",
|
||||
"@tldraw/intersect": "<rootDir>/packages/intersect/src",
|
||||
"@tldraw/core": "<rootDir>/packages/core/src",
|
||||
"@tldraw/tldraw": "<rootDir>/packages/tldraw/src",
|
||||
"\\~(.*)": "<rootDir>/packages/tldraw/src/$1",
|
||||
|
|
|
@ -55,7 +55,9 @@
|
|||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tldraw/vec": "^0.0.86",
|
||||
"@tldraw/intersect": "^0.0.86",
|
||||
"@use-gesture/react": "^10.0.0-beta.24"
|
||||
},
|
||||
"gitHead": "55da8880eb3d8ab5fb62b5eb7853065922c95dcf"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable */
|
||||
const fs = require('fs')
|
||||
const esbuild = require('esbuild')
|
||||
const { gzip } = require('zlib')
|
||||
|
||||
const name = process.env.npm_package_name || ''
|
||||
|
||||
|
@ -25,9 +26,10 @@ async function main() {
|
|||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.build.json',
|
||||
external: ['react', 'react-dom'],
|
||||
metafile: true,
|
||||
})
|
||||
|
||||
esbuild.buildSync({
|
||||
const esmResult = esbuild.buildSync({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/esm',
|
||||
minify: true,
|
||||
|
@ -38,9 +40,23 @@ async function main() {
|
|||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
external: ['react', 'react-dom'],
|
||||
metafile: true,
|
||||
})
|
||||
|
||||
console.log(`✔ ${name}: Built package.`)
|
||||
let esmSize = 0
|
||||
Object.values(esmResult.metafile.outputs).forEach((output) => {
|
||||
esmSize += output.bytes
|
||||
})
|
||||
|
||||
fs.readFile('./dist/esm/index.js', (_err, data) => {
|
||||
gzip(data, (_err, result) => {
|
||||
console.log(
|
||||
`✔ ${name}: Built package. ${(esmSize / 1000).toFixed(2)}kb (${(
|
||||
result.length / 1000
|
||||
).toFixed(2)}kb minified)`
|
||||
)
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(`× ${name}: Build failed due to an error.`)
|
||||
console.log(e)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react'
|
||||
import { Vec } from '+utils'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import type { TLShape } from '+types'
|
||||
import { Handle } from './handle'
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ import type {
|
|||
TLBinding,
|
||||
TLBounds,
|
||||
} from '+types'
|
||||
import { Utils, Vec } from '+utils'
|
||||
import { Utils } from '+utils'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
|
||||
function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
|
||||
shape: T,
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import * as React from 'react'
|
||||
import { useTLContext } from './useTLContext'
|
||||
import Utils, { Vec } from '+utils'
|
||||
import { useGesture } from '@use-gesture/react'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
|
||||
// Capture zoom gestures (pinches, wheels and pans)
|
||||
export function useZoomEvents<T extends Element>(ref: React.RefObject<T>) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type React from 'react'
|
||||
import type { TLKeyboardInfo, TLPointerInfo } from './types'
|
||||
import { Vec, Utils } from './utils'
|
||||
import { Utils } from './utils'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
|
||||
const DOUBLE_CLICK_DURATION = 250
|
||||
|
||||
|
@ -354,14 +355,11 @@ export class Inputs {
|
|||
e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent,
|
||||
offset = [0, 0]
|
||||
): number[] {
|
||||
return [
|
||||
Number(e.clientX.toPrecision(5)) - offset[0],
|
||||
Number(e.clientY.toPrecision(5)) - offset[1],
|
||||
]
|
||||
return [+e.clientX.toFixed(2) - offset[0], +e.clientY.toFixed(2) - offset[1]]
|
||||
}
|
||||
|
||||
static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) {
|
||||
return 'pressure' in e ? Number(e.pressure.toPrecision(5)) || 0.5 : 0.5
|
||||
return 'pressure' in e ? +e.pressure.toFixed(2) || 0.5 : 0.5
|
||||
}
|
||||
|
||||
static commandKey(): string {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import * as React from 'react'
|
||||
import { TLShapeUtil, TLShape, TLShapeProps, TLBounds, TLRenderInfo, TLTransformInfo } from '+types'
|
||||
import Utils, { Intersect } from '+utils'
|
||||
import Utils from '+utils'
|
||||
|
||||
export interface BoxShape extends TLShape {
|
||||
size: number[]
|
||||
|
@ -62,15 +62,6 @@ export class Box extends TLShapeUtil<BoxShape, SVGGElement> {
|
|||
return Utils.pointInBounds(point, this.getBounds(shape))
|
||||
}
|
||||
|
||||
hitTestBounds(shape: BoxShape, bounds: TLBounds) {
|
||||
const rotatedCorners = Utils.getRotatedCorners(this.getBounds(shape), shape.rotation)
|
||||
|
||||
return (
|
||||
rotatedCorners.every((point) => Utils.pointInBounds(point, bounds)) ||
|
||||
Intersect.polyline.bounds(rotatedCorners, bounds).length > 0
|
||||
)
|
||||
}
|
||||
|
||||
transform(shape: BoxShape, bounds: TLBounds, _info: TLTransformInfo<BoxShape>): BoxShape {
|
||||
return { ...shape, point: [bounds.minX, bounds.minY] }
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* --------------------- Primary -------------------- */
|
||||
|
||||
import { Intersect, Vec } from '+utils'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import React, { ForwardedRef } from 'react'
|
||||
import { intersectPolylineBounds } from '@tldraw/intersect'
|
||||
|
||||
export type Patch<T> = Partial<{ [P in keyof T]: T | Partial<T> | Patch<T[P]> }>
|
||||
|
||||
|
@ -426,7 +427,7 @@ export abstract class TLShapeUtil<T extends TLShape, E extends Element> {
|
|||
point[1] < bounds.minY ||
|
||||
point[1] > bounds.maxY
|
||||
)
|
||||
) || Intersect.polyline.bounds(corners, bounds).length > 0
|
||||
) || intersectPolylineBounds(corners, bounds).length > 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { Utils } from './utils'
|
||||
export { Intersect } from './intersect'
|
||||
export { Utils } from './utils'
|
||||
export { Svg } from './svg'
|
||||
export { Vec } from './vec'
|
||||
|
||||
export default Utils
|
||||
|
|
|
@ -1,680 +0,0 @@
|
|||
import type { TLBounds, TLIntersection } from '../types'
|
||||
import { Vec } from './vec'
|
||||
import { Utils } from './utils'
|
||||
|
||||
/* ----------------- Start Copy Here ---------------- */
|
||||
|
||||
function getIntersection(message: string, ...points: number[][]): TLIntersection {
|
||||
const didIntersect = points.length > 0
|
||||
return { didIntersect, message, points }
|
||||
}
|
||||
|
||||
export class Intersect {
|
||||
static ray = {
|
||||
// Intersect a ray with a ray.
|
||||
ray(p0: number[], n0: number[], p1: number[], n1: number[]): TLIntersection {
|
||||
const dx = p1[0] - p0[0]
|
||||
const dy = p1[1] - p0[1]
|
||||
const det = n1[0] * n0[1] - n1[1] * n0[0]
|
||||
const u = (dy * n1[0] - dx * n1[1]) / det
|
||||
const v = (dy * n0[0] - dx * n0[1]) / det
|
||||
if (u < 0 || v < 0) return getIntersection('miss')
|
||||
|
||||
const m0 = n0[1] / n0[0]
|
||||
const m1 = n1[1] / n1[0]
|
||||
const b0 = p0[1] - m0 * p0[0]
|
||||
const b1 = p1[1] - m1 * p1[0]
|
||||
const x = (b1 - b0) / (m0 - m1)
|
||||
const y = m0 * x + b0
|
||||
|
||||
return Number.isFinite(x)
|
||||
? getIntersection('intersection', [x, y])
|
||||
: getIntersection('parallel')
|
||||
},
|
||||
|
||||
// Interseg a ray with a line segment.
|
||||
lineSegment(origin: number[], direction: number[], a1: number[], a2: number[]): TLIntersection {
|
||||
const [x, y] = origin
|
||||
const [dx, dy] = direction
|
||||
const [x1, y1] = a1
|
||||
const [x2, y2] = a2
|
||||
|
||||
if (dy / dx !== (y2 - y1) / (x2 - x1)) {
|
||||
const d = dx * (y2 - y1) - dy * (x2 - x1)
|
||||
if (d !== 0) {
|
||||
const r = ((y - y1) * (x2 - x1) - (x - x1) * (y2 - y1)) / d
|
||||
const s = ((y - y1) * dx - (x - x1) * dy) / d
|
||||
if (r >= 0 && s >= 0 && s <= 1) {
|
||||
return getIntersection('intersection', [x + r * dx, y + r * dy])
|
||||
}
|
||||
}
|
||||
}
|
||||
return getIntersection('no intersection')
|
||||
},
|
||||
|
||||
// Intersect a ray with a rectangle.
|
||||
rectangle(
|
||||
origin: number[],
|
||||
direction: number[],
|
||||
point: number[],
|
||||
size: number[],
|
||||
rotation = 0
|
||||
): TLIntersection[] {
|
||||
return Intersect.rectangle.ray(point, size, rotation, origin, direction)
|
||||
},
|
||||
|
||||
// Intersect a ray with an ellipse.
|
||||
ellipse(
|
||||
origin: number[],
|
||||
direction: number[],
|
||||
center: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation: number
|
||||
): TLIntersection {
|
||||
const a1 = origin
|
||||
const a2 = Vec.mul(direction, 999999999)
|
||||
return Intersect.lineSegment.ellipse(a1, a2, center, rx, ry, rotation)
|
||||
},
|
||||
|
||||
// Intersect a ray with a bounding box.
|
||||
bounds(
|
||||
origin: number[],
|
||||
direction: number[],
|
||||
bounds: TLBounds,
|
||||
rotation = 0
|
||||
): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.ray.rectangle(origin, direction, [minX, minY], [width, height], rotation)
|
||||
},
|
||||
}
|
||||
|
||||
static lineSegment = {
|
||||
// Intersect a line segment with a ray.
|
||||
ray(a1: number[], a2: number[], origin: number[], direction: number[]): TLIntersection {
|
||||
return Intersect.ray.lineSegment(origin, direction, a1, a2)
|
||||
},
|
||||
|
||||
// Intersect a line segment with a line segment.
|
||||
lineSegment(a1: number[], a2: number[], b1: number[], b2: number[]): TLIntersection {
|
||||
const AB = Vec.sub(a1, b1)
|
||||
const BV = Vec.sub(b2, b1)
|
||||
const AV = Vec.sub(a2, a1)
|
||||
|
||||
const ua_t = BV[0] * AB[1] - BV[1] * AB[0]
|
||||
const ub_t = AV[0] * AB[1] - AV[1] * AB[0]
|
||||
const u_b = BV[1] * AV[0] - BV[0] * AV[1]
|
||||
|
||||
if (ua_t === 0 || ub_t === 0) {
|
||||
return getIntersection('coincident')
|
||||
}
|
||||
|
||||
if (u_b === 0) {
|
||||
return getIntersection('parallel')
|
||||
}
|
||||
|
||||
if (u_b !== 0) {
|
||||
const ua = ua_t / u_b
|
||||
const ub = ub_t / u_b
|
||||
if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
|
||||
return getIntersection('intersection', Vec.add(a1, Vec.mul(AV, ua)))
|
||||
}
|
||||
}
|
||||
|
||||
return getIntersection('no intersection')
|
||||
},
|
||||
|
||||
// Intersect a line segment with a rectangle
|
||||
rectangle(a1: number[], a2: number[], point: number[], size: number[]): TLIntersection[] {
|
||||
return Intersect.rectangle.lineSegment(point, size, a1, a2)
|
||||
},
|
||||
|
||||
// Intersect a line segment with an arc.
|
||||
arc(
|
||||
a1: number[],
|
||||
a2: number[],
|
||||
center: number[],
|
||||
radius: number,
|
||||
start: number[],
|
||||
end: number[]
|
||||
): TLIntersection {
|
||||
const sa = Vec.angle(center, start)
|
||||
const ea = Vec.angle(center, end)
|
||||
const ellipseTest = Intersect.ellipse.lineSegment(center, radius, radius, 0, a1, a2)
|
||||
|
||||
if (!ellipseTest.didIntersect) return getIntersection('No intersection')
|
||||
|
||||
const points = ellipseTest.points.filter((point) =>
|
||||
Utils.isAngleBetween(sa, ea, Vec.angle(center, point))
|
||||
)
|
||||
|
||||
if (points.length === 0) {
|
||||
return getIntersection('No intersection')
|
||||
}
|
||||
|
||||
return getIntersection('intersection', ...points)
|
||||
},
|
||||
|
||||
// Intersect a line segment with a circle.
|
||||
circle(a1: number[], a2: number[], c: number[], r: number): TLIntersection {
|
||||
const a = (a2[0] - a1[0]) * (a2[0] - a1[0]) + (a2[1] - a1[1]) * (a2[1] - a1[1])
|
||||
const b = 2 * ((a2[0] - a1[0]) * (a1[0] - c[0]) + (a2[1] - a1[1]) * (a1[1] - c[1]))
|
||||
const cc =
|
||||
c[0] * c[0] +
|
||||
c[1] * c[1] +
|
||||
a1[0] * a1[0] +
|
||||
a1[1] * a1[1] -
|
||||
2 * (c[0] * a1[0] + c[1] * a1[1]) -
|
||||
r * r
|
||||
|
||||
const deter = b * b - 4 * a * cc
|
||||
|
||||
if (deter < 0) {
|
||||
return getIntersection('outside')
|
||||
}
|
||||
|
||||
if (deter === 0) {
|
||||
return getIntersection('tangent')
|
||||
}
|
||||
|
||||
const e = Math.sqrt(deter)
|
||||
const u1 = (-b + e) / (2 * a)
|
||||
const u2 = (-b - e) / (2 * a)
|
||||
if ((u1 < 0 || u1 > 1) && (u2 < 0 || u2 > 1)) {
|
||||
if ((u1 < 0 && u2 < 0) || (u1 > 1 && u2 > 1)) {
|
||||
return getIntersection('outside')
|
||||
} else {
|
||||
return getIntersection('inside')
|
||||
}
|
||||
}
|
||||
|
||||
const results: number[][] = []
|
||||
if (0 <= u1 && u1 <= 1) results.push(Vec.lrp(a1, a2, u1))
|
||||
if (0 <= u2 && u2 <= 1) results.push(Vec.lrp(a1, a2, u2))
|
||||
|
||||
return getIntersection('intersection', ...results)
|
||||
},
|
||||
|
||||
// Intersect a line segment with an ellipse.
|
||||
ellipse(
|
||||
a1: number[],
|
||||
a2: number[],
|
||||
center: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation = 0
|
||||
): TLIntersection {
|
||||
// If the ellipse or line segment are empty, return no tValues.
|
||||
if (rx === 0 || ry === 0 || Vec.isEqual(a1, a2)) {
|
||||
return getIntersection('No intersection')
|
||||
}
|
||||
|
||||
// Get the semimajor and semiminor axes.
|
||||
rx = rx < 0 ? rx : -rx
|
||||
ry = ry < 0 ? ry : -ry
|
||||
|
||||
// Rotate points and translate so the ellipse is centered at the origin.
|
||||
a1 = Vec.sub(Vec.rotWith(a1, center, -rotation), center)
|
||||
a2 = Vec.sub(Vec.rotWith(a2, center, -rotation), center)
|
||||
|
||||
// Calculate the quadratic parameters.
|
||||
const diff = Vec.sub(a2, a1)
|
||||
|
||||
const A = (diff[0] * diff[0]) / rx / rx + (diff[1] * diff[1]) / ry / ry
|
||||
const B = (2 * a1[0] * diff[0]) / rx / rx + (2 * a1[1] * diff[1]) / ry / ry
|
||||
const C = (a1[0] * a1[0]) / rx / rx + (a1[1] * a1[1]) / ry / ry - 1
|
||||
|
||||
// Make a list of t values (normalized points on the line where intersections occur).
|
||||
const tValues: number[] = []
|
||||
|
||||
// Calculate the discriminant.
|
||||
const discriminant = B * B - 4 * A * C
|
||||
|
||||
if (discriminant === 0) {
|
||||
// One real solution.
|
||||
tValues.push(-B / 2 / A)
|
||||
} else if (discriminant > 0) {
|
||||
const root = Math.sqrt(discriminant)
|
||||
// Two real solutions.
|
||||
tValues.push((-B + root) / 2 / A)
|
||||
tValues.push((-B - root) / 2 / A)
|
||||
}
|
||||
|
||||
// Filter to only points that are on the segment.
|
||||
// Solve for points, then counter-rotate points.
|
||||
const points = tValues
|
||||
.filter((t) => t >= 0 && t <= 1)
|
||||
.map((t) => Vec.add(center, Vec.add(a1, Vec.mul(Vec.sub(a2, a1), t))))
|
||||
.map((p) => Vec.rotWith(p, center, rotation))
|
||||
|
||||
return getIntersection('intersection', ...points)
|
||||
},
|
||||
|
||||
// Intersect a line segment with a bounding box.
|
||||
bounds(a1: number[], a2: number[], bounds: TLBounds): TLIntersection[] {
|
||||
return Intersect.bounds.lineSegment(bounds, a1, a2)
|
||||
},
|
||||
|
||||
// Intersect a line segment with a polyline
|
||||
polyline(a1: number[], a2: number[], points: number[][]): TLIntersection[] {
|
||||
const intersections: TLIntersection[] = []
|
||||
|
||||
for (let i = 1; i < points.length + 1; i++) {
|
||||
const int = Intersect.lineSegment.lineSegment(
|
||||
a1,
|
||||
a2,
|
||||
points[i - 1],
|
||||
points[i % points.length]
|
||||
)
|
||||
|
||||
if (int) {
|
||||
intersections.push(int)
|
||||
}
|
||||
}
|
||||
|
||||
return intersections
|
||||
},
|
||||
}
|
||||
|
||||
static rectangle = {
|
||||
// Intersect a rectangle with a ray.
|
||||
ray(
|
||||
point: number[],
|
||||
size: number[],
|
||||
rotation: number,
|
||||
origin: number[],
|
||||
direction: number[]
|
||||
): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point, size, rotation).reduce<
|
||||
TLIntersection[]
|
||||
>((acc, [message, [a1, a2]]) => {
|
||||
const intersection = Intersect.ray.lineSegment(origin, direction, a1, a2)
|
||||
|
||||
if (intersection) {
|
||||
acc.push(getIntersection(message, ...intersection.points))
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
|
||||
// Intersect a rectangle with a line segment.
|
||||
lineSegment(point: number[], size: number[], a1: number[], a2: number[]): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point, size).reduce<TLIntersection[]>(
|
||||
(acc, [message, [b1, b2]]) => {
|
||||
const intersection = Intersect.lineSegment.lineSegment(a1, a2, b1, b2)
|
||||
|
||||
if (intersection) {
|
||||
acc.push(getIntersection(message, ...intersection.points))
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
|
||||
// Intersect a rectangle with a rectangle.
|
||||
rectangle(
|
||||
point1: number[],
|
||||
size1: number[],
|
||||
point2: number[],
|
||||
size2: number[]
|
||||
): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point1, size1).reduce<TLIntersection[]>(
|
||||
(acc, [message, [a1, a2]]) => {
|
||||
const intersections = Intersect.rectangle.lineSegment(point2, size2, a1, a2)
|
||||
|
||||
acc.push(
|
||||
...intersections.map((int) =>
|
||||
getIntersection(`${message} ${int.message}`, ...int.points)
|
||||
)
|
||||
)
|
||||
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
|
||||
// Intersect a rectangle with an arc.
|
||||
arc(
|
||||
point: number[],
|
||||
size: number[],
|
||||
center: number[],
|
||||
radius: number,
|
||||
start: number[],
|
||||
end: number[]
|
||||
): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point, size).reduce<TLIntersection[]>(
|
||||
(acc, [message, [a1, a2]]) => {
|
||||
const intersection = Intersect.arc.lineSegment(center, radius, start, end, a1, a2)
|
||||
|
||||
if (intersection) {
|
||||
acc.push({ ...intersection, message })
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
|
||||
// Intersect a rectangle with a circle.
|
||||
circle(point: number[], size: number[], c: number[], r: number): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point, size).reduce<TLIntersection[]>(
|
||||
(acc, [message, [a1, a2]]) => {
|
||||
const intersection = Intersect.lineSegment.circle(a1, a2, c, r)
|
||||
|
||||
if (intersection) {
|
||||
acc.push({ ...intersection, message })
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
|
||||
// Intersect a rectangle with an ellipse.
|
||||
ellipse(
|
||||
point: number[],
|
||||
size: number[],
|
||||
c: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation = 0
|
||||
): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point, size).reduce<TLIntersection[]>(
|
||||
(acc, [message, [a1, a2]]) => {
|
||||
const intersection = Intersect.lineSegment.ellipse(a1, a2, c, rx, ry, rotation)
|
||||
|
||||
if (intersection) {
|
||||
acc.push({ ...intersection, message })
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
|
||||
// Intersect a rectangle with a bounding box.
|
||||
bounds(point: number[], size: number[], bounds: TLBounds): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.rectangle.rectangle(point, size, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
// Intersect a rectangle with a polyline
|
||||
polyline(point: number[], size: number[], points: number[][]): TLIntersection[] {
|
||||
const sideIntersections = Utils.getRectangleSides(point, size).reduce<TLIntersection[]>(
|
||||
(acc, [message, [a1, a2]]) => {
|
||||
const intersections = Intersect.lineSegment.polyline(a1, a2, points)
|
||||
|
||||
if (intersections.length > 0) {
|
||||
acc.push(getIntersection(message, ...intersections.flatMap((i) => i.points)))
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
return sideIntersections.filter((int) => int.didIntersect)
|
||||
},
|
||||
}
|
||||
|
||||
static arc = {
|
||||
// Intersect an arc with a line segment.
|
||||
lineSegment(
|
||||
center: number[],
|
||||
radius: number,
|
||||
start: number[],
|
||||
end: number[],
|
||||
a1: number[],
|
||||
a2: number[]
|
||||
): TLIntersection {
|
||||
return Intersect.lineSegment.arc(a1, a2, center, radius, start, end)
|
||||
},
|
||||
|
||||
// Intersect an arc with a rectangle.
|
||||
rectangle(
|
||||
center: number[],
|
||||
radius: number,
|
||||
start: number[],
|
||||
end: number[],
|
||||
point: number[],
|
||||
size: number[]
|
||||
): TLIntersection[] {
|
||||
return Intersect.rectangle.arc(point, size, center, radius, start, end)
|
||||
},
|
||||
|
||||
// Intersect an arc with a bounding box.
|
||||
bounds(
|
||||
center: number[],
|
||||
radius: number,
|
||||
start: number[],
|
||||
end: number[],
|
||||
bounds: TLBounds
|
||||
): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.arc.rectangle(center, radius, start, end, [minX, minY], [width, height])
|
||||
},
|
||||
}
|
||||
|
||||
static circle = {
|
||||
// Intersect a circle with a line segment.
|
||||
lineSegment(c: number[], r: number, a1: number[], a2: number[]): TLIntersection {
|
||||
return Intersect.lineSegment.circle(a1, a2, c, r)
|
||||
},
|
||||
|
||||
// Intersect a circle with a circle.
|
||||
circle(c1: number[], r1: number, c2: number[], r2: number): TLIntersection {
|
||||
let dx = c2[0] - c1[0],
|
||||
dy = c2[1] - c1[1]
|
||||
|
||||
const d = Math.sqrt(dx * dx + dy * dy),
|
||||
x = (d * d - r2 * r2 + r1 * r1) / (2 * d),
|
||||
y = Math.sqrt(r1 * r1 - x * x)
|
||||
|
||||
dx /= d
|
||||
dy /= d
|
||||
|
||||
return getIntersection(
|
||||
'intersection',
|
||||
[c1[0] + dx * x - dy * y, c1[1] + dy * x + dx * y],
|
||||
[c1[0] + dx * x + dy * y, c1[1] + dy * x - dx * y]
|
||||
)
|
||||
},
|
||||
|
||||
// Intersect a circle with a rectangle.
|
||||
rectangle(c: number[], r: number, point: number[], size: number[]): TLIntersection[] {
|
||||
return Intersect.rectangle.circle(point, size, c, r)
|
||||
},
|
||||
|
||||
// Intersect a circle with a bounding box.
|
||||
bounds(c: number[], r: number, bounds: TLBounds): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.circle.rectangle(c, r, [minX, minY], [width, height])
|
||||
},
|
||||
}
|
||||
|
||||
static ellipse = {
|
||||
// Intersect an ellipse with a ray.
|
||||
ray(
|
||||
center: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation: number,
|
||||
point: number[],
|
||||
direction: number[]
|
||||
): TLIntersection {
|
||||
return Intersect.ray.ellipse(point, direction, center, rx, ry, rotation)
|
||||
},
|
||||
|
||||
// Intersect an ellipse with a line segment.
|
||||
lineSegment(
|
||||
center: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation = 0,
|
||||
a1: number[],
|
||||
a2: number[]
|
||||
): TLIntersection {
|
||||
if (rx === ry) {
|
||||
return Intersect.lineSegment.circle(a1, a2, center, rx)
|
||||
}
|
||||
|
||||
return Intersect.lineSegment.ellipse(a1, a2, center, rx, ry, rotation)
|
||||
},
|
||||
|
||||
// Intersect an ellipse with a rectangle.
|
||||
rectangle(
|
||||
center: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation = 0,
|
||||
point: number[],
|
||||
size: number[]
|
||||
): TLIntersection[] {
|
||||
if (rx === ry) {
|
||||
return Intersect.rectangle.circle(point, size, center, rx)
|
||||
}
|
||||
|
||||
return Intersect.rectangle.ellipse(point, size, center, rx, ry, rotation)
|
||||
},
|
||||
|
||||
// Get an intersection between an ellipse and a second ellipse.
|
||||
// Adapted from https://gist.github.com/drawable/92792f59b6ff8869d8b1
|
||||
ellipse(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_c1: number[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_rx1: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_ry1: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_r1: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_c2: number[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_rx2: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_ry2: number,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_r2: number
|
||||
): TLIntersection {
|
||||
// TODO
|
||||
return getIntersection('no intersection')
|
||||
},
|
||||
|
||||
circle(
|
||||
c: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation: number,
|
||||
c2: number[],
|
||||
r2: number
|
||||
): TLIntersection {
|
||||
return Intersect.ellipse.ellipse(c, rx, ry, rotation, c2, r2, r2, 0)
|
||||
},
|
||||
|
||||
// Intersect an ellipse with a bounding box.
|
||||
bounds(
|
||||
c: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
rotation: number,
|
||||
bounds: TLBounds
|
||||
): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.ellipse.rectangle(c, rx, ry, rotation, [minX, minY], [width, height])
|
||||
},
|
||||
}
|
||||
|
||||
static bounds = {
|
||||
ray(bounds: TLBounds, origin: number[], direction: number[]): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.ray.rectangle(origin, direction, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
lineSegment(bounds: TLBounds, a1: number[], a2: number[]): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.lineSegment.rectangle(a1, a2, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
rectangle(bounds: TLBounds, point: number[], size: number[]): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.rectangle.rectangle(point, size, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
bounds(bounds1: TLBounds, bounds2: TLBounds): TLIntersection[] {
|
||||
return Intersect.rectangle.rectangle(
|
||||
[bounds1.minX, bounds1.minY],
|
||||
[bounds1.width, bounds1.height],
|
||||
[bounds2.minX, bounds2.minY],
|
||||
[bounds2.width, bounds2.height]
|
||||
)
|
||||
},
|
||||
|
||||
arc(
|
||||
bounds: TLBounds,
|
||||
center: number[],
|
||||
radius: number,
|
||||
start: number[],
|
||||
end: number[]
|
||||
): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.arc.rectangle(center, radius, start, end, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
circle(bounds: TLBounds, c: number[], r: number): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.circle.rectangle(c, r, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
ellipse(bounds: TLBounds, c: number[], rx: number, ry: number, rotation = 0): TLIntersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return Intersect.ellipse.rectangle(c, rx, ry, rotation, [minX, minY], [width, height])
|
||||
},
|
||||
|
||||
polyline(bounds: TLBounds, points: number[][]): TLIntersection[] {
|
||||
return Intersect.polyline.bounds(points, bounds)
|
||||
},
|
||||
}
|
||||
|
||||
static polyline = {
|
||||
// Intersect a polyline with a line segment.
|
||||
lineSegment(points: number[][], a1: number[], a2: number[]): TLIntersection[] {
|
||||
return Intersect.lineSegment.polyline(a1, a2, points)
|
||||
},
|
||||
|
||||
// Interesct a polyline with a rectangle.
|
||||
rectangle(points: number[][], point: number[], size: number[]): TLIntersection[] {
|
||||
return Intersect.rectangle.polyline(point, size, points)
|
||||
},
|
||||
|
||||
// Intersect a polyline with a bounding box.
|
||||
bounds(points: number[][], bounds: TLBounds): TLIntersection[] {
|
||||
return Intersect.rectangle.polyline(
|
||||
[bounds.minX, bounds.minY],
|
||||
[bounds.width, bounds.height],
|
||||
points
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default Intersect
|
|
@ -4,7 +4,7 @@
|
|||
/* eslint-disable no-redeclare */
|
||||
import type React from 'react'
|
||||
import { TLBezierCurveSegment, TLBounds, TLBoundsCorner, TLBoundsEdge } from '../types'
|
||||
import vec from './vec'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import './polyfills'
|
||||
import type { Patch } from '+index'
|
||||
|
||||
|
@ -204,10 +204,10 @@ export class Utils {
|
|||
|
||||
static getRectangleSides(point: number[], size: number[], rotation = 0): [string, number[][]][] {
|
||||
const center = [point[0] + size[0] / 2, point[1] + size[1] / 2]
|
||||
const tl = vec.rotWith(point, center, rotation)
|
||||
const tr = vec.rotWith(vec.add(point, [size[0], 0]), center, rotation)
|
||||
const br = vec.rotWith(vec.add(point, size), center, rotation)
|
||||
const bl = vec.rotWith(vec.add(point, [0, size[1]]), center, rotation)
|
||||
const tl = Vec.rotWith(point, center, rotation)
|
||||
const tr = Vec.rotWith(Vec.add(point, [size[0], 0]), center, rotation)
|
||||
const br = Vec.rotWith(Vec.add(point, size), center, rotation)
|
||||
const bl = Vec.rotWith(Vec.add(point, [0, size[1]]), center, rotation)
|
||||
|
||||
return [
|
||||
['top', [tl, tr]],
|
||||
|
@ -258,10 +258,10 @@ export class Utils {
|
|||
P: number[],
|
||||
side: number
|
||||
): number[] | null {
|
||||
const B = vec.lrp(C, P, 0.5)
|
||||
const r1 = vec.dist(C, B)
|
||||
const delta = vec.sub(B, C)
|
||||
const d = vec.len(delta)
|
||||
const B = Vec.lrp(C, P, 0.5)
|
||||
const r1 = Vec.dist(C, B)
|
||||
const delta = Vec.sub(B, C)
|
||||
const d = Vec.len(delta)
|
||||
|
||||
if (!(d <= r + r1 && d >= Math.abs(r - r1))) {
|
||||
return null
|
||||
|
@ -269,11 +269,11 @@ export class Utils {
|
|||
|
||||
const a = (r * r - r1 * r1 + d * d) / (2.0 * d)
|
||||
const n = 1 / d
|
||||
const p = vec.add(C, vec.mul(delta, a * n))
|
||||
const p = Vec.add(C, Vec.mul(delta, a * n))
|
||||
const h = Math.sqrt(r * r - a * a)
|
||||
const k = vec.mul(vec.per(delta), h * n)
|
||||
const k = Vec.mul(Vec.per(delta), h * n)
|
||||
|
||||
return side === 0 ? vec.add(p, k) : vec.sub(p, k)
|
||||
return side === 0 ? Vec.add(p, k) : Vec.sub(p, k)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,8 +292,8 @@ export class Utils {
|
|||
C1: number[],
|
||||
r1: number
|
||||
): number[][] | null {
|
||||
const a0 = vec.angle(C0, C1)
|
||||
const d = vec.dist(C0, C1)
|
||||
const a0 = Vec.angle(C0, C1)
|
||||
const d = Vec.dist(C0, C1)
|
||||
|
||||
// Circles are overlapping, no tangents
|
||||
if (d < Math.abs(r1 - r0)) {
|
||||
|
@ -319,8 +319,8 @@ export class Utils {
|
|||
* @param P The point.
|
||||
*/
|
||||
static getClosestPointOnCircle(C: number[], r: number, P: number[]): number[] {
|
||||
const v = vec.sub(C, P)
|
||||
return vec.sub(C, vec.mul(vec.div(v, vec.len(v)), r))
|
||||
const v = Vec.sub(C, P)
|
||||
return Vec.sub(C, Vec.mul(Vec.div(v, Vec.len(v)), r))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -411,7 +411,7 @@ export class Utils {
|
|||
* @param B
|
||||
*/
|
||||
static getSweep(C: number[], A: number[], B: number[]): number {
|
||||
return Utils.angleDelta(vec.angle(C, A), vec.angle(C, B))
|
||||
return Utils.angleDelta(Vec.angle(C, A), Vec.angle(C, B))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -766,7 +766,7 @@ export class Utils {
|
|||
const len = pts.length
|
||||
const res: number[][] = [] // results
|
||||
|
||||
let t1x: number, // tension vectors
|
||||
let t1x: number, // tension Vectors
|
||||
t2x: number,
|
||||
t1y: number,
|
||||
t2y: number,
|
||||
|
@ -871,7 +871,7 @@ export class Utils {
|
|||
* @returns
|
||||
*/
|
||||
static pointInCircle(A: number[], C: number[], r: number): boolean {
|
||||
return vec.dist(A, C) <= r
|
||||
return Vec.dist(A, C) <= r
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -887,7 +887,7 @@ export class Utils {
|
|||
rotation = rotation || 0
|
||||
const cos = Math.cos(rotation)
|
||||
const sin = Math.sin(rotation)
|
||||
const delta = vec.sub(A, C)
|
||||
const delta = Vec.sub(A, C)
|
||||
const tdx = cos * delta[0] + sin * delta[1]
|
||||
const tdy = sin * delta[0] - cos * delta[1]
|
||||
|
||||
|
@ -914,10 +914,10 @@ export class Utils {
|
|||
points.forEach((a, i) => {
|
||||
const b = points[(i + 1) % points.length]
|
||||
if (a[1] <= p[1]) {
|
||||
if (b[1] > p[1] && vec.cross(a, b, p) > 0) {
|
||||
if (b[1] > p[1] && Vec.cross(a, b, p) > 0) {
|
||||
wn += 1
|
||||
}
|
||||
} else if (b[1] <= p[1] && vec.cross(a, b, p) < 0) {
|
||||
} else if (b[1] <= p[1] && Vec.cross(a, b, p) < 0) {
|
||||
wn -= 1
|
||||
}
|
||||
})
|
||||
|
@ -1024,7 +1024,7 @@ export class Utils {
|
|||
|
||||
if (rotation !== 0) {
|
||||
return Utils.getBoundsFromPoints(
|
||||
points.map((pt) => vec.rotWith(pt, [(minX + maxX) / 2, (minY + maxY) / 2], rotation))
|
||||
points.map((pt) => Vec.rotWith(pt, [(minX + maxX) / 2, (minY + maxY) / 2], rotation))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1074,8 +1074,8 @@ export class Utils {
|
|||
* @param rotation
|
||||
*/
|
||||
static rotateBounds(bounds: TLBounds, center: number[], rotation: number): TLBounds {
|
||||
const [minX, minY] = vec.rotWith([bounds.minX, bounds.minY], center, rotation)
|
||||
const [maxX, maxY] = vec.rotWith([bounds.maxX, bounds.maxY], center, rotation)
|
||||
const [minX, minY] = Vec.rotWith([bounds.minX, bounds.minY], center, rotation)
|
||||
const [maxX, maxY] = Vec.rotWith([bounds.maxX, bounds.maxY], center, rotation)
|
||||
|
||||
return {
|
||||
minX,
|
||||
|
@ -1158,7 +1158,7 @@ export class Utils {
|
|||
[b.maxX, b.minY],
|
||||
[b.maxX, b.maxY],
|
||||
[b.minX, b.maxY],
|
||||
].map((point) => vec.rotWith(point, center, rotation))
|
||||
].map((point) => Vec.rotWith(point, center, rotation))
|
||||
}
|
||||
|
||||
static getTransformedBoundingBox(
|
||||
|
@ -1192,7 +1192,7 @@ export class Utils {
|
|||
|
||||
// Counter rotate the delta. This lets us make changes as if
|
||||
// the (possibly rotated) boxes were axis aligned.
|
||||
const [dx, dy] = vec.rot(delta, -rotation)
|
||||
const [dx, dy] = Vec.rot(delta, -rotation)
|
||||
|
||||
/*
|
||||
1. Delta
|
||||
|
@ -1299,67 +1299,67 @@ new box's aspect ratio matches the original aspect ratio.
|
|||
/*
|
||||
3. Rotation
|
||||
|
||||
If the bounds are rotated, get a vector from the rotated anchor
|
||||
If the bounds are rotated, get a Vector from the rotated anchor
|
||||
corner in the inital bounds to the rotated anchor corner in the
|
||||
result's bounds. Subtract this vector from the result's corners,
|
||||
result's bounds. Subtract this Vector from the result's corners,
|
||||
so that the two anchor points (initial and result) will be equal.
|
||||
*/
|
||||
|
||||
if (rotation % (Math.PI * 2) !== 0) {
|
||||
let cv = [0, 0]
|
||||
|
||||
const c0 = vec.med([ax0, ay0], [ax1, ay1])
|
||||
const c1 = vec.med([bx0, by0], [bx1, by1])
|
||||
const c0 = Vec.med([ax0, ay0], [ax1, ay1])
|
||||
const c1 = Vec.med([bx0, by0], [bx1, by1])
|
||||
|
||||
switch (handle) {
|
||||
case TLBoundsCorner.TopLeft: {
|
||||
cv = vec.sub(vec.rotWith([bx1, by1], c1, rotation), vec.rotWith([ax1, ay1], c0, rotation))
|
||||
cv = Vec.sub(Vec.rotWith([bx1, by1], c1, rotation), Vec.rotWith([ax1, ay1], c0, rotation))
|
||||
break
|
||||
}
|
||||
case TLBoundsCorner.TopRight: {
|
||||
cv = vec.sub(vec.rotWith([bx0, by1], c1, rotation), vec.rotWith([ax0, ay1], c0, rotation))
|
||||
cv = Vec.sub(Vec.rotWith([bx0, by1], c1, rotation), Vec.rotWith([ax0, ay1], c0, rotation))
|
||||
break
|
||||
}
|
||||
case TLBoundsCorner.BottomRight: {
|
||||
cv = vec.sub(vec.rotWith([bx0, by0], c1, rotation), vec.rotWith([ax0, ay0], c0, rotation))
|
||||
cv = Vec.sub(Vec.rotWith([bx0, by0], c1, rotation), Vec.rotWith([ax0, ay0], c0, rotation))
|
||||
break
|
||||
}
|
||||
case TLBoundsCorner.BottomLeft: {
|
||||
cv = vec.sub(vec.rotWith([bx1, by0], c1, rotation), vec.rotWith([ax1, ay0], c0, rotation))
|
||||
cv = Vec.sub(Vec.rotWith([bx1, by0], c1, rotation), Vec.rotWith([ax1, ay0], c0, rotation))
|
||||
break
|
||||
}
|
||||
case TLBoundsEdge.Top: {
|
||||
cv = vec.sub(
|
||||
vec.rotWith(vec.med([bx0, by1], [bx1, by1]), c1, rotation),
|
||||
vec.rotWith(vec.med([ax0, ay1], [ax1, ay1]), c0, rotation)
|
||||
cv = Vec.sub(
|
||||
Vec.rotWith(Vec.med([bx0, by1], [bx1, by1]), c1, rotation),
|
||||
Vec.rotWith(Vec.med([ax0, ay1], [ax1, ay1]), c0, rotation)
|
||||
)
|
||||
break
|
||||
}
|
||||
case TLBoundsEdge.Left: {
|
||||
cv = vec.sub(
|
||||
vec.rotWith(vec.med([bx1, by0], [bx1, by1]), c1, rotation),
|
||||
vec.rotWith(vec.med([ax1, ay0], [ax1, ay1]), c0, rotation)
|
||||
cv = Vec.sub(
|
||||
Vec.rotWith(Vec.med([bx1, by0], [bx1, by1]), c1, rotation),
|
||||
Vec.rotWith(Vec.med([ax1, ay0], [ax1, ay1]), c0, rotation)
|
||||
)
|
||||
break
|
||||
}
|
||||
case TLBoundsEdge.Bottom: {
|
||||
cv = vec.sub(
|
||||
vec.rotWith(vec.med([bx0, by0], [bx1, by0]), c1, rotation),
|
||||
vec.rotWith(vec.med([ax0, ay0], [ax1, ay0]), c0, rotation)
|
||||
cv = Vec.sub(
|
||||
Vec.rotWith(Vec.med([bx0, by0], [bx1, by0]), c1, rotation),
|
||||
Vec.rotWith(Vec.med([ax0, ay0], [ax1, ay0]), c0, rotation)
|
||||
)
|
||||
break
|
||||
}
|
||||
case TLBoundsEdge.Right: {
|
||||
cv = vec.sub(
|
||||
vec.rotWith(vec.med([bx0, by0], [bx0, by1]), c1, rotation),
|
||||
vec.rotWith(vec.med([ax0, ay0], [ax0, ay1]), c0, rotation)
|
||||
cv = Vec.sub(
|
||||
Vec.rotWith(Vec.med([bx0, by0], [bx0, by1]), c1, rotation),
|
||||
Vec.rotWith(Vec.med([ax0, ay0], [ax0, ay1]), c0, rotation)
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
;[bx0, by0] = vec.sub([bx0, by0], cv)
|
||||
;[bx1, by1] = vec.sub([bx1, by1], cv)
|
||||
;[bx0, by0] = Vec.sub([bx0, by0], cv)
|
||||
;[bx1, by1] = Vec.sub([bx1, by1], cv)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1500,10 +1500,10 @@ left past the initial left edge) then swap points on that axis.
|
|||
* @param rotation
|
||||
*/
|
||||
static getRotatedSize(size: number[], rotation: number): number[] {
|
||||
const center = vec.div(size, 2)
|
||||
const center = Vec.div(size, 2)
|
||||
|
||||
const points = [[0, 0], [size[0], 0], size, [0, size[1]]].map((point) =>
|
||||
vec.rotWith(point, center, rotation)
|
||||
Vec.rotWith(point, center, rotation)
|
||||
)
|
||||
|
||||
const bounds = Utils.getBoundsFromPoints(points)
|
||||
|
@ -1534,7 +1534,7 @@ left past the initial left edge) then swap points on that axis.
|
|||
*/
|
||||
static removeDuplicatePoints(points: number[][]) {
|
||||
return points.reduce<number[][]>((acc, pt, i) => {
|
||||
if (i === 0 || !vec.isEqual(pt, acc[i - 1])) {
|
||||
if (i === 0 || !Vec.isEqual(pt, acc[i - 1])) {
|
||||
acc.push(pt)
|
||||
}
|
||||
return acc
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "docs"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "./dist/types",
|
||||
"rootDir": "src",
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"+*": ["./*"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../../packages/intersect"
|
||||
},
|
||||
{ "path": "../../packages/vec" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"README.md",
|
||||
"src"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@tldraw/tldraw": "^0.0.86",
|
||||
"idb": "^6.1.2",
|
||||
|
@ -37,4 +38,4 @@
|
|||
"typescript": "4.2.3"
|
||||
},
|
||||
"gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6"
|
||||
}
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
"include": ["src"],
|
||||
"exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts", "dist"],
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
"composite": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
|
|
3
packages/intersect/README.md
Normal file
3
packages/intersect/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Dev Server
|
||||
|
||||
Dev server with fast refresh.
|
57
packages/intersect/package.json
Normal file
57
packages/intersect/package.json
Normal file
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"name": "@tldraw/intersect",
|
||||
"version": "0.0.86",
|
||||
"private": false,
|
||||
"description": "A tiny little drawing app (intersect)",
|
||||
"author": "@steveruizok",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tldraw/tldraw.git",
|
||||
"directory": "packages/intersect"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [],
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"main": "./dist/cjs/index.js",
|
||||
"module": "./dist/esm/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"typings": "./dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"start:pre": "node scripts/pre-dev && yarn types:pre",
|
||||
"start": "node scripts/dev & yarn types:dev",
|
||||
"build": "node scripts/build && yarn types:build",
|
||||
"types:pre": "tsc",
|
||||
"types:dev": "tsc --watch",
|
||||
"types:build": "tsc --project tsconfig.build.json",
|
||||
"lint": "eslint src/ --ext .ts,.tsx",
|
||||
"clean": "rm -rf dist",
|
||||
"ts-node": "ts-node",
|
||||
"docs": "typedoc",
|
||||
"docs:watch": "typedoc --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/preset-env": "^7.15.4",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.10",
|
||||
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
||||
"@typescript-eslint/parser": "^4.30.0",
|
||||
"esbuild": "^0.12.24",
|
||||
"eslint": "^7.32.0",
|
||||
"lerna": "^4.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typedoc": "^0.21.9",
|
||||
"typescript": "^4.4.2"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"dependencies": {
|
||||
"@tldraw/vec": "^0.0.86"
|
||||
},
|
||||
"gitHead": "55da8880eb3d8ab5fb62b5eb7853065922c95dcf"
|
||||
}
|
50
packages/intersect/scripts/build.js
Normal file
50
packages/intersect/scripts/build.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* eslint-disable */
|
||||
const fs = require('fs')
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
const name = process.env.npm_package_name || ''
|
||||
|
||||
async function main() {
|
||||
if (fs.existsSync('./dist')) {
|
||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
||||
if (e) {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
esbuild.buildSync({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/cjs',
|
||||
minify: true,
|
||||
bundle: true,
|
||||
format: 'cjs',
|
||||
target: 'es6',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.build.json',
|
||||
external: ['react', 'react-dom'],
|
||||
})
|
||||
|
||||
esbuild.buildSync({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/esm',
|
||||
minify: true,
|
||||
bundle: true,
|
||||
format: 'esm',
|
||||
target: 'es6',
|
||||
tsconfig: './tsconfig.build.json',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
external: ['react', 'react-dom'],
|
||||
})
|
||||
|
||||
console.log(`✔ ${name}: Built package.`)
|
||||
} catch (e) {
|
||||
console.log(`× ${name}: Build failed due to an error.`)
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
31
packages/intersect/scripts/dev.js
Normal file
31
packages/intersect/scripts/dev.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* eslint-disable */
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
const name = process.env.npm_package_name || ''
|
||||
|
||||
async function main() {
|
||||
esbuild.build({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/cjs',
|
||||
minify: false,
|
||||
bundle: true,
|
||||
format: 'cjs',
|
||||
target: 'es6',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.json',
|
||||
external: ['react', 'react-dom'],
|
||||
incremental: true,
|
||||
watch: {
|
||||
onRebuild(error) {
|
||||
if (error) {
|
||||
console.log(`× ${name}: An error in prevented the rebuild.`)
|
||||
return
|
||||
}
|
||||
console.log(`✔ ${name}: Rebuilt.`)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
28
packages/intersect/scripts/pre-dev.js
Normal file
28
packages/intersect/scripts/pre-dev.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* eslint-disable */
|
||||
const fs = require('fs')
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
async function main() {
|
||||
if (fs.existsSync('./dist')) {
|
||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
||||
if (e) {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
esbuild.build({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/cjs',
|
||||
minify: false,
|
||||
bundle: true,
|
||||
format: 'cjs',
|
||||
target: 'es6',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.json',
|
||||
external: ['react', 'react-dom'],
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
1147
packages/intersect/src/index.ts
Normal file
1147
packages/intersect/src/index.ts
Normal file
File diff suppressed because it is too large
Load diff
19
packages/intersect/tsconfig.build.json
Normal file
19
packages/intersect/tsconfig.build.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.test.tsx",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.spec.ts",
|
||||
"src/test",
|
||||
"dist",
|
||||
"docs"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"incremental": false,
|
||||
"declarationMap": false,
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
11
packages/intersect/tsconfig.json
Normal file
11
packages/intersect/tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "docs"],
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/types",
|
||||
"rootDir": "src",
|
||||
"baseUrl": "src"
|
||||
},
|
||||
"references": [{ "path": "../../packages/vec" }]
|
||||
}
|
|
@ -65,9 +65,11 @@
|
|||
"@radix-ui/react-tooltip": "^0.0.20",
|
||||
"@stitches/react": "^1.0.0",
|
||||
"@tldraw/core": "^0.0.86",
|
||||
"@tldraw/vec": "^0.0.86",
|
||||
"@tldraw/intersect": "^0.0.86",
|
||||
"perfect-freehand": "^0.5.3",
|
||||
"react-hotkeys-hook": "^3.4.0",
|
||||
"rko": "^0.5.25"
|
||||
},
|
||||
"gitHead": "55da8880eb3d8ab5fb62b5eb7853065922c95dcf"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable */
|
||||
const fs = require('fs')
|
||||
const esbuild = require('esbuild')
|
||||
const { gzip } = require('zlib')
|
||||
|
||||
const name = process.env.npm_package_name || ''
|
||||
|
||||
|
@ -25,9 +26,10 @@ async function main() {
|
|||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.json',
|
||||
external: ['react', 'react-dom'],
|
||||
metafile: true,
|
||||
})
|
||||
|
||||
esbuild.buildSync({
|
||||
const esmResult = esbuild.buildSync({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/esm',
|
||||
minify: true,
|
||||
|
@ -38,6 +40,22 @@ async function main() {
|
|||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
external: ['react', 'react-dom'],
|
||||
metafile: true,
|
||||
})
|
||||
|
||||
let esmSize = 0
|
||||
Object.values(esmResult.metafile.outputs).forEach((output) => {
|
||||
esmSize += output.bytes
|
||||
})
|
||||
|
||||
fs.readFile('./dist/esm/index.js', (_err, data) => {
|
||||
gzip(data, (_err, result) => {
|
||||
console.log(
|
||||
`✔ ${name}: Built package. ${(esmSize / 1000).toFixed(2)}kb (${(
|
||||
result.length / 1000
|
||||
).toFixed(2)}kb minified)`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
console.log(`✔ ${name}: Built package.`)
|
||||
|
|
|
@ -3,13 +3,13 @@ import {
|
|||
SVGContainer,
|
||||
TLBounds,
|
||||
Utils,
|
||||
Vec,
|
||||
TLTransformInfo,
|
||||
Intersect,
|
||||
TLHandle,
|
||||
TLPointerInfo,
|
||||
TLShapeProps,
|
||||
} from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
|
||||
import getStroke from 'perfect-freehand'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '~shape/shape-styles'
|
||||
import {
|
||||
|
@ -22,6 +22,14 @@ import {
|
|||
TLDrawShape,
|
||||
ArrowBinding,
|
||||
} from '~types'
|
||||
import {
|
||||
intersectArcBounds,
|
||||
intersectCircleCircle,
|
||||
intersectCircleLineSegment,
|
||||
intersectLineSegmentBounds,
|
||||
intersectRayBounds,
|
||||
intersectRayEllipse,
|
||||
} from '@tldraw/intersect'
|
||||
|
||||
export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGSVGElement> {
|
||||
type = TLDrawShapeType.Arrow as const
|
||||
|
@ -299,12 +307,12 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGSVGElement> {
|
|||
}
|
||||
|
||||
if (Vec.isEqual(Vec.med(start.point, end.point), bend.point)) {
|
||||
return Intersect.lineSegment.bounds(sp, ep, brushBounds).length > 0
|
||||
return intersectLineSegmentBounds(sp, ep, brushBounds).length > 0
|
||||
} else {
|
||||
const [cx, cy, r] = getCtp(shape)
|
||||
const cp = Vec.add(shape.point, [cx, cy])
|
||||
|
||||
return Intersect.arc.bounds(cp, r, sp, ep, brushBounds).length > 0
|
||||
return intersectArcBounds(cp, r, sp, ep, brushBounds).length > 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,15 +438,13 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGSVGElement> {
|
|||
const direction = Vec.uni(Vec.sub(Vec.add(anchor, shape.point), origin))
|
||||
|
||||
if ([TLDrawShapeType.Rectangle, TLDrawShapeType.Text].includes(target.type)) {
|
||||
let hits = Intersect.ray
|
||||
.bounds(origin, direction, intersectBounds, target.rotation)
|
||||
let hits = intersectRayBounds(origin, direction, intersectBounds, target.rotation)
|
||||
.filter((int) => int.didIntersect)
|
||||
.map((int) => int.points[0])
|
||||
.sort((a, b) => Vec.dist(a, origin) - Vec.dist(b, origin))
|
||||
|
||||
if (hits.length < 2) {
|
||||
hits = Intersect.ray
|
||||
.bounds(origin, Vec.neg(direction), intersectBounds)
|
||||
hits = intersectRayBounds(origin, Vec.neg(direction), intersectBounds)
|
||||
.filter((int) => int.didIntersect)
|
||||
.map((int) => int.points[0])
|
||||
.sort((a, b) => Vec.dist(a, origin) - Vec.dist(b, origin))
|
||||
|
@ -451,16 +457,14 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGSVGElement> {
|
|||
|
||||
handlePoint = Vec.sub(hits[0], shape.point)
|
||||
} else if (target.type === TLDrawShapeType.Ellipse) {
|
||||
const hits = Intersect.ray
|
||||
.ellipse(
|
||||
origin,
|
||||
direction,
|
||||
center,
|
||||
target.radius[0] + binding.distance,
|
||||
target.radius[1] + binding.distance,
|
||||
target.rotation || 0
|
||||
)
|
||||
.points.sort((a, b) => Vec.dist(a, origin) - Vec.dist(b, origin))
|
||||
const hits = intersectRayEllipse(
|
||||
origin,
|
||||
direction,
|
||||
center,
|
||||
target.radius[0] + binding.distance,
|
||||
target.radius[1] + binding.distance,
|
||||
target.rotation || 0
|
||||
).points.sort((a, b) => Vec.dist(a, origin) - Vec.dist(b, origin))
|
||||
|
||||
if (!hits[0]) {
|
||||
console.warn('No intersections')
|
||||
|
@ -701,7 +705,7 @@ function getCurvedArrowHeadPoints(
|
|||
r2: number,
|
||||
sweep: boolean
|
||||
) {
|
||||
const ints = Intersect.circle.circle(A, r1 * 0.618, C, r2).points
|
||||
const ints = intersectCircleCircle(A, r1 * 0.618, C, r2).points
|
||||
if (!ints) {
|
||||
console.warn('Could not find an intersection for the arrow head.')
|
||||
return { left: A, right: A }
|
||||
|
@ -714,7 +718,7 @@ function getCurvedArrowHeadPoints(
|
|||
}
|
||||
|
||||
function getStraightArrowHeadPoints(A: number[], B: number[], r: number) {
|
||||
const ints = Intersect.circle.lineSegment(A, r, A, B).points
|
||||
const ints = intersectCircleLineSegment(A, r, A, B).points
|
||||
if (!ints) {
|
||||
console.warn('Could not find an intersection for the arrow head.')
|
||||
return { left: A, right: A }
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { SVGContainer, TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import { SVGContainer, TLBounds, Utils, TLTransformInfo } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { intersectBoundsBounds, intersectBoundsPolyline } from '@tldraw/intersect'
|
||||
import getStroke, { getStrokePoints } from 'perfect-freehand'
|
||||
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
|
||||
import {
|
||||
|
@ -202,10 +204,10 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGSVGElement> {
|
|||
return (
|
||||
Utils.boundsContain(brushBounds, bounds) ||
|
||||
((Utils.boundsContain(bounds, brushBounds) ||
|
||||
Intersect.bounds.bounds(bounds, brushBounds).length > 0) &&
|
||||
Intersect.polyline.bounds(
|
||||
shape.points,
|
||||
Utils.translateBounds(brushBounds, Vec.neg(shape.point))
|
||||
intersectBoundsBounds(bounds, brushBounds).length > 0) &&
|
||||
intersectBoundsPolyline(
|
||||
Utils.translateBounds(brushBounds, Vec.neg(shape.point)),
|
||||
shape.points
|
||||
).length > 0)
|
||||
)
|
||||
}
|
||||
|
@ -220,7 +222,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGSVGElement> {
|
|||
|
||||
return (
|
||||
Utils.boundsContain(brushBounds, rBounds) ||
|
||||
Intersect.bounds.polyline(
|
||||
intersectBoundsPolyline(
|
||||
Utils.translateBounds(brushBounds, Vec.neg(shape.point)),
|
||||
rotatedBounds
|
||||
).length > 0
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import {
|
||||
SVGContainer,
|
||||
Utils,
|
||||
TLTransformInfo,
|
||||
TLBounds,
|
||||
Intersect,
|
||||
TLShapeProps,
|
||||
Vec,
|
||||
} from '@tldraw/core'
|
||||
import { SVGContainer, Utils, TLTransformInfo, TLBounds, TLShapeProps } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import {
|
||||
ArrowShape,
|
||||
DashStyle,
|
||||
|
@ -18,6 +11,11 @@ import {
|
|||
} from '~types'
|
||||
import { defaultStyle, getPerfectDashProps, getShapeStyle } from '~shape/shape-styles'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import {
|
||||
intersectLineSegmentEllipse,
|
||||
intersectPolylineBounds,
|
||||
intersectRayEllipse,
|
||||
} from '@tldraw/intersect'
|
||||
|
||||
// TODO
|
||||
// [ ] Improve indicator shape for drawn shapes
|
||||
|
@ -181,7 +179,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGSVGElement> {
|
|||
|
||||
return (
|
||||
rotatedCorners.every((point) => Utils.pointInBounds(point, bounds)) ||
|
||||
Intersect.polyline.bounds(rotatedCorners, bounds).length > 0
|
||||
intersectPolylineBounds(rotatedCorners, bounds).length > 0
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -226,15 +224,24 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGSVGElement> {
|
|||
// .map((int) => int.points[0])
|
||||
// .sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
|
||||
|
||||
let intersection = Intersect.ray
|
||||
.ellipse(origin, direction, center, shape.radius[0], shape.radius[1], shape.rotation || 0)
|
||||
|
||||
.points.sort((a, b) => Vec.dist(a, origin) - Vec.dist(b, origin))[0]
|
||||
let intersection = intersectRayEllipse(
|
||||
origin,
|
||||
direction,
|
||||
center,
|
||||
shape.radius[0],
|
||||
shape.radius[1],
|
||||
shape.rotation || 0
|
||||
).points.sort((a, b) => Vec.dist(a, origin) - Vec.dist(b, origin))[0]
|
||||
|
||||
if (!intersection) {
|
||||
intersection = Intersect.lineSegment
|
||||
.ellipse(point, center, center, shape.radius[0], shape.radius[1], shape.rotation || 0)
|
||||
.points.sort((a, b) => Vec.dist(a, point) - Vec.dist(b, point))[0]
|
||||
intersection = intersectLineSegmentEllipse(
|
||||
point,
|
||||
center,
|
||||
center,
|
||||
shape.radius[0],
|
||||
shape.radius[1],
|
||||
shape.rotation || 0
|
||||
).points.sort((a, b) => Vec.dist(a, point) - Vec.dist(b, point))[0]
|
||||
}
|
||||
|
||||
// The anchor is a point between the handle and the intersection
|
||||
|
@ -258,7 +265,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGSVGElement> {
|
|||
distance = 16
|
||||
} else {
|
||||
// Find the distance between the point and the ellipse
|
||||
const innerIntersection = Intersect.lineSegment.ellipse(
|
||||
const innerIntersection = intersectLineSegmentEllipse(
|
||||
point,
|
||||
center,
|
||||
center,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import { SVGContainer, TLBounds, Utils, Vec, Intersect, TLShapeProps } from '@tldraw/core'
|
||||
import { SVGContainer, TLBounds, Utils, TLShapeProps } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { intersectRayBounds, intersectPolylineBounds } from '@tldraw/intersect'
|
||||
import { defaultStyle, getPerfectDashProps } from '~shape/shape-styles'
|
||||
import {
|
||||
GroupShape,
|
||||
|
@ -179,8 +181,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGSVGElement> {
|
|||
// origin through point and expanded bounds.
|
||||
|
||||
// TODO: Make this a ray vs rounded rect intersection
|
||||
const intersection = Intersect.ray
|
||||
.bounds(origin, direction, expandedBounds)
|
||||
const intersection = intersectRayBounds(origin, direction, expandedBounds)
|
||||
.filter((int) => int.didIntersect)
|
||||
.map((int) => int.points[0])
|
||||
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
|
||||
|
@ -228,7 +229,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGSVGElement> {
|
|||
|
||||
return (
|
||||
rotatedCorners.every((point) => Utils.pointInBounds(point, bounds)) ||
|
||||
Intersect.polyline.bounds(rotatedCorners, bounds).length > 0
|
||||
intersectPolylineBounds(rotatedCorners, bounds).length > 0
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
import * as React from 'react'
|
||||
import {
|
||||
TLBounds,
|
||||
Utils,
|
||||
Vec,
|
||||
TLTransformInfo,
|
||||
Intersect,
|
||||
TLShapeProps,
|
||||
HTMLContainer,
|
||||
} from '@tldraw/core'
|
||||
import { TLBounds, Utils, TLTransformInfo, TLShapeProps, HTMLContainer } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
|
||||
import { PostItShape, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType, ArrowShape } from '~types'
|
||||
import { intersectPolylineBounds, intersectRayBounds } from '@tldraw/intersect'
|
||||
|
||||
// TODO
|
||||
// [ ] - Make sure that fill does not extend drawn shape at corners
|
||||
|
@ -154,8 +148,7 @@ export class PostIt extends TLDrawShapeUtil<PostItShape, HTMLDivElement> {
|
|||
// origin through point and expanded bounds.
|
||||
|
||||
// TODO: Make this a ray vs rounded rect intersection
|
||||
const intersection = Intersect.ray
|
||||
.bounds(origin, direction, expandedBounds)
|
||||
const intersection = intersectRayBounds(origin, direction, expandedBounds)
|
||||
.filter((int) => int.didIntersect)
|
||||
.map((int) => int.points[0])
|
||||
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
|
||||
|
@ -198,7 +191,7 @@ export class PostIt extends TLDrawShapeUtil<PostItShape, HTMLDivElement> {
|
|||
|
||||
return (
|
||||
rotatedCorners.every((point) => Utils.pointInBounds(point, bounds)) ||
|
||||
Intersect.polyline.bounds(rotatedCorners, bounds).length > 0
|
||||
intersectPolylineBounds(rotatedCorners, bounds).length > 0
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import * as React from 'react'
|
||||
import {
|
||||
TLBounds,
|
||||
Utils,
|
||||
Vec,
|
||||
TLTransformInfo,
|
||||
Intersect,
|
||||
TLShapeProps,
|
||||
SVGContainer,
|
||||
HTMLContainer,
|
||||
} from '@tldraw/core'
|
||||
import { TLBounds, Utils, TLTransformInfo, TLShapeProps, SVGContainer } from '@tldraw/core'
|
||||
import { intersectRayBounds } from '@tldraw/intersect'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import { getPerfectDashProps, defaultStyle, getShapeStyle } from '~shape/shape-styles'
|
||||
import {
|
||||
|
@ -234,8 +227,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGSVGElement> {
|
|||
// origin through point and expanded bounds.
|
||||
|
||||
// TODO: Make this a ray vs rounded rect intersection
|
||||
const intersection = Intersect.ray
|
||||
.bounds(origin, direction, expandedBounds)
|
||||
const intersection = intersectRayBounds(origin, direction, expandedBounds)
|
||||
.filter((int) => int.didIntersect)
|
||||
.map((int) => int.points[0])
|
||||
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
|
||||
|
@ -278,12 +270,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGSVGElement> {
|
|||
bounds: TLBounds,
|
||||
{ initialShape, transformOrigin, scaleX, scaleY }: TLTransformInfo<RectangleShape>
|
||||
) {
|
||||
if (!shape.rotation && !shape.isAspectRatioLocked) {
|
||||
return {
|
||||
point: Vec.round([bounds.minX, bounds.minY]),
|
||||
size: Vec.round([bounds.width, bounds.height]),
|
||||
}
|
||||
} else {
|
||||
if (shape.rotation || shape.isAspectRatioLocked) {
|
||||
const size = Vec.round(
|
||||
Vec.mul(initialShape.size, Math.min(Math.abs(scaleX), Math.abs(scaleY)))
|
||||
)
|
||||
|
@ -309,6 +296,11 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGSVGElement> {
|
|||
point,
|
||||
rotation,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
point: Vec.round([bounds.minX, bounds.minY]),
|
||||
size: Vec.round([bounds.width, bounds.height]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as React from 'react'
|
||||
import { HTMLContainer, TLBounds, Utils, Vec, TLTransformInfo, Intersect } from '@tldraw/core'
|
||||
import { HTMLContainer, TLBounds, Utils, TLTransformInfo } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { getShapeStyle, getFontStyle, defaultStyle } from '~shape/shape-styles'
|
||||
import {
|
||||
TextShape,
|
||||
|
@ -12,6 +13,7 @@ import {
|
|||
} from '~types'
|
||||
import styled from '~styles'
|
||||
import TextAreaUtils from './text-utils'
|
||||
import { intersectPolylineBounds, intersectRayBounds } from '@tldraw/intersect'
|
||||
|
||||
const LETTER_SPACING = -1.5
|
||||
|
||||
|
@ -255,7 +257,7 @@ export class Text extends TLDrawShapeUtil<TextShape, HTMLDivElement> {
|
|||
|
||||
return (
|
||||
rotatedCorners.every((point) => Utils.pointInBounds(point, bounds)) ||
|
||||
Intersect.polyline.bounds(rotatedCorners, bounds).length > 0
|
||||
intersectPolylineBounds(rotatedCorners, bounds).length > 0
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -373,8 +375,7 @@ export class Text extends TLDrawShapeUtil<TextShape, HTMLDivElement> {
|
|||
// origin through point and expanded bounds.
|
||||
|
||||
// TODO: Make this a ray vs rounded rect intersection
|
||||
const intersection = Intersect.ray
|
||||
.bounds(origin, direction, expandedBounds)
|
||||
const intersection = intersectRayBounds(origin, direction, expandedBounds)
|
||||
.filter((int) => int.didIntersect)
|
||||
.map((int) => int.points[0])
|
||||
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import { Utils, Vec } from '@tldraw/core'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import type { Data, PagePartial, TLDrawCommand } from '~types'
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { ArrowShape, Data, PagePartial, TLDrawCommand, TLDrawShape } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
import { Utils, Vec } from '@tldraw/core'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
|
||||
export function moveToPage(
|
||||
data: Data,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Utils, Vec } from '@tldraw/core'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import type { TLDrawCommand, Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Vec } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import type { Data, TLDrawCommand, PagePartial } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ import {
|
|||
Session,
|
||||
TLDrawStatus,
|
||||
} from '~types'
|
||||
import { Vec, Utils, TLHandle } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
export class ArrowSession implements Session {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { brushUpdater, Utils, Vec } from '@tldraw/core'
|
||||
import { brushUpdater, Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { Data, Session, TLDrawPatch, TLDrawStatus } from '~types'
|
||||
import { getShapeUtils } from '~shape'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Utils, Vec } from '@tldraw/core'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { Data, DrawShape, Session, TLDrawStatus } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Vec } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { ShapesWithProp, TLDrawStatus } from '~types'
|
||||
import type { Session } from '~types'
|
||||
import type { Data } from '~types'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Utils, Vec } from '@tldraw/core'
|
||||
import { Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { Session, TLDrawShape, TLDrawStatus } from '~types'
|
||||
import type { Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { TLBoundsCorner, TLBoundsEdge, Utils, Vec } from '@tldraw/core'
|
||||
import { TLBoundsCorner, TLBoundsEdge, Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { TLDrawShape, TLDrawStatus } from '~types'
|
||||
import type { Session } from '~types'
|
||||
import type { Data } from '~types'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { TLBoundsCorner, TLBoundsEdge, Utils, Vec } from '@tldraw/core'
|
||||
import { TLBoundsCorner, TLBoundsEdge, Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { Session, TLDrawShape, TLDrawStatus } from '~types'
|
||||
import type { Data } from '~types'
|
||||
import { TLDR } from '~state/tldr'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import { TLPageState, Utils, Vec } from '@tldraw/core'
|
||||
import { TLPageState, Utils } from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import {
|
||||
TLDrawShape,
|
||||
TLDrawBinding,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TLBounds, TLTransformInfo, Vec, Utils, TLPageState } from '@tldraw/core'
|
||||
import { TLBounds, TLTransformInfo, Utils, TLPageState } from '@tldraw/core'
|
||||
import { getShapeUtils } from '~shape'
|
||||
import type {
|
||||
Data,
|
||||
|
@ -11,6 +11,7 @@ import type {
|
|||
TLDrawCommand,
|
||||
TLDrawPatch,
|
||||
} from '~types'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
|
||||
export class TLDR {
|
||||
static getShapeUtils<T extends TLDrawShape>(
|
||||
|
|
|
@ -11,11 +11,11 @@ import {
|
|||
TLPointerEventHandler,
|
||||
TLWheelEventHandler,
|
||||
Utils,
|
||||
Vec,
|
||||
brushUpdater,
|
||||
TLPointerInfo,
|
||||
TLBounds,
|
||||
} from '@tldraw/core'
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import {
|
||||
FlipType,
|
||||
TextShape,
|
||||
|
|
|
@ -6,12 +6,16 @@
|
|||
"strict": true,
|
||||
"composite": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"rootDir": "src",
|
||||
"rootDir": ".",
|
||||
"outDir": "./dist/types",
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"~*": ["./*"]
|
||||
}
|
||||
},
|
||||
"references": [{ "path": "../../packages/core" }]
|
||||
"references": [
|
||||
{ "path": "../../packages/intersect" },
|
||||
{ "path": "../../packages/vec" },
|
||||
{ "path": "../../packages/core" }
|
||||
]
|
||||
}
|
||||
|
|
3
packages/vec/README.md
Normal file
3
packages/vec/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Dev Server
|
||||
|
||||
Dev server with fast refresh.
|
55
packages/vec/package.json
Normal file
55
packages/vec/package.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"name": "@tldraw/vec",
|
||||
"version": "0.0.86",
|
||||
"private": false,
|
||||
"description": "A tiny little drawing app (vec)",
|
||||
"author": "@steveruizok",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tldraw/tldraw.git",
|
||||
"directory": "packages/vec"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [],
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"main": "./dist/cjs/index.js",
|
||||
"module": "./dist/esm/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"typings": "./dist/types/index.d.ts",
|
||||
"scripts": {
|
||||
"start:pre": "node scripts/pre-dev && yarn types:pre",
|
||||
"start": "node scripts/dev & yarn types:dev",
|
||||
"build": "node scripts/build && yarn types:build",
|
||||
"types:pre": "tsc",
|
||||
"types:dev": "tsc --watch",
|
||||
"types:build": "tsc --project tsconfig.build.json",
|
||||
"lint": "eslint src/ --ext .ts,.tsx",
|
||||
"clean": "rm -rf dist",
|
||||
"ts-node": "ts-node",
|
||||
"docs": "typedoc",
|
||||
"docs:watch": "typedoc --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/preset-env": "^7.15.4",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.10",
|
||||
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
||||
"@typescript-eslint/parser": "^4.30.0",
|
||||
"esbuild": "^0.12.24",
|
||||
"eslint": "^7.32.0",
|
||||
"lerna": "^4.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"ts-node": "^10.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typedoc": "^0.21.9",
|
||||
"typescript": "^4.4.2"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"dependencies": {},
|
||||
"gitHead": "55da8880eb3d8ab5fb62b5eb7853065922c95dcf"
|
||||
}
|
52
packages/vec/scripts/build.js
Normal file
52
packages/vec/scripts/build.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* eslint-disable */
|
||||
const fs = require('fs')
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
const name = process.env.npm_package_name || ''
|
||||
|
||||
async function main() {
|
||||
if (fs.existsSync('./dist')) {
|
||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
||||
if (e) {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
esbuild.buildSync({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/cjs',
|
||||
minify: true,
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
format: 'cjs',
|
||||
target: 'es6',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.build.json',
|
||||
external: ['react', 'react-dom'],
|
||||
})
|
||||
|
||||
esbuild.buildSync({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/esm',
|
||||
minify: true,
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
format: 'esm',
|
||||
target: 'es6',
|
||||
tsconfig: './tsconfig.build.json',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
external: ['react', 'react-dom'],
|
||||
})
|
||||
|
||||
console.log(`✔ ${name}: Built package.`)
|
||||
} catch (e) {
|
||||
console.log(`× ${name}: Build failed due to an error.`)
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
31
packages/vec/scripts/dev.js
Normal file
31
packages/vec/scripts/dev.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* eslint-disable */
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
const name = process.env.npm_package_name || ''
|
||||
|
||||
async function main() {
|
||||
esbuild.build({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/cjs',
|
||||
minify: false,
|
||||
bundle: true,
|
||||
format: 'cjs',
|
||||
target: 'es6',
|
||||
jsxFactory: 'React.createElement',
|
||||
jsxFragment: 'React.Fragment',
|
||||
tsconfig: './tsconfig.json',
|
||||
external: ['react', 'react-dom'],
|
||||
incremental: true,
|
||||
watch: {
|
||||
onRebuild(error) {
|
||||
if (error) {
|
||||
console.log(`× ${name}: An error in prevented the rebuild.`)
|
||||
return
|
||||
}
|
||||
console.log(`✔ ${name}: Rebuilt.`)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
27
packages/vec/scripts/pre-dev.js
Normal file
27
packages/vec/scripts/pre-dev.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* eslint-disable */
|
||||
const fs = require('fs')
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
async function main() {
|
||||
if (fs.existsSync('./dist')) {
|
||||
fs.rmSync('./dist', { recursive: true }, (e) => {
|
||||
if (e) {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
esbuild.build({
|
||||
entryPoints: ['./src/index.ts'],
|
||||
outdir: 'dist/cjs',
|
||||
minify: true,
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
format: 'cjs',
|
||||
target: 'es6',
|
||||
tsconfig: './tsconfig.json',
|
||||
external: ['react', 'react-dom'],
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
19
packages/vec/tsconfig.build.json
Normal file
19
packages/vec/tsconfig.build.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.test.tsx",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.spec.ts",
|
||||
"src/test",
|
||||
"dist",
|
||||
"docs"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"incremental": false,
|
||||
"declarationMap": false,
|
||||
"sourceMap": false
|
||||
}
|
||||
}
|
10
packages/vec/tsconfig.json
Normal file
10
packages/vec/tsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "docs"],
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/types",
|
||||
"rootDir": "src",
|
||||
"baseUrl": "src"
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
// For references
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"composite": true,
|
||||
"sourceMap": true,
|
||||
"emitDeclarationOnly": true,
|
||||
// Other
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
|
|
|
@ -3,12 +3,19 @@
|
|||
"extends": "./tsconfig.base.json",
|
||||
"exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts"],
|
||||
"files": [],
|
||||
"references": [{ "path": "./packages/tldraw" }, { "path": "./packages/core" }],
|
||||
"references": [
|
||||
{ "path": "./packages/vec" },
|
||||
{ "path": "./packages/intersect" },
|
||||
{ "path": "./packages/tldraw" },
|
||||
{ "path": "./packages/core" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@tldraw/tldraw": ["./packages/tldraw/dist"],
|
||||
"@tldraw/core": ["./packages/core/dist"],
|
||||
"@tldraw/tldraw": ["./packages/tldraw"],
|
||||
"@tldraw/core": ["./packages/core"],
|
||||
"@tldraw/vec": ["./packages/vec"],
|
||||
"@tldraw/intersect": ["./packages/intersect"],
|
||||
"+*": ["./packages/core/src/*"],
|
||||
"~*": ["./packages/tldraw/src/*"]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue