Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
6bdae6547a | |||
30a67b729d | |||
3e2c61e212 | |||
7a37dec196 | |||
bac792f188 | |||
e4c3fa0e57 | |||
76f7df7579 | |||
d7e671175c | |||
f4b7230a67 | |||
afe0f9b0f7 | |||
23b6edc4bf | |||
13b3c1a194 | |||
7e58b9b699 | |||
08a860bd80 | |||
4a966a73f1 | |||
542216000c | |||
b323929f03 | |||
1e3647738d | |||
838b3f7f9a | |||
0ff2d7866d | |||
4c978d793a | |||
06ca53ae89 | |||
84990d66a9 | |||
4825172423 | |||
b69af383a6 | |||
6e1a613798 | |||
b5005f3b10 | |||
044b585f24 | |||
94fe26e390 | |||
ca832f52db | |||
6f2fe212df | |||
96be357af1 |
@ -1,6 +0,0 @@
|
||||
/// <reference path="../node_modules/pxt-core/built/pxtlib.d.ts" />
|
||||
|
||||
import * as path from "path";
|
||||
export let pxtCore = require("pxt-core");
|
||||
// require.resolve() gives path to [pxt dir]/built/pxt.js, so move up twice to get pxt root dir
|
||||
export let pxtCoreDir = path.resolve(require.resolve("pxt-core"), "..", "..");
|
BIN
docs/static/icons/android-chrome-192x192.png
vendored
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 723 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 477 KiB After Width: | Height: | Size: 123 KiB |
BIN
docs/static/tutorials/run-motors/run-motors.gif
vendored
Before Width: | Height: | Size: 509 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 95 KiB |
BIN
docs/static/tutorials/touch-to-run/touch-to-run.gif
vendored
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 74 KiB |
BIN
docs/static/tutorials/what-color/color-detector.gif
vendored
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 45 KiB |
@ -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.
|
||||
|
||||

|
||||
|
||||
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!
|
||||
|
@ -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.
|
||||
|
@ -33,10 +33,8 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="description">
|
||||
<span class="ui yellow circular label">1</span>
|
||||
<strong>${lf("Connect EV3 to computer with USB cable")}</strong>
|
||||
<br/>
|
||||
${lf("Use the miniUSB port on top of EV3 brick")}
|
||||
<span class="ui blue circular label">1</span>
|
||||
<strong>${lf("Connect the EV3 brick to your computer with a USB cable.")}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,9 +55,9 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P
|
||||
</div>
|
||||
<div class="ui one column grid">
|
||||
<div class="column">
|
||||
<a href="/troubleshoot" target="_blank" class="ui segment container yellow">
|
||||
<a href="/troubleshoot" target="_blank" class="ui segment container" style="background:#ffdb90">
|
||||
<i class="icon exclamation circle large" aria-hidden="true"></i>
|
||||
${lf("Something wrong? Click here to troubleshoot..")}
|
||||
${lf("Did you prepare your EV3 brick for MakeCode?")}
|
||||
</a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
@ -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 |
@ -185,7 +185,7 @@ namespace sensors {
|
||||
* @param handler the code to run when detected
|
||||
*/
|
||||
//% help=sensors/color-sensor/on-light-detected
|
||||
//% block="on **color sensor** %this|detected %mode|%condition"
|
||||
//% block="on **color sensor** %this|%mode|%condition"
|
||||
//% blockId=colorOnLightDetected
|
||||
//% parts="colorsensor"
|
||||
//% blockNamespace=sensors
|
||||
@ -202,7 +202,7 @@ namespace sensors {
|
||||
* @param color the color to detect
|
||||
*/
|
||||
//% help=sensors/color-sensor/pause-until-light-detected
|
||||
//% block="pause until **color sensor** %this|detected %mode|%condition"
|
||||
//% block="pause until **color sensor** %this|%mode|%condition"
|
||||
//% blockId=colorPauseUntilLightDetected
|
||||
//% parts="colorsensor"
|
||||
//% blockNamespace=sensors
|
||||
|
@ -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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"
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
491
package-lock.json
generated
11
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-ev3",
|
||||
"version": "0.1.32",
|
||||
"version": "0.1.41",
|
||||
"description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
@ -30,13 +30,8 @@
|
||||
"docs/*/*.md",
|
||||
"docs/*/*/*.md"
|
||||
],
|
||||
"main": "built/pxtrequire.js",
|
||||
"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 +39,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.17"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node node_modules/pxt-core/built/pxt.js travis"
|
||||
|
@ -110,8 +110,8 @@
|
||||
"path": "http://service.lego.com"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade Firmware",
|
||||
"path": "https://ev3manager.education.lego.com/"
|
||||
"name": "Troubleshoot",
|
||||
"path": "/troubleshoot"
|
||||
},
|
||||
{
|
||||
"name": "Blocks",
|
||||
|
@ -17,7 +17,8 @@ namespace pxsim {
|
||||
private speedCmdTime: number;
|
||||
private _synchedMotor: MotorNode; // non-null if synchronized
|
||||
|
||||
private manualSpeed: number = undefined;
|
||||
private manualReferenceAngle: number = undefined;
|
||||
private manualAngle: number = undefined;
|
||||
|
||||
constructor(port: number, large: boolean) {
|
||||
super(port);
|
||||
@ -103,14 +104,18 @@ namespace pxsim {
|
||||
}
|
||||
|
||||
manualMotorDown() {
|
||||
this.manualReferenceAngle = this.angle;
|
||||
this.manualAngle = 0;
|
||||
}
|
||||
|
||||
manualMotorMove(speed: number) {
|
||||
this.manualSpeed = speed;
|
||||
// position: 0, 360
|
||||
manualMotorAngle(angle: number) {
|
||||
this.manualAngle = angle;
|
||||
}
|
||||
|
||||
manualMotorUp() {
|
||||
this.manualSpeed = undefined;
|
||||
delete this.manualReferenceAngle;
|
||||
delete this.manualAngle;
|
||||
}
|
||||
|
||||
updateState(elapsed: number) {
|
||||
@ -126,7 +131,7 @@ namespace pxsim {
|
||||
}
|
||||
|
||||
private updateStateStep(elapsed: number) {
|
||||
if (!this.manualSpeed) {
|
||||
if (this.manualAngle === undefined) {
|
||||
// compute new speed
|
||||
switch (this.speedCmd) {
|
||||
case DAL.opOutputSpeed:
|
||||
@ -202,7 +207,11 @@ namespace pxsim {
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.speed = this.manualSpeed;
|
||||
// the user is holding the handle - so position is the angle
|
||||
this.speed = 0;
|
||||
// rotate by the desired angle change
|
||||
this.angle = this.manualReferenceAngle + this.manualAngle;
|
||||
this.setChangedState();
|
||||
}
|
||||
this.speed = Math.round(this.speed); // integer only
|
||||
|
||||
@ -217,7 +226,8 @@ namespace pxsim {
|
||||
|
||||
// if the motor was stopped or there are no speed commands,
|
||||
// let it coast to speed 0
|
||||
if (this.speed && !(this.started || this.speedCmd)) {
|
||||
if ((this.manualReferenceAngle === undefined)
|
||||
&& this.speed && !(this.started || this.speedCmd)) {
|
||||
// decay speed 5% per tick
|
||||
this.speed = Math.round(Math.max(0, Math.abs(this.speed) - 10) * sign(this.speed));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
42
sim/visuals/controls/backgroundView.ts
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export class BackgroundViewControl extends ControlView<PortNode> {
|
||||
private backgroundGroup: SVGGElement;
|
||||
private backgroundRect: SVGRectElement;
|
||||
|
||||
getInnerView() {
|
||||
this.backgroundGroup = svg.elt("g") as SVGGElement;
|
||||
this.backgroundRect = pxsim.svg.child(this.backgroundGroup,
|
||||
"rect", {
|
||||
'x': 0, 'y': 0,
|
||||
'width': '100%',
|
||||
'height': '100%',
|
||||
'style': `fill: #d6edff; stroke: #A8A9A8; stroke-width: 3px; stroke-opacity: 0.2`
|
||||
}) as SVGRectElement;
|
||||
return this.backgroundGroup;
|
||||
}
|
||||
|
||||
buildDom(): SVGElement {
|
||||
this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement;
|
||||
this.content.appendChild(this.getInnerView());
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public resize(width: number, height: number, strict?: boolean) {
|
||||
super.resize(width, height, strict);
|
||||
this.backgroundRect.setAttribute('stroke-dasharray', `${height + width + height} 1000`);
|
||||
this.backgroundRect.setAttribute('stroke-dashoffset', `-${width}`);
|
||||
}
|
||||
|
||||
getInnerWidth(): number {
|
||||
return 76.84;
|
||||
}
|
||||
|
||||
getInnerHeight(): number {
|
||||
return 173.86;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -9,7 +9,7 @@ namespace pxsim.visuals {
|
||||
this.closeGroup = svg.elt("g") as SVGGElement;
|
||||
this.closeGroup.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;
|
||||
}
|
||||
|
@ -5,28 +5,36 @@ 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];
|
||||
|
||||
private colorDivs: Element[] = [];
|
||||
|
||||
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]}` });
|
||||
this.colorDivs.push(circle);
|
||||
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` });
|
||||
const circle = pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle sim-color-grid-white', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
|
||||
this.colorDivs.push(circle);
|
||||
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 +50,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.colorDivs[c] 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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: [
|
||||
|
@ -13,7 +13,7 @@ namespace pxsim.visuals {
|
||||
|
||||
private static SLIDER_RADIUS = 100;
|
||||
|
||||
private internalSpeed: number = 0;
|
||||
private internalAngle: number = 0;
|
||||
|
||||
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
|
||||
this.group = svg.elt("g") as SVGGElement;
|
||||
@ -66,9 +66,6 @@ namespace pxsim.visuals {
|
||||
}, () => {
|
||||
captured = false;
|
||||
this.handleSliderUp();
|
||||
}, () => {
|
||||
captured = false;
|
||||
this.handleSliderUp();
|
||||
})
|
||||
|
||||
return this.group;
|
||||
@ -105,9 +102,9 @@ namespace pxsim.visuals {
|
||||
} else if (dx >= 0 && dy <= 0) {
|
||||
deg = 90 - deg;
|
||||
}
|
||||
const value = Math.abs(Math.ceil((deg % 360) / 360 * this.getMax()));
|
||||
const value = Math.abs(Math.ceil((deg % 360)));
|
||||
|
||||
this.internalSpeed = value;
|
||||
this.internalAngle = value;
|
||||
this.updateDial();
|
||||
|
||||
this.prevVal = deg;
|
||||
@ -122,7 +119,7 @@ namespace pxsim.visuals {
|
||||
private handleSliderMove() {
|
||||
this.dial.setAttribute('cursor', '-webkit-grabbing');
|
||||
const state = this.state;
|
||||
state.manualMotorMove(this.internalSpeed);
|
||||
state.manualMotorAngle(this.internalAngle);
|
||||
}
|
||||
|
||||
private handleSliderUp() {
|
||||
@ -130,19 +127,18 @@ namespace pxsim.visuals {
|
||||
const state = this.state;
|
||||
state.manualMotorUp();
|
||||
|
||||
this.internalSpeed = 0;
|
||||
this.internalAngle = 0;
|
||||
this.updateDial();
|
||||
}
|
||||
|
||||
private updateDial() {
|
||||
let speed = this.internalSpeed;
|
||||
let angle = this.internalAngle;
|
||||
|
||||
// Update dial position
|
||||
const deg = speed / this.getMax() * 360; // degrees
|
||||
const radius = MotorSliderControl.SLIDER_RADIUS;
|
||||
const dialRadius = 5;
|
||||
const x = Math.ceil((radius - dialRadius) * Math.sin(deg * Math.PI / 180)) + radius;
|
||||
const y = Math.ceil((radius - dialRadius) * -Math.cos(deg * Math.PI / 180)) + radius;
|
||||
const x = Math.ceil((radius - dialRadius) * Math.sin(angle * Math.PI / 180)) + radius;
|
||||
const y = Math.ceil((radius - dialRadius) * -Math.cos(angle * Math.PI / 180)) + radius;
|
||||
this.dial.setAttribute('transform', `translate(${x}, ${y})`);
|
||||
}
|
||||
|
||||
@ -151,18 +147,10 @@ namespace pxsim.visuals {
|
||||
return;
|
||||
}
|
||||
const node = this.state;
|
||||
const speed = node.getSpeed();
|
||||
const angle = node.getAngle() % 360;
|
||||
|
||||
// Update reporter
|
||||
this.reporter.textContent = `${speed}`;
|
||||
}
|
||||
|
||||
private getMin() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private getMax() {
|
||||
return 100;
|
||||
this.reporter.textContent = `${angle}°`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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: [
|
||||
|
@ -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() {
|
||||
|
@ -12,6 +12,9 @@ namespace pxsim.visuals {
|
||||
export const MODULE_INNER_PADDING_RATIO = 1 / 35;
|
||||
|
||||
export const MAX_MODULE_WIDTH = 100;
|
||||
export const MIN_MODULE_HEIGHT = 40;
|
||||
|
||||
export const CLOSE_ICON_GAP_MULTIPLIER = 0.3;
|
||||
|
||||
export interface LayoutElement extends View {
|
||||
getId(): number;
|
||||
@ -33,6 +36,9 @@ namespace pxsim.visuals {
|
||||
private inputCloseIcons: View[] = [];
|
||||
private outputCloseIcons: View[] = [];
|
||||
|
||||
private inputBackgroundViews: View[] = [];
|
||||
private outputBackgroundViews: View[] = [];
|
||||
|
||||
private inputWires: WireView[] = [];
|
||||
private outputWires: WireView[] = [];
|
||||
|
||||
@ -119,7 +125,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 +137,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 +163,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 +175,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 +287,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 +364,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 = Math.max(view.getActualHeight(), MIN_MODULE_HEIGHT);
|
||||
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 +428,7 @@ namespace pxsim.visuals {
|
||||
|
||||
public getModuleBounds() {
|
||||
return {
|
||||
width: this.width / 4,
|
||||
width: Math.min(this.getAbosluteModuleWidth(), MAX_MODULE_WIDTH),
|
||||
height: this.getModuleHeight()
|
||||
}
|
||||
}
|
||||
@ -407,8 +441,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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,25 @@ 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.stroke = color;
|
||||
if (color != '#ffffff') {
|
||||
sensorHole.style.strokeWidth = '2px';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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}`);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -15,6 +15,6 @@
|
||||
"Maker": "maker"
|
||||
},
|
||||
"electronManifest": {
|
||||
"latest": "v0.1.19"
|
||||
"latest": "v0.1.36"
|
||||
}
|
||||
}
|
||||
|