kopia lustrzana https://github.com/Tldraw/Tldraw
fixes code bug, adds sketchy circle
rodzic
dd1210c617
commit
5a7e79121a
|
@ -6,7 +6,6 @@ export default function Cursor() {
|
|||
|
||||
useEffect(() => {
|
||||
function updatePosition(e: PointerEvent) {
|
||||
console.log('hi')
|
||||
const cursor = rCursor.current
|
||||
|
||||
cursor.setAttribute(
|
||||
|
|
|
@ -82,7 +82,7 @@ export default function CodePanel() {
|
|||
let error = null
|
||||
|
||||
try {
|
||||
const { shapes, controls } = generateFromCode(data.code)
|
||||
const { shapes, controls } = generateFromCode(state.data, data.code)
|
||||
state.send('GENERATED_FROM_CODE', { shapes, controls })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
|
|
@ -11,10 +11,10 @@ export default class Circle extends CodeShape<CircleShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Circle,
|
||||
isGenerated: true,
|
||||
name: 'Circle',
|
||||
parentId: 'page0',
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
rotation: 0,
|
||||
|
@ -22,8 +22,8 @@ export default class Circle extends CodeShape<CircleShape> {
|
|||
isAspectRatioLocked: false,
|
||||
isLocked: false,
|
||||
isHidden: false,
|
||||
style: defaultStyle,
|
||||
...props,
|
||||
style: { ...defaultStyle, ...props.style },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@ export default class Dot extends CodeShape<DotShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Dot,
|
||||
isGenerated: true,
|
||||
name: 'Dot',
|
||||
parentId: 'page0',
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
rotation: 0,
|
||||
|
@ -25,7 +25,7 @@ export default class Dot extends CodeShape<DotShape> {
|
|||
style: {
|
||||
...defaultStyle,
|
||||
...props.style,
|
||||
isFilled: false,
|
||||
isFilled: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ export default class Ellipse extends CodeShape<EllipseShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Ellipse,
|
||||
isGenerated: true,
|
||||
name: 'Ellipse',
|
||||
parentId: 'page0',
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
radiusX: 20,
|
||||
|
@ -23,8 +23,8 @@ export default class Ellipse extends CodeShape<EllipseShape> {
|
|||
isAspectRatioLocked: false,
|
||||
isLocked: false,
|
||||
isHidden: false,
|
||||
style: defaultStyle,
|
||||
...props,
|
||||
style: { ...defaultStyle, ...props.style },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import Rectangle from "./rectangle"
|
||||
import Circle from "./circle"
|
||||
import Ellipse from "./ellipse"
|
||||
import Polyline from "./polyline"
|
||||
import Dot from "./dot"
|
||||
import Ray from "./ray"
|
||||
import Line from "./line"
|
||||
import Vector from "./vector"
|
||||
import Utils from "./utils"
|
||||
import { NumberControl, VectorControl, codeControls, controls } from "./control"
|
||||
import { codeShapes } from "./index"
|
||||
import { CodeControl } from "types"
|
||||
import Rectangle from './rectangle'
|
||||
import Circle from './circle'
|
||||
import Ellipse from './ellipse'
|
||||
import Polyline from './polyline'
|
||||
import Dot from './dot'
|
||||
import Ray from './ray'
|
||||
import Line from './line'
|
||||
import Vector from './vector'
|
||||
import Utils from './utils'
|
||||
import { NumberControl, VectorControl, codeControls, controls } from './control'
|
||||
import { codeShapes } from './index'
|
||||
import { CodeControl, Data } from 'types'
|
||||
|
||||
const baseScope = {
|
||||
Dot,
|
||||
|
@ -30,12 +30,14 @@ const baseScope = {
|
|||
* collected shapes as an array.
|
||||
* @param code
|
||||
*/
|
||||
export function generateFromCode(code: string) {
|
||||
export function generateFromCode(data: Data, code: string) {
|
||||
codeControls.clear()
|
||||
codeShapes.clear()
|
||||
;(window as any).isUpdatingCode = false
|
||||
;(window as any).currentPageId = data.currentPageId
|
||||
|
||||
const scope = { ...baseScope, controls }
|
||||
const { currentPageId } = data
|
||||
const scope = { ...baseScope, controls, currentPageId }
|
||||
|
||||
new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
|
||||
|
||||
|
@ -53,15 +55,16 @@ export function generateFromCode(code: string) {
|
|||
* collected shapes as an array.
|
||||
* @param code
|
||||
*/
|
||||
export function updateFromCode(
|
||||
code: string,
|
||||
controls: Record<string, CodeControl>
|
||||
) {
|
||||
export function updateFromCode(data: Data, code: string) {
|
||||
codeShapes.clear()
|
||||
;(window as any).isUpdatingCode = true
|
||||
;(window as any).currentPageId = data.currentPageId
|
||||
|
||||
const { currentPageId } = data
|
||||
|
||||
const scope = {
|
||||
...baseScope,
|
||||
currentPageId,
|
||||
controls: Object.fromEntries(
|
||||
Object.entries(controls).map(([id, control]) => [
|
||||
control.label,
|
||||
|
|
|
@ -12,10 +12,10 @@ export default class Line extends CodeShape<LineShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Line,
|
||||
isGenerated: true,
|
||||
name: 'Line',
|
||||
parentId: 'page0',
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
direction: [-0.5, 0.5],
|
||||
|
|
|
@ -12,10 +12,10 @@ export default class Polyline extends CodeShape<PolylineShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Polyline,
|
||||
isGenerated: true,
|
||||
name: 'Polyline',
|
||||
parentId: 'page0',
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
points: [[0, 0]],
|
||||
|
|
|
@ -12,10 +12,10 @@ export default class Rectangle extends CodeShape<RectangleShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
seed: Math.random(),
|
||||
parentId: (window as any).currentPageId,
|
||||
type: ShapeType.Rectangle,
|
||||
isGenerated: true,
|
||||
name: 'Rectangle',
|
||||
parentId: 'page0',
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
size: [100, 100],
|
||||
|
@ -24,8 +24,8 @@ export default class Rectangle extends CodeShape<RectangleShape> {
|
|||
isAspectRatioLocked: false,
|
||||
isLocked: false,
|
||||
isHidden: false,
|
||||
style: defaultStyle,
|
||||
...props,
|
||||
style: { ...defaultStyle, ...props.style },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Bounds } from "types"
|
||||
import Vector, { Point } from "./vector"
|
||||
import { Bounds } from 'types'
|
||||
import Vector, { Point } from './vector'
|
||||
|
||||
export default class Utils {
|
||||
static getRayRayIntersection(p0: Vector, n0: Vector, p1: Vector, n1: Vector) {
|
||||
|
|
|
@ -16,7 +16,7 @@ export default class Vector {
|
|||
constructor(vector: Vector, b?: undefined)
|
||||
constructor(options: Point, b?: undefined)
|
||||
constructor(a: VectorOptions | Vector | number, b?: number) {
|
||||
if (typeof a === "number") {
|
||||
if (typeof a === 'number') {
|
||||
this.x = a
|
||||
this.y = b
|
||||
} else {
|
||||
|
@ -415,7 +415,7 @@ export default class Vector {
|
|||
}
|
||||
|
||||
static cast(v: Point | Vector) {
|
||||
return "cast" in v ? v : new Vector(v)
|
||||
return 'cast' in v ? v : new Vector(v)
|
||||
}
|
||||
|
||||
static from(v: Vector) {
|
||||
|
|
|
@ -34,7 +34,7 @@ const dot = registerShapeUtils<DotShape>({
|
|||
},
|
||||
|
||||
render({ id }) {
|
||||
return <use href="#dot" />
|
||||
return <use id={id} href="#dot" fill="black" />
|
||||
},
|
||||
|
||||
getBounds(shape) {
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
import { v4 as uuid } from 'uuid'
|
||||
import * as vec from 'utils/vec'
|
||||
import { EllipseShape, ShapeType } from 'types'
|
||||
import { registerShapeUtils } from './index'
|
||||
import { getShapeUtils, registerShapeUtils } from './index'
|
||||
import { boundsContained, getRotatedEllipseBounds } from 'utils/bounds'
|
||||
import { intersectEllipseBounds } from 'utils/intersections'
|
||||
import { pointInEllipse } from 'utils/hitTests'
|
||||
import {
|
||||
ease,
|
||||
getBoundsFromPoints,
|
||||
getRotatedCorners,
|
||||
getSvgPathFromStroke,
|
||||
pointsBetween,
|
||||
rng,
|
||||
rotateBounds,
|
||||
shuffleArr,
|
||||
translateBounds,
|
||||
} from 'utils/utils'
|
||||
import { defaultStyle, getShapeStyle } from 'lib/shape-styles'
|
||||
import getStroke from 'perfect-freehand'
|
||||
|
||||
const pathCache = new WeakMap<EllipseShape, string>([])
|
||||
|
||||
const ellipse = registerShapeUtils<EllipseShape>({
|
||||
boundsCache: new WeakMap([]),
|
||||
|
@ -37,17 +45,39 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
|||
}
|
||||
},
|
||||
|
||||
render({ id, radiusX, radiusY, style }) {
|
||||
render(shape) {
|
||||
const { id, radiusX, radiusY, style } = shape
|
||||
const styles = getShapeStyle(style)
|
||||
|
||||
if (!pathCache.has(shape)) {
|
||||
renderPath(shape)
|
||||
}
|
||||
|
||||
const path = pathCache.get(shape)
|
||||
|
||||
return (
|
||||
<g id={id}>
|
||||
<ellipse
|
||||
id={id}
|
||||
cx={radiusX}
|
||||
cy={radiusY}
|
||||
rx={Math.max(0, radiusX - Number(styles.strokeWidth) / 2)}
|
||||
ry={Math.max(0, radiusY - Number(styles.strokeWidth) / 2)}
|
||||
stroke="none"
|
||||
/>
|
||||
<path d={path} fill={styles.stroke} />
|
||||
</g>
|
||||
)
|
||||
|
||||
// return (
|
||||
// <ellipse
|
||||
// id={id}
|
||||
// cx={radiusX}
|
||||
// cy={radiusY}
|
||||
// rx={Math.max(0, radiusX - Number(styles.strokeWidth) / 2)}
|
||||
// ry={Math.max(0, radiusY - Number(styles.strokeWidth) / 2)}
|
||||
// />
|
||||
// )
|
||||
},
|
||||
|
||||
getBounds(shape) {
|
||||
|
@ -129,3 +159,53 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
|||
})
|
||||
|
||||
export default ellipse
|
||||
|
||||
function renderPath(shape: EllipseShape) {
|
||||
const { style, id, radiusX, radiusY, point } = shape
|
||||
|
||||
const getRandom = rng(id)
|
||||
|
||||
const center = vec.sub(getShapeUtils(shape).getCenter(shape), point)
|
||||
|
||||
const strokeWidth = +getShapeStyle(style).strokeWidth
|
||||
|
||||
const rx = radiusX + getRandom() * strokeWidth
|
||||
const ry = radiusY + getRandom() * strokeWidth
|
||||
|
||||
const points: number[][] = []
|
||||
const start = Math.PI + Math.PI * getRandom()
|
||||
|
||||
const overlap = Math.PI / 12
|
||||
|
||||
for (let i = 2; i < 8; i++) {
|
||||
const rads = start + overlap * 2 * (i / 8)
|
||||
const x = rx * Math.cos(rads) + center[0]
|
||||
const y = ry * Math.sin(rads) + center[1]
|
||||
points.push([x, y])
|
||||
}
|
||||
|
||||
for (let i = 5; i < 32; i++) {
|
||||
const rads = start + overlap * 2 + Math.PI * 2.5 * ease(i / 35)
|
||||
const x = rx * Math.cos(rads) + center[0]
|
||||
const y = ry * Math.sin(rads) + center[1]
|
||||
points.push([x, y])
|
||||
}
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const rads = start + overlap * 2 * (i / 4)
|
||||
const x = rx * Math.cos(rads) + center[0]
|
||||
const y = ry * Math.sin(rads) + center[1]
|
||||
points.push([x, y])
|
||||
}
|
||||
|
||||
const stroke = getStroke(points, {
|
||||
size: 1 + strokeWidth * 2,
|
||||
thinning: 0.6,
|
||||
easing: (t) => t * t * t * t,
|
||||
end: { taper: strokeWidth * 20 },
|
||||
start: { taper: strokeWidth * 20 },
|
||||
simulatePressure: false,
|
||||
})
|
||||
|
||||
pathCache.set(shape, getSvgPathFromStroke(stroke))
|
||||
}
|
||||
|
|
|
@ -2,7 +2,13 @@ import { v4 as uuid } from 'uuid'
|
|||
import * as vec from 'utils/vec'
|
||||
import { RectangleShape, ShapeType } from 'types'
|
||||
import { registerShapeUtils } from './index'
|
||||
import { getSvgPathFromStroke, translateBounds, getNoise } from 'utils/utils'
|
||||
import {
|
||||
getSvgPathFromStroke,
|
||||
translateBounds,
|
||||
rng,
|
||||
shuffleArr,
|
||||
pointsBetween,
|
||||
} from 'utils/utils'
|
||||
import { defaultStyle, getShapeStyle } from 'lib/shape-styles'
|
||||
import getStroke from 'perfect-freehand'
|
||||
|
||||
|
@ -33,7 +39,7 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
|||
},
|
||||
|
||||
render(shape) {
|
||||
const { id, size, radius, style, point } = shape
|
||||
const { id, size, radius, style } = shape
|
||||
const styles = getShapeStyle(style)
|
||||
|
||||
if (!pathCache.has(shape)) {
|
||||
|
@ -117,29 +123,16 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
|||
|
||||
export default rectangle
|
||||
|
||||
function easeInOut(t: number) {
|
||||
return t * (2 - t)
|
||||
}
|
||||
|
||||
function ease(t: number) {
|
||||
return t * t * t
|
||||
}
|
||||
|
||||
function pointsBetween(a: number[], b: number[], steps = 6) {
|
||||
return Array.from(Array(steps))
|
||||
.map((_, i) => ease(i / steps))
|
||||
.map((t) => [...vec.lrp(a, b, t), (1 - t) / 2])
|
||||
}
|
||||
|
||||
function renderPath(shape: RectangleShape) {
|
||||
const styles = getShapeStyle(shape.style)
|
||||
|
||||
const noise = getNoise(shape.seed)
|
||||
const off = -0.25 + shape.seed / 2
|
||||
const getRandom = rng(shape.id)
|
||||
|
||||
const baseOffset = +styles.strokeWidth / 2
|
||||
|
||||
const offsets = Array.from(Array(4)).map((_, i) => [
|
||||
noise(i, i + 1) * off * 16,
|
||||
noise(i + 2, i + 3) * off * 16,
|
||||
getRandom() * baseOffset,
|
||||
getRandom() * baseOffset,
|
||||
])
|
||||
|
||||
const [w, h] = shape.size
|
||||
|
@ -155,17 +148,11 @@ function renderPath(shape: RectangleShape) {
|
|||
pointsBetween(bl, tl),
|
||||
pointsBetween(tl, tr),
|
||||
],
|
||||
shape.id.charCodeAt(5)
|
||||
Math.floor(5 + getRandom() * 4)
|
||||
)
|
||||
|
||||
const stroke = getStroke(
|
||||
[
|
||||
...lines.flat().slice(4),
|
||||
...lines[0].slice(0, 4),
|
||||
lines[0][4],
|
||||
lines[0][5],
|
||||
lines[0][5],
|
||||
],
|
||||
[...lines.flat().slice(2), ...lines[0], ...lines[0].slice(4)],
|
||||
{
|
||||
size: 1 + +styles.strokeWidth * 2,
|
||||
thinning: 0.6,
|
||||
|
@ -178,7 +165,3 @@ function renderPath(shape: RectangleShape) {
|
|||
|
||||
pathCache.set(shape, getSvgPathFromStroke(stroke))
|
||||
}
|
||||
|
||||
function shuffleArr<T>(arr: T[], offset: number): T[] {
|
||||
return arr.map((_, i) => arr[(i + offset) % arr.length])
|
||||
}
|
||||
|
|
|
@ -33,9 +33,6 @@ export default class TranslateSession extends BaseSession {
|
|||
const { shapes } = getPage(data, currentPageId)
|
||||
|
||||
const delta = vec.vec(this.origin, point)
|
||||
const trueDelta = vec.sub(delta, this.prev)
|
||||
this.delta = delta
|
||||
this.prev = delta
|
||||
|
||||
if (isAligned) {
|
||||
if (Math.abs(delta[0]) < Math.abs(delta[1])) {
|
||||
|
@ -45,13 +42,17 @@ export default class TranslateSession extends BaseSession {
|
|||
}
|
||||
}
|
||||
|
||||
const trueDelta = vec.sub(delta, this.prev)
|
||||
this.delta = delta
|
||||
this.prev = delta
|
||||
|
||||
if (isCloning) {
|
||||
if (!this.isCloning) {
|
||||
this.isCloning = true
|
||||
|
||||
for (const { id, point } of initialShapes) {
|
||||
for (const { id } of initialShapes) {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translateTo(shape, point)
|
||||
getShapeUtils(shape).translateBy(shape, trueDelta)
|
||||
}
|
||||
|
||||
for (const clone of clones) {
|
||||
|
@ -70,9 +71,9 @@ export default class TranslateSession extends BaseSession {
|
|||
)
|
||||
}
|
||||
|
||||
for (const { id, point } of clones) {
|
||||
for (const { id } of clones) {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translateTo(shape, vec.add(point, delta))
|
||||
getShapeUtils(shape).translateBy(shape, trueDelta)
|
||||
}
|
||||
|
||||
updateParents(
|
||||
|
@ -186,6 +187,7 @@ export function getTranslateSnapshot(data: Data) {
|
|||
clones: selectedShapes
|
||||
.filter((shape) => shape.type !== ShapeType.Group)
|
||||
.flatMap((shape) => {
|
||||
// TODO: Clone children recursively
|
||||
const clone = {
|
||||
...shape,
|
||||
id: uuid(),
|
||||
|
|
|
@ -1382,8 +1382,8 @@ const state = createState({
|
|||
|
||||
try {
|
||||
const { shapes } = updateFromCode(
|
||||
data.document.code[data.currentCodeFileId].code,
|
||||
data.codeControls
|
||||
data,
|
||||
data.document.code[data.currentCodeFileId].code
|
||||
)
|
||||
|
||||
commands.generate(data, data.currentPageId, shapes)
|
||||
|
|
|
@ -1695,68 +1695,45 @@ const Grad = [
|
|||
[0, -1],
|
||||
]
|
||||
|
||||
// Thanks to joshforisha
|
||||
// https://github.com/joshforisha/fast-simplex-noise-js/blob/main/src/2d.ts
|
||||
export function getNoise(seed = Math.random()) {
|
||||
const p = new Uint8Array(256)
|
||||
for (let i = 0; i < 256; i++) p[i] = i
|
||||
/**
|
||||
* Seeded random number generator, using [xorshift](https://en.wikipedia.org/wiki/Xorshift).
|
||||
* The result will always be betweeen -1 and 1.
|
||||
*
|
||||
* Adapted from [seedrandom](https://github.com/davidbau/seedrandom).
|
||||
*/
|
||||
export function rng(seed = '') {
|
||||
let x = 0
|
||||
let y = 0
|
||||
let z = 0
|
||||
let w = 0
|
||||
|
||||
let n: number
|
||||
let q: number
|
||||
for (let i = 255; i > 0; i--) {
|
||||
n = Math.floor((i + 1) * seed)
|
||||
q = p[i]
|
||||
p[i] = p[n]
|
||||
p[n] = q
|
||||
function next() {
|
||||
const t = x ^ (x << 11)
|
||||
x = y
|
||||
y = z
|
||||
z = w
|
||||
w ^= ((w >>> 19) ^ t ^ (t >>> 8)) >>> 0
|
||||
return w / 0x100000000
|
||||
}
|
||||
|
||||
const perm = new Uint8Array(512)
|
||||
const permMod12 = new Uint8Array(512)
|
||||
for (let i = 0; i < 512; i++) {
|
||||
perm[i] = p[i & 255]
|
||||
permMod12[i] = perm[i] % 12
|
||||
for (var k = 0; k < seed.length + 64; k++) {
|
||||
x ^= seed.charCodeAt(k) | 0
|
||||
next()
|
||||
}
|
||||
|
||||
return (x: number, y: number): number => {
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
const s = (x + y) * 0.5 * (Math.sqrt(3.0) - 1.0) // Hairy factor for 2D
|
||||
const i = Math.floor(x + s)
|
||||
const j = Math.floor(y + s)
|
||||
const t = (i + j) * G2
|
||||
const X0 = i - t // Unskew the cell origin back to (x,y) space
|
||||
const Y0 = j - t
|
||||
const x0 = x - X0 // The x,y distances from the cell origin
|
||||
const y0 = y - Y0
|
||||
|
||||
// Determine which simplex we are in.
|
||||
const i1 = x0 > y0 ? 1 : 0
|
||||
const j1 = x0 > y0 ? 0 : 1
|
||||
|
||||
// Offsets for corners
|
||||
const x1 = x0 - i1 + G2
|
||||
const y1 = y0 - j1 + G2
|
||||
const x2 = x0 - 1.0 + 2.0 * G2
|
||||
const y2 = y0 - 1.0 + 2.0 * G2
|
||||
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
const ii = i & 255
|
||||
const jj = j & 255
|
||||
const g0 = Grad[permMod12[ii + perm[jj]]]
|
||||
const g1 = Grad[permMod12[ii + i1 + perm[jj + j1]]]
|
||||
const g2 = Grad[permMod12[ii + 1 + perm[jj + 1]]]
|
||||
|
||||
// Calculate the contribution from the three corners
|
||||
const t0 = 0.5 - x0 * x0 - y0 * y0
|
||||
const n0 = t0 < 0 ? 0.0 : Math.pow(t0, 4) * (g0[0] * x0 + g0[1] * y0)
|
||||
|
||||
const t1 = 0.5 - x1 * x1 - y1 * y1
|
||||
const n1 = t1 < 0 ? 0.0 : Math.pow(t1, 4) * (g1[0] * x1 + g1[1] * y1)
|
||||
|
||||
const t2 = 0.5 - x2 * x2 - y2 * y2
|
||||
const n2 = t2 < 0 ? 0.0 : Math.pow(t2, 4) * (g2[0] * x2 + g2[1] * y2)
|
||||
|
||||
// Add contributions from each corner to get the final noise value.
|
||||
// The result is scaled to return values in the interval [-1, 1]
|
||||
return 70.14805770653952 * (n0 + n1 + n2)
|
||||
return next
|
||||
}
|
||||
|
||||
export function ease(t: number) {
|
||||
return t * t * t
|
||||
}
|
||||
|
||||
export function pointsBetween(a: number[], b: number[], steps = 6) {
|
||||
return Array.from(Array(steps))
|
||||
.map((_, i) => ease(i / steps))
|
||||
.map((t) => [...vec.lrp(a, b, t), (1 - t) / 2])
|
||||
}
|
||||
|
||||
export function shuffleArr<T>(arr: T[], offset: number): T[] {
|
||||
return arr.map((_, i) => arr[(i + offset) % arr.length])
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue