finished the shadows

This commit is contained in:
Matei Adriel 2019-07-16 12:15:49 +03:00
parent fc9833d830
commit 92937d16ce
9 changed files with 75 additions and 132 deletions

5
package-lock.json generated
View file

@ -5609,11 +5609,6 @@
"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",

View file

@ -40,7 +40,6 @@
"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",

View file

@ -1,13 +1,21 @@
import { Transform, vector2 } from './Transform' import { Transform, vector2 } from './Transform'
import { Screen } from '../../core/classes/Screen' import { Screen } from '../../core/classes/Screen'
import { relativeTo } from '../../vector2/helpers/basic'
export class Camera { export class Camera {
private screen = new Screen() private screen = new Screen()
public transform = new Transform([0, 0], [this.screen.x, this.screen.y]) 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) { public toWordPostition(position: vector2) {
return position.map( return relativeTo(this.transform.position, position)
(value, index) => value + this.transform.position[index]
) as vector2
} }
} }

View file

@ -46,7 +46,10 @@ export class MouseManager {
} }
public getDirection() { public getDirection() {
return clamp(-1, 1, this.total / this.history.length) if (this.history.length) {
return clamp(-1, 1, this.total / this.history.length)
}
return 0
} }
public update(maybeAgain = true) { public update(maybeAgain = true) {

View file

@ -9,6 +9,7 @@ import { renderGate } from '../helpers/renderGate'
import { renderGateShadow } from '../helpers/renderGateShadow' import { renderGateShadow } from '../helpers/renderGateShadow'
import { MouseManager } from './MouseManager' import { MouseManager } from './MouseManager'
import { Screen } from '../../core/classes/Screen' import { Screen } from '../../core/classes/Screen'
import { relativeTo, add, invert } from '../../vector2/helpers/basic'
export interface SimulationRendererOptions { export interface SimulationRendererOptions {
shadows: { shadows: {
@ -27,7 +28,7 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
enabled: true, enabled: true,
color: 'rgba(0,0,0,0.3)', color: 'rgba(0,0,0,0.3)',
gateHeight: 10, gateHeight: 10,
lightHeight: 50 lightHeight: 100
}, },
dnd: { dnd: {
rotation: Math.PI / 12 // 7.5 degrees rotation: Math.PI / 12 // 7.5 degrees
@ -41,9 +42,13 @@ export class SimulationRenderer {
public mouseMoveOutput = new Subject<MouseEventInfo>() public mouseMoveOutput = new Subject<MouseEventInfo>()
public selectedGate: number | null = null public selectedGate: number | null = null
public selectOffset: vector2 = [0, 0] public lastMousePosition: vector2 = [0, 0]
public movedSelection = false public movedSelection = false
// first bit = dragging
// second bit = moving around
private mouseState = 0b00
private options: SimulationRendererOptions private options: SimulationRendererOptions
private mouseManager = new MouseManager(this.mouseMoveOutput) private mouseManager = new MouseManager(this.mouseMoveOutput)
private screen = new Screen() private screen = new Screen()
@ -71,10 +76,11 @@ export class SimulationRenderer {
if (pointInSquare(worldPosition, transform)) { if (pointInSquare(worldPosition, transform)) {
this.mouseManager.clear(worldPosition[0]) this.mouseManager.clear(worldPosition[0])
this.mouseState |= 1
this.movedSelection = false this.movedSelection = false
this.selectedGate = id this.selectedGate = id
this.selectOffset = worldPosition.map( this.lastMousePosition = worldPosition.map(
(position, index) => (position, index) =>
position - transform.position[index] position - transform.position[index]
) as vector2 ) as vector2
@ -84,8 +90,13 @@ export class SimulationRenderer {
if (gateNode) { if (gateNode) {
return this.simulation.gates.moveOnTop(gateNode) return this.simulation.gates.moveOnTop(gateNode)
} }
return
} }
} }
this.lastMousePosition = worldPosition
this.mouseState |= 2
}) })
this.mouseUpOutput.subscribe(event => { this.mouseUpOutput.subscribe(event => {
@ -97,34 +108,56 @@ export class SimulationRenderer {
} }
this.selectedGate = null this.selectedGate = null
this.mouseState &= 0
} }
this.mouseState &= 0b00
}) })
this.mouseMoveOutput.subscribe(event => { 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) const gate = this.getGateById(this.selectedGate)
if (!gate || !gate.data) return if (!gate || !gate.data) return
const transform = gate.data.transform const transform = gate.data.transform
const worldPosition = this.camera.toWordPostition(
event.position
)
transform.x = worldPosition[0] - this.selectOffset[0] transform.x = worldPosition[0] - this.lastMousePosition[0]
transform.y = worldPosition[1] - this.selectOffset[1] transform.y = worldPosition[1] - this.lastMousePosition[1]
if (!this.movedSelection) { if (!this.movedSelection) {
this.movedSelection = true 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) { public render(ctx: CanvasRenderingContext2D) {
this.clear(ctx) 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 // render gates
for (const gate of this.simulation.gates) { for (const gate of this.simulation.gates) {
@ -140,11 +173,12 @@ export class SimulationRenderer {
renderGate(ctx, gate) renderGate(ctx, gate)
} }
ctx.translate(...invert(this.camera.transform.position))
} }
public clear(ctx: CanvasRenderingContext2D) { public clear(ctx: CanvasRenderingContext2D) {
const boundingBox = this.camera.transform.getBoundingBox() ctx.clearRect(0, 0, ...this.camera.transform.scale)
ctx.clearRect(...boundingBox)
} }
public getGateById(id: number) { public getGateById(id: number) {
@ -159,6 +193,9 @@ export class SimulationRenderer {
selected.transform.rotation = selected.transform.rotation =
this.mouseManager.getDirection() * this.options.dnd.rotation this.mouseManager.getDirection() * this.options.dnd.rotation
} else { } else {
if (selected) {
selected.transform.rotation = 0
}
this.mouseManager.update() this.mouseManager.update()
} }
} }

View file

@ -1,4 +1,3 @@
import { BehaviorSubject } from 'rxjs'
import { allCombinations } from '../helpers/allCombinations' import { allCombinations } from '../helpers/allCombinations'
import { rotateAroundVector } from '../../vector2/helpers/rotate' import { rotateAroundVector } from '../../vector2/helpers/rotate'
@ -98,4 +97,12 @@ export class Transform {
set y(value: number) { set y(value: number) {
this.position = [this.x, value] this.position = [this.x, value]
} }
set width(value: number) {
this.scale = [value, this.height]
}
set height(value: number) {
this.scale = [this.width, value]
}
} }

View file

@ -1,41 +1,7 @@
import { Gate } from '../classes/Gate' import { Gate } from '../classes/Gate'
import { projectPointOnPlane } from './projectPoint' import { projectPointOnPlane } from './projectPoint'
import { drawPolygon } from './drawPolygon' import { drawPolygon } from './drawPolygon'
import { vector3, vector2, vector4, vector8 } from '../classes/Transform' import { vector3 } 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
}
export const renderGateShadow = ( export const renderGateShadow = (
ctx: CanvasRenderingContext2D, ctx: CanvasRenderingContext2D,
@ -47,66 +13,9 @@ export const renderGateShadow = (
ctx.fillStyle = color ctx.fillStyle = color
const points = gate.transform.getPoints() const points = gate.transform.getPoints()
const exposedPoints = points.filter((point, index) => const projections = points.map(point =>
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]
projectPointOnPlane([point[0], point[1], gateHeight], light) projectPointOnPlane([point[0], point[1], gateHeight], light)
) )
if (exposedPoints.length === 2) { drawPolygon(ctx, projections)
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)
} }

View file

@ -1,7 +1,5 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"paths": { "*": ["types/*"] },
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true, "esModuleInterop": true,
"jsx": "preserve", "jsx": "preserve",

View file

@ -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