finished the shadows
This commit is contained in:
parent
fc9833d830
commit
92937d16ce
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -5609,11 +5609,6 @@
|
|||
"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": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
"dependencies": {
|
||||
"@eix-js/utils": "0.0.6",
|
||||
"deepmerge": "^4.0.0",
|
||||
"line-intersect": "^2.2.1",
|
||||
"mainloop.js": "^1.0.4",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
import { Transform, vector2 } from './Transform'
|
||||
import { Screen } from '../../core/classes/Screen'
|
||||
import { relativeTo } from '../../vector2/helpers/basic'
|
||||
|
||||
export class Camera {
|
||||
private screen = new Screen()
|
||||
public transform = new Transform([0, 0], [this.screen.x, this.screen.y])
|
||||
|
||||
public constructor() {
|
||||
this.screen.height.subscribe(value => {
|
||||
this.transform.height = value
|
||||
})
|
||||
this.screen.width.subscribe(value => {
|
||||
this.transform.width = value
|
||||
})
|
||||
}
|
||||
|
||||
public toWordPostition(position: vector2) {
|
||||
return position.map(
|
||||
(value, index) => value + this.transform.position[index]
|
||||
) as vector2
|
||||
return relativeTo(this.transform.position, position)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,11 @@ export class MouseManager {
|
|||
}
|
||||
|
||||
public getDirection() {
|
||||
if (this.history.length) {
|
||||
return clamp(-1, 1, this.total / this.history.length)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
public update(maybeAgain = true) {
|
||||
if (this.lastDirection === 0) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { renderGate } from '../helpers/renderGate'
|
|||
import { renderGateShadow } from '../helpers/renderGateShadow'
|
||||
import { MouseManager } from './MouseManager'
|
||||
import { Screen } from '../../core/classes/Screen'
|
||||
import { relativeTo, add, invert } from '../../vector2/helpers/basic'
|
||||
|
||||
export interface SimulationRendererOptions {
|
||||
shadows: {
|
||||
|
@ -27,7 +28,7 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
|||
enabled: true,
|
||||
color: 'rgba(0,0,0,0.3)',
|
||||
gateHeight: 10,
|
||||
lightHeight: 50
|
||||
lightHeight: 100
|
||||
},
|
||||
dnd: {
|
||||
rotation: Math.PI / 12 // 7.5 degrees
|
||||
|
@ -41,9 +42,13 @@ export class SimulationRenderer {
|
|||
public mouseMoveOutput = new Subject<MouseEventInfo>()
|
||||
|
||||
public selectedGate: number | null = null
|
||||
public selectOffset: vector2 = [0, 0]
|
||||
public lastMousePosition: vector2 = [0, 0]
|
||||
public movedSelection = false
|
||||
|
||||
// first bit = dragging
|
||||
// second bit = moving around
|
||||
private mouseState = 0b00
|
||||
|
||||
private options: SimulationRendererOptions
|
||||
private mouseManager = new MouseManager(this.mouseMoveOutput)
|
||||
private screen = new Screen()
|
||||
|
@ -71,10 +76,11 @@ export class SimulationRenderer {
|
|||
if (pointInSquare(worldPosition, transform)) {
|
||||
this.mouseManager.clear(worldPosition[0])
|
||||
|
||||
this.mouseState |= 1
|
||||
this.movedSelection = false
|
||||
|
||||
this.selectedGate = id
|
||||
this.selectOffset = worldPosition.map(
|
||||
this.lastMousePosition = worldPosition.map(
|
||||
(position, index) =>
|
||||
position - transform.position[index]
|
||||
) as vector2
|
||||
|
@ -84,8 +90,13 @@ export class SimulationRenderer {
|
|||
if (gateNode) {
|
||||
return this.simulation.gates.moveOnTop(gateNode)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.lastMousePosition = worldPosition
|
||||
this.mouseState |= 2
|
||||
})
|
||||
|
||||
this.mouseUpOutput.subscribe(event => {
|
||||
|
@ -97,34 +108,56 @@ export class SimulationRenderer {
|
|||
}
|
||||
|
||||
this.selectedGate = null
|
||||
this.mouseState &= 0
|
||||
}
|
||||
|
||||
this.mouseState &= 0b00
|
||||
})
|
||||
|
||||
this.mouseMoveOutput.subscribe(event => {
|
||||
if (this.selectedGate !== null) {
|
||||
const worldPosition = this.camera.toWordPostition(event.position)
|
||||
|
||||
if (this.mouseState & 1 && this.selectedGate !== null) {
|
||||
const gate = this.getGateById(this.selectedGate)
|
||||
|
||||
if (!gate || !gate.data) return
|
||||
|
||||
const transform = gate.data.transform
|
||||
const worldPosition = this.camera.toWordPostition(
|
||||
event.position
|
||||
)
|
||||
|
||||
transform.x = worldPosition[0] - this.selectOffset[0]
|
||||
transform.y = worldPosition[1] - this.selectOffset[1]
|
||||
transform.x = worldPosition[0] - this.lastMousePosition[0]
|
||||
transform.y = worldPosition[1] - this.lastMousePosition[1]
|
||||
|
||||
if (!this.movedSelection) {
|
||||
this.movedSelection = true
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.mouseState >> 1) & 1) {
|
||||
const offset = invert(
|
||||
relativeTo(this.lastMousePosition, worldPosition)
|
||||
)
|
||||
|
||||
this.camera.transform.position = add(
|
||||
this.camera.transform.position,
|
||||
invert(offset)
|
||||
)
|
||||
|
||||
this.lastMousePosition = this.camera.toWordPostition(
|
||||
event.position
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D) {
|
||||
this.clear(ctx)
|
||||
|
||||
const center = this.screen.center
|
||||
ctx.translate(...this.camera.transform.position)
|
||||
|
||||
const center = relativeTo(
|
||||
this.camera.transform.position,
|
||||
this.screen.center
|
||||
)
|
||||
|
||||
// render gates
|
||||
for (const gate of this.simulation.gates) {
|
||||
|
@ -140,11 +173,12 @@ export class SimulationRenderer {
|
|||
|
||||
renderGate(ctx, gate)
|
||||
}
|
||||
|
||||
ctx.translate(...invert(this.camera.transform.position))
|
||||
}
|
||||
|
||||
public clear(ctx: CanvasRenderingContext2D) {
|
||||
const boundingBox = this.camera.transform.getBoundingBox()
|
||||
ctx.clearRect(...boundingBox)
|
||||
ctx.clearRect(0, 0, ...this.camera.transform.scale)
|
||||
}
|
||||
|
||||
public getGateById(id: number) {
|
||||
|
@ -159,6 +193,9 @@ export class SimulationRenderer {
|
|||
selected.transform.rotation =
|
||||
this.mouseManager.getDirection() * this.options.dnd.rotation
|
||||
} else {
|
||||
if (selected) {
|
||||
selected.transform.rotation = 0
|
||||
}
|
||||
this.mouseManager.update()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { BehaviorSubject } from 'rxjs'
|
||||
import { allCombinations } from '../helpers/allCombinations'
|
||||
import { rotateAroundVector } from '../../vector2/helpers/rotate'
|
||||
|
||||
|
@ -98,4 +97,12 @@ export class Transform {
|
|||
set y(value: number) {
|
||||
this.position = [this.x, value]
|
||||
}
|
||||
|
||||
set width(value: number) {
|
||||
this.scale = [value, this.height]
|
||||
}
|
||||
|
||||
set height(value: number) {
|
||||
this.scale = [this.width, value]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,7 @@
|
|||
import { Gate } from '../classes/Gate'
|
||||
import { projectPointOnPlane } from './projectPoint'
|
||||
import { drawPolygon } from './drawPolygon'
|
||||
import { vector3, vector2, vector4, vector8 } from '../classes/Transform'
|
||||
import { checkIntersection } from 'line-intersect'
|
||||
import { reverseArray } from './reverseArray'
|
||||
import { minVector, maxVector } from '../../vector2/helpers/minmaxVector'
|
||||
import { relativeTo } from '../../vector2/helpers/basic'
|
||||
|
||||
export const pointRecivesLight = (
|
||||
points: vector2[], //this needs to have an even length
|
||||
light: vector3,
|
||||
index: number
|
||||
) => {
|
||||
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
|
||||
}
|
||||
import { vector3 } from '../classes/Transform'
|
||||
|
||||
export const renderGateShadow = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
|
@ -47,66 +13,9 @@ export const renderGateShadow = (
|
|||
ctx.fillStyle = color
|
||||
|
||||
const points = gate.transform.getPoints()
|
||||
const exposedPoints = points.filter((point, index) =>
|
||||
pointRecivesLight(points, light, index)
|
||||
)
|
||||
|
||||
let includedPoints = [...points]
|
||||
|
||||
if (exposedPoints.length === 4) {
|
||||
return
|
||||
}
|
||||
|
||||
if (exposedPoints.length === 3) {
|
||||
const minimum = minVector(
|
||||
...exposedPoints.map(point =>
|
||||
relativeTo(point, light.slice(0, 2) as vector2)
|
||||
)
|
||||
)
|
||||
|
||||
includedPoints.splice(includedPoints.indexOf(exposedPoints[minimum]), 1)
|
||||
}
|
||||
|
||||
if (includedPoints.length === 3) {
|
||||
const maximum = maxVector(
|
||||
...includedPoints.map(point =>
|
||||
relativeTo(point, light.slice(0, 2) as vector2)
|
||||
)
|
||||
)
|
||||
|
||||
const otherIndices = [(maximum + 1) % 3, (maximum + 2) % 3]
|
||||
const newIndices = [otherIndices[0], maximum, otherIndices[1]]
|
||||
|
||||
includedPoints = newIndices.map(index => includedPoints[index])
|
||||
}
|
||||
|
||||
let projections = includedPoints.map(point =>
|
||||
// ts doesnt let me do [...point, gateHeight]
|
||||
const projections = points.map(point =>
|
||||
projectPointOnPlane([point[0], point[1], gateHeight], light)
|
||||
)
|
||||
|
||||
if (exposedPoints.length === 2) {
|
||||
const toProject = includedPoints.filter(
|
||||
point => exposedPoints.indexOf(point) === -1
|
||||
)
|
||||
|
||||
projections = toProject.map(point =>
|
||||
projectPointOnPlane([point[0], point[1], gateHeight], light)
|
||||
)
|
||||
|
||||
includedPoints = reverseArray(exposedPoints)
|
||||
|
||||
const firstIncludedIndex = points.indexOf(toProject[0])
|
||||
const firstExposedPointIndex = points.indexOf(includedPoints[0])
|
||||
|
||||
if ((firstIncludedIndex + 2) % 4 === firstExposedPointIndex % 4) {
|
||||
const temporary = includedPoints[0]
|
||||
includedPoints[0] = includedPoints[1]
|
||||
includedPoints[1] = temporary
|
||||
}
|
||||
}
|
||||
|
||||
const polygon = [includedPoints, reverseArray(projections)].flat()
|
||||
|
||||
drawPolygon(ctx, polygon)
|
||||
drawPolygon(ctx, projections)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": { "*": ["types/*"] },
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
|
|
13
types/line-intersect.d.ts
vendored
13
types/line-intersect.d.ts
vendored
|
@ -1,13 +0,0 @@
|
|||
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