Update simulator as per latest lego UI design for sensors. Fix full screen layout issues. (#554)
This commit is contained in:
		@@ -49,14 +49,14 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            font-size:16px;
 | 
					            font-size:16px;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        .sim-text.large {
 | 
					        .sim-text.large {
 | 
				
			||||||
            font-size:30px;
 | 
					            font-size:20px;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        .sim-text.number {
 | 
					        .sim-text.number {
 | 
				
			||||||
            font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro;
 | 
					            font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro;
 | 
				
			||||||
            /*font-weight: bold;*/
 | 
					            /*font-weight: bold;*/
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        .sim-text.inverted {
 | 
					        .sim-text.inverted {
 | 
				
			||||||
            fill:#5A5A5A;
 | 
					            fill: #5A5A5A; /*#F12B21;*/
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .no-drag, .sim-text, .sim-text-pin {
 | 
					        .no-drag, .sim-text, .sim-text-pin {
 | 
				
			||||||
@@ -74,6 +74,10 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            stroke: #000;
 | 
					            stroke: #000;
 | 
				
			||||||
            cursor: pointer;
 | 
					            cursor: pointer;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        .sim-color-selected {
 | 
				
			||||||
 | 
					            stroke-width: 1px !important;
 | 
				
			||||||
 | 
					            stroke: #A8AAA8 !important;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        .sim-color-wheel-half:hover {
 | 
					        .sim-color-wheel-half:hover {
 | 
				
			||||||
            stroke-width: 1;
 | 
					            stroke-width: 1;
 | 
				
			||||||
            stroke: #000;
 | 
					            stroke: #000;
 | 
				
			||||||
@@ -136,6 +140,8 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private cachedControlNodes: { [index: string]: View[] } = {};
 | 
					        private cachedControlNodes: { [index: string]: View[] } = {};
 | 
				
			||||||
        private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
 | 
					        private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
 | 
				
			||||||
 | 
					        private cachedCloseIcons: { [index: string]: View } = {};
 | 
				
			||||||
 | 
					        private cachedBackgroundViews: { [index: string]: View } = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private screenCanvas: HTMLCanvasElement;
 | 
					        private screenCanvas: HTMLCanvasElement;
 | 
				
			||||||
        private screenCanvasCtx: CanvasRenderingContext2D;
 | 
					        private screenCanvasCtx: CanvasRenderingContext2D;
 | 
				
			||||||
@@ -298,8 +304,22 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return undefined;
 | 
					            return undefined;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private getCloseIconView() {
 | 
					        private getCloseIconView(port: number) {
 | 
				
			||||||
            return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
 | 
					            if (this.cachedCloseIcons[port]) {
 | 
				
			||||||
 | 
					                return this.cachedCloseIcons[port];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const closeIcon = new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
 | 
				
			||||||
 | 
					            this.cachedCloseIcons[port] = closeIcon;
 | 
				
			||||||
 | 
					            return closeIcon;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getBackgroundView(port: number) {
 | 
				
			||||||
 | 
					            if (this.cachedBackgroundViews[port]) {
 | 
				
			||||||
 | 
					                return this.cachedBackgroundViews[port];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const backgroundView = new BackgroundViewControl(this.element, this.defs, new PortNode(-1), -1);
 | 
				
			||||||
 | 
					            this.cachedBackgroundViews[port] = backgroundView;
 | 
				
			||||||
 | 
					            return backgroundView;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private buildDom() {
 | 
					        private buildDom() {
 | 
				
			||||||
@@ -392,29 +412,29 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.layoutView.getLandscapeBrick().kill();
 | 
					            this.layoutView.getLandscapeBrick().kill();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Save previous inputs for the next cycle
 | 
					            // Save previous inputs for the next cycle
 | 
				
			||||||
            EV3View.previousSelectedInputs = ev3board().getInputNodes().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1)
 | 
					            EV3View.previousSelectedInputs = {};
 | 
				
			||||||
            EV3View.previousSeletedOutputs = ev3board().getMotors().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1);
 | 
					            ev3board().getInputNodes().forEach((node, index) =>
 | 
				
			||||||
 | 
					                EV3View.previousSelectedInputs[index] = (this.getDisplayViewForNode(node.id, index).getSelected()));
 | 
				
			||||||
 | 
					            EV3View.previousSeletedOutputs = {};
 | 
				
			||||||
 | 
					            ev3board().getMotors().forEach((node, index) =>
 | 
				
			||||||
 | 
					                EV3View.previousSeletedOutputs[index] = (this.getDisplayViewForNode(node.id, index).getSelected()));
 | 
				
			||||||
            EV3View.previousBrickLandscape = this.layoutView.isBrickLandscape();
 | 
					            EV3View.previousBrickLandscape = this.layoutView.isBrickLandscape();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static previousSelectedInputs: number[];
 | 
					        private static previousSelectedInputs: pxt.Map<boolean> = {};
 | 
				
			||||||
        private static previousSeletedOutputs: number[];
 | 
					        private static previousSeletedOutputs: pxt.Map<boolean> = {};
 | 
				
			||||||
        private static previousBrickLandscape: boolean;
 | 
					        private static previousBrickLandscape: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static isPreviousInputSelected(index: number, id: number) {
 | 
					        private static isPreviousInputSelected(index: number) {
 | 
				
			||||||
            if (EV3View.previousSelectedInputs && EV3View.previousSelectedInputs[index] == id) {
 | 
					            const previousInput = EV3View.previousSelectedInputs[index];
 | 
				
			||||||
                EV3View.previousSelectedInputs[index] = undefined;
 | 
					            delete EV3View.previousSelectedInputs[index];
 | 
				
			||||||
                return true;
 | 
					            return previousInput;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static isPreviousOutputSelected(index: number, id: number) {
 | 
					        private static isPreviousOutputSelected(index: number) {
 | 
				
			||||||
            if (EV3View.previousSeletedOutputs && EV3View.previousSeletedOutputs[index] == id) {
 | 
					            const previousOutput = EV3View.previousSeletedOutputs[index];
 | 
				
			||||||
                EV3View.previousSeletedOutputs[index] = undefined;
 | 
					            delete EV3View.previousSeletedOutputs[index];
 | 
				
			||||||
                return true;
 | 
					            return previousOutput;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static isPreviousBrickLandscape() {
 | 
					        private static isPreviousBrickLandscape() {
 | 
				
			||||||
@@ -463,11 +483,13 @@ namespace pxsim.visuals {
 | 
				
			|||||||
                const view = this.getDisplayViewForNode(node.id, index);
 | 
					                const view = this.getDisplayViewForNode(node.id, index);
 | 
				
			||||||
                if (!node.didChange() && !view.didChange()) return;
 | 
					                if (!node.didChange() && !view.didChange()) return;
 | 
				
			||||||
                if (view) {
 | 
					                if (view) {
 | 
				
			||||||
                    const isSelected = EV3View.isPreviousInputSelected(index, node.id) || view.getSelected();
 | 
					                    const previousSelected = EV3View.isPreviousInputSelected(index);
 | 
				
			||||||
                    if (isSelected && !view.getSelected()) view.setSelected(true);
 | 
					                    const isSelected = previousSelected != undefined ? previousSelected : view.getSelected();
 | 
				
			||||||
 | 
					                    view.setSelected(isSelected);
 | 
				
			||||||
                    const control = isSelected ? this.getControlForNode(node.id, index, !node.modeChange()) : undefined;
 | 
					                    const control = isSelected ? this.getControlForNode(node.id, index, !node.modeChange()) : undefined;
 | 
				
			||||||
                    const closeIcon = control && view.hasClose() ? this.getCloseIconView() : undefined;
 | 
					                    const closeIcon = control ? this.getCloseIconView(index + 10) : undefined;
 | 
				
			||||||
                    this.layoutView.setInput(index, view, control, closeIcon);
 | 
					                    const backgroundView = control && view.hasBackground() ? this.getBackgroundView(index + 10) : undefined;
 | 
				
			||||||
 | 
					                    this.layoutView.setInput(index, view, control, closeIcon, backgroundView);
 | 
				
			||||||
                    view.updateState();
 | 
					                    view.updateState();
 | 
				
			||||||
                    if (control) control.updateState();
 | 
					                    if (control) control.updateState();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -485,11 +507,13 @@ namespace pxsim.visuals {
 | 
				
			|||||||
                const view = this.getDisplayViewForNode(node.id, index);
 | 
					                const view = this.getDisplayViewForNode(node.id, index);
 | 
				
			||||||
                if (!node.didChange() && !view.didChange()) return;
 | 
					                if (!node.didChange() && !view.didChange()) return;
 | 
				
			||||||
                if (view) {
 | 
					                if (view) {
 | 
				
			||||||
                    const isSelected = EV3View.isPreviousOutputSelected(index, node.id) || view.getSelected();
 | 
					                    const previousSelected = EV3View.isPreviousOutputSelected(index);
 | 
				
			||||||
                    if (isSelected && !view.getSelected()) view.setSelected(true);
 | 
					                    const isSelected = previousSelected != undefined ? previousSelected : view.getSelected();
 | 
				
			||||||
 | 
					                    view.setSelected(isSelected);
 | 
				
			||||||
                    const control = isSelected ? this.getControlForNode(node.id, index) : undefined;
 | 
					                    const control = isSelected ? this.getControlForNode(node.id, index) : undefined;
 | 
				
			||||||
                    const closeIcon = control ? this.getCloseIconView() : undefined;
 | 
					                    const closeIcon = control ? this.getCloseIconView(index) : undefined;
 | 
				
			||||||
                    this.layoutView.setOutput(index, view, control, closeIcon);
 | 
					                    const backgroundView = control && view.hasBackground() ? this.getBackgroundView(index) : undefined;
 | 
				
			||||||
 | 
					                    this.layoutView.setOutput(index, view, control, closeIcon, backgroundView);
 | 
				
			||||||
                    view.updateState();
 | 
					                    view.updateState();
 | 
				
			||||||
                    if (control) control.updateState();
 | 
					                    if (control) control.updateState();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace pxsim.visuals {
 | 
					namespace pxsim.visuals {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export const CONTROL_WIDTH = 87.5;
 | 
					    export const CONTROL_WIDTH = 76.84;
 | 
				
			||||||
    export const CONTROL_HEIGHT = 175;
 | 
					    export const CONTROL_HEIGHT = 112.72;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export const CONTROL_TEXT_COLOR = '#000';
 | 
					    export const CONTROL_TEXT_COLOR = '#000';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,28 +43,29 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return this.content;
 | 
					            return this.content;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public resize(width: number, height: number) {
 | 
					        public resize(width: number, height: number, strict?: boolean) {
 | 
				
			||||||
            super.resize(width, height);
 | 
					            super.resize(width, height);
 | 
				
			||||||
            this.updateDimensions(width, height);
 | 
					            this.updateDimensions(width, height, strict);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private updateDimensions(width: number, height: number) {
 | 
					        private updateDimensions(width: number, height: number, strict?: boolean) {
 | 
				
			||||||
            if (this.content) {
 | 
					            if (this.content) {
 | 
				
			||||||
                const currentWidth = this.getInnerWidth();
 | 
					                const currentWidth = this.getInnerWidth();
 | 
				
			||||||
                const currentHeight = this.getInnerHeight();
 | 
					                const currentHeight = this.getInnerHeight();
 | 
				
			||||||
                const newHeight = currentHeight / currentWidth * width;
 | 
					                const newHeight = currentHeight / currentWidth * width;
 | 
				
			||||||
                const newWidth = currentWidth / currentHeight * height;
 | 
					                const newWidth = currentWidth / currentHeight * height;
 | 
				
			||||||
                if (newHeight > height) {
 | 
					                if (strict) {
 | 
				
			||||||
 | 
					                    this.content.setAttribute('width', `${width}`);
 | 
				
			||||||
 | 
					                    this.content.setAttribute('height', `${height}`);
 | 
				
			||||||
 | 
					                } else if (newHeight > height) {
 | 
				
			||||||
                    // scale width instead
 | 
					                    // scale width instead
 | 
				
			||||||
                    this.content.setAttribute('width', `${newWidth}`);
 | 
					                    this.content.setAttribute('width', `${newWidth}`);
 | 
				
			||||||
                    this.content.setAttribute('height', `${height}`);
 | 
					                    this.content.setAttribute('height', `${height}`);
 | 
				
			||||||
                    // translate to the middle (width)
 | 
					                    this.width = newWidth;
 | 
				
			||||||
                    this.translate(width / 2 - newWidth / 2, 0);
 | 
					 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    this.content.setAttribute('width', `${width}`);
 | 
					                    this.content.setAttribute('width', `${width}`);
 | 
				
			||||||
                    this.content.setAttribute('height', `${newHeight}`);
 | 
					                    this.content.setAttribute('height', `${newHeight}`);
 | 
				
			||||||
                    // translate to the middle (height)
 | 
					                    this.height = newHeight;
 | 
				
			||||||
                    this.translate(0, height / 2 - newHeight / 2);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										42
									
								
								sim/visuals/controls/backgroundView.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								sim/visuals/controls/backgroundView.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace pxsim.visuals {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    export class BackgroundViewControl extends ControlView<PortNode> {
 | 
				
			||||||
 | 
					        private backgroundGroup: SVGGElement;
 | 
				
			||||||
 | 
					        private backgroundRect: SVGRectElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerView() {
 | 
				
			||||||
 | 
					            this.backgroundGroup = svg.elt("g") as SVGGElement;
 | 
				
			||||||
 | 
					            this.backgroundRect = pxsim.svg.child(this.backgroundGroup,
 | 
				
			||||||
 | 
					                "rect", {
 | 
				
			||||||
 | 
					                    'x': 0, 'y': 0,
 | 
				
			||||||
 | 
					                    'width': '100%',
 | 
				
			||||||
 | 
					                    'height': '100%',
 | 
				
			||||||
 | 
					                    'style': `fill: #d6edff; stroke: #A8A9A8; stroke-width: 3px; stroke-opacity: 0.2`
 | 
				
			||||||
 | 
					                }) as SVGRectElement;
 | 
				
			||||||
 | 
					            return this.backgroundGroup;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buildDom(): SVGElement {
 | 
				
			||||||
 | 
					            this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement;
 | 
				
			||||||
 | 
					            this.content.appendChild(this.getInnerView());
 | 
				
			||||||
 | 
					            return this.content;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public resize(width: number, height: number, strict?: boolean) {
 | 
				
			||||||
 | 
					            super.resize(width, height, strict);
 | 
				
			||||||
 | 
					            this.backgroundRect.setAttribute('stroke-dasharray', `${height + width + height} 1000`);
 | 
				
			||||||
 | 
					            this.backgroundRect.setAttribute('stroke-dashoffset', `-${width}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerWidth(): number {
 | 
				
			||||||
 | 
					            return 76.84;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerHeight(): number {
 | 
				
			||||||
 | 
					            return 173.86;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,7 +9,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.closeGroup = svg.elt("g") as SVGGElement;
 | 
					            this.closeGroup = svg.elt("g") as SVGGElement;
 | 
				
			||||||
            this.closeGroup.style.cursor = 'pointer';
 | 
					            this.closeGroup.style.cursor = 'pointer';
 | 
				
			||||||
            const circleCloseWrapper = pxsim.svg.child(this.closeGroup, "g");
 | 
					            const circleCloseWrapper = pxsim.svg.child(this.closeGroup, "g");
 | 
				
			||||||
            pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "16", 'style': "fill: #fff" });
 | 
					            pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "16", 'style': "fill: transparent;" });
 | 
				
			||||||
            pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "15", 'style': "fill: none;stroke: #a8aaa8;stroke-width: 2px" });
 | 
					            pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "15", 'style': "fill: none;stroke: #a8aaa8;stroke-width: 2px" });
 | 
				
			||||||
            pxsim.svg.child(this.closeGroup, "rect", { 'x': "10", 'y': "16", 'width': "18", 'height': "2", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
 | 
					            pxsim.svg.child(this.closeGroup, "rect", { 'x': "10", 'y': "16", 'width': "18", 'height': "2", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
 | 
				
			||||||
            pxsim.svg.child(this.closeGroup, "rect", { 'x': "18", 'y': "8", 'width': "2", 'height': "18", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
 | 
					            pxsim.svg.child(this.closeGroup, "rect", { 'x': "18", 'y': "8", 'width': "2", 'height': "18", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
 | 
				
			||||||
@@ -18,15 +18,11 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        buildDom(): SVGElement {
 | 
					        buildDom(): SVGElement {
 | 
				
			||||||
            this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement;
 | 
					            this.content = svg.elt("svg", { width: "100%", height: "100%", viewBox:"0 0 32 32"}) as SVGSVGElement;
 | 
				
			||||||
            this.content.appendChild(this.getInnerView());
 | 
					            this.content.appendChild(this.getInnerView());
 | 
				
			||||||
            return this.content;
 | 
					            return this.content;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public resize(width: number, height: number) {
 | 
					 | 
				
			||||||
            super.resize(width, height);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public getInnerHeight() {
 | 
					        public getInnerHeight() {
 | 
				
			||||||
            return 32;
 | 
					            return 32;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,28 +5,32 @@ namespace pxsim.visuals {
 | 
				
			|||||||
    export class ColorGridControl extends ControlView<ColorSensorNode> {
 | 
					    export class ColorGridControl extends ControlView<ColorSensorNode> {
 | 
				
			||||||
        private group: SVGGElement;
 | 
					        private group: SVGGElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey', 'white'];
 | 
				
			||||||
 | 
					        private static colorValue = [5, 4, 2, 3, 1, 7, 6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        getInnerView() {
 | 
					        getInnerView() {
 | 
				
			||||||
            this.group = svg.elt("g") as SVGGElement;
 | 
					            this.group = svg.elt("g") as SVGGElement;
 | 
				
			||||||
            this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`)
 | 
					            this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
 | 
					 | 
				
			||||||
            const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
 | 
					            const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
 | 
				
			||||||
            const colorValue = [5, 4, 2, 3, 1, 7];
 | 
					            const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let cy = -4;
 | 
					            let cy = -4;
 | 
				
			||||||
            for (let c = 0; c < colorIds.length; c++) {
 | 
					            for (let c = 0; c < colorIds.length; c++) {
 | 
				
			||||||
                const cx = c % 2 == 0 ? 2.2 : 7.5;
 | 
					                const cx = c % 2 == 0 ? 2.2 : 7.5;
 | 
				
			||||||
                if (c % 2 == 0) cy += 5;
 | 
					                if (c % 2 == 0) cy += 5;
 | 
				
			||||||
                if (colorIds[c]) {
 | 
					                if (colorIds[c]) {
 | 
				
			||||||
                    const circle = pxsim.svg.child(this.group, "circle", { 'class': 'sim-color-grid-circle', 'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` });
 | 
					                    const circle = pxsim.svg.child(this.group, "circle", { 
 | 
				
			||||||
 | 
					                        'class': `sim-color-grid-circle sim-color-grid-${colorIds[c]}`,
 | 
				
			||||||
 | 
					                        'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` });
 | 
				
			||||||
                    pointerEvents.down.forEach(evid => circle.addEventListener(evid, ev => {
 | 
					                    pointerEvents.down.forEach(evid => circle.addEventListener(evid, ev => {
 | 
				
			||||||
                        this.setColor(colorValue[c]);
 | 
					                        this.setColor(ColorGridControl.colorValue[c]);
 | 
				
			||||||
                    }));
 | 
					                    }));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
 | 
					            const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
 | 
				
			||||||
            pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
 | 
					            pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle sim-color-grid-white', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
 | 
				
			||||||
            pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` });
 | 
					            pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` });
 | 
				
			||||||
            pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => {
 | 
					            pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => {
 | 
				
			||||||
                this.setColor(6);
 | 
					                this.setColor(6);
 | 
				
			||||||
@@ -42,9 +46,34 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return 15;
 | 
					            return 15;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public updateState() {
 | 
				
			||||||
 | 
					            if (!this.visible) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const node = this.state;
 | 
				
			||||||
 | 
					            const color = node.getValue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (let c = 0; c < ColorGridControl.colorValue.length; c++) {
 | 
				
			||||||
 | 
					                const colorId = ColorGridControl.colorIds[c];
 | 
				
			||||||
 | 
					                const colorValue = ColorGridControl.colorValue[c];
 | 
				
			||||||
 | 
					                const colorDiv = this.group.getElementsByClassName(`sim-color-grid-${colorId}`)[0] as HTMLElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (colorValue == color) {
 | 
				
			||||||
 | 
					                    pxsim.U.addClass(colorDiv, 'sim-color-selected');
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    pxsim.U.removeClass(colorDiv, 'sim-color-selected');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private setColor(color: number) {
 | 
					        private setColor(color: number) {
 | 
				
			||||||
            const state = this.state;
 | 
					            const state = this.state;
 | 
				
			||||||
 | 
					            const currentColor = state.getValue();
 | 
				
			||||||
 | 
					            if (currentColor == color) {
 | 
				
			||||||
 | 
					                state.setColor(0);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
                state.setColor(color);
 | 
					                state.setColor(color);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -15,7 +15,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private getReporterHeight() {
 | 
					        private getReporterHeight() {
 | 
				
			||||||
            return 58;
 | 
					            return 38;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private getSliderWidth() {
 | 
					        private getSliderWidth() {
 | 
				
			||||||
@@ -23,10 +23,10 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private getSliderHeight() {
 | 
					        private getSliderHeight() {
 | 
				
			||||||
            return 111;
 | 
					            return 131;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private getMax() {
 | 
					        private getMaxValue() {
 | 
				
			||||||
            return 100;
 | 
					            return 100;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,9 +36,9 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            const node = this.state;
 | 
					            const node = this.state;
 | 
				
			||||||
            const percentage = node.getValue();
 | 
					            const percentage = node.getValue();
 | 
				
			||||||
            const inversePercentage = this.getMax() - percentage;
 | 
					            const inversePercentage = this.getMaxValue() - percentage;
 | 
				
			||||||
            svg.setGradientValue(this.colorGradient, inversePercentage + "%");
 | 
					            svg.setGradientValue(this.colorGradient, inversePercentage + "%");
 | 
				
			||||||
            this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
 | 
					            this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}%`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
 | 
					        updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
 | 
				
			||||||
@@ -47,7 +47,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            const height = bBox.height;
 | 
					            const height = bBox.height;
 | 
				
			||||||
            let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
 | 
					            let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
 | 
				
			||||||
            const state = this.state;
 | 
					            const state = this.state;
 | 
				
			||||||
            state.setColor(t * this.getMax());
 | 
					            state.setColor(t * this.getMaxValue());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
 | 
					        getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
 | 
				
			||||||
@@ -60,7 +60,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            svg.setGradientColors(this.colorGradient, "black", "yellow");
 | 
					            svg.setGradientColors(this.colorGradient, "black", "yellow");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const reporterGroup = pxsim.svg.child(this.group, "g");
 | 
					            const reporterGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
            reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 50)`);
 | 
					            reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`);
 | 
				
			||||||
            this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
 | 
					            this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
					            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,11 +7,59 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        private gradient: SVGLinearGradientElement;
 | 
					        private gradient: SVGLinearGradientElement;
 | 
				
			||||||
        private slider: SVGGElement;
 | 
					        private slider: SVGGElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private rect: SVGRectElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private reporter: SVGTextElement;
 | 
					        private reporter: SVGTextElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static SLIDER_HANDLE_HEIGHT = 26;
 | 
					        private static SLIDER_HANDLE_HEIGHT = 26;
 | 
				
			||||||
        private static SLIDER_SIDE_PADDING = 6;
 | 
					        private static SLIDER_SIDE_PADDING = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerWidth() {
 | 
				
			||||||
 | 
					            return 111;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerHeight() {
 | 
				
			||||||
 | 
					            return 192;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getReporterHeight() {
 | 
				
			||||||
 | 
					            return 38;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getSliderWidth() {
 | 
				
			||||||
 | 
					            return 62;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getSliderHeight() {
 | 
				
			||||||
 | 
					            return 131;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getMaxValue() {
 | 
				
			||||||
 | 
					            return 250; // cm
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updateState() {
 | 
				
			||||||
 | 
					            if (!this.visible) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const node = this.state;
 | 
				
			||||||
 | 
					            const percentage = node.getValue() / 10; /* convert back to cm */
 | 
				
			||||||
 | 
					            const y = this.getSliderHeight() * percentage / this.getMaxValue();
 | 
				
			||||||
 | 
					            this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
 | 
				
			||||||
 | 
					            // Update reporter text
 | 
				
			||||||
 | 
					            this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}cm`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
 | 
				
			||||||
 | 
					            let cur = svg.cursorPoint(pt, parent, ev);
 | 
				
			||||||
 | 
					            const bBox = this.rect.getBoundingClientRect();
 | 
				
			||||||
 | 
					            const height = bBox.height;
 | 
				
			||||||
 | 
					            let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const state = this.state;
 | 
				
			||||||
 | 
					            state.setDistance((1 - t) * (this.getMaxValue()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
 | 
					        getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
 | 
				
			||||||
            let gid = "gradient-slider-" + this.getPort();
 | 
					            let gid = "gradient-slider-" + this.getPort();
 | 
				
			||||||
            this.group = svg.elt("g") as SVGGElement;
 | 
					            this.group = svg.elt("g") as SVGGElement;
 | 
				
			||||||
@@ -28,13 +76,14 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.group = svg.elt("g") as SVGGElement;
 | 
					            this.group = svg.elt("g") as SVGGElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const reporterGroup = pxsim.svg.child(this.group, "g");
 | 
					            const reporterGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
            reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 42)`);
 | 
					            reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`);
 | 
				
			||||||
            this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
 | 
					            this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
					            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
            sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
 | 
					            sliderGroup.setAttribute("transform", `translate(${this.getInnerWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': DistanceSliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - DistanceSliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
 | 
					            const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': DistanceSliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - DistanceSliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
 | 
				
			||||||
 | 
					            this.rect = rect as SVGRectElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
 | 
					            this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
 | 
				
			||||||
            const sliderInner = pxsim.svg.child(this.slider, "g");
 | 
					            const sliderInner = pxsim.svg.child(this.slider, "g");
 | 
				
			||||||
@@ -48,7 +97,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
                height: this.getInnerHeight(),
 | 
					                height: this.getInnerHeight(),
 | 
				
			||||||
                opacity: 0,
 | 
					                opacity: 0,
 | 
				
			||||||
                cursor: '-webkit-grab'
 | 
					                cursor: '-webkit-grab'
 | 
				
			||||||
            })
 | 
					            }) as SVGRectElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let pt = parent.createSVGPoint();
 | 
					            let pt = parent.createSVGPoint();
 | 
				
			||||||
            let captured = false;
 | 
					            let captured = false;
 | 
				
			||||||
@@ -72,56 +121,6 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return this.group;
 | 
					            return this.group;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        getInnerHeight() {
 | 
					 | 
				
			||||||
            return 192;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        getInnerWidth() {
 | 
					 | 
				
			||||||
            return 111;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getReporterHeight() {
 | 
					 | 
				
			||||||
            return 50;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getSliderHeight() {
 | 
					 | 
				
			||||||
            return 110;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getSliderWidth() {
 | 
					 | 
				
			||||||
            return 62;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        updateState() {
 | 
					 | 
				
			||||||
            if (!this.visible) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            const node = this.state;
 | 
					 | 
				
			||||||
            const percentage = node.getValue() / 10; /* convert back to cm */
 | 
					 | 
				
			||||||
            const y = this.getSliderHeight() * percentage / this.getMax();
 | 
					 | 
				
			||||||
            this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
 | 
					 | 
				
			||||||
            // Update reporter text
 | 
					 | 
				
			||||||
            this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
 | 
					 | 
				
			||||||
            let cur = svg.cursorPoint(pt, parent, ev);
 | 
					 | 
				
			||||||
            const height = this.getSliderHeight();
 | 
					 | 
				
			||||||
            const bBox = this.content.getBoundingClientRect();
 | 
					 | 
				
			||||||
            let t = Math.max(0, Math.min(1, (DistanceSliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const state = this.state;
 | 
					 | 
				
			||||||
            state.setDistance((1 - t) * (this.getMax()));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getMin() {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getMax() {
 | 
					 | 
				
			||||||
            return 250; //cm
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getGradientDefinition(): LinearGradientDefinition {
 | 
					        private getGradientDefinition(): LinearGradientDefinition {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
                stops: [
 | 
					                stops: [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,11 +7,60 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        private gradient: SVGLinearGradientElement;
 | 
					        private gradient: SVGLinearGradientElement;
 | 
				
			||||||
        private slider: SVGGElement;
 | 
					        private slider: SVGGElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private rect: SVGRectElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private reporter: SVGTextElement;
 | 
					        private reporter: SVGTextElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static SLIDER_HANDLE_HEIGHT = 26;
 | 
					        private static SLIDER_HANDLE_HEIGHT = 26;
 | 
				
			||||||
        private static SLIDER_SIDE_PADDING = 6;
 | 
					        private static SLIDER_SIDE_PADDING = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerWidth() {
 | 
				
			||||||
 | 
					            return 111;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getInnerHeight() {
 | 
				
			||||||
 | 
					            return 192;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getReporterHeight() {
 | 
				
			||||||
 | 
					            return 38;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getSliderWidth() {
 | 
				
			||||||
 | 
					            return 62;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getSliderHeight() {
 | 
				
			||||||
 | 
					            return 131;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private getMaxValue() {
 | 
				
			||||||
 | 
					            return 100;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        updateState() {
 | 
				
			||||||
 | 
					            if (!this.visible) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const node = this.state;
 | 
				
			||||||
 | 
					            const percentage = node.getValue(); 
 | 
				
			||||||
 | 
					            const y = this.getSliderHeight() * percentage / this.getMaxValue();
 | 
				
			||||||
 | 
					            this.slider.setAttribute("transform", `translate(0, ${y - ProximitySliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
 | 
				
			||||||
 | 
					            // Update reporter text
 | 
				
			||||||
 | 
					            this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
 | 
				
			||||||
 | 
					            let cur = svg.cursorPoint(pt, parent, ev);
 | 
				
			||||||
 | 
					            const bBox = this.rect.getBoundingClientRect();
 | 
				
			||||||
 | 
					            const height = bBox.height;
 | 
				
			||||||
 | 
					            let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const state = this.state;
 | 
				
			||||||
 | 
					            const v = Math.floor((1 - t) * (this.getMaxValue()));
 | 
				
			||||||
 | 
					            state.setPromixity(v);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
 | 
					        getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
 | 
				
			||||||
            let gid = "gradient-slider-" + this.getId();
 | 
					            let gid = "gradient-slider-" + this.getId();
 | 
				
			||||||
            this.group = svg.elt("g") as SVGGElement;
 | 
					            this.group = svg.elt("g") as SVGGElement;
 | 
				
			||||||
@@ -27,13 +76,14 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.group = svg.elt("g") as SVGGElement;
 | 
					            this.group = svg.elt("g") as SVGGElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const reporterGroup = pxsim.svg.child(this.group, "g");
 | 
					            const reporterGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
            reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 42)`);
 | 
					            reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`);
 | 
				
			||||||
            this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
 | 
					            this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
					            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
            sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
 | 
					            sliderGroup.setAttribute("transform", `translate(${this.getInnerWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': ProximitySliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - ProximitySliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
 | 
					            const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': ProximitySliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - ProximitySliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
 | 
				
			||||||
 | 
					            this.rect = rect as SVGRectElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
 | 
					            this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
 | 
				
			||||||
            const sliderInner = pxsim.svg.child(this.slider, "g");
 | 
					            const sliderInner = pxsim.svg.child(this.slider, "g");
 | 
				
			||||||
@@ -71,57 +121,6 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return this.group;
 | 
					            return this.group;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        getInnerHeight() {
 | 
					 | 
				
			||||||
            return 192;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        getInnerWidth() {
 | 
					 | 
				
			||||||
            return 111;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getReporterHeight() {
 | 
					 | 
				
			||||||
            return 50;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getSliderHeight() {
 | 
					 | 
				
			||||||
            return 110;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getSliderWidth() {
 | 
					 | 
				
			||||||
            return 62;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        updateState() {
 | 
					 | 
				
			||||||
            if (!this.visible) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            const node = this.state;
 | 
					 | 
				
			||||||
            const percentage = node.getValue(); 
 | 
					 | 
				
			||||||
            const y = this.getSliderHeight() * percentage / this.getMax();
 | 
					 | 
				
			||||||
            this.slider.setAttribute("transform", `translate(0, ${y - ProximitySliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
 | 
					 | 
				
			||||||
            // Update reporter text
 | 
					 | 
				
			||||||
            this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
 | 
					 | 
				
			||||||
            let cur = svg.cursorPoint(pt, parent, ev);
 | 
					 | 
				
			||||||
            const height = this.getSliderHeight();
 | 
					 | 
				
			||||||
            const bBox = this.content.getBoundingClientRect();
 | 
					 | 
				
			||||||
            let t = Math.max(0, Math.min(1, (ProximitySliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const state = this.state;
 | 
					 | 
				
			||||||
            const v = Math.floor((1 - t) * (this.getMax()));
 | 
					 | 
				
			||||||
            state.setPromixity(v);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getMin() {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getMax() {
 | 
					 | 
				
			||||||
            return 100;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private getGradientDefinition(): LinearGradientDefinition {
 | 
					        private getGradientDefinition(): LinearGradientDefinition {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
                stops: [
 | 
					                stops: [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,10 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.group = svg.elt("g") as SVGGElement;
 | 
					            this.group = svg.elt("g") as SVGGElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
					            const sliderGroup = pxsim.svg.child(this.group, "g");
 | 
				
			||||||
            sliderGroup.setAttribute("transform", `translate(5, ${10 + this.getTopPadding()})`)
 | 
					            sliderGroup.setAttribute("transform", `translate(10,0)`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const rotationLine = pxsim.svg.child(sliderGroup, "g");
 | 
					            const rotationLine = pxsim.svg.child(sliderGroup, "g");
 | 
				
			||||||
            pxsim.svg.child(rotationLine, "path", { 'transform': 'translate(5.11 -31.1)', 'd': 'M68.71,99.5l6.1-8S61.3,79.91,42.69,78.35,12,83.14,6.49,85.63a48.69,48.69,0,0,0-9.6,5.89L3.16,99.3S19.27,87.7,37.51,87.94,68.71,99.5,68.71,99.5Z', 'style': 'fill: #626262' });
 | 
					            pxsim.svg.child(rotationLine, "path", { 'transform': 'translate(7.11 -31.1)', 'd': 'M68.71,99.5l6.1-8S61.3,79.91,42.69,78.35,12,83.14,6.49,85.63a48.69,48.69,0,0,0-9.6,5.89L3.16,99.3S19.27,87.7,37.51,87.94,68.71,99.5,68.71,99.5Z', 'style': 'fill: #626262' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.slider = pxsim.svg.child(sliderGroup, "g") as SVGGElement;
 | 
					            this.slider = pxsim.svg.child(sliderGroup, "g") as SVGGElement;
 | 
				
			||||||
            const handleInner = pxsim.svg.child(sliderGroup, "g");
 | 
					            const handleInner = pxsim.svg.child(sliderGroup, "g");
 | 
				
			||||||
@@ -52,8 +52,8 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return this.group;
 | 
					            return this.group;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private getTopPadding() {
 | 
					        getInnerWidth() {
 | 
				
			||||||
            return this.getInnerHeight() / 4;
 | 
					            return RotationSliderControl.SLIDER_WIDTH * 1.5;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        updateState() {
 | 
					        updateState() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,8 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    export const MAX_MODULE_WIDTH = 100;
 | 
					    export const MAX_MODULE_WIDTH = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    export const CLOSE_ICON_GAP_MULTIPLIER = 0.3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export interface LayoutElement extends View {
 | 
					    export interface LayoutElement extends View {
 | 
				
			||||||
        getId(): number;
 | 
					        getId(): number;
 | 
				
			||||||
        getPort(): number;
 | 
					        getPort(): number;
 | 
				
			||||||
@@ -33,6 +35,9 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        private inputCloseIcons: View[] = [];
 | 
					        private inputCloseIcons: View[] = [];
 | 
				
			||||||
        private outputCloseIcons: View[] = [];
 | 
					        private outputCloseIcons: View[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private inputBackgroundViews: View[] = [];
 | 
				
			||||||
 | 
					        private outputBackgroundViews: View[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private inputWires: WireView[] = [];
 | 
					        private inputWires: WireView[] = [];
 | 
				
			||||||
        private outputWires: WireView[] = [];
 | 
					        private outputWires: WireView[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -119,7 +124,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            else this.setlectBrick();
 | 
					            else this.setlectBrick();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
 | 
					        public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View, backgroundView?: View) {
 | 
				
			||||||
            if (this.inputs[port] != view || this.inputControls[port] != control) {
 | 
					            if (this.inputs[port] != view || this.inputControls[port] != control) {
 | 
				
			||||||
                if (this.inputs[port]) {
 | 
					                if (this.inputs[port]) {
 | 
				
			||||||
                    // Remove current input
 | 
					                    // Remove current input
 | 
				
			||||||
@@ -131,10 +136,12 @@ namespace pxsim.visuals {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                this.inputControls[port] = control;
 | 
					                this.inputControls[port] = control;
 | 
				
			||||||
                this.inputCloseIcons[port] = closeIcon;
 | 
					                this.inputCloseIcons[port] = closeIcon;
 | 
				
			||||||
 | 
					                this.inputBackgroundViews[port] = backgroundView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                this.inputContainers[port].clear();
 | 
					                this.inputContainers[port].clear();
 | 
				
			||||||
                this.inputContainers[port].addView(view);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (control && backgroundView) this.inputContainers[port].addView(backgroundView);
 | 
				
			||||||
 | 
					                this.inputContainers[port].addView(view);
 | 
				
			||||||
                if (control) this.inputContainers[port].addView(control);
 | 
					                if (control) this.inputContainers[port].addView(control);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (view.hasClick()) view.registerClick((ev: any) => {
 | 
					                if (view.hasClick()) view.registerClick((ev: any) => {
 | 
				
			||||||
@@ -155,7 +162,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.position();
 | 
					            this.position();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
 | 
					        public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View, backgroundView?: View) {
 | 
				
			||||||
            if (this.outputs[port] != view || this.outputControls[port] != control) {
 | 
					            if (this.outputs[port] != view || this.outputControls[port] != control) {
 | 
				
			||||||
                if (this.outputs[port]) {
 | 
					                if (this.outputs[port]) {
 | 
				
			||||||
                    // Remove current output
 | 
					                    // Remove current output
 | 
				
			||||||
@@ -167,10 +174,12 @@ namespace pxsim.visuals {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                this.outputControls[port] = control;
 | 
					                this.outputControls[port] = control;
 | 
				
			||||||
                this.outputCloseIcons[port] = closeIcon;
 | 
					                this.outputCloseIcons[port] = closeIcon;
 | 
				
			||||||
 | 
					                this.outputBackgroundViews[port] = backgroundView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                this.outputContainers[port].clear();
 | 
					                this.outputContainers[port].clear();
 | 
				
			||||||
                this.outputContainers[port].addView(view);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (control && backgroundView) this.outputContainers[port].addView(backgroundView);
 | 
				
			||||||
 | 
					                this.outputContainers[port].addView(view);
 | 
				
			||||||
                if (control) this.outputContainers[port].addView(control);
 | 
					                if (control) this.outputContainers[port].addView(control);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (view.hasClick()) view.registerClick((ev: any) => {
 | 
					                if (view.hasClick()) view.registerClick((ev: any) => {
 | 
				
			||||||
@@ -277,35 +286,48 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            const modulePadding = this.getModulePadding();
 | 
					            const modulePadding = this.getModulePadding();
 | 
				
			||||||
            const moduleSpacing = contentWidth / 4;
 | 
					            const moduleSpacing = contentWidth / 4;
 | 
				
			||||||
            const moduleWidth = this.getInnerModuleWidth();
 | 
					 | 
				
			||||||
            let currentX = this.getModulePadding();
 | 
					            let currentX = this.getModulePadding();
 | 
				
			||||||
            let currentY = 0;
 | 
					            let currentY = 0;
 | 
				
			||||||
            this.outputs.forEach((n, i) => {
 | 
					            this.outputs.forEach((n, i) => {
 | 
				
			||||||
                this.outputContainers[i].translate(currentX, currentY);
 | 
					                this.outputContainers[i].translate(currentX + (this.getAbosluteModuleWidth() - this.getInnerModuleWidth()) / 2, currentY);
 | 
				
			||||||
                if (this.outputs[i]) {
 | 
					                if (this.outputs[i]) {
 | 
				
			||||||
                    const view = this.outputs[i];
 | 
					                    const view = this.outputs[i];
 | 
				
			||||||
                    const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
 | 
					                    const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
 | 
				
			||||||
                    const desiredOutputWidth = this.getInnerModuleWidth() - outputPadding * 2;
 | 
					 | 
				
			||||||
                    const outputWidth = Math.min(desiredOutputWidth, MAX_MODULE_WIDTH);
 | 
					 | 
				
			||||||
                    const outputHeight = this.getModuleHeight();
 | 
					                    const outputHeight = this.getModuleHeight();
 | 
				
			||||||
 | 
					                    const outputWidth = this.getInnerModuleWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Translate and resize view
 | 
					                    // Translate and resize view
 | 
				
			||||||
                    view.resize(outputWidth, outputHeight);
 | 
					                    view.resize(outputWidth - outputPadding * 2, outputHeight);
 | 
				
			||||||
                    const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
 | 
					                    // const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
 | 
				
			||||||
                    view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
 | 
					                    // view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
 | 
				
			||||||
 | 
					                    const viewHeight = view.getActualHeight();
 | 
				
			||||||
 | 
					                    view.translate(outputPadding, outputHeight - viewHeight, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Resize control
 | 
					                    // Resize control
 | 
				
			||||||
                    const control = this.outputControls[i];
 | 
					                    const control = this.outputControls[i];
 | 
				
			||||||
                    if (control) {
 | 
					                    if (control) {
 | 
				
			||||||
                        control.resize(this.getInnerModuleWidth(), outputHeight);
 | 
					                        const controlWidth = outputWidth;
 | 
				
			||||||
 | 
					                        const closeIconOffset = (this.getCloseIconSize() * (1 + CLOSE_ICON_GAP_MULTIPLIER));
 | 
				
			||||||
 | 
					                        const controlHeight = outputHeight - closeIconOffset;
 | 
				
			||||||
 | 
					                        control.resize(controlWidth, controlHeight);
 | 
				
			||||||
 | 
					                        control.translate((controlWidth - control.getActualWidth()) / 2,
 | 
				
			||||||
 | 
					                            closeIconOffset + ((controlHeight - control.getActualHeight()) / 2), true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // Translate close icon
 | 
					                        // Translate and resize close icon
 | 
				
			||||||
                        const closeIcon = this.outputCloseIcons[i];
 | 
					                        const closeIcon = this.outputCloseIcons[i];
 | 
				
			||||||
                        if (closeIcon) {
 | 
					                        if (closeIcon) {
 | 
				
			||||||
                            const closeIconWidth = closeIcon.getWidth();
 | 
					                            const closeIconSize = this.getCloseIconSize();
 | 
				
			||||||
                            closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, 0);
 | 
					                            closeIcon.resize(closeIconSize, closeIconSize);
 | 
				
			||||||
 | 
					                            closeIcon.translate((outputWidth - closeIcon.getActualWidth()) / 2, (CLOSE_ICON_GAP_MULTIPLIER * closeIcon.getActualHeight()), true);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Resize background
 | 
				
			||||||
 | 
					                    const backgroundView = this.inputBackgroundViews[i];
 | 
				
			||||||
 | 
					                    if (backgroundView) {
 | 
				
			||||||
 | 
					                        backgroundView.resize(this.getInnerModuleWidth(), outputHeight);
 | 
				
			||||||
 | 
					                        backgroundView.translate(0, 0, true)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                currentX += moduleSpacing;
 | 
					                currentX += moduleSpacing;
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
@@ -341,31 +363,42 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            currentY += brickHeight + this.getWiringHeight();
 | 
					            currentY += brickHeight + this.getWiringHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.inputs.forEach((n, i) => {
 | 
					            this.inputs.forEach((n, i) => {
 | 
				
			||||||
                this.inputContainers[i].translate(currentX, currentY);
 | 
					                this.inputContainers[i].translate(currentX + (this.getAbosluteModuleWidth() - this.getInnerModuleWidth()) / 2, currentY);
 | 
				
			||||||
                if (this.inputs[i]) {
 | 
					                if (this.inputs[i]) {
 | 
				
			||||||
                    const view = this.inputs[i];
 | 
					                    const view = this.inputs[i];
 | 
				
			||||||
                    const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
 | 
					                    const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
 | 
				
			||||||
                    const desiredInputWidth = this.getInnerModuleWidth() - inputPadding * 2;
 | 
					 | 
				
			||||||
                    const inputWidth = Math.min(desiredInputWidth, MAX_MODULE_WIDTH);
 | 
					 | 
				
			||||||
                    const inputHeight = this.getModuleHeight();
 | 
					                    const inputHeight = this.getModuleHeight();
 | 
				
			||||||
 | 
					                    const inputWidth = this.getInnerModuleWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Translate and resize view
 | 
					                    // Translate and resize view
 | 
				
			||||||
                    view.resize(inputWidth, inputHeight);
 | 
					                    view.resize(inputWidth - inputPadding * 2, inputHeight);
 | 
				
			||||||
                    view.translate(inputPadding + ((desiredInputWidth - inputWidth) / 2), 0, true);
 | 
					                    const viewHeight = view.getActualHeight();
 | 
				
			||||||
 | 
					                    view.translate(inputPadding, 0, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Resize control
 | 
					                    // Resize control
 | 
				
			||||||
                    const control = this.inputControls[i];
 | 
					                    const control = this.inputControls[i];
 | 
				
			||||||
                    if (control) {
 | 
					                    if (control) {
 | 
				
			||||||
                        control.resize(this.getInnerModuleWidth(), inputHeight);
 | 
					                        const controlWidth = inputWidth;
 | 
				
			||||||
 | 
					                        const controlHeight = inputHeight - viewHeight - (this.getCloseIconSize() * (1 + CLOSE_ICON_GAP_MULTIPLIER));
 | 
				
			||||||
 | 
					                        control.resize(controlWidth, controlHeight);
 | 
				
			||||||
 | 
					                        control.translate((controlWidth - control.getActualWidth()) / 2,
 | 
				
			||||||
 | 
					                            viewHeight + ((controlHeight - control.getActualHeight()) / 2), true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // Translate and resize close icon
 | 
					                        // Translate and resize close icon
 | 
				
			||||||
                        const closeIcon = this.inputCloseIcons[i];
 | 
					                        const closeIcon = this.inputCloseIcons[i];
 | 
				
			||||||
                        if (closeIcon) {
 | 
					                        if (closeIcon) {
 | 
				
			||||||
                            const closeIconWidth = closeIcon.getWidth();
 | 
					                            const closeIconSize = this.getCloseIconSize();
 | 
				
			||||||
                            const closeIconHeight = closeIcon.getHeight();
 | 
					                            closeIcon.resize(closeIconSize, closeIconSize);
 | 
				
			||||||
                            closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, this.getModuleHeight() - closeIconHeight);
 | 
					                            closeIcon.translate((inputWidth - closeIcon.getActualWidth()) / 2, inputHeight - ((1 + CLOSE_ICON_GAP_MULTIPLIER) * closeIcon.getActualHeight()), true);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Resize background
 | 
				
			||||||
 | 
					                    const backgroundView = this.inputBackgroundViews[i];
 | 
				
			||||||
 | 
					                    if (backgroundView) {
 | 
				
			||||||
 | 
					                        backgroundView.resize(this.getInnerModuleWidth(), inputHeight, true);
 | 
				
			||||||
 | 
					                        backgroundView.translate(0, 0, true)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                currentX += moduleSpacing;
 | 
					                currentX += moduleSpacing;
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
@@ -394,7 +427,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public getModuleBounds() {
 | 
					        public getModuleBounds() {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
                width: this.width / 4,
 | 
					                width: Math.min(this.getAbosluteModuleWidth(), MAX_MODULE_WIDTH),
 | 
				
			||||||
                height: this.getModuleHeight()
 | 
					                height: this.getModuleHeight()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -407,8 +440,16 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return this.getModuleBounds().width - (this.getModulePadding() * 2);
 | 
					            return this.getModuleBounds().width - (this.getModulePadding() * 2);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public getAbosluteModuleWidth() {
 | 
				
			||||||
 | 
					            return this.width / 4;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public getModuleHeight() {
 | 
					        public getModuleHeight() {
 | 
				
			||||||
            return this.height * MODULE_HEIGHT_RATIO;
 | 
					            return this.height * MODULE_HEIGHT_RATIO;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public getCloseIconSize() {
 | 
				
			||||||
 | 
					            return this.getInnerModuleWidth() / 4;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,8 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private control: ColorGridControl;
 | 
					        private control: ColorGridControl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static sensor_hole_id = 'color_sensor_white_big';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        constructor(port: number) {
 | 
					        constructor(port: number) {
 | 
				
			||||||
            super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port);
 | 
					            super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -19,7 +21,22 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public updateState() {
 | 
					        public updateState() {
 | 
				
			||||||
            super.updateState();
 | 
					            super.updateState();
 | 
				
			||||||
            // TODO: show different color modes
 | 
					
 | 
				
			||||||
 | 
					            const colorState = ev3board().getInputNodes()[this.port];
 | 
				
			||||||
 | 
					            if (!colorState) return;
 | 
				
			||||||
 | 
					            const mode = colorState.getMode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch (mode) {
 | 
				
			||||||
 | 
					                case ColorSensorMode.Colors: this.updateSensorLightVisual('#0062DD'); return; // blue
 | 
				
			||||||
 | 
					                case ColorSensorMode.Reflected: this.updateSensorLightVisual('#F86262'); return; // red
 | 
				
			||||||
 | 
					                case ColorSensorMode.Ambient: this.updateSensorLightVisual('#67C3E2'); return; // light blue
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.updateSensorLightVisual('#ffffff');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private updateSensorLightVisual(color: string) {
 | 
				
			||||||
 | 
					            const sensorHole = this.content.getElementById(this.normalizeId(ColorSensorView.sensor_hole_id)) as SVGCircleElement;
 | 
				
			||||||
 | 
					            sensorHole.style.fill = color;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -107,7 +107,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        public attachEvents() {
 | 
					        public attachEvents() {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public resize(width: number, height: number) {
 | 
					        public resize(width: number, height: number, strict?: boolean) {
 | 
				
			||||||
            super.resize(width, height);
 | 
					            super.resize(width, height);
 | 
				
			||||||
            this.updateDimensions(width, height);
 | 
					            this.updateDimensions(width, height);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -120,6 +120,7 @@ namespace pxsim.visuals {
 | 
				
			|||||||
                const newWidth = currentWidth / currentHeight * height;
 | 
					                const newWidth = currentWidth / currentHeight * height;
 | 
				
			||||||
                this.content.setAttribute('width', `${width}`);
 | 
					                this.content.setAttribute('width', `${width}`);
 | 
				
			||||||
                this.content.setAttribute('height', `${newHeight}`);
 | 
					                this.content.setAttribute('height', `${newHeight}`);
 | 
				
			||||||
 | 
					                this.height = newHeight;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,16 +139,22 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected updateOpacity() {
 | 
					        protected updateOpacity() {
 | 
				
			||||||
            if (this.rendered) {
 | 
					            if (this.rendered) {
 | 
				
			||||||
                const opacity = this.selected ? 0.2 : 1;
 | 
					                const opacity = this.selected && this.fadeWhenSelected() ? 0.2 : 1;
 | 
				
			||||||
                if (this.hasClick() && this.opacity != opacity) {
 | 
					                if (this.hasClick() && this.opacity != opacity) {
 | 
				
			||||||
                    this.opacity = opacity;
 | 
					                    this.opacity = opacity;
 | 
				
			||||||
                    this.setOpacity(this.opacity);
 | 
					                    this.setOpacity(this.opacity);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (this.hasClick()) {
 | 
				
			||||||
                    if (this.selected) this.content.style.cursor = "";
 | 
					                    if (this.selected) this.content.style.cursor = "";
 | 
				
			||||||
                    else this.content.style.cursor = "pointer";
 | 
					                    else this.content.style.cursor = "pointer";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected fadeWhenSelected() {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected setOpacity(opacity: number) {
 | 
					        protected setOpacity(opacity: number) {
 | 
				
			||||||
            this.element.setAttribute("opacity", `${opacity}`);
 | 
					            this.element.setAttribute("opacity", `${opacity}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,14 +6,16 @@ namespace pxsim.visuals {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        constructor(xml: string, prefix: string, id: NodeType, port: NodeType) {
 | 
					        constructor(xml: string, prefix: string, id: NodeType, port: NodeType) {
 | 
				
			||||||
            super(xml, prefix, id, port);
 | 
					            super(xml, prefix, id, port);
 | 
				
			||||||
 | 
					            // Shown by default
 | 
				
			||||||
 | 
					            this.selected = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public getSelected() {
 | 
					        protected fadeWhenSelected() {
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public hasClose() {
 | 
					 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public hasBackground() {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -130,11 +130,19 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            return this.element;
 | 
					            return this.element;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public resize(width: number, height: number) {
 | 
					        public resize(width: number, height: number, strict?: boolean) {
 | 
				
			||||||
            this.width = width;
 | 
					            this.width = width;
 | 
				
			||||||
            this.height = height;
 | 
					            this.height = height;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public getActualHeight() {
 | 
				
			||||||
 | 
					            return this.height;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public getActualWidth() {
 | 
				
			||||||
 | 
					            return this.width;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private updateTransform() {
 | 
					        private updateTransform() {
 | 
				
			||||||
            if (this.rendered) {
 | 
					            if (this.rendered) {
 | 
				
			||||||
                let left = this.left;
 | 
					                let left = this.left;
 | 
				
			||||||
@@ -212,10 +220,6 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public hasClose() {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected setChangedState() {
 | 
					        protected setChangedState() {
 | 
				
			||||||
            this.changed = true;
 | 
					            this.changed = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -225,6 +229,10 @@ namespace pxsim.visuals {
 | 
				
			|||||||
            this.changed = false;
 | 
					            this.changed = false;
 | 
				
			||||||
            return res;
 | 
					            return res;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public hasBackground() {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
 | 
					    export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
 | 
				
			||||||
@@ -272,9 +280,13 @@ namespace pxsim.visuals {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public clear() {
 | 
					        public clear() {
 | 
				
			||||||
 | 
					            const markForRemoval: Element[] = [];
 | 
				
			||||||
            forEachElement(this.element.childNodes, e => {
 | 
					            forEachElement(this.element.childNodes, e => {
 | 
				
			||||||
                this.element.removeChild(e);
 | 
					                markForRemoval.push(e);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					            markForRemoval.forEach(e => {
 | 
				
			||||||
 | 
					                this.element.removeChild(e);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public onComponentInjected() {
 | 
					        public onComponentInjected() {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user