new file structure
This commit is contained in:
parent
92937d16ce
commit
a9bc28478e
|
@ -2,8 +2,10 @@ import React, { Component, createRef, Ref, RefObject } from 'react'
|
||||||
import FluidCanvas, { MouseEventInfo } from './FluidCanvas'
|
import FluidCanvas, { MouseEventInfo } from './FluidCanvas'
|
||||||
import loop from 'mainloop.js'
|
import loop from 'mainloop.js'
|
||||||
import { Gate } from '../../simulation/classes/Gate'
|
import { Gate } from '../../simulation/classes/Gate'
|
||||||
import { SimulationRenderer } from '../../simulation/classes/SimulationRenderer'
|
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
|
import { renderSimulation } from '../../simulationRenderer/helpers/renderSimulation'
|
||||||
|
import { updateSimulation } from '../../simulationRenderer/helpers/updateSimulation'
|
||||||
|
|
||||||
class Canvas extends Component {
|
class Canvas extends Component {
|
||||||
private canvasRef: RefObject<HTMLCanvasElement> = createRef()
|
private canvasRef: RefObject<HTMLCanvasElement> = createRef()
|
||||||
|
@ -25,9 +27,10 @@ class Canvas extends Component {
|
||||||
this.renderer.simulation.push(foo, bar)
|
this.renderer.simulation.push(foo, bar)
|
||||||
|
|
||||||
loop.setDraw(() => {
|
loop.setDraw(() => {
|
||||||
if (this.renderingContext)
|
if (this.renderingContext) {
|
||||||
this.renderer.render(this.renderingContext)
|
renderSimulation(this.renderingContext, this.renderer)
|
||||||
}).setUpdate(delta => this.renderer.update(delta))
|
}
|
||||||
|
}).setUpdate(delta => updateSimulation(this.renderer, delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export type DeepPartial<T> = { [key in keyof T]?: DeepPartial<T[key]> }
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Transform, vector2 } from './Transform'
|
import { Transform, vector2 } from '../../simulation/classes/Transform'
|
||||||
import { Screen } from '../../core/classes/Screen'
|
import { Screen } from '../../core/classes/Screen'
|
||||||
import { relativeTo } from '../../vector2/helpers/basic'
|
import { relativeTo } from '../../vector2/helpers/basic'
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Singleton } from '@eix-js/utils'
|
import { Singleton } from '@eix-js/utils'
|
||||||
import { MouseSubject } from '../../core/types/MouseSubject'
|
import { MouseSubject } from '../../core/types/MouseSubject'
|
||||||
import { clamp } from '../helpers/clamp'
|
import { clamp } from '../../simulation/helpers/clamp'
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
export class MouseManager {
|
export class MouseVelocityManager {
|
||||||
private history: number[] = []
|
private history: number[] = []
|
||||||
private total = 0
|
private total = 0
|
||||||
private limit = 10
|
private limit = 10
|
|
@ -1,63 +1,39 @@
|
||||||
import { Camera } from './Camera'
|
import { Camera } from './Camera'
|
||||||
import { Simulation } from './Simulation'
|
import { Simulation } from '../../simulation/classes/Simulation'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { MouseEventInfo } from '../../core/components/FluidCanvas'
|
import { MouseEventInfo } from '../../core/components/FluidCanvas'
|
||||||
import { pointInSquare } from '../helpers/pointInSquare'
|
import { pointInSquare } from '../helpers/pointInSquare'
|
||||||
import { vector2 } from './Transform'
|
import { vector2 } from '../../simulation/classes/Transform'
|
||||||
import merge from 'deepmerge'
|
import { MouseVelocityManager } from './MouseVelocityManager'
|
||||||
import { renderGate } from '../helpers/renderGate'
|
|
||||||
import { renderGateShadow } from '../helpers/renderGateShadow'
|
|
||||||
import { MouseManager } from './MouseManager'
|
|
||||||
import { Screen } from '../../core/classes/Screen'
|
import { Screen } from '../../core/classes/Screen'
|
||||||
import { relativeTo, add, invert } from '../../vector2/helpers/basic'
|
import { relativeTo, add, invert } from '../../vector2/helpers/basic'
|
||||||
|
import { SimulationRendererOptions } from '../types/SimulationRendererOptions'
|
||||||
export interface SimulationRendererOptions {
|
import { defaultSimulationRendererOptions } from '../constants'
|
||||||
shadows: {
|
import merge from 'deepmerge'
|
||||||
enabled: boolean
|
|
||||||
color: string
|
|
||||||
lightHeight: number
|
|
||||||
gateHeight: number
|
|
||||||
}
|
|
||||||
dnd: {
|
|
||||||
rotation: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
|
||||||
shadows: {
|
|
||||||
enabled: true,
|
|
||||||
color: 'rgba(0,0,0,0.3)',
|
|
||||||
gateHeight: 10,
|
|
||||||
lightHeight: 100
|
|
||||||
},
|
|
||||||
dnd: {
|
|
||||||
rotation: Math.PI / 12 // 7.5 degrees
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SimulationRenderer {
|
export class SimulationRenderer {
|
||||||
public camera = new Camera()
|
|
||||||
public mouseDownOutput = new Subject<MouseEventInfo>()
|
public mouseDownOutput = new Subject<MouseEventInfo>()
|
||||||
public mouseUpOutput = new Subject<MouseEventInfo>()
|
public mouseUpOutput = new Subject<MouseEventInfo>()
|
||||||
public mouseMoveOutput = new Subject<MouseEventInfo>()
|
public mouseMoveOutput = new Subject<MouseEventInfo>()
|
||||||
|
|
||||||
public selectedGate: number | null = null
|
|
||||||
public lastMousePosition: vector2 = [0, 0]
|
|
||||||
public movedSelection = false
|
|
||||||
|
|
||||||
// first bit = dragging
|
// first bit = dragging
|
||||||
// second bit = moving around
|
// second bit = moving around
|
||||||
private mouseState = 0b00
|
private mouseState = 0b00
|
||||||
|
|
||||||
private options: SimulationRendererOptions
|
private selectedGate: number | null = null
|
||||||
private mouseManager = new MouseManager(this.mouseMoveOutput)
|
private gateSelectionOffset: vector2 = [0, 0]
|
||||||
private screen = new Screen()
|
|
||||||
|
public movedSelection = false
|
||||||
|
public options: SimulationRendererOptions
|
||||||
|
public mouseManager = new MouseVelocityManager(this.mouseMoveOutput)
|
||||||
|
public screen = new Screen()
|
||||||
|
public camera = new Camera()
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
options: Partial<SimulationRendererOptions> = {},
|
options: Partial<SimulationRendererOptions> = {},
|
||||||
public simulation = new Simulation()
|
public simulation = new Simulation()
|
||||||
) {
|
) {
|
||||||
this.options = merge(options, defaultSimulationRendererOptions)
|
this.options = merge(defaultSimulationRendererOptions, options)
|
||||||
|
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
@ -80,7 +56,7 @@ export class SimulationRenderer {
|
||||||
this.movedSelection = false
|
this.movedSelection = false
|
||||||
|
|
||||||
this.selectedGate = id
|
this.selectedGate = id
|
||||||
this.lastMousePosition = worldPosition.map(
|
this.gateSelectionOffset = worldPosition.map(
|
||||||
(position, index) =>
|
(position, index) =>
|
||||||
position - transform.position[index]
|
position - transform.position[index]
|
||||||
) as vector2
|
) as vector2
|
||||||
|
@ -95,7 +71,7 @@ export class SimulationRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastMousePosition = worldPosition
|
this.gateSelectionOffset = worldPosition
|
||||||
this.mouseState |= 2
|
this.mouseState |= 2
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -124,8 +100,8 @@ export class SimulationRenderer {
|
||||||
|
|
||||||
const transform = gate.data.transform
|
const transform = gate.data.transform
|
||||||
|
|
||||||
transform.x = worldPosition[0] - this.lastMousePosition[0]
|
transform.x = worldPosition[0] - this.gateSelectionOffset[0]
|
||||||
transform.y = worldPosition[1] - this.lastMousePosition[1]
|
transform.y = worldPosition[1] - this.gateSelectionOffset[1]
|
||||||
|
|
||||||
if (!this.movedSelection) {
|
if (!this.movedSelection) {
|
||||||
this.movedSelection = true
|
this.movedSelection = true
|
||||||
|
@ -134,7 +110,7 @@ export class SimulationRenderer {
|
||||||
|
|
||||||
if ((this.mouseState >> 1) & 1) {
|
if ((this.mouseState >> 1) & 1) {
|
||||||
const offset = invert(
|
const offset = invert(
|
||||||
relativeTo(this.lastMousePosition, worldPosition)
|
relativeTo(this.gateSelectionOffset, worldPosition)
|
||||||
)
|
)
|
||||||
|
|
||||||
this.camera.transform.position = add(
|
this.camera.transform.position = add(
|
||||||
|
@ -142,64 +118,17 @@ export class SimulationRenderer {
|
||||||
invert(offset)
|
invert(offset)
|
||||||
)
|
)
|
||||||
|
|
||||||
this.lastMousePosition = this.camera.toWordPostition(
|
this.gateSelectionOffset = this.camera.toWordPostition(
|
||||||
event.position
|
event.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(ctx: CanvasRenderingContext2D) {
|
|
||||||
this.clear(ctx)
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (this.options.shadows.enabled) {
|
|
||||||
renderGateShadow(
|
|
||||||
ctx,
|
|
||||||
this.options.shadows.color,
|
|
||||||
gate,
|
|
||||||
this.options.shadows.gateHeight,
|
|
||||||
[center[0], center[1], this.options.shadows.lightHeight]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderGate(ctx, gate)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.translate(...invert(this.camera.transform.position))
|
|
||||||
}
|
|
||||||
|
|
||||||
public clear(ctx: CanvasRenderingContext2D) {
|
|
||||||
ctx.clearRect(0, 0, ...this.camera.transform.scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
public getGateById(id: number) {
|
public getGateById(id: number) {
|
||||||
return this.simulation.gates.get(id)
|
return this.simulation.gates.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(delta: number) {
|
|
||||||
const selected = this.getSelected()
|
|
||||||
|
|
||||||
if (selected && this.movedSelection) {
|
|
||||||
this.mouseManager.update()
|
|
||||||
selected.transform.rotation =
|
|
||||||
this.mouseManager.getDirection() * this.options.dnd.rotation
|
|
||||||
} else {
|
|
||||||
if (selected) {
|
|
||||||
selected.transform.rotation = 0
|
|
||||||
}
|
|
||||||
this.mouseManager.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSelected() {
|
public getSelected() {
|
||||||
if (this.selectedGate === null) return null
|
if (this.selectedGate === null) return null
|
||||||
|
|
13
src/modules/simulationRenderer/constants.ts
Normal file
13
src/modules/simulationRenderer/constants.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { SimulationRendererOptions } from './types/SimulationRendererOptions'
|
||||||
|
|
||||||
|
export const defaultSimulationRendererOptions: SimulationRendererOptions = {
|
||||||
|
shadows: {
|
||||||
|
enabled: true,
|
||||||
|
color: 'rgba(0,0,0,0.3)',
|
||||||
|
gateHeight: 10,
|
||||||
|
lightHeight: 100
|
||||||
|
},
|
||||||
|
dnd: {
|
||||||
|
rotation: Math.PI / 12 // 7.5 degrees
|
||||||
|
}
|
||||||
|
}
|
8
src/modules/simulationRenderer/helpers/clearCanvas.ts
Normal file
8
src/modules/simulationRenderer/helpers/clearCanvas.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { SimulationRenderer } from '../classes/SimulationRenderer'
|
||||||
|
|
||||||
|
export const clearCanvas = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
renderer: SimulationRenderer
|
||||||
|
) => {
|
||||||
|
ctx.clearRect(0, 0, ...renderer.camera.transform.scale)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { vector2 } from '../classes/Transform'
|
import { vector2 } from '../../simulation/classes/Transform'
|
||||||
|
|
||||||
export const drawPolygon = (
|
export const drawPolygon = (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
|
@ -1,4 +1,4 @@
|
||||||
import { Transform } from '../classes/Transform'
|
import { Transform } from '../../simulation/classes/Transform'
|
||||||
|
|
||||||
export const drawRotatedSquare = (
|
export const drawRotatedSquare = (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
|
@ -1,5 +1,4 @@
|
||||||
import { vector2, Transform } from '../classes/Transform'
|
import { vector2, Transform } from '../../simulation/classes/Transform'
|
||||||
import { LruCacheNode } from '@eix-js/utils'
|
|
||||||
|
|
||||||
export const pointInSquare = (point: vector2, square: Transform) => {
|
export const pointInSquare = (point: vector2, square: Transform) => {
|
||||||
return (
|
return (
|
|
@ -1,4 +1,4 @@
|
||||||
import { vector3, vector2 } from '../classes/Transform'
|
import { vector3, vector2 } from '../../simulation/classes/Transform'
|
||||||
|
|
||||||
export const projectPointOnPlane = (point: vector3, light: vector3) =>
|
export const projectPointOnPlane = (point: vector3, light: vector3) =>
|
||||||
point.slice(0, 2).map((position, index) => {
|
point.slice(0, 2).map((position, index) => {
|
|
@ -1,4 +1,4 @@
|
||||||
import { Gate } from '../classes/Gate'
|
import { Gate } from '../../simulation/classes/Gate'
|
||||||
import { drawRotatedSquare } from './drawRotatedSquare'
|
import { drawRotatedSquare } from './drawRotatedSquare'
|
||||||
|
|
||||||
export const renderGate = (ctx: CanvasRenderingContext2D, gate: Gate) => {
|
export const renderGate = (ctx: CanvasRenderingContext2D, gate: Gate) => {
|
|
@ -1,7 +1,7 @@
|
||||||
import { Gate } from '../classes/Gate'
|
import { Gate } from '../../simulation/classes/Gate'
|
||||||
import { projectPointOnPlane } from './projectPoint'
|
import { projectPointOnPlane } from './projectPoint'
|
||||||
import { drawPolygon } from './drawPolygon'
|
import { drawPolygon } from './drawPolygon'
|
||||||
import { vector3 } from '../classes/Transform'
|
import { vector3 } from '../../simulation/classes/Transform'
|
||||||
|
|
||||||
export const renderGateShadow = (
|
export const renderGateShadow = (
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
36
src/modules/simulationRenderer/helpers/renderSimulation.ts
Normal file
36
src/modules/simulationRenderer/helpers/renderSimulation.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { SimulationRenderer } from '../classes/SimulationRenderer'
|
||||||
|
import { relativeTo, invert } from '../../vector2/helpers/basic'
|
||||||
|
import { renderGateShadow } from './renderGateShadow'
|
||||||
|
import { renderGate } from './renderGate'
|
||||||
|
import { clearCanvas } from './clearCanvas'
|
||||||
|
|
||||||
|
export const renderSimulation = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
renderer: SimulationRenderer
|
||||||
|
) => {
|
||||||
|
clearCanvas(ctx, renderer)
|
||||||
|
|
||||||
|
ctx.translate(...renderer.camera.transform.position)
|
||||||
|
|
||||||
|
const center = relativeTo(
|
||||||
|
renderer.camera.transform.position,
|
||||||
|
renderer.screen.center
|
||||||
|
)
|
||||||
|
|
||||||
|
// render gates
|
||||||
|
for (const gate of renderer.simulation.gates) {
|
||||||
|
if (renderer.options.shadows.enabled) {
|
||||||
|
renderGateShadow(
|
||||||
|
ctx,
|
||||||
|
renderer.options.shadows.color,
|
||||||
|
gate,
|
||||||
|
renderer.options.shadows.gateHeight,
|
||||||
|
[center[0], center[1], renderer.options.shadows.lightHeight]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGate(ctx, gate)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.translate(...invert(renderer.camera.transform.position))
|
||||||
|
}
|
19
src/modules/simulationRenderer/helpers/updateSimulation.ts
Normal file
19
src/modules/simulationRenderer/helpers/updateSimulation.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { SimulationRenderer } from '../classes/SimulationRenderer'
|
||||||
|
|
||||||
|
export const updateSimulation = (
|
||||||
|
renderer: SimulationRenderer,
|
||||||
|
delta: number
|
||||||
|
) => {
|
||||||
|
const selected = renderer.getSelected()
|
||||||
|
|
||||||
|
if (selected && renderer.movedSelection) {
|
||||||
|
renderer.mouseManager.update()
|
||||||
|
selected.transform.rotation =
|
||||||
|
renderer.mouseManager.getDirection() * renderer.options.dnd.rotation
|
||||||
|
} else {
|
||||||
|
if (selected) {
|
||||||
|
selected.transform.rotation = 0
|
||||||
|
}
|
||||||
|
renderer.mouseManager.update()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
export interface SimulationRendererOptions {
|
||||||
|
shadows: {
|
||||||
|
enabled: boolean
|
||||||
|
color: string
|
||||||
|
lightHeight: number
|
||||||
|
gateHeight: number
|
||||||
|
}
|
||||||
|
dnd: {
|
||||||
|
rotation: number
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue