early shadows
This commit is contained in:
parent
9eba227ec3
commit
b5f3d9e4eb
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -5609,6 +5609,11 @@
|
||||||
"invert-kv": "^1.0.0"
|
"invert-kv": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"line-intersect": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/line-intersect/-/line-intersect-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-uOPErCqtEnHYsnesl56XmKm9nWF27kOqZCuibJEkyAJ23FHsKmOHo8FPT6SRAp2h3wzvyXJNjbPsq9FF5x29vw=="
|
||||||
|
},
|
||||||
"load-json-file": {
|
"load-json-file": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eix-js/utils": "0.0.6",
|
"@eix-js/utils": "0.0.6",
|
||||||
"deepmerge": "^4.0.0",
|
"deepmerge": "^4.0.0",
|
||||||
|
"line-intersect": "^2.2.1",
|
||||||
"mainloop.js": "^1.0.4",
|
"mainloop.js": "^1.0.4",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Singleton } from '@eix-js/utils'
|
import { Singleton } from '@eix-js/utils'
|
||||||
import { Observable, fromEvent, BehaviorSubject } from 'rxjs'
|
import { Observable, fromEvent, BehaviorSubject } from 'rxjs'
|
||||||
import { map } from 'rxjs/operators'
|
import { map } from 'rxjs/operators'
|
||||||
|
import { multiply } from '../../vector2/helpers/basic'
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
export class Screen {
|
export class Screen {
|
||||||
|
@ -25,4 +26,8 @@ export class Screen {
|
||||||
public get y() {
|
public get y() {
|
||||||
return this.height.value
|
return this.height.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get center() {
|
||||||
|
return multiply([this.x, this.y], 0.5)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { Transform, vector2 } from './Transform'
|
import { Transform } from './Transform'
|
||||||
|
|
||||||
export class Gate {
|
export class Gate {
|
||||||
public static lastId = 0
|
public static lastId = 0
|
||||||
public transform = new Transform()
|
public transform = new Transform()
|
||||||
public id = Gate.lastId++
|
public id = Gate.lastId++
|
||||||
public shadow: vector2 = [0, 0]
|
|
||||||
|
|
||||||
public constructor(public color = 'blue') {}
|
public constructor(public color = 'blue') {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,26 +5,17 @@ import { MouseEventInfo } from '../../core/components/FluidCanvas'
|
||||||
import { pointInSquare } from '../helpers/pointInSquare'
|
import { pointInSquare } from '../helpers/pointInSquare'
|
||||||
import { vector2 } from './Transform'
|
import { vector2 } from './Transform'
|
||||||
import merge from 'deepmerge'
|
import merge from 'deepmerge'
|
||||||
import { smoothStep } from '../../vector2/helpers/smoothStep'
|
|
||||||
import { renderGate } from '../helpers/renderGate'
|
import { renderGate } from '../helpers/renderGate'
|
||||||
import { renderGateShadow } from '../helpers/renderGateShadow'
|
import { renderGateShadow } from '../helpers/renderGateShadow'
|
||||||
import { Gate } from './Gate'
|
|
||||||
import { MouseManager } from './MouseManager'
|
import { MouseManager } from './MouseManager'
|
||||||
import { Screen } from '../../core/classes/Screen'
|
import { Screen } from '../../core/classes/Screen'
|
||||||
import {
|
|
||||||
add,
|
|
||||||
invert,
|
|
||||||
ofLength,
|
|
||||||
length,
|
|
||||||
multiply
|
|
||||||
} from '../../vector2/helpers/basic'
|
|
||||||
|
|
||||||
export interface SimulationRendererOptions {
|
export interface SimulationRendererOptions {
|
||||||
shadows: {
|
shadows: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
color: string
|
color: string
|
||||||
offset: number
|
lightHeight: number
|
||||||
speed: number
|
gateHeight: number
|
||||||
}
|
}
|
||||||
dnd: {
|
dnd: {
|
||||||
rotation: number
|
rotation: number
|
||||||
|
@ -35,8 +26,8 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
||||||
shadows: {
|
shadows: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
color: 'rgba(0,0,0,0.3)',
|
color: 'rgba(0,0,0,0.3)',
|
||||||
offset: 15,
|
gateHeight: 10,
|
||||||
speed: 1
|
lightHeight: 50
|
||||||
},
|
},
|
||||||
dnd: {
|
dnd: {
|
||||||
rotation: Math.PI / 12 // 7.5 degrees
|
rotation: Math.PI / 12 // 7.5 degrees
|
||||||
|
@ -133,13 +124,22 @@ export class SimulationRenderer {
|
||||||
public render(ctx: CanvasRenderingContext2D) {
|
public render(ctx: CanvasRenderingContext2D) {
|
||||||
this.clear(ctx)
|
this.clear(ctx)
|
||||||
|
|
||||||
|
const center = this.screen.center
|
||||||
|
|
||||||
// render gates
|
// render gates
|
||||||
for (const gate of this.simulation.gates) {
|
for (const gate of this.simulation.gates) {
|
||||||
|
renderGate(ctx, gate)
|
||||||
if (this.options.shadows.enabled) {
|
if (this.options.shadows.enabled) {
|
||||||
renderGateShadow(ctx, this.options.shadows.color, gate)
|
renderGateShadow(
|
||||||
|
ctx,
|
||||||
|
this.options.shadows.color,
|
||||||
|
gate,
|
||||||
|
this.options.shadows.gateHeight,
|
||||||
|
[center[0], center[1], this.options.shadows.lightHeight]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGate(ctx, gate)
|
// renderGate(ctx, gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,32 +152,7 @@ export class SimulationRenderer {
|
||||||
return this.simulation.gates.get(id)
|
return this.simulation.gates.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOptimalShadow(gate: Gate) {
|
|
||||||
const center = multiply([this.screen.x, this.screen.y] as vector2, 0.5)
|
|
||||||
|
|
||||||
const difference = add(center, invert(gate.transform.position))
|
|
||||||
|
|
||||||
return add(
|
|
||||||
add(difference, center),
|
|
||||||
ofLength(difference, this.options.shadows.offset)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public getShadowPosition(gate: Gate) {
|
|
||||||
return gate.transform.position.map(
|
|
||||||
(value, index) => value - this.getOptimalShadow(gate)[index]
|
|
||||||
) as vector2
|
|
||||||
}
|
|
||||||
|
|
||||||
public update(delta: number) {
|
public update(delta: number) {
|
||||||
for (const gate of this.simulation.gates) {
|
|
||||||
gate.shadow = smoothStep(
|
|
||||||
this.options.shadows.speed,
|
|
||||||
gate.shadow,
|
|
||||||
this.getShadowPosition(gate)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const selected = this.getSelected()
|
const selected = this.getSelected()
|
||||||
|
|
||||||
if (selected && this.movedSelection) {
|
if (selected && this.movedSelection) {
|
||||||
|
@ -187,6 +162,10 @@ export class SimulationRenderer {
|
||||||
} else {
|
} else {
|
||||||
this.mouseManager.update()
|
this.mouseManager.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for (const gate of this.simulation.gates) {
|
||||||
|
// gate.transform.rotation += 0.01
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSelected() {
|
public getSelected() {
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
import { BehaviorSubject } from 'rxjs'
|
import { BehaviorSubject } from 'rxjs'
|
||||||
|
import { allCombinations } from '../helpers/allCombinations'
|
||||||
|
import { rotateAroundVector } from '../../vector2/helpers/rotate'
|
||||||
|
|
||||||
export type vector2 = [number, number]
|
export type vector2 = [number, number]
|
||||||
|
export type vector3 = [number, number, number]
|
||||||
export type vector4 = [number, number, number, number]
|
export type vector4 = [number, number, number, number]
|
||||||
|
export type vector8 = [
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number,
|
||||||
|
number
|
||||||
|
]
|
||||||
|
|
||||||
export class Transform {
|
export class Transform {
|
||||||
public constructor(
|
public constructor(
|
||||||
|
@ -14,6 +27,40 @@ export class Transform {
|
||||||
return [...this.position, ...this.scale] as vector4
|
return [...this.position, ...this.scale] as vector4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getPoints() {
|
||||||
|
const combinations = Array.from(allCombinations([0, 1], [0, 1]))
|
||||||
|
|
||||||
|
// those are not in the right order
|
||||||
|
const points = combinations.map(combination => [
|
||||||
|
this.x + this.height * combination[0],
|
||||||
|
this.y + this.width * combination[1]
|
||||||
|
])
|
||||||
|
|
||||||
|
const pointsInTheRightOrder = [
|
||||||
|
points[0],
|
||||||
|
points[1],
|
||||||
|
points[3],
|
||||||
|
points[2]
|
||||||
|
] as vector2[]
|
||||||
|
|
||||||
|
const result = pointsInTheRightOrder.map(point =>
|
||||||
|
rotateAroundVector(point, this.center, this.rotation)
|
||||||
|
) as vector2[]
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEdges() {
|
||||||
|
const points = this.getPoints()
|
||||||
|
const edges = []
|
||||||
|
|
||||||
|
for (let index = 0; index < points.length; index++) {
|
||||||
|
edges.push([points[index], points[(index + 1) % points.length]])
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges as [vector2, vector2][]
|
||||||
|
}
|
||||||
|
|
||||||
/** Short forms for random stuff */
|
/** Short forms for random stuff */
|
||||||
|
|
||||||
get x() {
|
get x() {
|
||||||
|
@ -40,6 +87,10 @@ export class Transform {
|
||||||
return this.y + this.height
|
return this.y + this.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get center() {
|
||||||
|
return [this.x + this.width / 2, this.y + this.height / 2] as vector2
|
||||||
|
}
|
||||||
|
|
||||||
set x(value: number) {
|
set x(value: number) {
|
||||||
this.position = [value, this.y]
|
this.position = [value, this.y]
|
||||||
}
|
}
|
||||||
|
|
8
src/modules/simulation/helpers/allCombinations.ts
Normal file
8
src/modules/simulation/helpers/allCombinations.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export function* allCombinations<T>(first: T[], second: T[]): Iterable<[T, T]> {
|
||||||
|
for (const item of first) {
|
||||||
|
// TODO: change name
|
||||||
|
for (const element of second) {
|
||||||
|
yield [item, element]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/modules/simulation/helpers/drawPolygon.ts
Normal file
23
src/modules/simulation/helpers/drawPolygon.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { vector2 } from '../classes/Transform'
|
||||||
|
|
||||||
|
export const drawPolygon = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
points: vector2[],
|
||||||
|
fill = true,
|
||||||
|
stroke = false
|
||||||
|
) => {
|
||||||
|
ctx.beginPath()
|
||||||
|
|
||||||
|
for (const point of points) {
|
||||||
|
ctx.lineTo(...point)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.closePath()
|
||||||
|
|
||||||
|
if (fill) {
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
if (stroke) {
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,30 +2,16 @@ import { Transform } from '../classes/Transform'
|
||||||
|
|
||||||
export const drawRotatedSquare = (
|
export const drawRotatedSquare = (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
{ position, scale, rotation }: Transform,
|
{ position, scale, rotation }: Transform
|
||||||
rotationMode = 0
|
|
||||||
) => {
|
) => {
|
||||||
ctx.save()
|
ctx.save()
|
||||||
|
|
||||||
ctx.translate(...position)
|
ctx.translate(...position)
|
||||||
|
ctx.translate(scale[0] / 2, scale[1] / 2)
|
||||||
if (rotationMode === 0) {
|
|
||||||
ctx.translate(scale[0] / 2, scale[1] / 2)
|
|
||||||
} else if (rotationMode === 1) {
|
|
||||||
ctx.translate(scale[0], scale[1])
|
|
||||||
} else if (rotationMode === 1) {
|
|
||||||
ctx.translate(0, scale[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.rotate(rotation)
|
ctx.rotate(rotation)
|
||||||
|
|
||||||
if (rotationMode === 0) {
|
ctx.fillRect(scale[0] / -2, scale[1] / -2, ...scale)
|
||||||
ctx.fillRect(scale[0] / -2, scale[1] / -2, ...scale)
|
|
||||||
} else if (rotationMode === 1) {
|
|
||||||
ctx.fillRect(-scale[0], -scale[1], ...scale)
|
|
||||||
} else if (rotationMode === -1) {
|
|
||||||
ctx.fillRect(0, 0, ...scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
|
|
8
src/modules/simulation/helpers/projectPoint.ts
Normal file
8
src/modules/simulation/helpers/projectPoint.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { vector3, vector2 } from '../classes/Transform'
|
||||||
|
|
||||||
|
export const projectPointOnPlane = (point: vector3, light: vector3) =>
|
||||||
|
point.slice(0, 2).map((position, index) => {
|
||||||
|
const delta = light[index] - position
|
||||||
|
|
||||||
|
return light[index] - (delta + (point[2] * delta) / light[2])
|
||||||
|
}) as vector2
|
|
@ -1,13 +1,7 @@
|
||||||
import { Gate } from '../classes/Gate'
|
import { Gate } from '../classes/Gate'
|
||||||
import { drawRotatedSquare } from './drawRotatedSquare'
|
import { drawRotatedSquare } from './drawRotatedSquare'
|
||||||
import { MouseManager } from '../classes/MouseManager'
|
|
||||||
|
|
||||||
export const renderGate = (ctx: CanvasRenderingContext2D, gate: Gate) => {
|
export const renderGate = (ctx: CanvasRenderingContext2D, gate: Gate) => {
|
||||||
let mode = 0
|
|
||||||
|
|
||||||
if (gate.transform.rotation > 0) mode = 1
|
|
||||||
else if (gate.transform.rotation < 0) mode = -1
|
|
||||||
|
|
||||||
ctx.fillStyle = gate.color
|
ctx.fillStyle = gate.color
|
||||||
drawRotatedSquare(ctx, gate.transform, mode)
|
drawRotatedSquare(ctx, gate.transform)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,110 @@
|
||||||
import { Gate } from '../classes/Gate'
|
import { Gate } from '../classes/Gate'
|
||||||
import { vector2, Transform } from '../classes/Transform'
|
import { projectPointOnPlane } from './projectPoint'
|
||||||
import { clamp } from './clamp'
|
import { drawPolygon } from './drawPolygon'
|
||||||
import { drawRotatedSquare } from './drawRotatedSquare'
|
import { vector3, vector2, vector4, vector8 } from '../classes/Transform'
|
||||||
|
import { checkIntersection } from 'line-intersect'
|
||||||
|
import { reverseArray } from './reverseArray'
|
||||||
|
import { length, add, invert } from '../../vector2/helpers/basic'
|
||||||
|
|
||||||
|
export const pointRecivesLight = (
|
||||||
|
points: vector2[], //this needs to have an even length
|
||||||
|
light: vector3,
|
||||||
|
index: number,
|
||||||
|
ctx: CanvasRenderingContext2D
|
||||||
|
) => {
|
||||||
|
const point = points[index]
|
||||||
|
const oposittePoint = points[(index + points.length / 2) % points.length]
|
||||||
|
|
||||||
|
const edgesToCheck = [
|
||||||
|
[oposittePoint, points[(index + 1) % points.length]],
|
||||||
|
[oposittePoint, points[index === 0 ? points.length - 1 : index - 1]]
|
||||||
|
].map(points => points.flat() as vector4)
|
||||||
|
|
||||||
|
for (const edge of edgesToCheck) {
|
||||||
|
const intersectionCheckParameters = [
|
||||||
|
[light[0], light[1]],
|
||||||
|
point,
|
||||||
|
edge
|
||||||
|
].flat() as vector8
|
||||||
|
|
||||||
|
const result = checkIntersection(...intersectionCheckParameters).type
|
||||||
|
|
||||||
|
if (result === 'intersecting') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
export const renderGateShadow = (
|
export const renderGateShadow = (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
color: string,
|
color: string,
|
||||||
gate: Gate
|
gate: Gate,
|
||||||
|
gateHeight: number,
|
||||||
|
light: vector3
|
||||||
) => {
|
) => {
|
||||||
const scale = gate.transform.scale
|
|
||||||
|
|
||||||
ctx.fillStyle = color
|
ctx.fillStyle = color
|
||||||
|
|
||||||
drawRotatedSquare(
|
const points = gate.transform.getPoints()
|
||||||
ctx,
|
const exposedPoints = points.filter((point, index) =>
|
||||||
new Transform(gate.shadow, scale, gate.transform.rotation)
|
pointRecivesLight(points, light, index, ctx)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let includedPoints = [...points]
|
||||||
|
|
||||||
|
if (exposedPoints.length === 3) {
|
||||||
|
let min = Infinity
|
||||||
|
let current: null | vector2 = null
|
||||||
|
|
||||||
|
for (const point of exposedPoints) {
|
||||||
|
const size = length(
|
||||||
|
add(point, invert(light.slice(0, 2) as vector2))
|
||||||
|
)
|
||||||
|
|
||||||
|
if (size < min) {
|
||||||
|
min = size
|
||||||
|
current = point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current) {
|
||||||
|
includedPoints.splice(includedPoints.indexOf(current), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
includedPoints[0][1] < light[1] &&
|
||||||
|
includedPoints[1][1] < light[1] &&
|
||||||
|
!(includedPoints[2][1] > light[1])
|
||||||
|
) {
|
||||||
|
const temporary = includedPoints[0]
|
||||||
|
includedPoints[0] = includedPoints[1]
|
||||||
|
includedPoints[1] = temporary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exposedPoints.length === 2) {
|
||||||
|
includedPoints = points.filter(
|
||||||
|
point => exposedPoints.indexOf(point) === -1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const projections = includedPoints.map(point =>
|
||||||
|
// ts doesnt let me do [...point, gateHeight]
|
||||||
|
projectPointOnPlane([point[0], point[1], gateHeight], light)
|
||||||
|
)
|
||||||
|
|
||||||
|
const polygon = [includedPoints, reverseArray(projections)].flat()
|
||||||
|
|
||||||
|
drawPolygon(ctx, polygon)
|
||||||
|
|
||||||
|
ctx.fillStyle = 'red'
|
||||||
|
for (const point of [...includedPoints, ...projections, light]) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.ellipse(point[0], point[1], 10, 10, 0, 0, Math.PI * 2)
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.strokeStyle = 'yellow'
|
||||||
|
drawPolygon(ctx, points, false, true)
|
||||||
}
|
}
|
||||||
|
|
10
src/modules/simulation/helpers/reverseArray.ts
Normal file
10
src/modules/simulation/helpers/reverseArray.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export const reverseArray = <T>(array: T[]) => {
|
||||||
|
const arr: T[] = []
|
||||||
|
|
||||||
|
for (let index = array.length - 1; index >= 0; index--) {
|
||||||
|
const element = array[index]
|
||||||
|
arr.push(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
|
@ -2,8 +2,15 @@ import { vector2 } from '../../simulation/classes/Transform'
|
||||||
|
|
||||||
// Basic stuff for arrays
|
// Basic stuff for arrays
|
||||||
|
|
||||||
export const add = (first: vector2, second: vector2) =>
|
// If i don't say vector2 as the type adnotation
|
||||||
first.map((value, index) => value + second[index]) as vector2
|
// ts will throw some errors (because this is recursive)
|
||||||
|
export const add = (...vectors: vector2[]): vector2 => {
|
||||||
|
const first = vectors[0]
|
||||||
|
const others = vectors.slice(1)
|
||||||
|
const othersSum = others.length > 1 ? add(...others) : others[0]
|
||||||
|
|
||||||
|
return first.map((value, index) => value + othersSum[index]) as vector2
|
||||||
|
}
|
||||||
|
|
||||||
export const invert = (vector: vector2) => vector.map(val => -val) as vector2
|
export const invert = (vector: vector2) => vector.map(val => -val) as vector2
|
||||||
|
|
||||||
|
|
22
src/modules/vector2/helpers/rotate.ts
Normal file
22
src/modules/vector2/helpers/rotate.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { vector2 } from '../../simulation/classes/Transform'
|
||||||
|
import { add, invert } from './basic'
|
||||||
|
|
||||||
|
const { cos, sin } = Math
|
||||||
|
|
||||||
|
export const rotate = (vector: vector2, angle: number): vector2 => {
|
||||||
|
const x = cos(angle) * vector[0] - sin(angle) * vector[1]
|
||||||
|
const y = sin(angle) * vector[0] + cos(angle) * vector[1]
|
||||||
|
|
||||||
|
return [x, y]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rotateAroundVector = (
|
||||||
|
vector: vector2,
|
||||||
|
around: vector2,
|
||||||
|
angle: number
|
||||||
|
) => {
|
||||||
|
const translated = add(vector, invert(around))
|
||||||
|
const rotated = rotate(translated, angle)
|
||||||
|
|
||||||
|
return add(rotated, around)
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": { "*": ["types/*"] },
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
|
|
13
types/line-intersect.d.ts
vendored
Normal file
13
types/line-intersect.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export const checkIntersection: (
|
||||||
|
x1: number,
|
||||||
|
y1: number,
|
||||||
|
x2: number,
|
||||||
|
y2: number,
|
||||||
|
x3: number,
|
||||||
|
y3: number,
|
||||||
|
x4: number,
|
||||||
|
y4: number
|
||||||
|
) => {
|
||||||
|
type: 'colinear' | 'parallel' | 'none' | 'intersecting'
|
||||||
|
}
|
||||||
|
export const colinearPointWithinSegment: any
|
Loading…
Reference in a new issue