Merge pull request #125 from Microsoft/sim_refactor_resize
Refactor sim for better resizing and support for multiple controls to be open
This commit is contained in:
commit
88df2e14cb
@ -23,20 +23,6 @@ namespace pxsim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class EV3Board extends CoreBoard {
|
export class EV3Board extends CoreBoard {
|
||||||
// state & update logic for component services
|
|
||||||
// neopixelState: CommonNeoPixelState;
|
|
||||||
buttonState: EV3ButtonState;
|
|
||||||
slideSwitchState: SlideSwitchState;
|
|
||||||
lightSensorState: AnalogSensorState;
|
|
||||||
thermometerState: AnalogSensorState;
|
|
||||||
thermometerUnitState: number;
|
|
||||||
microphoneState: AnalogSensorState;
|
|
||||||
edgeConnectorState: EdgeConnectorState;
|
|
||||||
capacitiveSensorState: CapacitiveSensorState;
|
|
||||||
accelerometerState: AccelerometerState;
|
|
||||||
touchButtonState: TouchButtonState;
|
|
||||||
irState: InfraredState;
|
|
||||||
|
|
||||||
view: SVGSVGElement;
|
view: SVGSVGElement;
|
||||||
|
|
||||||
outputState: EV3OutputState;
|
outputState: EV3OutputState;
|
||||||
@ -86,11 +72,6 @@ namespace pxsim {
|
|||||||
// TODO
|
// TODO
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "irpacket": {
|
|
||||||
let ev = <SimulatorInfraredPacketMessage>msg;
|
|
||||||
this.irState.receive(new RefBuffer(ev.packet));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +101,9 @@ namespace pxsim {
|
|||||||
document.body.innerHTML = ""; // clear children
|
document.body.innerHTML = ""; // clear children
|
||||||
document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
|
document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
|
||||||
|
|
||||||
|
this.inputNodes = [];
|
||||||
|
this.outputNodes = [];
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,14 +111,6 @@ namespace pxsim {
|
|||||||
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
|
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
|
||||||
}
|
}
|
||||||
|
|
||||||
//defaultNeopixelPin() {
|
|
||||||
// return this.edgeConnectorState.getPin(CPlayPinName.D8);
|
|
||||||
//}
|
|
||||||
|
|
||||||
getDefaultPitchPin() {
|
|
||||||
return this.edgeConnectorState.getPin(CPlayPinName.D6);
|
|
||||||
}
|
|
||||||
|
|
||||||
getBrickNode() {
|
getBrickNode() {
|
||||||
return this.brickNode;
|
return this.brickNode;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace pxsim {
|
|||||||
export class ColorSensorNode extends UartSensorNode {
|
export class ColorSensorNode extends UartSensorNode {
|
||||||
id = NodeType.ColorSensor;
|
id = NodeType.ColorSensor;
|
||||||
|
|
||||||
private color: number;
|
private color: number = 50;
|
||||||
|
|
||||||
constructor(port: number) {
|
constructor(port: number) {
|
||||||
super(port);
|
super(port);
|
||||||
@ -31,10 +31,8 @@ namespace pxsim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setColor(color: number) {
|
setColor(color: number) {
|
||||||
if (this.color != color) {
|
this.color = color;
|
||||||
this.color = color;
|
this.setChangedState();
|
||||||
this.setChangedState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue() {
|
getValue() {
|
||||||
|
@ -48,6 +48,7 @@ namespace pxsim {
|
|||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
|
this.setSpeed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
@ -88,7 +88,7 @@ namespace pxsim {
|
|||||||
const inputNodes = ev3board().getInputNodes();
|
const inputNodes = ev3board().getInputNodes();
|
||||||
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
||||||
const node = inputNodes[port];
|
const node = inputNodes[port];
|
||||||
if (node) {
|
if (node && node.isUart()) {
|
||||||
// Actual
|
// Actual
|
||||||
const index = 0; //UartOff.Actual + port * 2;
|
const index = 0; //UartOff.Actual + port * 2;
|
||||||
util.map16Bit(data, UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index, Math.floor(node.getValue()))
|
util.map16Bit(data, UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index, Math.floor(node.getValue()))
|
||||||
|
@ -99,16 +99,9 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
private layoutView: LayoutView;
|
private layoutView: LayoutView;
|
||||||
|
|
||||||
private controlGroup: ViewContainer;
|
|
||||||
private selectedNode: NodeType;
|
|
||||||
private selectedPort: number;
|
|
||||||
private controlView: View;
|
|
||||||
private cachedControlNodes: { [index: string]: View[] } = {};
|
private cachedControlNodes: { [index: string]: View[] } = {};
|
||||||
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
|
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
|
||||||
|
|
||||||
private closeGroup: ViewContainer;
|
|
||||||
private closeIconView: View;
|
|
||||||
|
|
||||||
private screenCanvas: HTMLCanvasElement;
|
private screenCanvas: HTMLCanvasElement;
|
||||||
private screenCanvasCtx: CanvasRenderingContext2D;
|
private screenCanvasCtx: CanvasRenderingContext2D;
|
||||||
private screenCanvasData: ImageData;
|
private screenCanvasData: ImageData;
|
||||||
@ -143,7 +136,12 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
Runtime.messagePosted = (msg) => {
|
Runtime.messagePosted = (msg) => {
|
||||||
switch (msg.type || "") {
|
switch (msg.type || "") {
|
||||||
case "status": if ((msg as pxsim.SimulatorStateMessage).state == "killed") this.kill(); break;
|
case "status": {
|
||||||
|
const state = (msg as pxsim.SimulatorStateMessage).state;
|
||||||
|
if (state == "killed") this.kill();
|
||||||
|
if (state == "running") this.begin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,36 +175,6 @@ namespace pxsim.visuals {
|
|||||||
this.layoutView.updateTheme(theme);
|
this.layoutView.updateTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize() {
|
|
||||||
const bounds = this.element.getBoundingClientRect();
|
|
||||||
this.width = bounds.width;
|
|
||||||
this.height = bounds.height;
|
|
||||||
this.layoutView.layout(bounds.width, bounds.height);
|
|
||||||
|
|
||||||
if (this.selectedNode) {
|
|
||||||
const scale = this.width / this.closeIconView.getInnerWidth() / 10;
|
|
||||||
// Translate close icon
|
|
||||||
this.closeIconView.scale(Math.max(0, Math.min(1, scale)));
|
|
||||||
const closeIconWidth = this.closeIconView.getWidth();
|
|
||||||
const closeIconHeight = this.closeIconView.getHeight();
|
|
||||||
const closeCoords = this.layoutView.getCloseIconCoords(closeIconWidth, closeIconHeight);
|
|
||||||
this.closeIconView.translate(closeCoords.x, closeCoords.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.controlView) {
|
|
||||||
const h = this.controlView.getInnerHeight();
|
|
||||||
const w = this.controlView.getInnerWidth();
|
|
||||||
const bh = this.layoutView.getModuleBounds().height - this.closeIconView.getHeight();
|
|
||||||
const bw = this.layoutView.getModuleBounds().width - (this.width * MODULE_INNER_PADDING_RATIO * 2);
|
|
||||||
this.controlView.scale(Math.min(bh / h, bw / w), false);
|
|
||||||
|
|
||||||
const controlCoords = this.layoutView.getSelectedCoords();
|
|
||||||
this.controlView.translate(controlCoords.x, controlCoords.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
//this.updateScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
private getControlForNode(id: NodeType, port: number) {
|
private getControlForNode(id: NodeType, port: number) {
|
||||||
if (this.cachedControlNodes[id] && this.cachedControlNodes[id][port]) {
|
if (this.cachedControlNodes[id] && this.cachedControlNodes[id][port]) {
|
||||||
return this.cachedControlNodes[id][port];
|
return this.cachedControlNodes[id][port];
|
||||||
@ -285,6 +253,10 @@ namespace pxsim.visuals {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCloseIconView() {
|
||||||
|
return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
|
||||||
|
}
|
||||||
|
|
||||||
private buildDom() {
|
private buildDom() {
|
||||||
this.wrapper = document.createElement('div');
|
this.wrapper = document.createElement('div');
|
||||||
this.wrapper.style.display = 'inline';
|
this.wrapper.style.display = 'inline';
|
||||||
@ -299,25 +271,10 @@ namespace pxsim.visuals {
|
|||||||
this.layoutView = new LayoutView();
|
this.layoutView = new LayoutView();
|
||||||
this.layoutView.inject(this.element);
|
this.layoutView.inject(this.element);
|
||||||
|
|
||||||
this.controlGroup = new ViewContainer();
|
|
||||||
this.controlGroup.inject(this.element);
|
|
||||||
|
|
||||||
this.closeGroup = new ViewContainer();
|
|
||||||
this.closeGroup.inject(this.element);
|
|
||||||
|
|
||||||
// Add EV3 module element
|
// Add EV3 module element
|
||||||
this.layoutView.setBrick(new BrickView(-1));
|
this.layoutView.setBrick(new BrickView(-1));
|
||||||
|
|
||||||
this.closeIconView = new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
|
|
||||||
this.closeIconView.registerClick(() => {
|
|
||||||
this.layoutView.clearSelected();
|
|
||||||
this.updateState();
|
|
||||||
})
|
|
||||||
this.closeGroup.addView(this.closeIconView);
|
|
||||||
this.closeIconView.setVisible(false);
|
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
//this.updateState();
|
|
||||||
|
|
||||||
// Add Screen canvas to board
|
// Add Screen canvas to board
|
||||||
this.buildScreenCanvas();
|
this.buildScreenCanvas();
|
||||||
@ -329,6 +286,22 @@ namespace pxsim.visuals {
|
|||||||
window.addEventListener("resize", e => {
|
window.addEventListener("resize", e => {
|
||||||
this.resize();
|
this.resize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resize();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public resize() {
|
||||||
|
if (!this.element) return;
|
||||||
|
const bounds = this.element.getBoundingClientRect();
|
||||||
|
this.width = bounds.width;
|
||||||
|
this.height = bounds.height;
|
||||||
|
this.layoutView.layout(bounds.width, bounds.height);
|
||||||
|
|
||||||
|
this.updateState();
|
||||||
|
let state = ev3board().screenState;
|
||||||
|
this.updateScreenStep(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildScreenCanvas() {
|
private buildScreenCanvas() {
|
||||||
@ -357,12 +330,27 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private kill() {
|
private kill() {
|
||||||
if (this.lastAnimationId) cancelAnimationFrame(this.lastAnimationId);
|
this.running = false;
|
||||||
|
if (this.lastAnimationIds.length > 0) {
|
||||||
|
this.lastAnimationIds.forEach(animationId => {
|
||||||
|
cancelAnimationFrame(animationId);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lastAnimationId: number;
|
private begin() {
|
||||||
|
this.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private running: boolean = false;
|
||||||
|
private lastAnimationIds: number[] = [];
|
||||||
public updateState() {
|
public updateState() {
|
||||||
if (this.lastAnimationId) cancelAnimationFrame(this.lastAnimationId);
|
if (this.lastAnimationIds.length > 0) {
|
||||||
|
this.lastAnimationIds.forEach(animationId => {
|
||||||
|
cancelAnimationFrame(animationId);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!this.running) return;
|
||||||
const fps = GAME_LOOP_FPS;
|
const fps = GAME_LOOP_FPS;
|
||||||
let now;
|
let now;
|
||||||
let then = Date.now();
|
let then = Date.now();
|
||||||
@ -370,7 +358,8 @@ namespace pxsim.visuals {
|
|||||||
let delta;
|
let delta;
|
||||||
let that = this;
|
let that = this;
|
||||||
function loop() {
|
function loop() {
|
||||||
that.lastAnimationId = requestAnimationFrame(loop);
|
const animationId = requestAnimationFrame(loop);
|
||||||
|
that.lastAnimationIds.push(animationId);
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
delta = now - then;
|
delta = now - then;
|
||||||
if (delta > interval) {
|
if (delta > interval) {
|
||||||
@ -382,67 +371,46 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateStateStep(elapsed: number) {
|
private updateStateStep(elapsed: number) {
|
||||||
const selected = this.layoutView.getSelected();
|
|
||||||
let selectedChanged = false;
|
|
||||||
const inputNodes = ev3board().getInputNodes();
|
const inputNodes = ev3board().getInputNodes();
|
||||||
inputNodes.forEach((node, index) => {
|
inputNodes.forEach((node, index) => {
|
||||||
node.updateState(elapsed);
|
node.updateState(elapsed);
|
||||||
if (!node.didChange()) return;
|
|
||||||
const view = this.getDisplayViewForNode(node.id, index);
|
const view = this.getDisplayViewForNode(node.id, index);
|
||||||
|
if (!node.didChange() && !view.didChange()) return;
|
||||||
if (view) {
|
if (view) {
|
||||||
this.layoutView.setInput(index, view);
|
const control = view.getSelected() ? this.getControlForNode(node.id, index) : undefined;
|
||||||
|
const closeIcon = control ? this.getCloseIconView() : undefined;
|
||||||
|
this.layoutView.setInput(index, view, control, closeIcon);
|
||||||
view.updateState();
|
view.updateState();
|
||||||
if (selected == view) selectedChanged = true;
|
if (control) control.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const brickNode = ev3board().getBrickNode();
|
const brickNode = ev3board().getBrickNode();
|
||||||
if (brickNode.didChange()) {
|
if (brickNode.didChange()) {
|
||||||
this.getDisplayViewForNode(ev3board().getBrickNode().id, -1).updateState();
|
this.getDisplayViewForNode(brickNode.id, -1).updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputNodes = ev3board().getMotors();
|
const outputNodes = ev3board().getMotors();
|
||||||
outputNodes.forEach((node, index) => {
|
outputNodes.forEach((node, index) => {
|
||||||
node.updateState(elapsed);
|
node.updateState(elapsed);
|
||||||
if (!node.didChange()) return;
|
|
||||||
const view = this.getDisplayViewForNode(node.id, index);
|
const view = this.getDisplayViewForNode(node.id, index);
|
||||||
|
if (!node.didChange() && !view.didChange()) return;
|
||||||
if (view) {
|
if (view) {
|
||||||
this.layoutView.setOutput(index, view);
|
const control = view.getSelected() ? this.getControlForNode(node.id, index) : undefined;
|
||||||
|
const closeIcon = control ? this.getCloseIconView() : undefined;
|
||||||
|
this.layoutView.setOutput(index, view, control, closeIcon);
|
||||||
view.updateState();
|
view.updateState();
|
||||||
if (selected == view) selectedChanged = true;
|
if (control) control.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (selected && (selected.getId() !== this.selectedNode || selected.getPort() !== this.selectedPort)) {
|
let state = ev3board().screenState;
|
||||||
this.selectedNode = selected.getId();
|
if (!state.didChange()) {
|
||||||
this.selectedPort = selected.getPort();
|
this.updateScreenStep(state);
|
||||||
this.controlGroup.clear();
|
|
||||||
const control = this.getControlForNode(this.selectedNode, selected.getPort());
|
|
||||||
if (control) {
|
|
||||||
this.controlView = control;
|
|
||||||
this.controlGroup.addView(control);
|
|
||||||
}
|
|
||||||
this.closeIconView.setVisible(true);
|
|
||||||
this.resize();
|
|
||||||
} else if (!selected) {
|
|
||||||
this.controlGroup.clear();
|
|
||||||
this.controlView = undefined;
|
|
||||||
this.selectedNode = undefined;
|
|
||||||
this.selectedPort = undefined;
|
|
||||||
this.closeIconView.setVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedChanged && selected) {
|
|
||||||
this.controlView.updateState();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateScreenStep();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateScreenStep() {
|
private updateScreenStep(state: EV3ScreenState) {
|
||||||
let state = ev3board().screenState;
|
|
||||||
if (!state.didChange()) return;
|
|
||||||
|
|
||||||
const bBox = this.layoutView.getBrick().getScreenBBox();
|
const bBox = this.layoutView.getBrick().getScreenBBox();
|
||||||
if (!bBox || bBox.width == 0) return;
|
if (!bBox || bBox.width == 0) return;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/// <reference path="./nodes/staticView.ts" />
|
/// <reference path="./nodes/moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ namespace pxsim.visuals {
|
|||||||
export const CONTROL_HEIGHT = 175;
|
export const CONTROL_HEIGHT = 175;
|
||||||
|
|
||||||
export abstract class ControlView<T extends BaseNode> extends SimView<T> implements LayoutElement {
|
export abstract class ControlView<T extends BaseNode> extends SimView<T> implements LayoutElement {
|
||||||
private background: SVGSVGElement;
|
protected content: SVGSVGElement;
|
||||||
|
|
||||||
abstract getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement): SVGElement;
|
abstract getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement): SVGElement;
|
||||||
|
|
||||||
@ -34,18 +34,30 @@ namespace pxsim.visuals {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildDom(width: number): SVGElement {
|
buildDom(): SVGElement {
|
||||||
this.background = svg.elt("svg", { height: "100%", width: "100%"}) as SVGSVGElement;
|
this.content = svg.elt("svg", { viewBox: `0 0 ${this.getInnerWidth()} ${this.getInnerHeight()}`}) as SVGSVGElement;
|
||||||
this.background.appendChild(this.getInnerView(this.parent, this.globalDefs));
|
this.content.appendChild(this.getInnerView(this.parent, this.globalDefs));
|
||||||
return this.background;
|
return this.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public resize(width: number, height: number) {
|
||||||
|
super.resize(width, height);
|
||||||
|
this.updateDimensions(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateDimensions(width: number, height: number) {
|
||||||
|
if (this.content) {
|
||||||
|
const currentWidth = this.getInnerWidth();
|
||||||
|
const currentHeight = this.getInnerHeight();
|
||||||
|
const newHeight = currentHeight / currentWidth * width;
|
||||||
|
const newWidth = currentWidth / currentHeight * height;
|
||||||
|
this.content.setAttribute('width', `${width}`);
|
||||||
|
this.content.setAttribute('height', `${newHeight}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onComponentVisible() {
|
onComponentVisible() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getWeight() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,6 +17,17 @@ namespace pxsim.visuals {
|
|||||||
return this.closeGroup;
|
return this.closeGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
super.resize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
public getInnerHeight() {
|
public getInnerHeight() {
|
||||||
return 32;
|
return 32;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
getInnerView() {
|
getInnerView() {
|
||||||
this.group = svg.elt("g") as SVGGElement;
|
this.group = svg.elt("g") as SVGGElement;
|
||||||
this.group.setAttribute("transform", `translate(17, ${35 + this.getHeight() / 4}) scale(5)`)
|
this.group.setAttribute("transform", `translate(17, ${20 + this.getHeight() / 4}) scale(5)`)
|
||||||
|
|
||||||
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
|
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
|
||||||
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
|
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
|
||||||
|
@ -10,7 +10,7 @@ namespace pxsim.visuals {
|
|||||||
getInnerView(parent: SVGSVGElement) {
|
getInnerView(parent: SVGSVGElement) {
|
||||||
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
|
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
|
||||||
this.group = svg.elt("g") as SVGGElement;
|
this.group = svg.elt("g") as SVGGElement;
|
||||||
this.group.setAttribute("transform", `translate(12, ${this.getHeight() / 2 - 15}) scale(2.5)`)
|
this.group.setAttribute("transform", `translate(12, ${this.getHeight() / 2 - 15}) scale(2)`)
|
||||||
|
|
||||||
let gc = "gradient-color";
|
let gc = "gradient-color";
|
||||||
this.colorGradient = svg.linearGradient(this.defs, gc, true);
|
this.colorGradient = svg.linearGradient(this.defs, gc, true);
|
||||||
|
@ -100,7 +100,8 @@ namespace pxsim.visuals {
|
|||||||
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
||||||
let cur = svg.cursorPoint(pt, parent, ev);
|
let cur = svg.cursorPoint(pt, parent, ev);
|
||||||
const height = this.getContentHeight(); //DistanceSliderControl.SLIDER_HEIGHT;
|
const height = this.getContentHeight(); //DistanceSliderControl.SLIDER_HEIGHT;
|
||||||
let t = Math.max(0, Math.min(1, (this.getTopPadding() + height + this.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
|
const bBox = this.content.getBoundingClientRect();
|
||||||
|
let t = Math.max(0, Math.min(1, (this.getTopPadding() + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
|
||||||
|
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
state.setDistance((1 - t) * (this.getMax()));
|
state.setDistance((1 - t) * (this.getMax()));
|
||||||
|
@ -83,7 +83,8 @@ namespace pxsim.visuals {
|
|||||||
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
||||||
let cur = svg.cursorPoint(pt, parent, ev);
|
let cur = svg.cursorPoint(pt, parent, ev);
|
||||||
const width = CONTROL_WIDTH; //DistanceSliderControl.SLIDER_HEIGHT;
|
const width = CONTROL_WIDTH; //DistanceSliderControl.SLIDER_HEIGHT;
|
||||||
let t = Math.max(0, Math.min(1, (width + this.left / this.scaleFactor - cur.x / this.scaleFactor) / width))
|
const bBox = this.content.getBoundingClientRect();
|
||||||
|
let t = Math.max(0, Math.min(1, (width + bBox.left / this.scaleFactor - cur.x / this.scaleFactor) / width))
|
||||||
|
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
state.setAngle((1 - t) * (100));
|
state.setAngle((1 - t) * (100));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/// <reference path="./view.ts" />
|
/// <reference path="./view.ts" />
|
||||||
/// <reference path="./nodes/staticView.ts" />
|
/// <reference path="./nodes/moduleView.ts" />
|
||||||
/// <reference path="./nodes/portView.ts" />
|
/// <reference path="./nodes/portView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
@ -9,59 +9,51 @@ namespace pxsim.visuals {
|
|||||||
export const BRICK_HEIGHT_RATIO = 1 / 3;
|
export const BRICK_HEIGHT_RATIO = 1 / 3;
|
||||||
export const MODULE_AND_WIRING_HEIGHT_RATIO = 1 / 3; // For inputs and outputs
|
export const MODULE_AND_WIRING_HEIGHT_RATIO = 1 / 3; // For inputs and outputs
|
||||||
|
|
||||||
export const MODULE_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO * 3 / 4;
|
export const MODULE_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO * 4 / 5;
|
||||||
export const WIRING_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO / 4;
|
export const WIRING_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO / 5;
|
||||||
|
|
||||||
export const MODULE_INNER_PADDING_RATIO = 1 / 35;
|
export const MODULE_INNER_PADDING_RATIO = 1 / 35;
|
||||||
|
|
||||||
|
export const MAX_MODULE_WIDTH = 100;
|
||||||
|
|
||||||
export interface LayoutElement extends View {
|
export interface LayoutElement extends View {
|
||||||
getId(): number;
|
getId(): number;
|
||||||
getPort(): number;
|
getPort(): number;
|
||||||
getPaddingRatio(): number;
|
getPaddingRatio(): number;
|
||||||
getWiringRatio(): number;
|
getWiringRatio(): number;
|
||||||
setSelected(selected: boolean): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LayoutView extends ViewContainer {
|
export class LayoutView extends ViewContainer {
|
||||||
private inputs: LayoutElement[] = [];
|
private inputs: LayoutElement[] = [];
|
||||||
private outputs: LayoutElement[] = [];
|
private outputs: LayoutElement[] = [];
|
||||||
|
|
||||||
|
private inputContainers: ViewContainer[] = [];
|
||||||
|
private outputContainers: ViewContainer[] = [];
|
||||||
|
|
||||||
|
private inputControls: View[] = [];
|
||||||
|
private outputControls: View[] = [];
|
||||||
|
|
||||||
|
private inputCloseIcons: View[] = [];
|
||||||
|
private outputCloseIcons: View[] = [];
|
||||||
|
|
||||||
private inputWires: WireView[] = [];
|
private inputWires: WireView[] = [];
|
||||||
private outputWires: WireView[] = [];
|
private outputWires: WireView[] = [];
|
||||||
|
|
||||||
private selected: number;
|
|
||||||
private selectedIsInput: boolean;
|
|
||||||
private brick: BrickView;
|
private brick: BrickView;
|
||||||
private offsets: number[];
|
private offsets: number[];
|
||||||
private contentGroup: SVGGElement;
|
private contentGroup: SVGGElement;
|
||||||
private scrollGroup: SVGGElement;
|
private scrollGroup: SVGGElement;
|
||||||
private renderedViews: Map<boolean> = {};
|
private renderedViews: Map<boolean> = {};
|
||||||
|
|
||||||
private childScaleFactor: number;
|
|
||||||
|
|
||||||
private totalLength: number;
|
|
||||||
private height: number;
|
|
||||||
private hasDimensions = false;
|
private hasDimensions = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.outputs = [
|
this.outputContainers = [new ViewContainer(), new ViewContainer, new ViewContainer(), new ViewContainer()];
|
||||||
new PortView(0, 'A'),
|
this.inputContainers = [new ViewContainer(), new ViewContainer, new ViewContainer(), new ViewContainer()];
|
||||||
new PortView(1, 'B'),
|
|
||||||
new PortView(2, 'C'),
|
|
||||||
new PortView(3, 'D')
|
|
||||||
];
|
|
||||||
|
|
||||||
this.brick = new BrickView(0);
|
this.brick = new BrickView(0);
|
||||||
|
|
||||||
this.inputs = [
|
|
||||||
new PortView(0, '1'),
|
|
||||||
new PortView(1, '2'),
|
|
||||||
new PortView(2, '3'),
|
|
||||||
new PortView(3, '4')
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
||||||
this.outputWires[port] = new WireView(port);
|
this.outputWires[port] = new WireView(port);
|
||||||
}
|
}
|
||||||
@ -72,8 +64,7 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
public layout(width: number, height: number) {
|
public layout(width: number, height: number) {
|
||||||
this.hasDimensions = true;
|
this.hasDimensions = true;
|
||||||
this.width = width;
|
this.resize(width, height);
|
||||||
this.height = height;
|
|
||||||
this.scrollGroup.setAttribute("width", width.toString());
|
this.scrollGroup.setAttribute("width", width.toString());
|
||||||
this.scrollGroup.setAttribute("height", height.toString());
|
this.scrollGroup.setAttribute("height", height.toString());
|
||||||
this.position();
|
this.position();
|
||||||
@ -81,6 +72,7 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
public setBrick(brick: BrickView) {
|
public setBrick(brick: BrickView) {
|
||||||
this.brick = brick;
|
this.brick = brick;
|
||||||
|
this.brick.inject(this.scrollGroup);
|
||||||
this.position();
|
this.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,55 +80,112 @@ namespace pxsim.visuals {
|
|||||||
return this.brick;
|
return this.brick;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setInput(port: number, child: LayoutElement) {
|
public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
|
||||||
if (this.inputs[port]) {
|
if (this.inputs[port] != view || this.inputControls[port] != control) {
|
||||||
// Remove current input
|
if (this.inputs[port]) {
|
||||||
this.inputs[port].dispose();
|
// Remove current input
|
||||||
|
this.inputs[port].dispose();
|
||||||
|
}
|
||||||
|
this.inputs[port] = view;
|
||||||
|
if (this.inputControls[port]) {
|
||||||
|
this.inputControls[port].dispose();
|
||||||
|
}
|
||||||
|
this.inputControls[port] = control;
|
||||||
|
this.inputCloseIcons[port] = closeIcon;
|
||||||
|
|
||||||
|
this.inputContainers[port].clear();
|
||||||
|
this.inputContainers[port].addView(view);
|
||||||
|
|
||||||
|
if (control) this.inputContainers[port].addView(control);
|
||||||
|
|
||||||
|
if (view.hasClick()) view.registerClick((ev: any) => {
|
||||||
|
view.setSelected(true);
|
||||||
|
runtime.queueDisplayUpdate();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
if (control && closeIcon) {
|
||||||
|
this.inputContainers[port].addView(closeIcon);
|
||||||
|
closeIcon.registerClick(() => {
|
||||||
|
// Clear selection
|
||||||
|
view.setSelected(false);
|
||||||
|
runtime.queueDisplayUpdate();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.inputs[port] = child;
|
|
||||||
this.position();
|
this.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setOutput(port: number, child: LayoutElement) {
|
public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
|
||||||
if (this.outputs[port]) {
|
if (this.outputs[port] != view || this.outputControls[port] != control) {
|
||||||
// Remove current input
|
if (this.outputs[port]) {
|
||||||
this.outputs[port].dispose();
|
// Remove current output
|
||||||
|
this.outputs[port].dispose();
|
||||||
|
}
|
||||||
|
this.outputs[port] = view;
|
||||||
|
if (this.outputControls[port]) {
|
||||||
|
this.outputControls[port].dispose();
|
||||||
|
}
|
||||||
|
this.outputControls[port] = control;
|
||||||
|
this.outputCloseIcons[port] = closeIcon;
|
||||||
|
|
||||||
|
this.outputContainers[port].clear();
|
||||||
|
this.outputContainers[port].addView(view);
|
||||||
|
|
||||||
|
if (control) this.outputContainers[port].addView(control);
|
||||||
|
|
||||||
|
if (view.hasClick()) view.registerClick((ev: any) => {
|
||||||
|
view.setSelected(true);
|
||||||
|
runtime.queueDisplayUpdate();
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
if (control && closeIcon) {
|
||||||
|
this.outputContainers[port].addView(closeIcon);
|
||||||
|
closeIcon.registerClick(() => {
|
||||||
|
// Clear selection
|
||||||
|
view.setSelected(false);
|
||||||
|
runtime.queueDisplayUpdate();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.outputs[port] = child;
|
|
||||||
this.position();
|
this.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onClick(index: number, input: boolean, ev: any) {
|
protected buildDom() {
|
||||||
this.setSelected(index, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
public clearSelected() {
|
|
||||||
this.selected = undefined;
|
|
||||||
this.selectedIsInput = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setSelected(index: number, input?: boolean) {
|
|
||||||
if (index !== this.selected || input !== this.selectedIsInput) {
|
|
||||||
this.selected = index;
|
|
||||||
this.selectedIsInput = input;
|
|
||||||
const node = this.getSelected();
|
|
||||||
if (node) node.setSelected(true);
|
|
||||||
|
|
||||||
//this.redoPositioning();
|
|
||||||
runtime.queueDisplayUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSelected() {
|
|
||||||
if (this.selected !== undefined) {
|
|
||||||
return this.selectedIsInput ? this.inputs[this.selected] : this.outputs[this.selected];
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildDom(width: number) {
|
|
||||||
this.contentGroup = svg.elt("g") as SVGGElement;
|
this.contentGroup = svg.elt("g") as SVGGElement;
|
||||||
this.scrollGroup = svg.child(this.contentGroup, "g") as SVGGElement;
|
this.scrollGroup = svg.child(this.contentGroup, "g") as SVGGElement;
|
||||||
|
|
||||||
|
// Inject all view containers
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
this.inputContainers[i].inject(this.scrollGroup);
|
||||||
|
this.outputContainers[i].inject(this.scrollGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inputs = [];
|
||||||
|
this.outputs = [];
|
||||||
|
this.inputControls = [];
|
||||||
|
this.outputControls = [];
|
||||||
|
|
||||||
|
// Inject all wires
|
||||||
|
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
||||||
|
this.outputWires[port].inject(this.scrollGroup);
|
||||||
|
}
|
||||||
|
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
||||||
|
this.inputWires[port].inject(this.scrollGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject all ports
|
||||||
|
this.setInput(0, new PortView(0, 'A'));
|
||||||
|
this.setInput(1, new PortView(1, 'B'));
|
||||||
|
this.setInput(2, new PortView(2, 'C'));
|
||||||
|
this.setInput(3, new PortView(3, 'D'));
|
||||||
|
|
||||||
|
this.setOutput(0, new PortView(0, '1'));
|
||||||
|
this.setOutput(1, new PortView(1, '2'));
|
||||||
|
this.setOutput(2, new PortView(2, '3'));
|
||||||
|
this.setOutput(3, new PortView(3, '4'));
|
||||||
|
|
||||||
return this.contentGroup;
|
return this.contentGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,34 +220,47 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
this.offsets = [];
|
this.offsets = [];
|
||||||
|
|
||||||
const selectedNode = this.getSelected();
|
|
||||||
|
|
||||||
const contentWidth = this.width || DEFAULT_WIDTH;
|
const contentWidth = this.width || DEFAULT_WIDTH;
|
||||||
const contentHeight = this.height || DEFAULT_HEIGHT;
|
const contentHeight = this.height || DEFAULT_HEIGHT;
|
||||||
|
|
||||||
const moduleHeight = this.getModuleHeight();
|
const moduleHeight = this.getModuleHeight();
|
||||||
|
|
||||||
const brickHeight = this.getBrickHeight();
|
const brickHeight = this.getBrickHeight();
|
||||||
this.brick.inject(this.scrollGroup);
|
|
||||||
const brickWidth = this.brick.getInnerWidth() / this.brick.getInnerHeight() * brickHeight;
|
const brickWidth = this.brick.getInnerWidth() / this.brick.getInnerHeight() * brickHeight;
|
||||||
const brickPadding = (contentWidth - brickWidth) / 2;
|
const brickPadding = (contentWidth - brickWidth) / 2;
|
||||||
|
|
||||||
const modulePadding = contentWidth / 35;
|
const modulePadding = this.getModulePadding();
|
||||||
const moduleSpacing = contentWidth / 4;
|
const moduleSpacing = contentWidth / 4;
|
||||||
const moduleWidth = moduleSpacing - (modulePadding * 2);
|
const moduleWidth = this.getInnerModuleWidth();
|
||||||
let currentX = modulePadding;
|
let currentX = this.getModulePadding();
|
||||||
let currentY = 0;
|
let currentY = 0;
|
||||||
this.outputs.forEach((n, i) => {
|
this.outputs.forEach((n, i) => {
|
||||||
const outputPadding = moduleWidth * n.getPaddingRatio();
|
this.outputContainers[i].translate(currentX, currentY);
|
||||||
const outputWidth = moduleWidth - outputPadding * 2;
|
if (this.outputs[i]) {
|
||||||
n.inject(this.scrollGroup, outputWidth);
|
const view = this.outputs[i];
|
||||||
n.resize(outputWidth);
|
const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
|
||||||
const nHeight = n.getHeight() / n.getWidth() * outputWidth;
|
const desiredOutputWidth = this.getInnerModuleWidth() - outputPadding * 2;
|
||||||
n.translate(currentX + outputPadding, currentY + moduleHeight - nHeight);
|
const outputWidth = Math.min(desiredOutputWidth, MAX_MODULE_WIDTH);
|
||||||
n.setSelected(n == selectedNode);
|
const outputHeight = this.getModuleHeight();
|
||||||
if (n.hasClick()) n.registerClick((ev: any) => {
|
|
||||||
this.onClick(i, false, ev);
|
// Translate and resize view
|
||||||
})
|
view.resize(outputWidth, outputHeight);
|
||||||
|
const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
|
||||||
|
view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
|
||||||
|
|
||||||
|
// Resize control
|
||||||
|
const control = this.outputControls[i];
|
||||||
|
if (control) {
|
||||||
|
control.resize(this.getInnerModuleWidth(), outputHeight);
|
||||||
|
|
||||||
|
// Translate close icon
|
||||||
|
const closeIcon = this.outputCloseIcons[i];
|
||||||
|
if (closeIcon) {
|
||||||
|
const closeIconWidth = closeIcon.getWidth();
|
||||||
|
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
currentX += moduleSpacing;
|
currentX += moduleSpacing;
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -206,7 +268,7 @@ namespace pxsim.visuals {
|
|||||||
currentY = moduleHeight;
|
currentY = moduleHeight;
|
||||||
|
|
||||||
const wireBrickSpacing = brickWidth / 5;
|
const wireBrickSpacing = brickWidth / 5;
|
||||||
const wiringYPadding = 10;
|
const wiringYPadding = 0;
|
||||||
let wireStartX = 0;
|
let wireStartX = 0;
|
||||||
let wireEndX = brickPadding + wireBrickSpacing;
|
let wireEndX = brickPadding + wireBrickSpacing;
|
||||||
let wireEndY = currentY + this.getWiringHeight() + wiringYPadding;
|
let wireEndY = currentY + this.getWiringHeight() + wiringYPadding;
|
||||||
@ -214,7 +276,6 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
// Draw output lines
|
// Draw output lines
|
||||||
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
||||||
if (!this.outputWires[port].isRendered()) this.outputWires[port].inject(this.scrollGroup);
|
|
||||||
this.outputWires[port].updateDimensions(wireStartX + moduleSpacing * this.outputs[port].getWiringRatio(), wireStartY, wireEndX, wireEndY);
|
this.outputWires[port].updateDimensions(wireStartX + moduleSpacing * this.outputs[port].getWiringRatio(), wireStartY, wireEndX, wireEndY);
|
||||||
this.outputWires[port].setSelected(this.outputs[port].getId() == NodeType.Port);
|
this.outputWires[port].setSelected(this.outputs[port].getId() == NodeType.Port);
|
||||||
wireStartX += moduleSpacing;
|
wireStartX += moduleSpacing;
|
||||||
@ -225,22 +286,39 @@ namespace pxsim.visuals {
|
|||||||
currentY += this.getWiringHeight();
|
currentY += this.getWiringHeight();
|
||||||
|
|
||||||
// Render the brick in the middle
|
// Render the brick in the middle
|
||||||
this.brick.resize(brickWidth);
|
this.brick.resize(brickWidth, brickHeight);
|
||||||
this.brick.translate(currentX, currentY);
|
this.brick.translate(currentX, currentY);
|
||||||
|
|
||||||
currentX = modulePadding;
|
currentX = modulePadding;
|
||||||
currentY += brickHeight + this.getWiringHeight();
|
currentY += brickHeight + this.getWiringHeight();
|
||||||
|
|
||||||
this.inputs.forEach((n, i) => {
|
this.inputs.forEach((n, i) => {
|
||||||
const inputPadding = moduleWidth * n.getPaddingRatio();
|
this.inputContainers[i].translate(currentX, currentY);
|
||||||
const inputWidth = moduleWidth - inputPadding * 2;
|
if (this.inputs[i]) {
|
||||||
n.inject(this.scrollGroup, inputWidth);
|
const view = this.inputs[i];
|
||||||
n.resize(inputWidth);
|
const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
|
||||||
n.translate(currentX + inputPadding, currentY);
|
const desiredInputWidth = this.getInnerModuleWidth() - inputPadding * 2;
|
||||||
n.setSelected(n == selectedNode);
|
const inputWidth = Math.min(desiredInputWidth, MAX_MODULE_WIDTH);
|
||||||
if (n.hasClick()) n.registerClick((ev: any) => {
|
const inputHeight = this.getModuleHeight();
|
||||||
this.onClick(i, true, ev);
|
|
||||||
})
|
// Translate and resize view
|
||||||
|
view.resize(inputWidth, inputHeight);
|
||||||
|
view.translate(inputPadding + ((desiredInputWidth - inputWidth) / 2), 0, true);
|
||||||
|
|
||||||
|
// Resize control
|
||||||
|
const control = this.inputControls[i];
|
||||||
|
if (control) {
|
||||||
|
control.resize(this.getInnerModuleWidth(), inputHeight);
|
||||||
|
|
||||||
|
// Translate and resize close icon
|
||||||
|
const closeIcon = this.inputCloseIcons[i];
|
||||||
|
if (closeIcon) {
|
||||||
|
const closeIconWidth = closeIcon.getWidth();
|
||||||
|
const closeIconHeight = closeIcon.getHeight();
|
||||||
|
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, this.getModuleHeight() - closeIconHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
currentX += moduleSpacing;
|
currentX += moduleSpacing;
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -251,7 +329,6 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
// Draw input lines
|
// Draw input lines
|
||||||
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
||||||
if (!this.inputWires[port].isRendered()) this.inputWires[port].inject(this.scrollGroup);
|
|
||||||
this.inputWires[port].updateDimensions(wireStartX, wireStartY, wireEndX, wireEndY);
|
this.inputWires[port].updateDimensions(wireStartX, wireStartY, wireEndX, wireEndY);
|
||||||
this.inputWires[port].setSelected(this.inputs[port].getId() == NodeType.Port);
|
this.inputWires[port].setSelected(this.inputs[port].getId() == NodeType.Port);
|
||||||
wireStartX += moduleSpacing;
|
wireStartX += moduleSpacing;
|
||||||
@ -259,27 +336,6 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSelectedCoords() {
|
|
||||||
const selected = this.getSelected();
|
|
||||||
if (!selected) return undefined;
|
|
||||||
const port = this.getSelected().getPort();
|
|
||||||
return {
|
|
||||||
x: this.getSelected().getPort() * this.width / 4 + this.width * MODULE_INNER_PADDING_RATIO,
|
|
||||||
y: this.selectedIsInput ? this.getModuleHeight() + 2 * this.getWiringHeight() + this.getBrickHeight() : this.getModuleHeight() / 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCloseIconCoords(closeIconWidth: number, closeIconHeight: number) {
|
|
||||||
return {
|
|
||||||
x: this.getSelected().getPort() * this.width / 4 + this.getModuleBounds().width / 2 - closeIconWidth / 2,
|
|
||||||
y: this.selectedIsInput ? this.getModuleHeight() + 2 * this.getWiringHeight() + this.getBrickHeight() + this.getModuleHeight() - closeIconHeight : 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getModuleHeight() {
|
|
||||||
return (this.height || DEFAULT_HEIGHT) * MODULE_HEIGHT_RATIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getBrickHeight() {
|
public getBrickHeight() {
|
||||||
return (this.height || DEFAULT_HEIGHT) * BRICK_HEIGHT_RATIO;
|
return (this.height || DEFAULT_HEIGHT) * BRICK_HEIGHT_RATIO;
|
||||||
}
|
}
|
||||||
@ -290,9 +346,21 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
public getModuleBounds() {
|
public getModuleBounds() {
|
||||||
return {
|
return {
|
||||||
width: this.width / 4,
|
width: (this.width || DEFAULT_WIDTH) / 4,
|
||||||
height: this.getModuleHeight()
|
height: this.getModuleHeight()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getModulePadding() {
|
||||||
|
return this.getModuleBounds().width / 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInnerModuleWidth() {
|
||||||
|
return this.getModuleBounds().width - (this.getModulePadding() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getModuleHeight() {
|
||||||
|
return (this.height || DEFAULT_HEIGHT) * MODULE_HEIGHT_RATIO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
|
||||||
export class BrickView extends StaticModuleView implements LayoutElement {
|
export class BrickView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
private static EV3_SCREEN_ID = "ev3_screen";
|
private static EV3_SCREEN_ID = "ev3_screen";
|
||||||
private static EV3_LIGHT_ID = "btn_color";
|
private static EV3_LIGHT_ID = "btn_color";
|
||||||
@ -41,9 +41,9 @@ namespace pxsim.visuals {
|
|||||||
|
|
||||||
public updateThemeCore() {
|
public updateThemeCore() {
|
||||||
let theme = this.theme;
|
let theme = this.theme;
|
||||||
svg.fill(this.buttons[0], theme.buttonUps[0]);
|
// svg.fill(this.buttons[0], theme.buttonUps[0]);
|
||||||
svg.fill(this.buttons[1], theme.buttonUps[1]);
|
// svg.fill(this.buttons[1], theme.buttonUps[1]);
|
||||||
svg.fill(this.buttons[2], theme.buttonUps[2]);
|
// svg.fill(this.buttons[2], theme.buttonUps[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lastLightPattern: number = -1;
|
private lastLightPattern: number = -1;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
export class ColorSensorView extends StaticModuleView implements LayoutElement {
|
export class ColorSensorView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
private control: ColorGridControl;
|
private control: ColorGridControl;
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getPaddingRatio() {
|
public getPaddingRatio() {
|
||||||
return 1 / 8;
|
return 1 / 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateState() {
|
public updateState() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
export class GyroSensorView extends StaticModuleView implements LayoutElement {
|
export class GyroSensorView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
constructor(port: number) {
|
constructor(port: number) {
|
||||||
super(GYRO_SVG, "gyro", NodeType.GyroSensor, port);
|
super(GYRO_SVG, "gyro", NodeType.GyroSensor, port);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
export class LargeMotorView extends StaticModuleView implements LayoutElement {
|
export class LargeMotorView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
private static ROTATING_ECLIPSE_ID = "1eb2ae58-2419-47d4-86bf-4f26a7f0cf61";
|
private static ROTATING_ECLIPSE_ID = "1eb2ae58-2419-47d4-86bf-4f26a7f0cf61";
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
|
||||||
export const MOTOR_ROTATION_FPS = 32;
|
export const MOTOR_ROTATION_FPS = 32;
|
||||||
|
|
||||||
export class MediumMotorView extends StaticModuleView implements LayoutElement {
|
export class MediumMotorView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
private static ROTATING_ECLIPSE_ID = "Hole";
|
private static ROTATING_ECLIPSE_ID = "Hole";
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getPaddingRatio() {
|
public getPaddingRatio() {
|
||||||
return 1 / 10;
|
return 1 / 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState() {
|
updateState() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
|
||||||
export class StaticModuleView extends View implements LayoutElement {
|
export class ModuleView extends View implements LayoutElement {
|
||||||
protected content: SVGSVGElement;
|
protected content: SVGSVGElement;
|
||||||
|
|
||||||
protected controlShown: boolean;
|
protected controlShown: boolean;
|
||||||
@ -45,9 +45,8 @@ namespace pxsim.visuals {
|
|||||||
return 0.5;
|
return 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected buildDom(width: number): SVGElement {
|
protected buildDom(): SVGElement {
|
||||||
this.content = svg.parseString(this.xml);
|
this.content = svg.parseString(this.xml);
|
||||||
this.updateDimensions(width);
|
|
||||||
this.buildDomCore();
|
this.buildDomCore();
|
||||||
this.attachEvents();
|
this.attachEvents();
|
||||||
if (this.hasClick())
|
if (this.hasClick())
|
||||||
@ -82,15 +81,17 @@ namespace pxsim.visuals {
|
|||||||
public attachEvents() {
|
public attachEvents() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(width: number) {
|
public resize(width: number, height: number) {
|
||||||
this.updateDimensions(width);
|
super.resize(width, height);
|
||||||
|
this.updateDimensions(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateDimensions(width: number) {
|
private updateDimensions(width: number, height: number) {
|
||||||
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;
|
||||||
this.content.setAttribute('width', `${width}`);
|
this.content.setAttribute('width', `${width}`);
|
||||||
this.content.setAttribute('height', `${newHeight}`);
|
this.content.setAttribute('height', `${newHeight}`);
|
||||||
}
|
}
|
||||||
@ -101,13 +102,13 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setSelected(selected: boolean) {
|
public setSelected(selected: boolean) {
|
||||||
this.selected = selected;
|
super.setSelected(selected);
|
||||||
this.updateOpacity();
|
this.updateOpacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateOpacity() {
|
protected updateOpacity() {
|
||||||
if (this.rendered) {
|
if (this.rendered) {
|
||||||
const opacity = this.selected ? "0.5" : "1";
|
const opacity = this.selected ? "0.2" : "1";
|
||||||
if (this.hasClick()) {
|
if (this.hasClick()) {
|
||||||
this.setOpacity(opacity);
|
this.setOpacity(opacity);
|
||||||
if (this.selected) this.content.style.cursor = "";
|
if (this.selected) this.content.style.cursor = "";
|
@ -1,8 +1,8 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
|
||||||
export class PortView extends StaticModuleView implements LayoutElement {
|
export class PortView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
constructor(port: NodeType, private label: string) {
|
constructor(port: NodeType, private label: string) {
|
||||||
super(PORT_SVG, "port", NodeType.Port, port);
|
super(PORT_SVG, "port", NodeType.Port, port);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
export class TouchSensorView extends StaticModuleView implements LayoutElement {
|
export class TouchSensorView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
private static RECT_ID = ["touch_gradient4", "touch_gradient3", "touch_gradient2", "touch_gradient1"];
|
private static RECT_ID = ["touch_gradient4", "touch_gradient3", "touch_gradient2", "touch_gradient1"];
|
||||||
private static TOUCH_GRADIENT_UNPRESSED = ["linear-gradient-2", "linear-gradient-3", "linear-gradient-4", "linear-gradient-5"];
|
private static TOUCH_GRADIENT_UNPRESSED = ["linear-gradient-2", "linear-gradient-3", "linear-gradient-4", "linear-gradient-5"];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/// <reference path="./staticView.ts" />
|
/// <reference path="./moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
export class UltrasonicSensorView extends StaticModuleView implements LayoutElement {
|
export class UltrasonicSensorView extends ModuleView implements LayoutElement {
|
||||||
|
|
||||||
constructor(port: number) {
|
constructor(port: number) {
|
||||||
super(ULTRASONIC_SVG, "ultrasonic", NodeType.UltrasonicSensor, port);
|
super(ULTRASONIC_SVG, "ultrasonic", NodeType.UltrasonicSensor, port);
|
||||||
|
@ -3,14 +3,19 @@ namespace pxsim.visuals {
|
|||||||
protected element: SVGGElement;
|
protected element: SVGGElement;
|
||||||
protected rendered = false;
|
protected rendered = false;
|
||||||
protected visible = false;
|
protected visible = false;
|
||||||
|
protected selected: boolean;
|
||||||
protected width: number = 0;
|
protected width: number = 0;
|
||||||
|
protected height: number = 0;
|
||||||
protected left: number = 0;
|
protected left: number = 0;
|
||||||
protected top: number = 0;
|
protected top: number = 0;
|
||||||
protected scaleFactor: number = 1;
|
protected scaleFactor: number = 1;
|
||||||
|
|
||||||
|
private changed: boolean;
|
||||||
|
private hover: boolean = false;
|
||||||
|
|
||||||
protected theme: IBoardTheme;
|
protected theme: IBoardTheme;
|
||||||
|
|
||||||
protected abstract buildDom(width: number): SVGElement;
|
protected abstract buildDom(): SVGElement;
|
||||||
public abstract getInnerWidth(): number;
|
public abstract getInnerWidth(): number;
|
||||||
public abstract getInnerHeight(): number;
|
public abstract getInnerHeight(): number;
|
||||||
|
|
||||||
@ -83,9 +88,26 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onClickHandler: (ev: any) => void;
|
private onClickHandler: (ev: any) => void;
|
||||||
public registerClick(handler: (ev: any) => void) {
|
public registerClick(handler: (ev: any) => void, zoom?: boolean) {
|
||||||
this.onClickHandler = handler;
|
this.onClickHandler = handler;
|
||||||
this.getView().addEventListener(pointerEvents.up, this.onClickHandler);
|
if (zoom) {
|
||||||
|
this.getView().addEventListener(pointerEvents.up, (ev: any) => {
|
||||||
|
if (!this.getSelected()) {
|
||||||
|
this.onClickHandler(ev);
|
||||||
|
this.setHover(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.getView().addEventListener(pointerEvents.move, () => {
|
||||||
|
if (!this.getSelected()) {
|
||||||
|
this.setHover(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.getView().addEventListener(pointerEvents.leave, () => {
|
||||||
|
this.setHover(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.getView().addEventListener(pointerEvents.up, this.onClickHandler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
@ -98,7 +120,7 @@ namespace pxsim.visuals {
|
|||||||
this.element = svg.elt("g") as SVGGElement;
|
this.element = svg.elt("g") as SVGGElement;
|
||||||
View.track(this);
|
View.track(this);
|
||||||
|
|
||||||
const content = this.buildDom(this.width);
|
const content = this.buildDom();
|
||||||
if (content) {
|
if (content) {
|
||||||
this.element.appendChild(content);
|
this.element.appendChild(content);
|
||||||
}
|
}
|
||||||
@ -108,16 +130,30 @@ namespace pxsim.visuals {
|
|||||||
return this.element;
|
return this.element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resize(width: number) {
|
public resize(width: number, height: number) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTransform() {
|
private updateTransform() {
|
||||||
if (this.rendered) {
|
if (this.rendered) {
|
||||||
let transform = `translate(${this.left} ${this.top})`;
|
let left = this.left;
|
||||||
|
let top = this.top;
|
||||||
|
let scaleFactor = this.scaleFactor;
|
||||||
|
if (this.hover) {
|
||||||
|
const hoverScaleFactor = scaleFactor + 0.05;
|
||||||
|
// Scale around center of module
|
||||||
|
const centerX = this.getWidth() / 2;
|
||||||
|
const centerY = this.getHeight() / 2;
|
||||||
|
left = left - centerX * (hoverScaleFactor - 1);
|
||||||
|
top = top - centerY * (hoverScaleFactor - 1);
|
||||||
|
scaleFactor = hoverScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.scaleFactor !== 1) {
|
let transform = `translate(${left} ${top})`;
|
||||||
transform += ` scale(${this.scaleFactor})`;
|
|
||||||
|
if (scaleFactor !== 1) {
|
||||||
|
transform += ` scale(${scaleFactor})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element.setAttribute("transform", transform);
|
this.element.setAttribute("transform", transform);
|
||||||
@ -149,6 +185,41 @@ namespace pxsim.visuals {
|
|||||||
delete View.allViews[id];
|
delete View.allViews[id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////// HOVERED STATE /////////////
|
||||||
|
|
||||||
|
public getHover() {
|
||||||
|
return this.hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHover(hover: boolean) {
|
||||||
|
if (this.hover != hover) {
|
||||||
|
this.hover = hover;
|
||||||
|
this.updateTransform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// SELECTED STATE /////////////
|
||||||
|
|
||||||
|
public getSelected() {
|
||||||
|
return this.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSelected(selected: boolean) {
|
||||||
|
this.selected = selected;
|
||||||
|
this.setChangedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setChangedState() {
|
||||||
|
this.changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public didChange() {
|
||||||
|
const res = this.changed;
|
||||||
|
this.changed = false;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
|
export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
|
||||||
@ -222,7 +293,7 @@ namespace pxsim.visuals {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected buildDom(width: number): SVGElement {
|
protected buildDom(): SVGElement {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
/// <reference path="./nodes/staticView.ts" />
|
/// <reference path="./nodes/moduleView.ts" />
|
||||||
|
|
||||||
namespace pxsim.visuals {
|
namespace pxsim.visuals {
|
||||||
|
|
||||||
export class WireView extends View implements LayoutElement {
|
export class WireView extends View implements LayoutElement {
|
||||||
private wire: SVGSVGElement;
|
private wire: SVGSVGElement;
|
||||||
private path: SVGPathElement;
|
private path: SVGPathElement;
|
||||||
private selected: boolean;
|
|
||||||
private hasDimensions: boolean;
|
private hasDimensions: boolean;
|
||||||
|
|
||||||
protected startX: number;
|
protected startX: number;
|
||||||
@ -30,13 +29,13 @@ namespace pxsim.visuals {
|
|||||||
this.updatePath();
|
this.updatePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildDom(width: number): SVGElement {
|
buildDom(): SVGElement {
|
||||||
this.wire = svg.elt("svg", { height: "100%", width: "100%" }) as SVGSVGElement;
|
this.wire = svg.elt("svg", { height: "100%", width: "100%" }) as SVGSVGElement;
|
||||||
this.path = pxsim.svg.child(this.wire, "path", {
|
this.path = pxsim.svg.child(this.wire, "path", {
|
||||||
'd': '',
|
'd': '',
|
||||||
'fill': 'transparent',
|
'fill': 'transparent',
|
||||||
'stroke': '#5A5A5A',
|
'stroke': '#5A5A5A',
|
||||||
'stroke-width': '3px'
|
'stroke-width': '2px'
|
||||||
}) as SVGPathElement;
|
}) as SVGPathElement;
|
||||||
this.setSelected(true);
|
this.setSelected(true);
|
||||||
return this.wire;
|
return this.wire;
|
||||||
@ -45,8 +44,8 @@ namespace pxsim.visuals {
|
|||||||
updatePath() {
|
updatePath() {
|
||||||
if (!this.hasDimensions) return;
|
if (!this.hasDimensions) return;
|
||||||
const height = this.endY - this.startY;
|
const height = this.endY - this.startY;
|
||||||
const quarterHeight = height / 4;
|
const thirdHeight = height / 3;
|
||||||
const middleHeight = this.port == 1 || this.port == 2 ? quarterHeight : quarterHeight * 2;
|
const middleHeight = this.port == 1 || this.port == 2 ? thirdHeight : thirdHeight * 2;
|
||||||
let d = `M${this.startX} ${this.startY}`;
|
let d = `M${this.startX} ${this.startY}`;
|
||||||
d += ` L${this.startX} ${this.startY + middleHeight}`;
|
d += ` L${this.startX} ${this.startY + middleHeight}`;
|
||||||
d += ` L${this.endX} ${this.startY + middleHeight}`;
|
d += ` L${this.endX} ${this.startY + middleHeight}`;
|
||||||
@ -79,7 +78,7 @@ namespace pxsim.visuals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setSelected(selected: boolean) {
|
public setSelected(selected: boolean) {
|
||||||
this.selected = selected;
|
super.setSelected(selected);
|
||||||
this.updateOpacity();
|
this.updateOpacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user