From e7abdd3c058cbe3c82f6c212e7d2310958550b9d Mon Sep 17 00:00:00 2001
From: Matei Adriel <rafaeladriel11@gmail.com>
Date: Tue, 4 Jun 2019 18:38:01 +0000
Subject: [PATCH] =?UTF-8?q?=20=F0=9F=86=96=20=20added=20silentRefresh=20an?=
 =?UTF-8?q?d=20fixed=20the=20glitchy=20wire=20bug=20exploit=20thing=20?=
 =?UTF-8?q?=F0=9F=8F=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/index.html                                | 134 +++++++++++++++++-
 src/ts/common/component/component.ts          |  53 ++++---
 .../componentManager/componentManager.ts      |  42 ++++--
 src/ts/common/screen.ts/screen.ts             |  58 +++++---
 src/ts/common/wires/wireManager.ts            |  19 +--
 5 files changed, 236 insertions(+), 70 deletions(-)

diff --git a/src/index.html b/src/index.html
index aa7287a..ec45343 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,14 +2,142 @@
 <html lang="en">
 
 <head>
-    <title>Game</title>
+	<title>Game</title>
+	<style>
+		html,
+		body {
+			padding: 0;
+			margin: 0;
+			height: 100%;
+			width: 100%;
+		}
+
+		body {
+			background: #222;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			flex-direction: column;
+		}
+
+		.sk-folding-cube {
+			margin: 20px auto;
+			width: 40px;
+			height: 40px;
+			position: relative;
+			-webkit-transform: rotateZ(45deg);
+			transform: rotateZ(45deg);
+		}
+
+		.sk-folding-cube .sk-cube {
+			float: left;
+			width: 50%;
+			height: 50%;
+			position: relative;
+			-webkit-transform: scale(1.1);
+			-ms-transform: scale(1.1);
+			transform: scale(1.1);
+		}
+
+		.sk-folding-cube .sk-cube:before {
+			content: '';
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: 100%;
+			background-color: #bbb;
+			-webkit-animation: sk-foldCubeAngle 2.4s infinite linear both;
+			animation: sk-foldCubeAngle 2.4s infinite linear both;
+			-webkit-transform-origin: 100% 100%;
+			-ms-transform-origin: 100% 100%;
+			transform-origin: 100% 100%;
+		}
+
+		.sk-folding-cube .sk-cube2 {
+			-webkit-transform: scale(1.1) rotateZ(90deg);
+			transform: scale(1.1) rotateZ(90deg);
+		}
+
+		.sk-folding-cube .sk-cube3 {
+			-webkit-transform: scale(1.1) rotateZ(180deg);
+			transform: scale(1.1) rotateZ(180deg);
+		}
+
+		.sk-folding-cube .sk-cube4 {
+			-webkit-transform: scale(1.1) rotateZ(270deg);
+			transform: scale(1.1) rotateZ(270deg);
+		}
+
+		.sk-folding-cube .sk-cube2:before {
+			-webkit-animation-delay: 0.3s;
+			animation-delay: 0.3s;
+		}
+
+		.sk-folding-cube .sk-cube3:before {
+			-webkit-animation-delay: 0.6s;
+			animation-delay: 0.6s;
+		}
+
+		.sk-folding-cube .sk-cube4:before {
+			-webkit-animation-delay: 0.9s;
+			animation-delay: 0.9s;
+		}
+
+		@-webkit-keyframes sk-foldCubeAngle {
+			0%,
+			10% {
+				-webkit-transform: perspective(140px) rotateX(-180deg);
+				transform: perspective(140px) rotateX(-180deg);
+				opacity: 0;
+			}
+			25%,
+			75% {
+				-webkit-transform: perspective(140px) rotateX(0deg);
+				transform: perspective(140px) rotateX(0deg);
+				opacity: 1;
+			}
+			90%,
+			100% {
+				-webkit-transform: perspective(140px) rotateY(180deg);
+				transform: perspective(140px) rotateY(180deg);
+				opacity: 0;
+			}
+		}
+
+		@keyframes sk-foldCubeAngle {
+			0%,
+			10% {
+				-webkit-transform: perspective(140px) rotateX(-180deg);
+				transform: perspective(140px) rotateX(-180deg);
+				opacity: 0;
+			}
+			25%,
+			75% {
+				-webkit-transform: perspective(140px) rotateX(0deg);
+				transform: perspective(140px) rotateX(0deg);
+				opacity: 1;
+			}
+			90%,
+			100% {
+				-webkit-transform: perspective(140px) rotateY(180deg);
+				transform: perspective(140px) rotateY(180deg);
+				opacity: 0;
+			}
+		}
+	</style>
 
     <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
-    <link rel='stylesheet' href='style.css'>
+	<link rel='stylesheet' href='style.css'>
 </head>
 
 <body ondragstart="return false;" ondrop="return false;" oncontextmenu="return false">
-    loading
+	<div class="sk-folding-cube">
+		<div class="sk-cube1 sk-cube"></div>
+		<div class="sk-cube2 sk-cube"></div>
+		<div class="sk-cube4 sk-cube"></div>
+		<div class="sk-cube3 sk-cube"></div>
+	</div>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/src/ts/common/component/component.ts b/src/ts/common/component/component.ts
index b807940..abe80c0 100644
--- a/src/ts/common/component/component.ts
+++ b/src/ts/common/component/component.ts
@@ -24,6 +24,7 @@ export class Component {
     public id: number
     public material: Material
     public clickedChanges = new BehaviorSubject(false)
+    public scaling = false
 
     private mouserDelta: number[]
     private strokeColor = "#888888"
@@ -35,6 +36,22 @@ export class Component {
     public inputPins: Pin[] = []
     public outputPins: Pin[] = []
 
+    public x = this.position.pipe(map(val =>
+        val[0]
+    ))
+
+    public y = this.position.pipe(map(val =>
+        val[1]
+    ))
+
+    public width = this.scale.pipe(map(val =>
+        val[0]
+    ))
+
+    public height = this.scale.pipe(map(val =>
+        val[1]
+    ))
+
     constructor(private template: string,
         position: [number, number] = [0, 0],
         scale: [number, number] = [0, 0],
@@ -87,6 +104,7 @@ export class Component {
 
     public handleMouseUp() {
         this.clicked = false
+        this.scaling = false
         this.clickedChanges.next(this.clicked)
     }
 
@@ -110,8 +128,6 @@ export class Component {
     }
 
     handleClick(e: MouseEvent) {
-        console.log(e.button)
-
         if (e.button === 0) {
             const mousePosition = Component.screen.getWorldPosition(e.clientX, e.clientY)
 
@@ -125,6 +141,9 @@ export class Component {
             this.activate(0)
         }
 
+        else if (e.button === 1) {
+            this.scaling = true
+        }
         else if (e.button === 2) {
             manager.components = manager.components.filter(({ id }) => id !== this.id)
             manager.wireManager.wires
@@ -132,9 +151,7 @@ export class Component {
                 .forEach(val => {
                     manager.wireManager.remove(val)
                 })
-            manager.wireManager.update.next(true)
-
-            manager.update()
+            manager.silentRefresh()
         }
     }
 
@@ -151,28 +168,6 @@ export class Component {
         }
     }
 
-    get x() {
-        return this.position.pipe(map(val =>
-            val[0]
-        ))
-    }
-    get y() {
-        return this.position.pipe(map(val =>
-            val[1]
-        ))
-    }
-
-    get width() {
-        return this.scale.pipe(map(val =>
-            val[0]
-        ))
-    }
-    get height() {
-        return this.scale.pipe(map(val =>
-            val[1]
-        ))
-    }
-
     pinsSvg(pinScale: number, pinLength = 20, mode = "input") {
         const stroke = 3
 
@@ -208,8 +203,8 @@ export class Component {
     }
 
     public pinx(mode = true, pinLength = 15) {
-        return this.x.pipe(
-            map(val => val + (
+        return this.position.pipe(
+            map(val => val[0] + (
                 (mode) ?
                     -pinLength :
                     this.scale.value[0] + pinLength
diff --git a/src/ts/common/componentManager/componentManager.ts b/src/ts/common/componentManager/componentManager.ts
index 4e4579c..1dbbaaa 100644
--- a/src/ts/common/componentManager/componentManager.ts
+++ b/src/ts/common/componentManager/componentManager.ts
@@ -27,9 +27,9 @@ export class ComponentManager {
     public placeholder = new BehaviorSubject("Create simulation")
     public barAlpha = new BehaviorSubject<string>("0");
     public wireManager = new WireManager()
+    public onTop: Component
 
     private temporaryCommnad = ""
-    private onTop: Component
     private clicked = false
 
     private screen = new Screen()
@@ -51,6 +51,7 @@ export class ComponentManager {
     private closeInputEvent = new KeyboardInput("enter")
     private ctrlEvent = new KeyboardInput("ctrl")
     private palleteEvent = new KeyboardInput("p")
+    private undoEvent = new KeyboardInput("z")
     private shiftEvent = new KeyboardInput("shift")
     private refreshEvent = new KeyboardInput("r")
     private clearEvent = new KeyboardInput("delete")
@@ -105,9 +106,10 @@ export class ComponentManager {
             clear: () => this.clear(),
             clean: () => this.smartClear(),
             save: () => this.save(),
-            refresh: () => this.refresh(),
+            undo: () => this.refresh(),
             download: () => download(this, [], []),
-            delete: () => this.delete(this.name)
+            delete: () => this.delete(this.name),
+            refresh: () => this.silentRefresh(true)
         }
 
     constructor() {
@@ -172,9 +174,12 @@ export class ComponentManager {
                     else if (this.saveEvent.value) {
                         this.save()
                     }
-                    else if (this.refreshEvent.value) {
+                    else if (this.undoEvent.value) {
                         this.refresh()
                     }
+                    else if (this.refreshEvent.value){
+                        this.silentRefresh(true)
+                    }
                 }
                 else if (this.clearEvent.value) {
                     if (this.shiftEvent.value)
@@ -328,7 +333,7 @@ All you work will be lost!`
         success("Succesfully cleared all components", "", this.alertOptions)
     }
 
-    refresh() {
+    public refresh() {
         if (this.store.get(this.name)) {
             this.loadState(this.store.get(this.name))
         }
@@ -369,10 +374,9 @@ All you work will be lost!`
                 }
             }
 
+            // if (false) { }
             if (toAddOnTop >= 0) {
-                this.onTop = this.components[toAddOnTop]
-                this.components.push(this.onTop)
-                this.update()
+                this.top(this.components[toAddOnTop])
             }
 
             else if (outsideComponents && this.clicked) {
@@ -385,7 +389,21 @@ All you work will be lost!`
         }
     }
 
-    render() {
+    public silentRefresh(verboose = false){
+        this.loadState(this.state)
+        if (verboose)
+            success("Succesfully reloaded all components", "", this.alertOptions)
+    }
+
+    public top(component: Component) {
+        if (this.onTop !== component) {
+            this.onTop = component
+            this.components.push(component)
+        }
+        this.update()
+    }
+
+    private render() {
         let toRemoveDuplicatesFor: Component
 
         const result = this.components.map(component => {
@@ -451,11 +469,15 @@ All you work will be lost!`
         }
     }
 
+    get scaling(): Component {
+        return this.components.find(val => val.scaling)
+    }
+
     public getComponentById(id: number) {
         return this.components.find(val => val.id === id)
     }
 
-    loadState(state: ManagerState) {
+    private loadState(state: ManagerState) {
         if (!state.wires) //old state
             return
 
diff --git a/src/ts/common/screen.ts/screen.ts b/src/ts/common/screen.ts/screen.ts
index 775ad34..e43ea4b 100644
--- a/src/ts/common/screen.ts/screen.ts
+++ b/src/ts/common/screen.ts/screen.ts
@@ -1,20 +1,21 @@
-import { Subject, fromEvent, BehaviorSubject, combineLatest } from "rxjs"
+import { fromEvent, BehaviorSubject, combineLatest } from "rxjs"
 import { Singleton } from "@eix/utils"
-import { map } from "rxjs/operators";
+import { map, take } from "rxjs/operators";
 import clamp from "../clamp/clamp";
+import { manager } from "../../main";
 
 @Singleton
 export class Screen {
     width = new BehaviorSubject<number>(0)
     height = new BehaviorSubject<number>(0)
-    viewBox = combineLatest(this.width, this.height).pipe(map((values: [number,number]) =>
+    viewBox = combineLatest(this.width, this.height).pipe(map((values: [number, number]) =>
         this.getViewBox(...values)
     ));
-    
+
     public position = [0, 0]
     public scale = [2, 2]
 
-    private zoomLimits: [number,number] = [0.1,10]
+    private zoomLimits: [number, number] = [0.1, 10]
 
     private scrollStep = 1.3
     public mousePosition = [this.width.value / 2, this.height.value / 2]
@@ -25,34 +26,51 @@ export class Screen {
         fromEvent(window, "resize").subscribe(() => this.update())
     }
 
-    updateMouse(e:MouseEvent){
-        this.mousePosition = [e.clientX,e.clientY]
+    updateMouse(e: MouseEvent) {
+        this.mousePosition = [e.clientX, e.clientY]
     }
 
-    handleScroll(e: WheelEvent){
+    handleScroll(e: WheelEvent) {
         e.preventDefault()
 
-        const size = [this.width.value,this.height.value]
-        const mouseFraction = size.map((value,index) => this.mousePosition[index] / value)
+        const componentToScale = manager.scaling
         const sign = e.deltaY / Math.abs(e.deltaY)
         const zoom = this.scrollStep ** sign
-        const newScale = this.scale.map(value => clamp(value * zoom, ...this.zoomLimits ))
-        const delta = this.scale.map((value,index) => 
-            size[index] * (newScale[index] - this.scale[index]) * mouseFraction[index]
-        )
 
-        this.scale = newScale
-        this.position = this.position.map((value,index) => value - delta[index])
-        this.update()
+        if (componentToScale) {
+            const oldScale = componentToScale.scale.value
+            const newScale = oldScale.map(val => val / zoom)
+
+            componentToScale.scale.next(newScale)
+            componentToScale.position.pipe(take(1)).subscribe(data => {
+                componentToScale.position.next(data.map((val, index) =>
+                    val - (newScale[index] - oldScale[index]) / 2
+                ))
+            })
+
+            manager.top(componentToScale)
+        }
+        else {
+            const size = [this.width.value, this.height.value]
+            const mouseFraction = size.map((value, index) => this.mousePosition[index] / value)
+            const newScale = this.scale.map(value => clamp(value * zoom, ...this.zoomLimits))
+            const delta = this.scale.map((_, index) =>
+                size[index] * (newScale[index] - this.scale[index]) * mouseFraction[index]
+            )
+
+            this.scale = newScale
+            this.position = this.position.map((value, index) => value - delta[index])
+            this.update()
+        }
     }
 
-    move(x:number, y:number) {
+    move(x: number, y: number) {
         this.position[0] += x * this.scale[0]
         this.position[1] += y * this.scale[1]
         this.update()
     }
 
-    getViewBox(width: number, height:number) {
+    getViewBox(width: number, height: number) {
         return [
             this.position[0],
             this.position[1],
@@ -66,7 +84,7 @@ export class Screen {
         this.width.next(window.innerWidth)
     }
 
-    getWorldPosition(x:number, y:number) {
+    getWorldPosition(x: number, y: number) {
         return [x * this.scale[0], y * this.scale[1]]
     }
 }
\ No newline at end of file
diff --git a/src/ts/common/wires/wireManager.ts b/src/ts/common/wires/wireManager.ts
index dc8e7a6..dbb323e 100644
--- a/src/ts/common/wires/wireManager.ts
+++ b/src/ts/common/wires/wireManager.ts
@@ -26,7 +26,7 @@ export class WireManager {
         this.tryResolving()
     }
 
-    public dispose(){
+    public dispose() {
         for (let i of this.wires)
             i.dispose()
 
@@ -57,24 +57,27 @@ export class WireManager {
     }
 
     get svg() {
-        return this.wires.map(val => {
+        return svg`${this.wires.map((val) => {
             const i = val.input.of
             const o = val.output.of
+            const inputIndex = i.outputPins.indexOf(val.input)
+            const input = i.piny(false, inputIndex)
+            const output = o.piny(true, o.inputPins.indexOf(val.output))
             return svg`
-            <line x1=${subscribe(i.pinx(false, 20))}
-                x2=${subscribe(o.pinx(true, 20))}
-                y1=${subscribe(i.piny(false,i.outputPins.indexOf(val.input)))}
-                y2=${subscribe(o.piny(true,o.inputPins.indexOf(val.output)))}
+            <line x2=${subscribe(i.pinx(false, 20))}
+                x1=${subscribe(o.pinx(true, 20))}
+                y2=${subscribe(input)}
+                y1=${subscribe(output)}
                 stroke=${subscribe(val.input.svgColor)}
                 stroke-width=10
                 @click=${() => this.remove(val)}
             >
             </line>
-        `})
+        `})}`
     }
 
     get state() {
-        return this.wires.map((val):WireStateVal => ({
+        return this.wires.map((val): WireStateVal => ({
             from: {
                 owner: val.input.of.id,
                 index: val.input.of.outputPins.indexOf(val.input)