diff --git a/package-lock.json b/package-lock.json index 2f702c9..430a299 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1886,6 +1886,11 @@ "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", "dev": true }, + "add-px-to-style": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz", + "integrity": "sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo=" + }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", @@ -3712,6 +3717,16 @@ "utila": "~0.4" } }, + "dom-css": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz", + "integrity": "sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI=", + "requires": { + "add-px-to-style": "1.0.0", + "prefix-style": "2.0.1", + "to-camel-case": "1.0.0" + } + }, "dom-helpers": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", @@ -7593,8 +7608,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "2.3.0", @@ -8257,6 +8271,11 @@ "integrity": "sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==", "dev": true }, + "prefix-style": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz", + "integrity": "sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY=" + }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -8428,6 +8447,14 @@ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8484,6 +8511,16 @@ "scheduler": "^0.13.6" } }, + "react-custom-scrollbars": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz", + "integrity": "sha1-gw/ZUCkn6X6KeMIIaBOJmyqLZts=", + "requires": { + "dom-css": "^2.0.0", + "prop-types": "^15.5.10", + "raf": "^3.1.0" + } + }, "react-dom": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", @@ -10060,12 +10097,25 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-camel-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", + "integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=", + "requires": { + "to-space-case": "^1.0.0" + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -10108,6 +10158,14 @@ "repeat-string": "^1.6.1" } }, + "to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "requires": { + "to-no-case": "^1.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", diff --git a/package.json b/package.json index 2ce34ec..2f0b86d 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "keycode": "^2.2.0", "mainloop.js": "^1.0.4", "react": "^16.8.6", + "react-custom-scrollbars": "^4.2.1", "react-dom": "^16.8.6", "react-router-dom": "^5.0.1", "react-toastify": "^5.3.2", diff --git a/src/modules/core/classes/Screen.ts b/src/modules/core/classes/Screen.ts index ac0f2cb..d3e86dc 100644 --- a/src/modules/core/classes/Screen.ts +++ b/src/modules/core/classes/Screen.ts @@ -2,17 +2,22 @@ import { Singleton } from '@eix-js/utils' import { Observable, fromEvent, BehaviorSubject } from 'rxjs' import { map } from 'rxjs/operators' import { multiply } from '../../vector2/helpers/basic' +import { sidebarWidth } from '../components/Sidebar' @Singleton export class Screen { - public width = new BehaviorSubject(window.innerWidth) + private getWidth() { + return window.innerWidth - sidebarWidth + } + + public width = new BehaviorSubject(this.getWidth()) public height = new BehaviorSubject(window.innerHeight) public constructor() { const resize = fromEvent(window, 'resize') resize - .pipe(map(() => window.innerWidth)) + .pipe(map(() => this.getWidth())) .subscribe(val => this.width.next(val)) resize .pipe(map(() => window.innerHeight)) diff --git a/src/modules/core/components/Sidebar.tsx b/src/modules/core/components/Sidebar.tsx index 0880958..3fee73e 100644 --- a/src/modules/core/components/Sidebar.tsx +++ b/src/modules/core/components/Sidebar.tsx @@ -9,7 +9,7 @@ import Language from './Language' /** * The width of the sidebar */ -const sidebarWidth = 240 +export const sidebarWidth = 240 /** * The z-index of the sidebar. diff --git a/src/modules/logic-gates/components/LogicGateModal.tsx b/src/modules/logic-gates/components/LogicGateModal.tsx index b43fd6e..15e35f7 100644 --- a/src/modules/logic-gates/components/LogicGateModal.tsx +++ b/src/modules/logic-gates/components/LogicGateModal.tsx @@ -54,7 +54,7 @@ const LogicGateModal = () => { key={index} className="logic-gate-item" onClick={() => { - addGate(renderer.simulation, name) + addGate(renderer, name) }} > @@ -70,7 +70,7 @@ const LogicGateModal = () => { href={randomItem(template.info)} onClick={e => { e.stopPropagation() - e.preventDefault() + // e.preventDefault() }} > info diff --git a/src/modules/modals/helpers/modalIsOpen.ts b/src/modules/modals/helpers/modalIsOpen.ts new file mode 100644 index 0000000..02c4342 --- /dev/null +++ b/src/modules/modals/helpers/modalIsOpen.ts @@ -0,0 +1,11 @@ +import { InputStore } from '../../input/stores/InputStore' +import { open as logicGateModalIsOpen } from '../../logic-gates/components/LogicGateModal' +import { CreateSimulationStore } from '../../create-simulation/stores/CreateSimulationStore' + +export const modalIsOpen = () => { + return ( + InputStore.data.open.value || + logicGateModalIsOpen.value || + CreateSimulationStore.data.open.value + ) +} diff --git a/src/modules/simulation/helpers/addGate.ts b/src/modules/simulation/helpers/addGate.ts index 2d84637..9ccafc0 100644 --- a/src/modules/simulation/helpers/addGate.ts +++ b/src/modules/simulation/helpers/addGate.ts @@ -2,12 +2,34 @@ import { templateStore } from '../../saving/stores/templateStore' import { SimulationError } from '../../errors/classes/SimulationError' import { Simulation } from '../classes/Simulation' import { Gate } from '../classes/Gate' +import { add, relativeTo, multiply } from '../../vector2/helpers/basic' +import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer' +import { DefaultGateTemplate } from '../constants' +import { vector2 } from '../../../common/math/classes/Transform' -export const addGate = (simulation: Simulation, templateName: string) => { +export const addGate = (renderer: SimulationRenderer, templateName: string) => { const template = templateStore.get(templateName) if (!template) throw new SimulationError(`Cannot find template ${templateName}`) - simulation.push(new Gate(template)) + const gate = new Gate(template) + + const gateScale = + template.shape && template.shape.scale + ? (template.shape.scale as vector2) + : DefaultGateTemplate.shape.scale + + const origin = relativeTo( + multiply(gateScale, 0.5), + relativeTo(renderer.camera.transform.position, renderer.screen.center) + ) + + const scalarOffset = renderer.options.spawning.spawnOffset + const offset = multiply([scalarOffset, scalarOffset], renderer.spawnCount) + + gate.transform.position = add(origin, offset) + + renderer.simulation.push(gate) + renderer.spawnCount++ } diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts index b924baf..971b8d2 100644 --- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts +++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts @@ -26,8 +26,8 @@ import merge from 'deepmerge' import { wireConnectedToGate } from '../helpers/wireConnectedToGate' import { updateMouse, handleScroll } from '../helpers/scaleCanvas' import { RefObject } from 'react' -import { Singleton } from '@eix-js/utils' import { dumpSimulation } from '../../saving/helpers/dumpSimulation' +import { modalIsOpen } from '../../modals/helpers/modalIsOpen' export class SimulationRenderer { public mouseDownOutput = new Subject() @@ -48,6 +48,9 @@ export class SimulationRenderer { private mouseState = 0b00 private gateSelectionOffset: vector2 = [0, 0] + // this is used for spawning gates + public spawnCount = 0 + public selectedPins: SelectedPins = { start: null, end: null @@ -204,6 +207,8 @@ export class SimulationRenderer { this.camera.transform.position, invert(offset) ) + + this.spawnCount = 0 } this.lastMousePosition = this.camera.toWordPostition(event.position) @@ -218,9 +223,11 @@ export class SimulationRenderer { public updateWheelListener() { if (this.ref.current) { this.ref.current.addEventListener('wheel', event => { - event.preventDefault() + if (!modalIsOpen()) { + event.preventDefault() - handleScroll(event, this.camera) + handleScroll(event, this.camera) + } }) } } diff --git a/src/modules/simulationRenderer/constants.ts b/src/modules/simulationRenderer/constants.ts index cefb7b6..783d2e0 100644 --- a/src/modules/simulationRenderer/constants.ts +++ b/src/modules/simulationRenderer/constants.ts @@ -23,6 +23,9 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = { wires: { temporaryWireColor: `rgba(128,128,128,0.5)`, curvePointOffset: 100 + }, + spawning: { + spawnOffset: 30 } } diff --git a/src/modules/simulationRenderer/types/SimulationRendererOptions.ts b/src/modules/simulationRenderer/types/SimulationRendererOptions.ts index 5920aaf..222d204 100644 --- a/src/modules/simulationRenderer/types/SimulationRendererOptions.ts +++ b/src/modules/simulationRenderer/types/SimulationRendererOptions.ts @@ -21,4 +21,7 @@ export interface SimulationRendererOptions { temporaryWireColor: string curvePointOffset: number } + spawning: { + spawnOffset: number + } }