Compare commits

...

12 Commits

Author SHA1 Message Date
4c978d793a 0.1.35 2018-05-02 14:00:27 -07:00
06ca53ae89 Remove unused dependencies 2018-05-02 14:00:08 -07:00
84990d66a9 Update simulator as per latest lego UI design for sensors. Fix full screen layout issues. (#554) 2018-05-02 13:58:59 -07:00
4825172423 Update numbers music icon (from lego) (#553) 2018-05-02 13:55:44 -07:00
b69af383a6 Better mouse event handling (#552)
* Use learnings from touch events in Blockly and apply to the Ev3 sim. Register move and up events on the document rather than the individual element allowing users greater flexibility where they move their mouse once it's down.

* Add mouse leave events
2018-05-02 13:54:06 -07:00
6e1a613798 0.1.34 2018-05-02 12:58:27 -07:00
b5005f3b10 bump pxt-core to 3.12.12, 2018-05-02 12:57:46 -07:00
044b585f24 Release Electron app v0.1.30 2018-05-02 10:13:07 -04:00
94fe26e390 0.1.33 2018-05-01 22:05:57 -07:00
ca832f52db upgrading pxt 2018-05-01 22:05:46 -07:00
6f2fe212df Tutorial updates (#551)
* updated line following, added simulator step

* full screen

* resizing some images
2018-05-01 21:53:28 -07:00
96be357af1 updated package-lock 2018-05-01 16:16:19 -07:00
28 changed files with 1279 additions and 998 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -104,8 +104,17 @@ forever(function () {
})
```
## Step 8
## Step 8 @fullscreen
Now, plug your EV3 Brick into the computer with the USB cable, and click the **Download** button at the bottom of your screen. Follow the directions to save your program to the brick.
Use the EV3 simulator to try out your code.
![Brick with color sensors tracking a yellow line](/static/tutorials/line-following/line-following.gif)
Move the slider under the Color Sensor to change the reflected light and check that motors
are moving as you would expect!
## Step 9
Plug your EV3 Brick into the computer with the USB cable, and click the **Download** button at the bottom of your screen. Follow the directions to save your program to the brick.
Attach a Color Sensor to Port 3 of your brick, and attach your brick to a driving base with large motors attached to Ports B and C. See the instructions for building a _Driving Base with Color Sensor Down_. Test your program by positioning your robot to the right of a dark, thick line and then let it drive!

View File

@ -46,6 +46,12 @@ brick.showString("Press my button!", 1)
## Step 5
Try out your code in the EV3 simulator!
Press the ``Enter`` button and check that the image shows up as you expected.
## Step 6
Open the ``||brick:Brick||`` Toolbox drawer. Drag out a ``||brick:set status light||`` block onto the Workspace, and drop it into the ``||brick:on button||`` block after the ``||brick:show image||`` block.
```blocks
@ -56,6 +62,6 @@ brick.buttonEnter.onEvent(ButtonEvent.Pressed, function () {
brick.showString("Press my button!", 1)
```
## Step 6
## Step 7
Now, plug your EV3 Brick into the computer with the USB cable, and click the **Download** button at the bottom of your screen. Follow the directions to save your program to the brick.
Plug your EV3 Brick into the computer with the USB cable, and click the **Download** button at the bottom of your screen. Follow the directions to save your program to the brick.

View File

@ -45,6 +45,14 @@
id="path140"
inkscape:connector-curvature="0" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath138-5">
<path
d="m 0,2662.542 4880.569,0 L 4880.569,0 0,0 0,2662.542 Z"
id="path140-3"
inkscape:connector-curvature="0" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
@ -53,8 +61,8 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="33.67086"
inkscape:zoom="0.7"
inkscape:cx="-89.900569"
inkscape:cy="152.39534"
inkscape:document-units="px"
inkscape:current-layer="g4178"
@ -102,35 +110,49 @@
<g
id="g4178"
transform="translate(0.33476913,-24.377094)">
<path
id="path4172"
style="font-style:normal;font-weight:normal;font-size:93.26059723px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -129.49327,130.11985 32.103871,0 0,7.74136 -43.169461,0 0,-7.74136 q 5.23681,-5.41895 14.25321,-14.52643 9.06194,-9.15302 11.38435,-11.79419 4.41713,-4.963577 6.14755,-8.378882 1.77596,-3.460842 1.77596,-6.785072 0,-5.418951 -3.82514,-8.834256 -3.77961,-3.415305 -9.88162,-3.415305 -4.32605,0 -9.15302,1.502734 -4.78142,1.502734 -10.24591,4.55374 l 0,-9.28963 q 5.55556,-2.231332 10.38253,-3.369767 4.82696,-1.138435 8.83425,-1.138435 10.56468,0 16.84884,5.282338 6.284162,5.282339 6.284162,14.116594 0,4.189441 -1.593809,7.969046 -1.548273,3.734066 -5.692173,8.834255 -1.13844,1.32058 -7.24045,7.65028 -6.10201,6.28417 -17.21314,17.62298 z"
transform="matrix(0.9566764,0,0,-0.9566764,3511.7806,1622.5188)"
inkscape:connector-curvature="0" />
<path
id="path4174"
style="font-style:normal;font-weight:normal;font-size:93.26059723px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -50.167113,101.2036 q 6.602923,1.41166 10.291452,5.87432 3.734067,4.46267 3.734067,11.02005 0,10.06377 -6.921685,15.5738 -6.921685,5.51002 -19.672157,5.51002 -4.280516,0 -8.834256,-0.86521 -4.508203,-0.81967 -9.335167,-2.50456 l 0,-8.87979 q 3.825142,2.23133 8.378882,3.36977 4.55374,1.13843 9.517316,1.13843 8.652107,0 13.160309,-3.4153 4.55374,-3.41531 4.55374,-9.92716 0,-6.01093 -4.234978,-9.3807 -4.189441,-3.41531 -11.703112,-3.41531 l -7.923508,0 0,-7.559204 8.287807,0 q 6.785073,0 10.382528,-2.686707 3.597454,-2.732244 3.597454,-7.832433 0,-5.236801 -3.734066,-8.014582 -3.68853,-2.823319 -10.610215,-2.823319 -3.779604,0 -8.105657,0.819673 -4.326053,0.819673 -9.517317,2.550094 l 0,-8.196732 q 5.236801,-1.457197 9.790541,-2.185795 4.599278,-0.728598 8.652106,-0.728598 10.473603,0 16.575614,4.781427 6.102012,4.735889 6.102012,12.841547 0,5.646637 -3.233155,9.562854 -3.233156,3.870679 -9.198555,5.373415 z"
transform="matrix(0.9566764,0,0,-0.9566764,3511.7806,1622.5188)"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.7891314,0,0,0.7891314,3352.046,1573.2574)"
id="g146">
<path
inkscape:connector-curvature="0"
id="path148"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 -18.637,-14.649 c -0.84,-6.455 -3.384,-12.372 -7.173,-17.286 l 2.958,-3.734 c 9.327,7.568 14.807,12.06 16.444,13.477 1.635,1.416 3.013,2.71 4.137,3.881 -0.391,-4.834 -0.585,-11.377 -0.585,-19.628 l 0,-69.141 17.211,0 L 14.355,0 0,0 Z" />
</g>
<g
transform="matrix(0.7891314,0,0,0.7891314,3322.4112,1563.8678)"
id="g150">
<path
inkscape:connector-curvature="0"
id="path152"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 c -0.348,-6.542 -5.815,-7.142 -5.815,-7.142 -1.215,-2.91 -3.873,-4.962 -6.997,-5.402 -0.033,7.214 -0.004,13.178 -0.004,13.178 2.809,-0.426 5.331,-1.963 6.996,-4.268 0.734,0.041 1.61,0 1.61,0 0,0 2.493,7.109 -2.864,12.154 -5.359,5.044 -15.686,5.047 -19.551,0 -2.815,-3.38 -3.687,-7.977 -2.304,-12.154 0.408,-0.075 0.827,-0.075 1.235,0 0,0 2.794,4.043 7.019,4.268 l 0,-13.178 c -3.048,0.623 -5.637,2.616 -7.019,5.402 -3.417,0.68 -5.892,3.66 -5.931,7.142 0.036,10.442 7.894,16.418 16.86,16.418 C -7.798,16.418 0.656,9.564 0,0 m -16.795,29.088 c -14.994,0 -27.15,-12.156 -27.15,-27.151 0,-14.995 12.156,-27.15 27.15,-27.15 14.995,0 27.15,12.155 27.15,27.15 0,14.995 -12.155,27.151 -27.15,27.151" />
transform="translate(1967.0046,-457.69592)"
id="g134-7">
<g
id="g136-0"
clip-path="url(#clipPath138-5)">
<g
id="g142-0"
transform="translate(1451.6599,1974.7396)">
<path
d="m 0,0 -59.338,0 0,12.483 21.308,21.541 c 6.309,6.463 10.431,10.945 12.367,13.441 1.934,2.497 3.328,4.809 4.181,6.938 0.851,2.129 1.277,4.335 1.277,6.619 0,3.406 -0.94,5.941 -2.816,7.606 -1.878,1.664 -4.384,2.497 -7.519,2.497 -3.291,0 -6.484,-0.755 -9.58,-2.264 -3.097,-1.51 -6.329,-3.658 -9.696,-6.445 l -9.755,11.554 c 4.181,3.56 7.645,6.077 10.393,7.548 2.748,1.47 5.748,2.603 9,3.397 3.251,0.793 6.889,1.19 10.915,1.19 5.303,0 9.987,-0.968 14.051,-2.903 4.065,-1.936 7.218,-4.645 9.464,-8.129 2.245,-3.483 3.368,-7.471 3.368,-11.96 0,-3.91 -0.688,-7.577 -2.061,-11.003 -1.375,-3.425 -3.504,-6.938 -6.387,-10.538 -2.885,-3.6 -7.965,-8.729 -15.241,-15.386 l -10.916,-10.277 0,-0.813 L 0,15.096 0,0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path144"
inkscape:connector-curvature="0" />
</g>
<g
id="g146"
transform="translate(1376.5061,2014.3344)">
<path
d="m 0,0 c -5.642,-7.604 -14.242,-12.871 -24.098,-14.121 l -9.612,-7.652 8.651,-10.799 9.755,7.838 c 1.161,0.929 3.231,2.883 6.212,5.864 l -0.29,-8.825 -0.174,-8.071 0,-49.12 17.941,0 L 8.385,0 0,0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path148"
inkscape:connector-curvature="0" />
</g>
<g
id="g150"
transform="translate(1507.6936,1995.3485)">
<path
d="m 0,0 c 0,-5.303 -1.606,-9.813 -4.819,-13.528 -3.213,-3.716 -7.722,-6.271 -13.528,-7.664 l 0,-0.349 c 6.851,-0.852 12.037,-2.932 15.56,-6.241 3.522,-3.31 5.284,-7.771 5.284,-13.384 0,-8.167 -2.961,-14.525 -8.884,-19.073 -5.922,-4.548 -14.38,-6.822 -25.373,-6.822 -9.212,0 -17.38,1.528 -24.501,4.587 l 0,15.27 c 3.289,-1.665 6.909,-3.019 10.857,-4.064 3.948,-1.045 7.858,-1.568 11.728,-1.568 5.923,0 10.297,1.006 13.122,3.019 2.825,2.013 4.239,5.244 4.239,9.696 0,3.987 -1.626,6.812 -4.877,8.477 -3.252,1.665 -8.439,2.497 -15.561,2.497 l -6.445,0 0,13.761 6.561,0 c 6.58,0 11.389,0.86 14.429,2.583 3.038,1.722 4.558,4.674 4.558,8.855 0,6.425 -4.027,9.638 -12.078,9.638 -2.786,0 -5.622,-0.464 -8.505,-1.394 -2.884,-0.928 -6.087,-2.536 -9.609,-4.818 l -8.304,12.366 c 7.742,5.574 16.974,8.361 27.696,8.361 8.786,0 15.725,-1.781 20.815,-5.341 C -2.545,11.302 0,6.347 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path152"
inkscape:connector-curvature="0" />
</g>
<g
id="g154"
transform="translate(1364.6829,2033.5956)">
<path
d="m 0,0 c -0.348,-6.542 -5.816,-7.142 -5.816,-7.142 -1.214,-2.91 -3.873,-4.962 -6.996,-5.401 -0.032,7.213 -0.004,13.177 -0.004,13.177 2.81,-0.426 5.331,-1.963 6.996,-4.268 0.734,0.041 1.611,0 1.611,0 0,0 2.493,7.109 -2.865,12.154 -5.358,5.045 -15.685,5.048 -19.551,0 -2.815,-3.38 -3.687,-7.977 -2.304,-12.154 0.408,-0.075 0.827,-0.075 1.235,0 0,0 2.794,4.043 7.019,4.268 l 0,-13.177 c -3.047,0.623 -5.637,2.615 -7.019,5.401 -3.417,0.68 -5.891,3.66 -5.931,7.142 0.037,10.442 7.894,16.419 16.86,16.419 C -7.798,16.419 0.656,9.564 0,0 m -16.794,29.088 c -14.995,0 -27.151,-12.156 -27.151,-27.151 0,-14.994 12.156,-27.15 27.151,-27.15 14.994,0 27.15,12.156 27.15,27.15 0,14.995 -12.156,27.151 -27.15,27.151"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path156"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

File diff suppressed because one or more lines are too long

View File

@ -25,7 +25,7 @@
"icon": "data:image/png;base64,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"
},
"numbers": {
"icon": "data:image/png;base64,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"
"icon": "data:image/png;base64,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"
},
"system": {
"icon": "data:image/png;base64,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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

491
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.1.32",
"version": "0.1.35",
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -34,9 +34,6 @@
"typings": "built/pxtrequire.d.ts",
"devDependencies": {
"typescript": "2.6.1",
"rtlcss": "^2.1.2",
"autoprefixer": "^6.7.6",
"less": "2.7.3",
"semantic-ui-less": "2.2.14",
"@types/bluebird": "2.0.33",
"@types/marked": "0.3.0",
@ -44,8 +41,8 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.21.13",
"pxt-core": "3.12.9"
"pxt-common-packages": "0.22.1",
"pxt-core": "3.12.12"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -49,14 +49,14 @@ namespace pxsim.visuals {
font-size:16px;
}
.sim-text.large {
font-size:30px;
font-size:20px;
}
.sim-text.number {
font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro;
/*font-weight: bold;*/
}
.sim-text.inverted {
fill:#5A5A5A;
fill: #5A5A5A; /*#F12B21;*/
}
.no-drag, .sim-text, .sim-text-pin {
@ -74,6 +74,10 @@ namespace pxsim.visuals {
stroke: #000;
cursor: pointer;
}
.sim-color-selected {
stroke-width: 1px !important;
stroke: #A8AAA8 !important;
}
.sim-color-wheel-half:hover {
stroke-width: 1;
stroke: #000;
@ -136,6 +140,8 @@ namespace pxsim.visuals {
private cachedControlNodes: { [index: string]: View[] } = {};
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
private cachedCloseIcons: { [index: string]: View } = {};
private cachedBackgroundViews: { [index: string]: View } = {};
private screenCanvas: HTMLCanvasElement;
private screenCanvasCtx: CanvasRenderingContext2D;
@ -298,8 +304,22 @@ namespace pxsim.visuals {
return undefined;
}
private getCloseIconView() {
return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
private getCloseIconView(port: number) {
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() {
@ -319,7 +339,7 @@ namespace pxsim.visuals {
const brick = new BrickViewPortrait(-1);
this.layoutView.setBrick(brick);
EV3View.isPreviousBrickLandscape() ? this.layoutView.setlectBrick() : this.layoutView.unselectBrick();
this.resize();
// Add Screen canvas to board
@ -392,29 +412,29 @@ namespace pxsim.visuals {
this.layoutView.getLandscapeBrick().kill();
// Save previous inputs for the next cycle
EV3View.previousSelectedInputs = ev3board().getInputNodes().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1)
EV3View.previousSeletedOutputs = ev3board().getMotors().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1);
EV3View.previousSelectedInputs = {};
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();
}
private static previousSelectedInputs: number[];
private static previousSeletedOutputs: number[];
private static previousSelectedInputs: pxt.Map<boolean> = {};
private static previousSeletedOutputs: pxt.Map<boolean> = {};
private static previousBrickLandscape: boolean;
private static isPreviousInputSelected(index: number, id: number) {
if (EV3View.previousSelectedInputs && EV3View.previousSelectedInputs[index] == id) {
EV3View.previousSelectedInputs[index] = undefined;
return true;
}
return false;
private static isPreviousInputSelected(index: number) {
const previousInput = EV3View.previousSelectedInputs[index];
delete EV3View.previousSelectedInputs[index];
return previousInput;
}
private static isPreviousOutputSelected(index: number, id: number) {
if (EV3View.previousSeletedOutputs && EV3View.previousSeletedOutputs[index] == id) {
EV3View.previousSeletedOutputs[index] = undefined;
return true;
}
return false;
private static isPreviousOutputSelected(index: number) {
const previousOutput = EV3View.previousSeletedOutputs[index];
delete EV3View.previousSeletedOutputs[index];
return previousOutput;
}
private static isPreviousBrickLandscape() {
@ -463,11 +483,13 @@ namespace pxsim.visuals {
const view = this.getDisplayViewForNode(node.id, index);
if (!node.didChange() && !view.didChange()) return;
if (view) {
const isSelected = EV3View.isPreviousInputSelected(index, node.id) || view.getSelected();
if (isSelected && !view.getSelected()) view.setSelected(true);
const previousSelected = EV3View.isPreviousInputSelected(index);
const isSelected = previousSelected != undefined ? previousSelected : view.getSelected();
view.setSelected(isSelected);
const control = isSelected ? this.getControlForNode(node.id, index, !node.modeChange()) : undefined;
const closeIcon = control && view.hasClose() ? this.getCloseIconView() : undefined;
this.layoutView.setInput(index, view, control, closeIcon);
const closeIcon = control ? this.getCloseIconView(index + 10) : undefined;
const backgroundView = control && view.hasBackground() ? this.getBackgroundView(index + 10) : undefined;
this.layoutView.setInput(index, view, control, closeIcon, backgroundView);
view.updateState();
if (control) control.updateState();
}
@ -485,11 +507,13 @@ namespace pxsim.visuals {
const view = this.getDisplayViewForNode(node.id, index);
if (!node.didChange() && !view.didChange()) return;
if (view) {
const isSelected = EV3View.isPreviousOutputSelected(index, node.id) || view.getSelected();
if (isSelected && !view.getSelected()) view.setSelected(true);
const previousSelected = EV3View.isPreviousOutputSelected(index);
const isSelected = previousSelected != undefined ? previousSelected : view.getSelected();
view.setSelected(isSelected);
const control = isSelected ? this.getControlForNode(node.id, index) : undefined;
const closeIcon = control ? this.getCloseIconView() : undefined;
this.layoutView.setOutput(index, view, control, closeIcon);
const closeIcon = control ? this.getCloseIconView(index) : undefined;
const backgroundView = control && view.hasBackground() ? this.getBackgroundView(index) : undefined;
this.layoutView.setOutput(index, view, control, closeIcon, backgroundView);
view.updateState();
if (control) control.updateState();
}

View File

@ -2,8 +2,8 @@
namespace pxsim.visuals {
export const CONTROL_WIDTH = 87.5;
export const CONTROL_HEIGHT = 175;
export const CONTROL_WIDTH = 76.84;
export const CONTROL_HEIGHT = 112.72;
export const CONTROL_TEXT_COLOR = '#000';
@ -43,28 +43,29 @@ namespace pxsim.visuals {
return this.content;
}
public resize(width: number, height: number) {
public resize(width: number, height: number, strict?: boolean) {
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) {
const currentWidth = this.getInnerWidth();
const currentHeight = this.getInnerHeight();
const newHeight = currentHeight / currentWidth * width;
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
this.content.setAttribute('width', `${newWidth}`);
this.content.setAttribute('height', `${height}`);
// translate to the middle (width)
this.translate(width / 2 - newWidth / 2, 0);
this.width = newWidth;
} else {
this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`);
// translate to the middle (height)
this.translate(0, height / 2 - newHeight / 2);
this.height = newHeight;
}
}
}

View 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;
}
}
}

View File

@ -9,7 +9,7 @@ namespace pxsim.visuals {
this.closeGroup = svg.elt("g") as SVGGElement;
this.closeGroup.style.cursor = 'pointer';
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(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" });
@ -18,15 +18,11 @@ namespace pxsim.visuals {
}
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());
return this.content;
}
public resize(width: number, height: number) {
super.resize(width, height);
}
public getInnerHeight() {
return 32;
}

View File

@ -5,28 +5,32 @@ namespace pxsim.visuals {
export class ColorGridControl extends ControlView<ColorSensorNode> {
private group: SVGGElement;
private static colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey', 'white'];
private static colorValue = [5, 4, 2, 3, 1, 7, 6];
getInnerView() {
this.group = svg.elt("g") as SVGGElement;
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 colorValue = [5, 4, 2, 3, 1, 7];
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
let cy = -4;
for (let c = 0; c < colorIds.length; c++) {
const cx = c % 2 == 0 ? 2.2 : 7.5;
if (c % 2 == 0) cy += 5;
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 => {
this.setColor(colorValue[c]);
this.setColor(ColorGridControl.colorValue[c]);
}));
}
}
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` });
pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => {
this.setColor(6);
@ -42,9 +46,34 @@ namespace pxsim.visuals {
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) {
const state = this.state;
state.setColor(color);
const currentColor = state.getValue();
if (currentColor == color) {
state.setColor(0);
} else {
state.setColor(color);
}
}
}
}

View File

@ -15,7 +15,7 @@ namespace pxsim.visuals {
}
private getReporterHeight() {
return 58;
return 38;
}
private getSliderWidth() {
@ -23,10 +23,10 @@ namespace pxsim.visuals {
}
private getSliderHeight() {
return 111;
return 131;
}
private getMax() {
private getMaxValue() {
return 100;
}
@ -36,9 +36,9 @@ namespace pxsim.visuals {
}
const node = this.state;
const percentage = node.getValue();
const inversePercentage = this.getMax() - percentage;
const inversePercentage = this.getMaxValue() - percentage;
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) {
@ -47,7 +47,7 @@ namespace pxsim.visuals {
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.setColor(t * this.getMax());
state.setColor(t * this.getMaxValue());
}
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
@ -60,7 +60,7 @@ namespace pxsim.visuals {
svg.setGradientColors(this.colorGradient, "black", "yellow");
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;
const sliderGroup = pxsim.svg.child(this.group, "g");
@ -93,9 +93,6 @@ namespace pxsim.visuals {
}, () => {
captured = false;
rect.setAttribute('cursor', '-webkit-grab');
}, () => {
captured = false;
rect.setAttribute('cursor', '-webkit-grab');
})
return this.group;

View File

@ -7,11 +7,59 @@ namespace pxsim.visuals {
private gradient: SVGLinearGradientElement;
private slider: SVGGElement;
private rect: SVGRectElement;
private reporter: SVGTextElement;
private static SLIDER_HANDLE_HEIGHT = 26;
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) {
let gid = "gradient-slider-" + this.getPort();
this.group = svg.elt("g") as SVGGElement;
@ -28,13 +76,14 @@ namespace pxsim.visuals {
this.group = svg.elt("g") as SVGGElement;
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;
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})` });
this.rect = rect as SVGRectElement;
this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
const sliderInner = pxsim.svg.child(this.slider, "g");
@ -48,7 +97,7 @@ namespace pxsim.visuals {
height: this.getInnerHeight(),
opacity: 0,
cursor: '-webkit-grab'
})
}) as SVGRectElement;
let pt = parent.createSVGPoint();
let captured = false;
@ -67,64 +116,11 @@ namespace pxsim.visuals {
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
})
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 {
return {
stops: [

View File

@ -66,9 +66,6 @@ namespace pxsim.visuals {
}, () => {
captured = false;
this.handleSliderUp();
}, () => {
captured = false;
this.handleSliderUp();
})
return this.group;

View File

@ -7,11 +7,60 @@ namespace pxsim.visuals {
private gradient: SVGLinearGradientElement;
private slider: SVGGElement;
private rect: SVGRectElement;
private reporter: SVGTextElement;
private static SLIDER_HANDLE_HEIGHT = 26;
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) {
let gid = "gradient-slider-" + this.getId();
this.group = svg.elt("g") as SVGGElement;
@ -27,13 +76,14 @@ namespace pxsim.visuals {
this.group = svg.elt("g") as SVGGElement;
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;
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})` });
this.rect = rect as SVGRectElement;
this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
const sliderInner = pxsim.svg.child(this.slider, "g");
@ -66,65 +116,11 @@ namespace pxsim.visuals {
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
}, () => {
captured = false;
dragSurface.setAttribute('cursor', '-webkit-grab');
})
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 {
return {
stops: [

View File

@ -13,10 +13,10 @@ namespace pxsim.visuals {
this.group = svg.elt("g") as SVGGElement;
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");
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;
const handleInner = pxsim.svg.child(sliderGroup, "g");
@ -47,15 +47,13 @@ namespace pxsim.visuals {
}
}, () => {
captured = false;
}, () => {
captured = false;
})
return this.group;
}
private getTopPadding() {
return this.getInnerHeight() / 4;
getInnerWidth() {
return RotationSliderControl.SLIDER_WIDTH * 1.5;
}
updateState() {

View File

@ -13,6 +13,8 @@ namespace pxsim.visuals {
export const MAX_MODULE_WIDTH = 100;
export const CLOSE_ICON_GAP_MULTIPLIER = 0.3;
export interface LayoutElement extends View {
getId(): number;
getPort(): number;
@ -33,6 +35,9 @@ namespace pxsim.visuals {
private inputCloseIcons: View[] = [];
private outputCloseIcons: View[] = [];
private inputBackgroundViews: View[] = [];
private outputBackgroundViews: View[] = [];
private inputWires: WireView[] = [];
private outputWires: WireView[] = [];
@ -119,7 +124,7 @@ namespace pxsim.visuals {
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]) {
// Remove current input
@ -131,10 +136,12 @@ namespace pxsim.visuals {
}
this.inputControls[port] = control;
this.inputCloseIcons[port] = closeIcon;
this.inputBackgroundViews[port] = backgroundView;
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 (view.hasClick()) view.registerClick((ev: any) => {
@ -155,7 +162,7 @@ namespace pxsim.visuals {
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]) {
// Remove current output
@ -167,10 +174,12 @@ namespace pxsim.visuals {
}
this.outputControls[port] = control;
this.outputCloseIcons[port] = closeIcon;
this.outputBackgroundViews[port] = backgroundView;
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 (view.hasClick()) view.registerClick((ev: any) => {
@ -277,35 +286,48 @@ namespace pxsim.visuals {
const modulePadding = this.getModulePadding();
const moduleSpacing = contentWidth / 4;
const moduleWidth = this.getInnerModuleWidth();
let currentX = this.getModulePadding();
let currentY = 0;
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]) {
const view = this.outputs[i];
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 outputWidth = this.getInnerModuleWidth();
// Translate and resize view
view.resize(outputWidth, outputHeight);
const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
view.resize(outputWidth - outputPadding * 2, outputHeight);
// const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
// view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
const viewHeight = view.getActualHeight();
view.translate(outputPadding, outputHeight - viewHeight, true);
// Resize control
const control = this.outputControls[i];
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];
if (closeIcon) {
const closeIconWidth = closeIcon.getWidth();
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, 0);
const closeIconSize = this.getCloseIconSize();
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;
})
@ -341,30 +363,41 @@ namespace pxsim.visuals {
currentY += brickHeight + this.getWiringHeight();
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]) {
const view = this.inputs[i];
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 inputWidth = this.getInnerModuleWidth();
// Translate and resize view
view.resize(inputWidth, inputHeight);
view.translate(inputPadding + ((desiredInputWidth - inputWidth) / 2), 0, true);
view.resize(inputWidth - inputPadding * 2, inputHeight);
const viewHeight = view.getActualHeight();
view.translate(inputPadding, 0, true);
// Resize control
const control = this.inputControls[i];
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
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);
const closeIconSize = this.getCloseIconSize();
closeIcon.resize(closeIconSize, closeIconSize);
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;
@ -394,7 +427,7 @@ namespace pxsim.visuals {
public getModuleBounds() {
return {
width: this.width / 4,
width: Math.min(this.getAbosluteModuleWidth(), MAX_MODULE_WIDTH),
height: this.getModuleHeight()
}
}
@ -407,8 +440,16 @@ namespace pxsim.visuals {
return this.getModuleBounds().width - (this.getModulePadding() * 2);
}
public getAbosluteModuleWidth() {
return this.width / 4;
}
public getModuleHeight() {
return this.height * MODULE_HEIGHT_RATIO;
}
public getCloseIconSize() {
return this.getInnerModuleWidth() / 4;
}
}
}

View File

@ -5,6 +5,8 @@ namespace pxsim.visuals {
private control: ColorGridControl;
private static sensor_hole_id = 'color_sensor_white_big';
constructor(port: number) {
super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port);
}
@ -19,7 +21,22 @@ namespace pxsim.visuals {
public 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;
}
}
}

View File

@ -107,7 +107,7 @@ namespace pxsim.visuals {
public attachEvents() {
}
public resize(width: number, height: number) {
public resize(width: number, height: number, strict?: boolean) {
super.resize(width, height);
this.updateDimensions(width, height);
}
@ -120,6 +120,7 @@ namespace pxsim.visuals {
const newWidth = currentWidth / currentHeight * height;
this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`);
this.height = newHeight;
}
}
@ -138,16 +139,22 @@ namespace pxsim.visuals {
protected updateOpacity() {
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) {
this.opacity = opacity;
this.setOpacity(this.opacity);
}
if (this.hasClick()) {
if (this.selected) this.content.style.cursor = "";
else this.content.style.cursor = "pointer";
}
}
}
protected fadeWhenSelected() {
return true;
}
protected setOpacity(opacity: number) {
this.element.setAttribute("opacity", `${opacity}`);
}

View File

@ -6,14 +6,16 @@ namespace pxsim.visuals {
constructor(xml: string, prefix: string, id: NodeType, port: NodeType) {
super(xml, prefix, id, port);
// Shown by default
this.selected = true;
}
public getSelected() {
return true;
}
public hasClose() {
protected fadeWhenSelected() {
return false;
}
public hasBackground() {
return true;
}
}
}

View File

@ -10,33 +10,85 @@ namespace pxsim.visuals {
export type TouchCallback = (event: MouseEvent | TouchEvent | PointerEvent) => void;
export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) {
export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback) {
if (Array.isArray(e)) {
e.forEach(el => bindEvents(el, move, down, up, leave));
e.forEach(el => bindEvents(el, move, down, up));
}
else {
bindEvents(e, move, down, up, leave);
bindEvents(e, move, down, up);
}
}
function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) {
function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback) {
const moveEvent = move ? (ev: MouseEvent) => {
move.call(this, ev);
ev.preventDefault();
ev.stopPropagation();
} : undefined;
const enterEvent = move ? (ev: MouseEvent) => {
if (ev.buttons != 1) {
// cancel all events when we re-enter without a button down
upEvent(ev);
}
} : undefined;
const upEvent = up ? (ev: MouseEvent) => {
up.call(this, ev);
ev.preventDefault();
ev.stopPropagation();
// Unregister document up and move events
if ((window as any).PointerEvent) {
if (moveEvent) document.removeEventListener("pointermove", moveEvent);
if (upEvent) document.removeEventListener("pointerup", upEvent);
if (upEvent) document.removeEventListener("pointercancel", upEvent);
if (moveEvent) document.removeEventListener("pointerenter", enterEvent);
} else {
if (moveEvent) document.removeEventListener("mousemove", moveEvent);
if (upEvent) document.removeEventListener("mouseup", upEvent);
if (moveEvent) document.removeEventListener("mouseenter", enterEvent);
if (pxsim.svg.isTouchEnabled()) {
if (moveEvent) document.removeEventListener("touchmove", moveEvent);
if (upEvent) document.removeEventListener("touchend", upEvent);
if (upEvent) document.removeEventListener("touchcancel", upEvent);
}
}
} : undefined;
const downEvent = down ? (ev: MouseEvent) => {
down.call(this, ev);
ev.preventDefault();
ev.stopPropagation();
// Register document up and move events
if ((window as any).PointerEvent) {
if (moveEvent) document.addEventListener("pointermove", moveEvent);
if (upEvent) document.addEventListener("pointerup", upEvent);
if (upEvent) document.addEventListener("pointercancel", upEvent);
if (moveEvent) document.addEventListener("pointerenter", enterEvent);
} else {
if (moveEvent) document.addEventListener("mousemove", moveEvent);
if (upEvent) document.addEventListener("mouseup", upEvent);
if (moveEvent) document.addEventListener("mouseenter", enterEvent);
if (pxsim.svg.isTouchEnabled()) {
if (moveEvent) document.addEventListener("touchmove", moveEvent);
if (upEvent) document.addEventListener("touchend", upEvent);
if (upEvent) document.addEventListener("touchcancel", upEvent);
}
}
} : undefined;
if ((window as any).PointerEvent) {
if (down) e.addEventListener("pointerdown", down);
if (up) e.addEventListener("pointerup", up);
if (leave) e.addEventListener("pointerleave", leave);
if (move) e.addEventListener("pointermove", move);
if (downEvent) e.addEventListener("pointerdown", downEvent);
}
else {
if (down) e.addEventListener("mousedown", down);
if (up) e.addEventListener("mouseup", up);
if (leave) e.addEventListener("mouseleave", leave);
if (move) e.addEventListener("mousemove", move);
if (downEvent) e.addEventListener("mousedown", downEvent);
if (pxsim.svg.isTouchEnabled()) {
if (down) e.addEventListener("touchstart", down);
if (up) e.addEventListener("touchend", up);
if (leave) e.addEventListener("touchcancel", leave);
if (move) e.addEventListener("touchmove", move);
if (downEvent) e.addEventListener("touchstart", downEvent);
}
}
}

View File

@ -130,11 +130,19 @@ namespace pxsim.visuals {
return this.element;
}
public resize(width: number, height: number) {
public resize(width: number, height: number, strict?: boolean) {
this.width = width;
this.height = height;
}
public getActualHeight() {
return this.height;
}
public getActualWidth() {
return this.width;
}
private updateTransform() {
if (this.rendered) {
let left = this.left;
@ -212,10 +220,6 @@ namespace pxsim.visuals {
}
}
public hasClose() {
return true;
}
protected setChangedState() {
this.changed = true;
}
@ -225,6 +229,10 @@ namespace pxsim.visuals {
this.changed = false;
return res;
}
public hasBackground() {
return false;
}
}
export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
@ -272,9 +280,13 @@ namespace pxsim.visuals {
}
public clear() {
const markForRemoval: Element[] = [];
forEachElement(this.element.childNodes, e => {
this.element.removeChild(e);
markForRemoval.push(e);
});
markForRemoval.forEach(e => {
this.element.removeChild(e);
})
}
public onComponentInjected() {

View File

@ -15,6 +15,6 @@
"Maker": "maker"
},
"electronManifest": {
"latest": "v0.1.19"
"latest": "v0.1.30"
}
}