Compare commits

..

102 Commits

Author SHA1 Message Date
783a561941 0.0.49 2018-01-03 15:02:35 -08:00
c916664ae7 Fix download and save icons. 2018-01-03 15:02:24 -08:00
4b836ede1b Merge pull request #167 from Microsoft/blockfixes
Block layout fixes.
2018-01-03 14:22:18 -08:00
52fdaeec99 remove colon from print line block 2018-01-03 14:22:00 -08:00
2aaa45e10d Merge pull request #168 from Microsoft/remainingiconfixes
Remaining icon fixes.
2018-01-03 14:21:04 -08:00
7993363e89 Remaining icon fixes. Using blockIcons to show custom icons for built in categories 2018-01-03 14:20:33 -08:00
4d671f6cb0 Block layout fixes. Fixes to screen blocks. Adding print line API. 2018-01-03 14:00:08 -08:00
cc020d5a81 Merge pull request #124 from Microsoft/icon_font
Icon font
2018-01-03 12:35:52 -08:00
5c39862a44 Fix the rest of the icons (sensors, math, variables and extensions) 2018-01-03 12:35:33 -08:00
e0e5c95989 Merge branch 'master' into icon_font 2018-01-03 11:56:28 -08:00
800b4ad224 Merge pull request #165 from Microsoft/fixnullrefthis
Fix null ref for this when _update is called.
2018-01-03 11:42:38 -08:00
40c3b4b0cf Tweaking sensor icon 2018-01-03 11:36:43 -08:00
64bdc35e6f Merge paths for sensor icon 2018-01-03 11:29:49 -08:00
2b5f702bb6 Fix null ref for this when _update is called. 2018-01-03 11:20:37 -08:00
1c81ecd23f send ports data to console 2018-01-03 08:24:54 -08:00
4d223374b5 Differential drive (#164)
* clear sync speed cmd

* differential drive model

* use cm/s

* fixed aggressive clearing of motor sync command

* better computation of turn artio

* improved robot dimensions

* moving block up

* hanbdle infinite case

* correct handling of inifinte time/step

* better stop handling
2018-01-03 00:27:05 -08:00
dd9cf9014f Merge pull request #163 from Microsoft/fixnullderefbuttons
Fix null dereferencing issue for buttons
2018-01-02 23:19:55 -08:00
eb11d7926c Fix null dereferencing issue for buttons 2018-01-02 23:18:54 -08:00
609740dc48 Merge pull request #162 from Microsoft/moresensorfixes
Better fix for null dereferencing issue.
2018-01-02 22:51:45 -08:00
955a2c9757 Better fix for null dereferencing issue. Fix initial Color sensor mode (default). 2018-01-02 22:51:12 -08:00
02838e6c30 Merge pull request #161 from Microsoft/colorgridfixes
Update Color sensor control (colorGrid) to match spec
2018-01-02 22:30:04 -08:00
b0c54e84e6 Update Color sensor control (colorGrid) to match spec 2018-01-02 22:29:12 -08:00
b6644b7a23 Merge pull request #160 from Microsoft/sensorfixes
Sensor fixes. Fix race condition
2018-01-02 22:23:14 -08:00
3c4c38eb59 Fix null dereferencing issue. Fix sensor mode changed not clearing cached control. 2018-01-02 22:22:14 -08:00
29aba7b10b Fix advanced icons 2018-01-02 17:30:19 -08:00
7427142243 Merge branch 'icon_font' of https://github.com/Microsoft/pxt-ev3 into icon_font 2018-01-02 17:17:13 -08:00
436500babb Merge 2018-01-02 17:11:24 -08:00
82be4e344b More work 2018-01-02 17:07:36 -08:00
81bfca4ed6 0.0.48 2018-01-02 16:20:30 -08:00
c35dbdd38f Motor sync support in simulator (#159)
* parse sync motor command

* better sim support

* sync support

* fixing tank
2018-01-02 16:20:00 -08:00
1bc93013e6 0.0.47 2018-01-02 14:51:00 -08:00
58148eb1c3 Square shaped corners for Blockly blocks and dropdowns. (#158) 2018-01-02 13:32:07 -08:00
a7c62b45b2 Use legoIcons font for flyout headings 2018-01-02 13:01:28 -08:00
bd765d49ee Add lego resources.AI 2018-01-02 12:59:48 -08:00
383ca5467d Remove advexpanded/advcollapsed 2018-01-02 12:49:25 -08:00
e240e3b394 Check, cancel 2018-01-02 10:47:04 -08:00
7affbf8cb6 Save, download 2018-01-02 10:47:04 -08:00
4dff282633 Initial work 2018-01-02 10:47:04 -08:00
5d470fdcef Merge pull request #143 from Microsoft/persistselected
Persist selected state of controls across simulator restarts
2017-12-29 14:02:50 -08:00
f30eac41e9 Persist selected state of controls across simulator restarts 2017-12-28 11:17:18 -08:00
d7f46c0fb5 Hide screen picker properly from typescript 2017-12-28 11:15:53 -08:00
58384017f2 0.0.46 2017-12-28 09:08:27 -08:00
01f7fe633c Motorworks2 (#141)
* fixing polarity

* allocate motor on motorUsed only

* perform sub-step integration step for better precision
2017-12-28 09:07:57 -08:00
a9a9a89811 Better fonts and better alignment of motor reporter control 2017-12-27 20:43:39 -08:00
7e2251d8ac Fix negative motor reporting 2017-12-27 20:35:36 -08:00
1903a6e347 Merge pull request #140 from Microsoft/motorreporters
Add motor reporter control
2017-12-27 20:32:53 -08:00
2fb75a2d83 Added todo 2017-12-27 17:07:50 -08:00
0da175a8cd Add motor reporter control 2017-12-27 17:06:23 -08:00
f01370e4fd Simulator support for motor commands (#137)
* support for "step" functions

* locale files

* enum issue

* fixing decoding of array

* implement clear count

* log unknown commands
2017-12-27 17:05:15 -08:00
751df2fe8c Merge pull request #139 from Microsoft/colorandsliderresizefixes
Fix resizing in controls to work for all sizes including full screen
2017-12-27 16:31:20 -08:00
8be4bb11d8 Fix resizing in controls to work for all sizes including full screen 2017-12-27 16:30:42 -08:00
342e714ae2 Merge pull request #138 from Microsoft/updatemotorassets
Update motor SVG assets and connections
2017-12-27 14:48:50 -08:00
fb31b81f7e Update motor SVG assets and connections 2017-12-27 14:48:15 -08:00
8204995749 Fix wire view to match spec size of wires. 2017-12-26 23:28:29 -08:00
85c14bb05a Fix rotation and distance slider update state 2017-12-26 23:23:26 -08:00
c398a5a133 Update padding and fix wiring order 2017-12-26 23:19:04 -08:00
afaedaa0b2 Merge pull request #134 from Microsoft/fixflickering
Fix resizing flickering issue in the simulator
2017-12-24 16:19:36 -08:00
0aa41e9a64 Fix resizing flickering issue, remove flip animation on start, remove the need for a default height and width in the layout engine 2017-12-24 11:59:01 -08:00
88df2e14cb Merge pull request #125 from Microsoft/sim_refactor_resize
Refactor sim for better resizing and support for multiple controls to be open
2017-12-22 14:01:35 -08:00
180f32f25c Simulator refactoring to support better resizing of modules and controls 2017-12-22 14:00:23 -08:00
995527675a Check, cancel 2017-12-21 11:30:32 -08:00
84eb849bc7 Save, download 2017-12-21 11:10:47 -08:00
5e0e35b4bd Initial work 2017-12-21 11:00:54 -08:00
300a2c1476 Merge pull request #123 from Microsoft/gradient_circle
Gradient circle
2017-12-20 16:34:59 -08:00
703bd01931 Increase stroke width 2017-12-20 16:25:26 -08:00
75a65eeab2 Add lego icons 2017-12-20 16:16:13 -08:00
38a9f153f7 pxtarget 2017-12-20 15:15:12 -08:00
3c5dae8c7b Small changes 2017-12-20 15:11:44 -08:00
85345969d3 Initial work 2017-12-20 14:58:41 -08:00
ac1e5d2846 Remove constant resize 2017-12-19 21:20:06 -08:00
11b4bbc07e Fix controls 2017-12-19 17:20:01 -08:00
7f5b8aed99 more tweaking of angle encoding 2017-12-19 17:15:53 -08:00
c989e2fdab Merge pull request #121 from Microsoft/motorphysics
Motor work
2017-12-19 17:07:15 -08:00
d3dcb5de85 Use setChangedState for touch and color sensors 2017-12-19 17:01:45 -08:00
9cca35d49f encoding 32bit angle into data buffer 2017-12-19 16:54:44 -08:00
7123bfecd3 removing cancel animation stuff 2017-12-19 16:21:14 -08:00
c8ac770983 read motor state into lms_motor 2017-12-19 16:18:17 -08:00
aa031036ee updating motor state in game loop 2017-12-19 16:03:26 -08:00
a7d002d949 0.0.45 2017-12-19 15:10:32 -08:00
93fd8c8c78 removing more icons 2017-12-19 15:10:13 -08:00
1765ca2d35 Merge pull request #116 from Microsoft/revert_screenopt
Use game loop instead of animation queue
2017-12-19 14:58:37 -08:00
1ab7ae6cfa minor PR feedback 2017-12-19 14:57:28 -08:00
3acf4e9ac5 More hardware tests (#119) 2017-12-19 14:55:48 -08:00
ef5fa9ae82 Minor fix to killing the animation when the sim is killed 2017-12-19 14:55:43 -08:00
e1f7a5b8cf Merge pull request #118 from Microsoft/ambient_light_threshold
Set high/low to 20/5 for ambient light mode
2017-12-19 14:42:54 -08:00
2c73bfc813 Set high/low to 20/5 2017-12-19 14:40:30 -08:00
d78d9c8686 basic ports view (#115)
* basic ports view

* slight adjustement of rendering
2017-12-19 14:26:57 -08:00
2157af3e63 Using game loop instead of queueAnimationUpdate 2017-12-19 14:20:35 -08:00
eac3e183c3 better test support 2017-12-19 13:10:40 -08:00
785ddff706 Reverting screen optimization to use SetInterval and didChange 2017-12-19 12:53:12 -08:00
e07d6e3a31 0.0.44 2017-12-19 11:53:50 -08:00
763ad3f763 0.0.43 2017-12-19 11:38:02 -08:00
919a03951c Removing icons (#114)
* removing icons

* added "pause for light"
2017-12-19 11:37:33 -08:00
9e427898ae added test framework (#113)
* added test framework

* added toString on motors

* enabling logs
2017-12-19 07:07:50 -08:00
60bf3a17d3 a mini-console support with scroll up / down (#112)
* a mini-console support with scroll up / down

* fix compile error
2017-12-18 22:36:32 -08:00
0529759a80 Fix ultrasonic value to use cm instead of 0.1 cm units (#110)
* Fix simulator

* use 250 instead of 255
2017-12-18 20:30:56 -08:00
b07f157181 0.0.42 2017-12-18 18:24:46 -08:00
2f5f7d4133 Merge pull request #111 from Microsoft/scale_screen
Add tiny padding around the screen for the canvas.
2017-12-18 17:08:02 -08:00
e6e1dce59f Add tiny padding around the screen for the canvas. 2017-12-18 17:07:23 -08:00
43a9d03231 fixing pauseUntilReady signature 2017-12-18 14:45:44 -08:00
c0f6cd3651 test passing on hw 2017-12-18 14:31:17 -08:00
f1445c6e89 Motor pause until ready (#108)
* adding command to pause until ready

* adding console API

* adding ready support for motors

* fix time output scale

* fixing angle
2017-12-18 14:13:38 -08:00
112 changed files with 13516 additions and 1289 deletions

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; font-family: "iconfont";
src: url("iconfont.eot?e05611aaee246c1da118a83eaf515de9?#iefix") format("embedded-opentype"), src: url("iconfont.eot?6e1ef95090bc1e1acc3a7e6bb86172de?#iefix") format("embedded-opentype"),
url("iconfont.woff2?e05611aaee246c1da118a83eaf515de9") format("woff2"), url("iconfont.woff2?6e1ef95090bc1e1acc3a7e6bb86172de") format("woff2"),
url("iconfont.woff?e05611aaee246c1da118a83eaf515de9") format("woff"); url("iconfont.woff?6e1ef95090bc1e1acc3a7e6bb86172de") format("woff");
} }
.icon { .icon {
@ -28,3 +28,60 @@ url("iconfont.woff?e05611aaee246c1da118a83eaf515de9") format("woff");
.icon-gyro:before { .icon-gyro:before {
content: "\f104"; content: "\f104";
} }
.icon-addpackage:before {
content: "\f105";
}
.icon-brick:before {
content: "\f106";
}
.icon-controls:before {
content: "\f107";
}
.icon-functions:before {
content: "\f108";
}
.icon-list:before {
content: "\f109";
}
.icon-logic:before {
content: "\f10a";
}
.icon-loops:before {
content: "\f10b";
}
.icon-math:before {
content: "\f10c";
}
.icon-motors:before {
content: "\f10d";
}
.icon-music:before {
content: "\f10e";
}
.icon-sensors:before {
content: "\f10f";
}
.icon-text:before {
content: "\f110";
}
.icon-variables:before {
content: "\f111";
}
.icon-cancel:before {
content: "\f112";
}
.icon-check:before {
content: "\f113";
}
.icon-download:before {
content: "\f114";
}
.icon-save:before {
content: "\f115";
}
.icon-advancedcollapsed:before {
content: "\f116";
}
.icon-advancedexpanded:before {
content: "\f117";
}

Binary file not shown.

View File

@ -19,6 +19,63 @@
<glyph glyph-name="gyro" <glyph glyph-name="gyro"
unicode="&#xF104;" unicode="&#xF104;"
horiz-adv-x="23.578015492438215" d=" M17 0H6.4A5.901881224640355 5.901881224640355 0 0 0 5.1 0.3H2.3A1.283659166359277 1.283659166359277 0 0 0 1 1.6V4.5H0.5A0.5016599040944301 0.5016599040944301 0 0 0 0 5V35.4A0.5016599040944301 0.5016599040944301 0 0 0 0.5 35.9H1V38.7A1.283659166359277 1.283659166359277 0 0 0 2.3 40H10.6V39.6H12.8V40H21.2A1.283659166359277 1.283659166359277 0 0 0 22.5 38.7V35.9H23.1A0.5016599040944301 0.5016599040944301 0 0 0 23.6 35.4V5.1A0.5016599040944301 0.5016599040944301 0 0 0 23.1 4.6H22.5V1.6A1.283659166359277 1.283659166359277 0 0 0 21.2 0.4H18.5C18.4 0.1 17.2 0 17 0zM6.2 34.5L3.7 33.8A1.0033198081888601 1.0033198081888601 0 0 1 3.4 33.7A0.2360752489856142 0.2360752489856142 0 0 1 3.3 33.5A0.20656584286241245 0.20656584286241245 0 0 1 3.4 33.3L4.1 32.8A7.657690888970861 7.657690888970861 0 0 1 3.1 30A8.51346366654371 8.51346366654371 0 0 1 4.8 23.5C5.8 24.2 6.5 24.7 6.5 24.7A7.377351530800443 7.377351530800443 0 0 0 5.4 26.9A6.300258207303577 6.300258207303577 0 0 0 6 31.8L6.5 31.5H6.5L6.6 31.5A0.3541128734784213 0.3541128734784213 0 0 1 6.8 31.4A0.17705643673921065 0.17705643673921065 0 0 1 7 31.5A0.6344522316488379 0.6344522316488379 0 0 1 7 32L6.2 34.5zM17.5 31.8H17.5A6.300258207303577 6.300258207303577 0 0 0 18 26.9A7.510143858354851 7.510143858354851 0 0 0 17 24.7L17.1 24.6L18.6 23.5A8.631501291036518 8.631501291036518 0 0 1 20.3 30A7.657690888970861 7.657690888970861 0 0 1 19.3 32.8L19.8 33.1L20 33.2A0.22132054592401326 0.22132054592401326 0 0 1 20.1 33.4A0.26558465510881596 0.26558465510881596 0 0 1 20 33.6A1.0033198081888601 1.0033198081888601 0 0 1 19.7 33.7L17.2 34.5S17.1 34.1 17 33.6S16.6 32.3 16.5 32A0.619697528587237 0.619697528587237 0 0 1 16.5 31.5A0.17705643673921065 0.17705643673921065 0 0 1 16.6 31.4A0.48690520103282936 0.48690520103282936 0 0 1 16.8 31.4L16.8 31.4L17.4 31.7zM11.7 30.6A1.9918849133161198 1.9918849133161198 0 1 1 13.7 28.6A1.9918849133161198 1.9918849133161198 0 0 1 11.7 30.6H11.7z" /> horiz-adv-x="23.578015492438215" d=" M17 0H6.4A5.901881224640355 5.901881224640355 0 0 0 5.1 0.3H2.3A1.283659166359277 1.283659166359277 0 0 0 1 1.6V4.5H0.5A0.5016599040944301 0.5016599040944301 0 0 0 0 5V35.4A0.5016599040944301 0.5016599040944301 0 0 0 0.5 35.9H1V38.7A1.283659166359277 1.283659166359277 0 0 0 2.3 40H10.6V39.6H12.8V40H21.2A1.283659166359277 1.283659166359277 0 0 0 22.5 38.7V35.9H23.1A0.5016599040944301 0.5016599040944301 0 0 0 23.6 35.4V5.1A0.5016599040944301 0.5016599040944301 0 0 0 23.1 4.6H22.5V1.6A1.283659166359277 1.283659166359277 0 0 0 21.2 0.4H18.5C18.4 0.1 17.2 0 17 0zM6.2 34.5L3.7 33.8A1.0033198081888601 1.0033198081888601 0 0 1 3.4 33.7A0.2360752489856142 0.2360752489856142 0 0 1 3.3 33.5A0.20656584286241245 0.20656584286241245 0 0 1 3.4 33.3L4.1 32.8A7.657690888970861 7.657690888970861 0 0 1 3.1 30A8.51346366654371 8.51346366654371 0 0 1 4.8 23.5C5.8 24.2 6.5 24.7 6.5 24.7A7.377351530800443 7.377351530800443 0 0 0 5.4 26.9A6.300258207303577 6.300258207303577 0 0 0 6 31.8L6.5 31.5H6.5L6.6 31.5A0.3541128734784213 0.3541128734784213 0 0 1 6.8 31.4A0.17705643673921065 0.17705643673921065 0 0 1 7 31.5A0.6344522316488379 0.6344522316488379 0 0 1 7 32L6.2 34.5zM17.5 31.8H17.5A6.300258207303577 6.300258207303577 0 0 0 18 26.9A7.510143858354851 7.510143858354851 0 0 0 17 24.7L17.1 24.6L18.6 23.5A8.631501291036518 8.631501291036518 0 0 1 20.3 30A7.657690888970861 7.657690888970861 0 0 1 19.3 32.8L19.8 33.1L20 33.2A0.22132054592401326 0.22132054592401326 0 0 1 20.1 33.4A0.26558465510881596 0.26558465510881596 0 0 1 20 33.6A1.0033198081888601 1.0033198081888601 0 0 1 19.7 33.7L17.2 34.5S17.1 34.1 17 33.6S16.6 32.3 16.5 32A0.619697528587237 0.619697528587237 0 0 1 16.5 31.5A0.17705643673921065 0.17705643673921065 0 0 1 16.6 31.4A0.48690520103282936 0.48690520103282936 0 0 1 16.8 31.4L16.8 31.4L17.4 31.7zM11.7 30.6A1.9918849133161198 1.9918849133161198 0 1 1 13.7 28.6A1.9918849133161198 1.9918849133161198 0 0 1 11.7 30.6H11.7z" />
<glyph glyph-name="addpackage"
unicode="&#xF105;"
horiz-adv-x="40" d=" M4.3 19.1C4.3 10.5 11.4 3.5 20 3.5C28.6 3.5 35.7 10.5 35.7 19.1C35.7 27.8 28.6 34.8 20 34.8C11.4 34.8 4.3 27.8 4.3 19.1z M30.4 20.9L21.7 20.9L21.7 29.6L18.3 29.6L18.3 20.9L9.6 20.9L9.6 17.4L18.3 17.4L18.3 8.7L21.7 8.7L21.7 17.4L30.4 17.4z" />
<glyph glyph-name="brick"
unicode="&#xF106;"
horiz-adv-x="40" d=" M34.3 0.9H5.7V39.1H34.4V0.9zM10.4 32.7V18.4H29.6V32.7H10.4z" />
<glyph glyph-name="controls"
unicode="&#xF107;"
horiz-adv-x="40" d=" M6.1 33H9.6V5.2H6.1V33z M18.3 33H21.7V5.2H18.3V33z M30.4 33H33.9V5.2H30.4V33z M2.6 15.7H13V7H2.6V15.7z M14.8 31.3H25.2V22.6H14.8V31.3z M27 22.6H37.4V13.9H27V22.6z" />
<glyph glyph-name="functions"
unicode="&#xF108;"
horiz-adv-x="40" d=" M12.2 25.4H9.1C9.1 26.3 9.1 26.5 9.9 26.5C11.5 26.5 12.5 27.2 13.1 28.6C13.8 30.1 14.8 31.5 15.7 33.1C16.5 34.3 18.1 35.3 19.7 35.5C20.7 35.7 21.9 35.3 22.5 34.5C22.8 33.9 22.8 33.4 22.6 32.7C22.3 32.2 21.8 32 21.1 32.2C20.5 32.2 20.2 32.7 20.2 33.3C20.2 33.4 20.2 33.4 20.2 33.6C20.2 33.9 20.4 34.3 20.4 34.6C20 34.6 19.5 34.8 19.2 34.6C18.5 34.3 17.9 33.9 17.4 33.1C16.7 32 16.2 30.6 15.7 29.4C15.3 28.6 15 27.3 14.8 26.5H17.2C17.1 26.1 17.1 25.8 16.9 25.4H14.5C13.9 23.5 13.6 21.9 12.9 20.2C12.2 16.7 10.8 13.4 9.4 10.1C8.7 8.6 7.7 7.3 6.6 6.3C5.8 5.4 4.5 4.9 3.3 4.9C2.6 4.7 1.8 5.1 1.1 5.4C0.7 5.8 0.5 6.5 0.7 7.2C1.1 7.7 1.6 8 2.5 7.9C3 7.9 3.2 7.3 3.2 6.8C3.2 6.6 3 6.5 3 6.5C2.8 6.3 2.8 6.1 2.8 5.9C3 5.9 3.2 5.8 3.5 5.8C4.4 5.8 5.4 6.3 5.9 7.2C6.5 8 7 9.1 7.2 9.9C8.5 15.2 10.1 20.4 11.7 25.4C12.2 25.3 12.2 25.3 12.2 25.4z M32.5 4.7C32.7 4.9 32.7 5.1 32.9 5.3C34.8 6.3 36 8.4 36.4 10.5C37.1 13.9 37.1 17.4 36.2 20.9C35.7 22.8 34.5 24.2 33.1 25.4C32.9 25.6 32.7 25.8 32.7 25.9C36 24.7 39.2 20.6 39.2 15.3C39.3 10.8 36.7 6.5 32.5 4.7z M23.9 25.8C23.9 25.6 23.7 25.4 23.7 25.4C21.8 24.4 20.5 22.3 20.2 20.2C19.5 16.7 19.5 13.3 20.4 9.6C20.9 7.7 22.1 6.3 23.5 5.1C23.7 4.9 23.9 4.9 23.9 4.6C20 6.5 17.4 10.5 17.6 15C17.4 20.7 20.9 24.9 23.9 25.8z M32 12.2C32 12.2 32.2 12.2 32.4 12C32.4 12 32.4 12 32.4 11.9C31.9 11 31.3 10.1 30.3 9.6C29.9 9.3 29.2 9.1 28.5 9.6C28.4 9.8 28.2 9.9 28.2 10.3C27.9 11.3 27.7 12.4 27.5 13.4C27.5 13.6 27.5 13.6 27.3 13.8C27.2 13.4 27 13.1 26.6 12.7C26.1 12 25.6 11 24.7 10.3C24.4 9.9 24 9.6 23.5 9.4C23 9.3 22.5 9.4 21.9 9.9V10.1C21.9 10.5 21.9 10.8 22.3 11C22.5 11.2 22.8 11.2 23.2 11L23.3 10.8C23.9 10.5 24 10.3 24.4 11C25.1 12 25.9 13.4 26.8 14.5C26.8 14.6 26.8 14.8 26.8 15C26.6 15.7 26.5 16.6 26.3 17.3C25.9 18.5 25.4 19 24 19H23.9C23.7 19.2 23.9 19.3 24 19.3C24.7 19.5 25.8 19.7 26.5 19.7C26.6 19.7 26.8 19.7 27 19.5C27.5 18.8 27.9 18.1 28 17.1C28 16.9 28.2 16.6 28.2 16.4C28.4 16.7 28.7 17.3 29.1 17.6C29.6 18.1 30.1 18.8 30.8 19.3C31.2 19.5 31.5 19.7 31.9 19.7C32.4 19.7 32.7 19.3 32.7 18.8V18.6C32.5 18.1 32.2 17.9 31.7 18.1L31.7 18.1C31.5 18.1 31.5 18.1 31.3 18.1C30.8 18.5 30.1 18.3 29.6 17.8L29.6 17.8C29.2 17.3 28.7 16.6 28.4 15.9C28.4 15.7 28.4 15.7 28.4 15.5C28.7 14.3 28.9 12.9 29.2 11.5C29.2 11.3 29.2 11.3 29.4 11.2C29.6 10.6 29.9 10.5 30.3 11C31.3 11.3 31.5 11.9 32 12.2z" />
<glyph glyph-name="list"
unicode="&#xF109;"
horiz-adv-x="40" d=" M11.1 30.4H36.9V27.3H11.1V30.4z M11.1 22.4H36.9V19.3H11.1V22.4z M11.1 14.4H36.9V11.3H11.1V14.4z M5.6 27.3H3.8V33.6H2.4V34.6C3.3 34.6 4 35.1 4.2 36.2H5.6V27.3z M7.7 17.6V16H1.4V16.7C1.4 18.8 2.8 19.7 4 20.3C5.7 21.2 5.7 21.6 5.7 22.1C5.7 22.6 5.4 23.1 4.5 23.1C3.7 23.1 3.1 22.4 3.1 21.6H1.4C1.4 23.1 2.4 24.7 4.5 24.7C6.6 24.7 7.5 23.3 7.5 22.1C7.5 20.3 6.3 19.8 4.9 19C4 18.4 3.3 17.9 3.3 17.4H7.7z M4.5 4.5C2.4 4.5 1.4 5.9 1.2 7.5H3C3 6.6 3.5 6.1 4.5 6.1C5.6 6.1 6.1 6.6 6.1 7.3C6.1 8 5.6 8.5 4.7 8.5H4.2V9.9H4.5C5.6 9.9 5.9 10.4 5.9 11C5.9 11.7 5.4 12 4.7 12C3.8 12 3.3 11.3 3.3 10.6H1.6C1.6 12.2 2.6 13.6 4.5 13.6C6.4 13.6 7.5 12.3 7.5 11.1C7.5 10.4 7.1 9.7 6.3 9.4C7.3 9 7.7 8.2 7.7 7.3C7.8 6.1 6.8 4.5 4.5 4.5z" />
<glyph glyph-name="logic"
unicode="&#xF10A;"
horiz-adv-x="40" d=" M2.8 14.1H7.4C10 14.2 12.2 16 13.1 18.6C15 23.1 18.3 31.3 23.7 31.3S29.8 31.3 29.8 31.3V35.8L36.9 28.3L29.8 21.5V26.4H24.9C22.3 25.7 20.2 24 19.4 21.5C17.3 17 15.2 9.4 8.2 9.2C5.1 9.2 3 9.2 3 9.2V14.1z M2.8 31.1V26.2H8.2C8.2 26.2 11.4 26.2 13.1 22.1C13.8 23.8 14.7 25.4 15.7 26.9C15.7 26.9 12.2 30.9 8.4 30.9S2.8 31.1 2.8 31.1z M19.5 18.4C19.5 18.4 21.8 13.9 24.4 13.9H30V18.4L37.3 11.3L30 4.2V9H23.7C23.7 9 20.2 9 16.9 13.4C18 14.9 18.8 16.7 19.5 18.4z" />
<glyph glyph-name="loops"
unicode="&#xF10B;"
horiz-adv-x="40" d=" M6.3 12.7C4.5 15.2 2.6 17.4 0.9 20H4.5C4.5 24.2 6.3 28.4 9.2 31.3C11.3 33.4 13.7 34.8 16.7 35.3C21.9 36.6 27.3 35 31.1 31.2C30.3 30.3 29.4 29.4 28.7 28.7C24.2 32.6 19.3 33.3 14.1 30.5C10.3 28.4 8.2 24.4 8.2 20.2H11.8C9.7 17.6 7.8 15 6.3 12.7z M32.2 20H28.5C30.4 22.5 32.2 24.7 33.9 27.3C35.8 24.9 37.6 22.6 39.3 20H35.6C35.6 15.7 33.7 11.5 30.6 8.6C28.5 6.6 26.1 5.3 23.3 4.7C18.1 3.5 12.7 5.1 8.9 8.9L11.3 11.3C15.6 7.5 20.7 6.6 25.9 9.6C29.9 11.7 32.2 15.7 32.2 20z" />
<glyph glyph-name="math"
unicode="&#xF10C;"
horiz-adv-x="40" d=" M15.2 5.3C14.9 4.9 14.2 4.2 13.8 3.9L9.3 8.4C7.6 6.7 6.2 5.3 4.8 3.9C4.4 4.2 3.8 4.9 3.4 5.3L7.9 9.8C6.2 11.5 4.8 12.9 3.4 14.3C3.8 14.7 4.4 15.4 4.8 15.7L9.3 11.2L13.8 15.7C14.2 15.4 14.9 14.7 15.2 14.3L10.7 9.8C12.1 8.2 13.7 6.7 15.2 5.3z M1.5 28V30H7.9V36.4C8.6 36.4 9.1 36.4 9.8 36.4V30H16.3V28H9.8V21.6H7.8V28H1.5z M38.4 10.7H22V14.1H38.4V10.7z M38.4 7.2V3.7H22V7.2H38.4z M38.4 31V27.5H22V31H38.4z" />
<glyph glyph-name="motors"
unicode="&#xF10D;"
horiz-adv-x="40" d=" M28.9 25.8C27.5 24.4 25.9 22.9 24.5 21.5H35.7V32.6L32.2 29.1C29.6 32.3 25.9 34.3 21.8 34.9C18.3 35.4 14.6 34.5 11.7 32.6C4.2 28.1 1.9 18.3 6.5 10.9S20.7 1.1 28.2 5.6C30.8 7.2 32.7 9.5 34.1 12.1C32.7 12.6 31.3 13.3 29.9 13.8C28.5 11 25.9 9 23 8.1C20.7 7.4 18.3 7.6 16 8.4C10.3 10.7 7.3 17.1 9.4 22.9C10.6 26.3 13.6 29 17.1 29.8C21.6 31.2 26.3 29.5 28.9 25.8z M16.7 19C16.7 17.1 18.3 15.7 20 15.7S23.3 17.3 23.3 19C23.3 21 21.8 22.3 20 22.3C18.1 22.5 16.7 21 16.7 19C16.7 19 16.7 19 16.7 19z" />
<glyph glyph-name="music"
unicode="&#xF10E;"
horiz-adv-x="40" d=" M24.2 17.7C27.1 17.2 29.9 15.7 31.7 13.2C32.5 13.2 33.4 13.2 33.4 13.2S36 20.9 30.3 26.3S13.4 31.7 9.2 26.3C6.3 22.6 5.2 17.6 6.8 13.2C7.3 13.2 7.7 13.2 8.2 13.2C8.2 13.2 11.1 17.6 15.8 17.7C15.7 10.1 15.7 3.5 15.7 3.5C12.3 4.2 9.6 6.3 8 9.4C4.5 10.1 1.7 13.2 1.7 17C1.7 28.3 10.3 34.8 20 34.8S38.8 27.3 38.1 17C37.7 9.9 31.8 9.4 31.8 9.4C30.4 6.3 27.7 4 24.3 3.5C24.2 11.3 24.2 17.7 24.2 17.7z" />
<glyph glyph-name="sensors"
unicode="&#xF10F;"
horiz-adv-x="40" d=" M20 36.9C10.6 36.9 3 29.2 3 19.8C3 10.4 10.6 2.8 20 2.8S37 10.4 37 19.8C36.9 29.4 29.4 36.9 20 36.9zM20 8.2C13.4 8.2 8.2 13.6 8.2 20S13.6 31.8 20 31.8S31.8 26.4 31.8 20S26.6 8.2 20 8.2z M13.2 20C13.2 16.3 16.3 13.2 20 13.2C23.7 13.2 26.8 16.3 26.8 20C26.8 23.7 23.7 26.8 20 26.8C16.3 26.8 13.2 23.7 13.2 20z" />
<glyph glyph-name="text"
unicode="&#xF110;"
horiz-adv-x="40" d=" M23.1 30.6V2.8H16.5V30.6H7.7V36.7H32.3V30.6H23.1z" />
<glyph glyph-name="variables"
unicode="&#xF111;"
horiz-adv-x="40" d=" M36.8 12V7.8H3.4V12H36.8z M36.8 22.1V17.9H3.4V22.1H36.8z M36.7 32.2V28H3.3V32.2H36.7z" />
<glyph glyph-name="cancel"
unicode="&#xF112;"
horiz-adv-x="40" d=" M4285.9 23284.1H4308.1V23280.4H4285.9V23284.1z M4285.9 23302.6H4289.6V23280.4H4285.9V23302.6z" />
<glyph glyph-name="check"
unicode="&#xF113;"
horiz-adv-x="40" d=" M4209.2 23456H4231.3V23452.3H4209.2V23456z M4201.2 23463.4H4204.9V23452.3H4201.2V23463.4z" />
<glyph glyph-name="download"
unicode="&#xF114;"
horiz-adv-x="40" d=" M5.2 15.7H36.5V1.7H5.2V15.7z M28.5 24.2L26.1 26.6L22.6 23.1L22.6 36.5L19.1 36.5L19.1 23.5L16 26.6L13.6 24.2L20.9 16.7L21 16.9L21.2 16.7z" />
<glyph glyph-name="save"
unicode="&#xF115;"
horiz-adv-x="40" d=" M2.3 20.1V36.6C2.3 37.8 2.4 38 3.7 38H11.5C12.7 38 12.9 37.8 12.9 36.6V28.6C12.9 27.6 13 27.4 14.1 27.4H25.9C27 27.4 27.1 27.6 27.1 28.6V36.8C27.1 37.8 27.3 38 28.3 38C30.8 38 30.8 38 32.3 36.4C34.1 34.7 35.7 33.1 37.4 31.4C37.7 31 37.9 30.5 37.9 30V3.6C37.9 2.5 37.7 2.2 36.7 2.2H3.5C2.4 2.2 2.3 2.3 2.3 3.6C2.3 9.1 2.3 14.7 2.3 20.1zM20.2 6H33C34.3 6 34.4 6.2 34.4 7.4V22.5C34.4 23.6 34.3 23.9 33 23.9H7.1C6.1 23.9 5.7 23.7 5.7 22.5V7.4C5.7 6.2 5.9 6 7.1 6H20.2z M24.7 34.5C24.7 33.6 24.7 32.8 24.7 31.9C24.7 31 24.3 30.9 23.7 30.9C23.1 30.9 22.4 30.9 22.1 30.9S21 31 21 31.7V31.9V36.9C21 37.3 21.2 37.8 21.9 38C21.9 38 21.9 38 22.1 38C22.6 38 23.3 38 23.8 38C24.3 38 24.9 37.6 24.9 37.1C24.9 37.1 24.9 37.1 24.9 36.9C24.7 36.3 24.7 35.2 24.7 34.5z M5.1 25H35.4V4.8H5.1V25z M6.2 24.1H34.5V5.9H6.2V24.1z" />
<glyph glyph-name="advancedcollapsed"
unicode="&#xF116;"
horiz-adv-x="40" d=" M39.7 28.2L36.2 31.5L20 15.3L3.8 31.5L0.3 28.2L18.3 10.3L18.3 10.3L20 8.5L20.5 9L20.5 9z" />
<glyph glyph-name="advancedexpanded"
unicode="&#xF117;"
horiz-adv-x="40" d=" M39.3 12L21.7 29.6L21.7 29.6L20 31.3L19.5 30.8L19.5 30.8L0.7 12L4 8.7L20 24.7L36 8.7z" />
</font> </font>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,66 +0,0 @@
let errors: string[] = [];
let tachoB = 0;
let tachoC = 0;
function assert(name: string, condition: boolean) {
if (!condition) {
errors.push(name)
}
}
function assertClose(name: string, expected: number, actual: number, tolerance = 10) {
assert(name + ` ${expected}/${actual}`, Math.abs(expected - actual) < tolerance);
}
function test(name: string, f: () => void, check?: () => void) {
motors.stopAllMotors();
loops.pause(500);
tachoB = motors.largeB.tachoCount()
tachoC = motors.largeB.tachoCount()
brick.clearScreen()
brick.print(name, 0, 0)
f();
loops.pause(3000);
motors.stopAllMotors();
motors.largeB.setReversed(false);
motors.largeC.setReversed(false);
motors.mediumA.setReversed(false);
loops.pause(1000);
if (check)
check()
}
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
test("lgB set speed 100", () => {
motors.largeB.setSpeed(100)
});
test("lgB set speed (reversed)", () => {
motors.largeB.setReversed(true)
motors.largeB.setSpeed(100)
})
test("lgBC set speed 100", () => {
motors.largeBC.setSpeed(100)
})
test("lgBC steer 50% 2x", () => {
motors.largeBC.steer(50, 50, 2, MoveUnit.Rotations)
}, () => {
assertClose("largeB", 720, motors.largeB.tachoCount() - tachoB)
});
test("lgBC steer 50% 500deg", () => {
motors.largeBC.steer(50, 50, 500, MoveUnit.Degrees)
}, () => {
assertClose("largeB", 500, motors.largeB.tachoCount() - tachoB)
});
test("lgBC steer 50% 2s", () => {
motors.largeBC.steer(50, 50, 2, MoveUnit.Seconds)
})
test("lgBC tank 50% 2s", () => {
motors.largeBC.tank(50, 50, 720, MoveUnit.Degrees)
})
brick.clearScreen()
brick.print(`${errors.length} errors`, 0, 0)
let l = 1;
for(const error of errors)
brick.print(`error: ${error}`, 0, l++ * 12)
})

View File

@ -139,8 +139,98 @@ namespace pxt.editor {
}) })
} }
/**
* Update the shape of Blockly blocks with square corners
*/
function updateBlocklyShape() {
/**
* Rounded corner radius.
* @const
*/
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
/**
* Inner space between edge of statement input and notch.
* @const
*/
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
/**
* SVG path for drawing next/previous notch from left to right.
* @const
*/
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
'l 8,8 ' +
'h 16 ' +
'l 8,-8 '
);
/**
* SVG path for drawing next/previous notch from right to left.
* @const
*/
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
'l -8,8 ' +
'h -16 ' +
'l -8,-8 '
);
/**
* SVG start point for drawing the top-left corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
'm 0,' + 0;
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
/**
* SVG path for drawing the rounded top-right corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-right corner.
* @const
*/
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-left corner.
* @const
*/
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
/**
* SVG path for drawing the top-left corner of a statement input.
* @const
*/
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
/**
* SVG path for drawing the bottom-left corner of a statement input.
* Includes the rounded inside corner.
* @const
*/
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
}
initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> { initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...') pxt.debug('loading pxt-ev3 target extensions...')
updateBlocklyShape();
const res: pxt.editor.ExtensionResult = { const res: pxt.editor.ExtensionResult = {
deployCoreAsync, deployCoreAsync,
}; };

File diff suppressed because one or more lines are too long

View File

@ -8,5 +8,8 @@
"sensors.ColorSensor.onColorDetected|param|handler": "the code to run when detected", "sensors.ColorSensor.onColorDetected|param|handler": "the code to run when detected",
"sensors.ColorSensor.onLightChanged": "Registers code to run when the ambient light changes.", "sensors.ColorSensor.onLightChanged": "Registers code to run when the ambient light changes.",
"sensors.ColorSensor.onLightChanged|param|condition": "the light condition", "sensors.ColorSensor.onLightChanged|param|condition": "the light condition",
"sensors.ColorSensor.onLightChanged|param|handler": "the code to run when detected" "sensors.ColorSensor.onLightChanged|param|handler": "the code to run when detected",
"sensors.ColorSensor.pauseForColor": "Waits for the given color to be detected",
"sensors.ColorSensor.pauseForColor|param|color": "the color to detect",
"sensors.ColorSensor.pauseForLight": "Waits for the given color to be detected"
} }

View File

@ -13,14 +13,16 @@
"LightCondition.Dark|block": "dark", "LightCondition.Dark|block": "dark",
"LightIntensityMode.Ambient|block": "ambient light", "LightIntensityMode.Ambient|block": "ambient light",
"LightIntensityMode.Reflected|block": "reflected light", "LightIntensityMode.Reflected|block": "reflected light",
"sensors.ColorSensor.color|block": "`icons.colorSensor` %sensor| color", "sensors.ColorSensor.color|block": "%sensor| color",
"sensors.ColorSensor.light|block": "`icons.colorSensor` %sensor|%mode", "sensors.ColorSensor.light|block": "%sensor|%mode",
"sensors.ColorSensor.onColorDetected|block": "on `icons.colorSensor` %sensor|detected color %color", "sensors.ColorSensor.onColorDetected|block": "on %sensor|detected color %color",
"sensors.ColorSensor.onLightChanged|block": "on `icons.colorSensor` %sensor|%mode|%condition", "sensors.ColorSensor.onLightChanged|block": "on %sensor|%mode|%condition",
"sensors.color1|block": "1", "sensors.ColorSensor.pauseForColor|block": "pause %sensor|for color %color",
"sensors.color2|block": "2", "sensors.ColorSensor.pauseForLight|block": "pause %sensor|for %mode|light %condition",
"sensors.color3|block": "3", "sensors.color1|block": "color 1",
"sensors.color4|block": "4", "sensors.color2|block": "color 2",
"sensors.color3|block": "color 3",
"sensors.color4|block": "color 4",
"sensors|block": "sensors", "sensors|block": "sensors",
"{id:category}Sensors": "Sensors", "{id:category}Sensors": "Sensors",
"{id:group}Color Sensor": "Color Sensor" "{id:group}Color Sensor": "Color Sensor"

View File

@ -56,6 +56,7 @@ namespace sensors {
constructor(port: number) { constructor(port: number) {
super(port) super(port)
this._setMode(ColorSensorMode.None);
this.thresholdDetector = new sensors.internal.ThresholdDetector(this.id()); this.thresholdDetector = new sensors.internal.ThresholdDetector(this.id());
} }
@ -68,13 +69,20 @@ namespace sensors {
} }
setMode(m: ColorSensorMode) { setMode(m: ColorSensorMode) {
if (m == ColorSensorMode.AmbientLightIntensity) {
this.thresholdDetector.setLowThreshold(5);
this.thresholdDetector.setHighThreshold(20);
} else {
this.thresholdDetector.setLowThreshold(20);
this.thresholdDetector.setHighThreshold(80);
}
this._setMode(m) this._setMode(m)
} }
/** /**
* Gets the current color mode * Gets the current color mode
*/ */
colorMode() { colorMode() {
return <ColorSensorMode>this.mode; return <ColorSensorMode>this.mode;
} }
@ -99,11 +107,9 @@ namespace sensors {
* @param handler the code to run when detected * @param handler the code to run when detected
*/ */
//% help=sensors/color-sensor/on-color-detected //% help=sensors/color-sensor/on-color-detected
//% block="on `icons.colorSensor` %sensor|detected color %color" //% block="on %sensor|detected color %color"
//% blockId=colorOnColorDetected //% blockId=colorOnColorDetected
//% parts="colorsensor" //% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=100 blockGap=8 //% weight=100 blockGap=8
//% group="Color Sensor" //% group="Color Sensor"
@ -115,16 +121,33 @@ namespace sensors {
control.raiseEvent(this._id, v); control.raiseEvent(this._id, v);
} }
/**
* Waits for the given color to be detected
* @param color the color to detect
*/
//% help=sensors/color-sensor/pause-for-color
//% block="pause %sensor|for color %color"
//% blockId=colorPauseForColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=99 blockGap=8
//% group="Color Sensor"
pauseForColor(color: ColorSensorColor) {
this.setMode(ColorSensorMode.Color);
if (this.color() != color) {
const v = this._colorEventValue(<number>color);
control.waitForEvent(this._id, v);
}
}
/** /**
* Get the current color from the color sensor. * Get the current color from the color sensor.
* @param sensor the color sensor to query the request * @param sensor the color sensor to query the request
*/ */
//% help=sensors/color-sensor/color //% help=sensors/color-sensor/color
//% block="`icons.colorSensor` %sensor| color" //% block="%sensor| color"
//% blockId=colorGetColor //% blockId=colorGetColor
//% parts="colorsensor" //% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=99 //% weight=99
//% group="Color Sensor" //% group="Color Sensor"
@ -139,17 +162,32 @@ namespace sensors {
* @param handler the code to run when detected * @param handler the code to run when detected
*/ */
//% help=sensors/color-sensor/on-light-changed //% help=sensors/color-sensor/on-light-changed
//% block="on `icons.colorSensor` %sensor|%mode|%condition" //% block="on %sensor|%mode|%condition"
//% blockId=colorOnLightChanged //% blockId=colorOnLightChanged
//% parts="colorsensor" //% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=89 blockGap=8 //% weight=89 blockGap=8
//% group="Color Sensor" //% group="Color Sensor"
onLightChanged(mode: LightIntensityMode, condition: LightCondition, handler: () => void) { onLightChanged(mode: LightIntensityMode, condition: LightCondition, handler: () => void) {
control.onEvent(this._id, <number>condition, handler);
this.setMode(<ColorSensorMode><number>mode) this.setMode(<ColorSensorMode><number>mode)
control.onEvent(this._id, <number>condition, handler);
}
/**
* Waits for the given color to be detected
* @param color the color to detect
*/
//% help=sensors/color-sensor/pause-for-light
//% block="pause %sensor|for %mode|light %condition"
//% blockId=colorPauseForLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=88 blockGap=8
//% group="Color Sensor"
pauseForLight(mode: LightIntensityMode, condition: LightCondition) {
this.setMode(<ColorSensorMode><number>mode)
if (this.thresholdDetector.state != <number>condition)
control.waitForEvent(this._id, <number>condition)
} }
/** /**
@ -157,13 +195,11 @@ namespace sensors {
* @param sensor the color sensor port * @param sensor the color sensor port
*/ */
//% help=sensors/color-sensor/light //% help=sensors/color-sensor/light
//% block="`icons.colorSensor` %sensor|%mode" //% block="%sensor|%mode"
//% blockId=colorLight //% blockId=colorLight
//% parts="colorsensor" //% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=88 //% weight=87
//% group="Color Sensor" //% group="Color Sensor"
light(mode: LightIntensityMode) { light(mode: LightIntensityMode) {
this.setMode(<ColorSensorMode><number>mode) this.setMode(<ColorSensorMode><number>mode)
@ -181,15 +217,15 @@ namespace sensors {
} }
} }
//% whenUsed block="1" weight=95 fixedInstance jres=icons.port1 //% whenUsed block="color 1" weight=95 fixedInstance jres=icons.port1
export const color1: ColorSensor = new ColorSensor(1) export const color1: ColorSensor = new ColorSensor(1)
//% whenUsed block="2" weight=90 fixedInstance jres=icons.port2 //% whenUsed block="color 2" weight=90 fixedInstance jres=icons.port2
export const color2: ColorSensor = new ColorSensor(2) export const color2: ColorSensor = new ColorSensor(2)
//% whenUsed block="3" weight=90 fixedInstance jres=icons.port3 //% whenUsed block="color 3" weight=90 fixedInstance jres=icons.port3
export const color3: ColorSensor = new ColorSensor(3) export const color3: ColorSensor = new ColorSensor(3)
//% whenUsed block="4" weight=90 fixedInstance jres=icons.port4 //% whenUsed block="color 4" weight=90 fixedInstance jres=icons.port4
export const color4: ColorSensor = new ColorSensor(4) export const color4: ColorSensor = new ColorSensor(4)
} }

View File

@ -21,8 +21,6 @@
"brick.Button.pauseUntil": "Waits until the event is raised", "brick.Button.pauseUntil": "Waits until the event is raised",
"brick.Button.pauseUntil|param|ev": "the event to wait for", "brick.Button.pauseUntil|param|ev": "the event to wait for",
"brick.Button.wasPressed": "See if the button was pressed again since the last time you checked.", "brick.Button.wasPressed": "See if the button was pressed again since the last time you checked.",
"brick._imagePicker": "An image",
"brick._imagePicker|param|image": "the image",
"brick.buttonDown": "Down button on the EV3 Brick.", "brick.buttonDown": "Down button on the EV3 Brick.",
"brick.buttonEnter": "Enter button on the EV3 Brick.", "brick.buttonEnter": "Enter button on the EV3 Brick.",
"brick.buttonLeft": "Left button on the EV3 Brick.", "brick.buttonLeft": "Left button on the EV3 Brick.",
@ -31,18 +29,21 @@
"brick.clearScreen": "Clears the screen", "brick.clearScreen": "Clears the screen",
"brick.lightPattern": "Pattern block.", "brick.lightPattern": "Pattern block.",
"brick.lightPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green", "brick.lightPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
"brick.print": "Show text on the screen.", "brick.printLine": "Show text on the screen at a specific line.",
"brick.print|param|text": "the text to print on the screen, eg: \"Hello world\"", "brick.printLine|param|line": "the line number to print the text at, eg: 0",
"brick.print|param|x": "the starting position's x coordinate, eg: 0", "brick.printLine|param|text": "the text to print on the screen, eg: \"Hello world\"",
"brick.print|param|y": "the starting position's x coordinate, eg: 0", "brick.printPorts": "Prints the port states on the screen",
"brick.setPixel": "Sets a pixel on or off", "brick.setLight": "Set lights.",
"brick.setPixel|param|on": "a value indicating if the pixel should be on or off", "brick.setLight|param|pattern": "the lights pattern to use.",
"brick.setPixel|param|x": "the starting position's x coordinate, eg: 0",
"brick.setPixel|param|y": "the starting position's x coordinate, eg: 0",
"brick.setStatusLight": "Set lights.",
"brick.setStatusLight|param|pattern": "the lights pattern to use.",
"brick.showImage": "Shows an image on screen", "brick.showImage": "Shows an image on screen",
"brick.showImage|param|image": "image to draw", "brick.showImage|param|image": "image to draw",
"console": "Reading and writing data to the console output.\n\nReading and writing data to the console output.",
"console.addListener": "Adds a listener for the log messages",
"console.log": "Write a line of text to the console output.",
"console.logValue": "Write a name:value pair as a line of text to the console output.",
"console.logValue|param|name": "name of the value stream, eg: \"x\"",
"console.logValue|param|value": "to write",
"console.sendToScreen": "Sends the log messages to the brick screen and uses the brick up and down buttons to scroll.",
"control": "Program controls and events.", "control": "Program controls and events.",
"control.allocateNotifyEvent": "Allocates the next user notification event", "control.allocateNotifyEvent": "Allocates the next user notification event",
"control.deviceFirmwareVersion": "Determine the version of system software currently running.", "control.deviceFirmwareVersion": "Determine the version of system software currently running.",
@ -51,31 +52,50 @@
"control.raiseEvent": "Announce that an event happened to registered handlers.", "control.raiseEvent": "Announce that an event happened to registered handlers.",
"control.raiseEvent|param|src": "ID of the Component that generated the event", "control.raiseEvent|param|src": "ID of the Component that generated the event",
"control.raiseEvent|param|value": "Component specific code indicating the cause of the event.", "control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
"motors.Motor.move": "Moves the motor by a number of rotations, degress or seconds", "motors.Motor.angle": "Gets motor ration angle.",
"motors.Motor.move|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50", "motors.Motor.clearCount": "Clears the motor count",
"motors.Motor.move|param|unit": "the meaning of the value", "motors.Motor.speed": "Gets motor actual speed.",
"motors.Motor.move|param|value": "the move quantity, eg: 2", "motors.Motor.toString": "Returns the status of the motor",
"motors.Motor.reset": "Resets the motor(s).", "motors.MotorBase.isReady": "Returns a value indicating if the motor is still running a previous command.",
"motors.Motor.setBrake": "Sets the automatic brake on or off when the motor is off", "motors.MotorBase.move": "Moves the motor by a number of rotations, degress or seconds",
"motors.Motor.setBrake|param|brake": "a value indicating if the motor should break when off", "motors.MotorBase.move|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.Motor.setReversed": "Reverses the motor polarity", "motors.MotorBase.move|param|unit": "the meaning of the value",
"motors.Motor.setSpeed": "Sets the speed of the motor.", "motors.MotorBase.move|param|value": "the move quantity, eg: 2",
"motors.Motor.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50", "motors.MotorBase.pauseUntilReady": "Pauses the execution until the previous command finished.",
"motors.Motor.stop": "Stops the motor(s).", "motors.MotorBase.pauseUntilReady|param|timeOut": "optional maximum pausing time in milliseconds",
"motors.SingleMotor.clearCount": "Clears the motor count", "motors.MotorBase.reset": "Resets the motor(s).",
"motors.SingleMotor.count": "Gets motor step count.", "motors.MotorBase.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.SingleMotor.speed": "Gets motor actual speed.", "motors.MotorBase.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.SingleMotor.tachoCount": "Gets motor tacho count.", "motors.MotorBase.setReversed": "Reverses the motor polarity",
"motors.MotorBase.setSpeed": "Sets the speed of the motor.",
"motors.MotorBase.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.stop": "Stops the motor(s).",
"motors.SynchedMotorPair.drive": "Makes a differential drive robot move with a given speed (%) and rotation rate (deg/s)\nusing a unicycle model.",
"motors.SynchedMotorPair.drive|param|rotationSpeed": "rotation of the robot around the center point, eg: 30",
"motors.SynchedMotorPair.drive|param|speed": "speed of the center point between motors, eg: 10",
"motors.SynchedMotorPair.drive|param|value": "the amount of movement, eg: 2",
"motors.SynchedMotorPair.setDimensions": "Sets the wheels radius and base length of a directional drive robot",
"motors.SynchedMotorPair.setDimensions|param|wheelRadius": "@param baseLength ",
"motors.SynchedMotorPair.steer": "Turns the motor and the follower motor by a number of rotations", "motors.SynchedMotorPair.steer": "Turns the motor and the follower motor by a number of rotations",
"motors.SynchedMotorPair.steer|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50", "motors.SynchedMotorPair.steer|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.SynchedMotorPair.steer|param|steering": "the ratio of power sent to the follower motor, from ``-100`` to ``100``", "motors.SynchedMotorPair.steer|param|turnRatio": "the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0",
"motors.SynchedMotorPair.steer|param|unit": "the meaning of the value", "motors.SynchedMotorPair.steer|param|unit": "the meaning of the value",
"motors.SynchedMotorPair.steer|param|value": "the move quantity, eg: 2", "motors.SynchedMotorPair.steer|param|value": "the move quantity, eg: 2",
"motors.SynchedMotorPair.tank": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.", "motors.SynchedMotorPair.tank": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.",
"motors.SynchedMotorPair.tank|param|speedRight": "the speed on the right motor, eg: 50", "motors.SynchedMotorPair.tank|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.tank|param|unit": "@param speedLeft the speed on the left motor, eg: 50", "motors.SynchedMotorPair.tank|param|unit": "@param speedLeft the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.tank|param|value": "the amount of movement, eg: 2", "motors.SynchedMotorPair.tank|param|value": "the amount of movement, eg: 2",
"motors.SynchedMotorPair.toString": "Returns the name(s) of the motor",
"motors.mkCmd": "Allocates a message buffer",
"motors.mkCmd|param|addSize": "required additional bytes",
"motors.mkCmd|param|cmd": "command id",
"motors.mkCmd|param|out": "ports",
"motors.readPWM": "Sends and receives a message from the motors device",
"motors.readPWM|param|buf": "message buffer",
"motors.resetAllMotors": "Resets all motors",
"motors.stopAllMotors": "Stops all motors", "motors.stopAllMotors": "Stops all motors",
"motors.writePWM": "Sends a command to the motors device",
"motors.writePWM|param|buf": "the command buffer",
"output.createBuffer": "Create a new zero-initialized buffer.", "output.createBuffer": "Create a new zero-initialized buffer.",
"output.createBuffer|param|size": "number of bytes in the buffer", "output.createBuffer|param|size": "number of bytes in the buffer",
"screen.clear": "Clear screen and reset font to normal.", "screen.clear": "Clear screen and reset font to normal.",

View File

@ -13,8 +13,8 @@
"LightsPattern.RedPulse|block": "Pulsing Red", "LightsPattern.RedPulse|block": "Pulsing Red",
"LightsPattern.Red|block": "Red", "LightsPattern.Red|block": "Red",
"MoveUnit.Degrees|block": "degrees", "MoveUnit.Degrees|block": "degrees",
"MoveUnit.MilliSeconds|block": "milliseconds",
"MoveUnit.Rotations|block": "rotations", "MoveUnit.Rotations|block": "rotations",
"MoveUnit.Seconds|block": "seconds",
"Output.AB|block": "A+B", "Output.AB|block": "A+B",
"Output.AD|block": "A+D", "Output.AD|block": "A+D",
"Output.ALL|block": "All", "Output.ALL|block": "All",
@ -24,34 +24,38 @@
"Output.CD|block": "C+D", "Output.CD|block": "C+D",
"Output.C|block": "C", "Output.C|block": "C",
"Output.D|block": "D", "Output.D|block": "D",
"brick.Button.isPressed|block": "`icons.brickButtons` %button|is pressed", "brick.Button.isPressed|block": "%button|is pressed",
"brick.Button.onEvent|block": "on `icons.brickButtons` %button|%event", "brick.Button.onEvent|block": "on %button|%event",
"brick.Button.pauseUntil|block": "pause until `icons.brickButtons` %button|%event", "brick.Button.pauseUntil|block": "pause until %button|%event",
"brick.Button.wasPressed|block": "`icons.brickButtons` %button|was pressed", "brick.Button.wasPressed|block": "%button|was pressed",
"brick._imagePicker|block": "%image",
"brick.buttonDown|block": "down", "brick.buttonDown|block": "down",
"brick.buttonEnter|block": "enter", "brick.buttonEnter|block": "enter",
"brick.buttonLeft|block": "left", "brick.buttonLeft|block": "left",
"brick.buttonRight|block": "right", "brick.buttonRight|block": "right",
"brick.buttonUp|block": "up", "brick.buttonUp|block": "up",
"brick.clearScreen|block": "`icons.brickDisplay` clear screen", "brick.clearScreen|block": "clear screen",
"brick.lightPattern|block": "%pattern", "brick.lightPattern|block": "%pattern",
"brick.print|block": "`icons.brickDisplay` print %text| at x: %x| y: %y", "brick.printLine|block": "print %text| at line %line",
"brick.setPixel|block": "`icons.brickDisplay` set pixel %on| at x: %x| y: %y", "brick.printPorts|block": "print ports",
"brick.setStatusLight|block": "set `icons.brickButtons` to %pattern=led_pattern", "brick.setLight|block": "set light to %pattern=led_pattern",
"brick.showImage|block": "`icons.brickDisplay` show image %image=screen_image_picker", "brick.showImage|block": "show image %image=screen_image_picker",
"brick|block": "brick", "brick|block": "brick",
"console.logValue|block": "console|log value %name|= %value",
"console.log|block": "console|log %text",
"console.sendToScreen|block": "send console to screen",
"console|block": "console",
"control.raiseEvent|block": "raise event|from %src|with value %value", "control.raiseEvent|block": "raise event|from %src|with value %value",
"control|block": "control", "control|block": "control",
"motors.Motor.move|block": "move `icons.motorLarge` %motor|for %value|%unit|at %speed|%", "motors.Motor.angle|block": "%motor|angle",
"motors.Motor.setBrake|block": "set `icons.motorLarge` %motor|brake %brake", "motors.Motor.speed|block": "%motor|speed",
"motors.Motor.setReversed|block": "set `icons.motorLarge` %motor|reversed %reversed", "motors.MotorBase.move|block": "move %motor|for %value|%unit|at %speed|%",
"motors.Motor.setSpeed|block": "set speed of `icons.motorLarge` %motor|to %speed|%", "motors.MotorBase.pauseUntilReady|block": "%motor|pause until ready",
"motors.SingleMotor.count|block": "`icons.motorLarge` %motor|count", "motors.MotorBase.setBrake|block": "set %motor|brake %brake",
"motors.SingleMotor.speed|block": "`icons.motorLarge` %motor|speed", "motors.MotorBase.setReversed|block": "set %motor|reversed %reversed",
"motors.SingleMotor.tachoCount|block": "`icons.motorLarge` %motor|tacho count", "motors.MotorBase.setSpeed|block": "set speed of %motor|to %speed|%",
"motors.SynchedMotorPair.steer|block": "steer %chassis|%steering|%|at speed %speed|%|by %value|%unit", "motors.SynchedMotorPair.drive|block": "drive %chassis|at %speed|cm/s|turning %rotationSpeed|deg/s|for %value|%unit",
"motors.SynchedMotorPair.tank|block": "tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit", "motors.SynchedMotorPair.steer|block": "steer %chassis turn by|%turnRatio|at speed %speed|%|for %value|%unit",
"motors.SynchedMotorPair.tank|block": "tank %chassis|left %speedLeft|%|right %speedRight|%|for %value|%unit",
"motors.largeAB|block": "large A+B", "motors.largeAB|block": "large A+B",
"motors.largeAD|block": "large A+D", "motors.largeAD|block": "large A+D",
"motors.largeA|block": "large A", "motors.largeA|block": "large A",
@ -71,6 +75,7 @@
"sensors|block": "sensors", "sensors|block": "sensors",
"serial|block": "serial", "serial|block": "serial",
"{id:category}Brick": "Brick", "{id:category}Brick": "Brick",
"{id:category}Console": "Console",
"{id:category}Control": "Control", "{id:category}Control": "Control",
"{id:category}Image": "Image", "{id:category}Image": "Image",
"{id:category}Images": "Images", "{id:category}Images": "Images",

View File

@ -66,6 +66,7 @@ namespace brick {
//% hidden //% hidden
_update(curr: boolean) { _update(curr: boolean) {
if (this == null) return
if (this._isPressed == curr) return if (this._isPressed == curr) return
this._isPressed = curr this._isPressed = curr
if (curr) { if (curr) {
@ -85,7 +86,7 @@ namespace brick {
* @param button the button to query the request * @param button the button to query the request
*/ */
//% help=input/button/is-pressed //% help=input/button/is-pressed
//% block="`icons.brickButtons` %button|is pressed" //% block="%button|is pressed"
//% blockId=buttonIsPressed //% blockId=buttonIsPressed
//% parts="brick" //% parts="brick"
//% blockNamespace=brick //% blockNamespace=brick
@ -100,11 +101,11 @@ namespace brick {
* @param button the button to query the request * @param button the button to query the request
*/ */
//% help=input/button/was-pressed //% help=input/button/was-pressed
//% block="`icons.brickButtons` %button|was pressed" //% block="%button|was pressed"
//% blockId=buttonWasPressed //% blockId=buttonWasPressed
//% parts="brick" //% parts="brick"
//% blockNamespace=brick //% blockNamespace=brick
//% weight=80 blockGap=8 //% weight=80
//% group="Buttons" //% group="Buttons"
wasPressed() { wasPressed() {
const r = this._wasPressed const r = this._wasPressed
@ -119,7 +120,7 @@ namespace brick {
* @param body code to run when the event is raised * @param body code to run when the event is raised
*/ */
//% help=input/button/on-event //% help=input/button/on-event
//% blockId=buttonEvent block="on `icons.brickButtons` %button|%event" //% blockId=buttonEvent block="on %button|%event"
//% parts="brick" //% parts="brick"
//% blockNamespace=brick //% blockNamespace=brick
//% weight=99 blockGap=8 //% weight=99 blockGap=8
@ -133,7 +134,7 @@ namespace brick {
* @param ev the event to wait for * @param ev the event to wait for
*/ */
//% help=input/button/pause-until //% help=input/button/pause-until
//% blockId=buttonWaitUntil block="pause until `icons.brickButtons` %button|%event" //% blockId=buttonWaitUntil block="pause until %button|%event"
//% parts="brick" //% parts="brick"
//% blockNamespace=brick //% blockNamespace=brick
//% weight=98 blockGap=8 //% weight=98 blockGap=8
@ -248,9 +249,9 @@ namespace brick {
* Set lights. * Set lights.
* @param pattern the lights pattern to use. * @param pattern the lights pattern to use.
*/ */
//% blockId=setLights block="set `icons.brickButtons` to %pattern=led_pattern" //% blockId=setLights block="set light to %pattern=led_pattern"
//% weight=100 group="Light" //% weight=100 group="Buttons"
export function setStatusLight(pattern: number): void { export function setLight(pattern: number): void {
if (currPattern === pattern) if (currPattern === pattern)
return return
currPattern = pattern currPattern = pattern

106
libs/core/console.ts Normal file
View File

@ -0,0 +1,106 @@
/// <reference no-default-lib="true"/>
/**
* Reading and writing data to the console output.
*/
//% weight=12 color=#002050 icon="\uf120"
//% advanced=true
namespace console {
type Listener = (text: string) => void;
const listeners: Listener[] = [
(text: string) => serial.writeLine(text)
];
/**
* Write a line of text to the console output.
* @param value to send
*/
//% weight=90
//% help=console/log blockGap=8
//% blockId=console_log block="console|log %text"
export function log(text: string): void {
for (let i = 0; i < listeners.length; ++i)
listeners[i](text);
}
/**
* Write a name:value pair as a line of text to the console output.
* @param name name of the value stream, eg: "x"
* @param value to write
*/
//% weight=88 blockGap=8
//% help=console/log-value
//% blockId=console_log_value block="console|log value %name|= %value"
export function logValue(name: string, value: number): void {
log(`${name}: ${value}`)
}
/**
* Adds a listener for the log messages
* @param listener
*/
//%
export function addListener(listener: (text: string) => void) {
if (!listener) return;
listeners.push(listener);
}
/**
* Sends the log messages to the brick screen and uses the brick up and down buttons to scroll.
*/
//% blockId=logsendtostreen block="send console to screen"
//% weight=1
export function sendToScreen(): void {
console.screen.attach();
}
}
namespace console.screen {
const maxLines = 100;
const screenLines = 8;
let lines: string[];
let scrollPosition = 0;
export function attach() {
if (!lines) {
lines = [];
console.addListener(log);
brick.buttonUp.onEvent(ButtonEvent.Click, () => scroll(-1))
brick.buttonDown.onEvent(ButtonEvent.Click, () => scroll(1))
}
}
function printLog() {
brick.clearScreen()
if (!lines) return;
for (let i = 0; i < screenLines; ++i) {
const line = lines[i + scrollPosition];
if (line)
brick.print(line, 0, 4 + i * brick.LINE_HEIGHT)
}
}
function scroll(pos: number) {
if (!pos) return;
scrollPosition += pos >> 0;
if (scrollPosition >= lines.length) scrollPosition = lines.length - 1;
if (scrollPosition < 0) scrollPosition = 0;
printLog();
}
function log(msg: string): void {
lines.push(msg);
if (lines.length + 5 > maxLines) {
lines.splice(0, maxLines - lines.length);
scrollPosition = Math.min(scrollPosition, lines.length - 1)
}
// move down scroll once it gets large than the screen
if (lines.length > screenLines
&& lines.length >= scrollPosition + screenLines) {
scrollPosition++;
}
printLog();
}
}

View File

@ -1,260 +1,260 @@
namespace images { namespace images {
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsBigSmile = screen.unpackPNG(hex``); export const expressionsBigSmile = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsHeartLarge = screen.unpackPNG(hex``); export const expressionsHeartLarge = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsHeartSmall = screen.unpackPNG(hex``); export const expressionsHeartSmall = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsMouth1open = screen.unpackPNG(hex``); export const expressionsMouth1open = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsMouth1shut = screen.unpackPNG(hex``); export const expressionsMouth1shut = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsMouth2open = screen.unpackPNG(hex``); export const expressionsMouth2open = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsMouth2shut = screen.unpackPNG(hex``); export const expressionsMouth2shut = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsSad = screen.unpackPNG(hex``); export const expressionsSad = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsSick = screen.unpackPNG(hex``); export const expressionsSick = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsSmile = screen.unpackPNG(hex``); export const expressionsSmile = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsSwearing = screen.unpackPNG(hex``); export const expressionsSwearing = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsTalking = screen.unpackPNG(hex``); export const expressionsTalking = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsWink = screen.unpackPNG(hex``); export const expressionsWink = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const expressionsZzz = screen.unpackPNG(hex``); export const expressionsZzz = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesAngry = screen.unpackPNG(hex``); export const eyesAngry = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesAwake = screen.unpackPNG(hex``); export const eyesAwake = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesBlackEye = screen.unpackPNG(hex``); export const eyesBlackEye = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesBottomLeft = screen.unpackPNG(hex``); export const eyesBottomLeft = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesBottomRight = screen.unpackPNG(hex``); export const eyesBottomRight = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesCrazy1 = screen.unpackPNG(hex``); export const eyesCrazy1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesCrazy2 = screen.unpackPNG(hex``); export const eyesCrazy2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesDisappointed = screen.unpackPNG(hex``); export const eyesDisappointed = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesDizzy = screen.unpackPNG(hex``); export const eyesDizzy = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesDown = screen.unpackPNG(hex``); export const eyesDown = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesEvil = screen.unpackPNG(hex``); export const eyesEvil = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesHurt = screen.unpackPNG(hex``); export const eyesHurt = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesKnockedOut = screen.unpackPNG(hex``); export const eyesKnockedOut = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesLove = screen.unpackPNG(hex``); export const eyesLove = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesMiddleLeft = screen.unpackPNG(hex``); export const eyesMiddleLeft = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesMiddleRight = screen.unpackPNG(hex``); export const eyesMiddleRight = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesNeutral = screen.unpackPNG(hex``); export const eyesNeutral = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesNuclear = screen.unpackPNG(hex``); export const eyesNuclear = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesPinchLeft = screen.unpackPNG(hex``); export const eyesPinchLeft = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesPinchMiddle = screen.unpackPNG(hex``); export const eyesPinchMiddle = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesPinchRight = screen.unpackPNG(hex``); export const eyesPinchRight = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesSleeping = screen.unpackPNG(hex``); export const eyesSleeping = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesTear = screen.unpackPNG(hex``); export const eyesTear = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesTiredLeft = screen.unpackPNG(hex``); export const eyesTiredLeft = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesTiredMiddle = screen.unpackPNG(hex``); export const eyesTiredMiddle = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesTiredRight = screen.unpackPNG(hex``); export const eyesTiredRight = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesToxic = screen.unpackPNG(hex``); export const eyesToxic = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesUp = screen.unpackPNG(hex``); export const eyesUp = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const eyesWinking = screen.unpackPNG(hex``); export const eyesWinking = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationAccept = screen.unpackPNG(hex``); export const informationAccept = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationBackward = screen.unpackPNG(hex``); export const informationBackward = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationDecline = screen.unpackPNG(hex``); export const informationDecline = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationForward = screen.unpackPNG(hex``); export const informationForward = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationLeft = screen.unpackPNG(hex``); export const informationLeft = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationNoGo = screen.unpackPNG(hex``); export const informationNoGo = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationQuestionMark = screen.unpackPNG(hex``); export const informationQuestionMark = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationRight = screen.unpackPNG(hex``); export const informationRight = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationStop1 = screen.unpackPNG(hex``); export const informationStop1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationStop2 = screen.unpackPNG(hex``); export const informationStop2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationThumbsDown = screen.unpackPNG(hex``); export const informationThumbsDown = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationThumbsUp = screen.unpackPNG(hex``); export const informationThumbsUp = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const informationWarning = screen.unpackPNG(hex``); export const informationWarning = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoColorSensor = screen.unpackPNG(hex``); export const legoColorSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoEv3icon = screen.unpackPNG(hex``); export const legoEv3icon = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoEv3 = screen.unpackPNG(hex``); export const legoEv3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoGyroSensor = screen.unpackPNG(hex``); export const legoGyroSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoIrBeacon = screen.unpackPNG(hex``); export const legoIrBeacon = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoIrSensor = screen.unpackPNG(hex``); export const legoIrSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoLego = screen.unpackPNG(hex``); export const legoLego = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoLargeMotor = screen.unpackPNG(hex``); export const legoLargeMotor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoMindstorms = screen.unpackPNG(hex``); export const legoMindstorms = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoMediumMotor = screen.unpackPNG(hex``); export const legoMediumMotor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoSoundSensor = screen.unpackPNG(hex``); export const legoSoundSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoTempSensor = screen.unpackPNG(hex``); export const legoTempSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoTouchSensor = screen.unpackPNG(hex``); export const legoTouchSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const legoUsSensor = screen.unpackPNG(hex``); export const legoUsSensor = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsBomb = screen.unpackPNG(hex``); export const objectsBomb = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsBoom = screen.unpackPNG(hex``); export const objectsBoom = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsFire = screen.unpackPNG(hex``); export const objectsFire = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsFlowers = screen.unpackPNG(hex``); export const objectsFlowers = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsForest = screen.unpackPNG(hex``); export const objectsForest = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsLightOff = screen.unpackPNG(hex``); export const objectsLightOff = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsLightOn = screen.unpackPNG(hex``); export const objectsLightOn = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsLightning = screen.unpackPNG(hex``); export const objectsLightning = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsNight = screen.unpackPNG(hex``); export const objectsNight = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsPirate = screen.unpackPNG(hex``); export const objectsPirate = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsSnow = screen.unpackPNG(hex``); export const objectsSnow = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const objectsTarget = screen.unpackPNG(hex``); export const objectsTarget = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressBar0 = screen.unpackPNG(hex``); export const progressBar0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressBar1 = screen.unpackPNG(hex``); export const progressBar1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressBar2 = screen.unpackPNG(hex``); export const progressBar2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressBar3 = screen.unpackPNG(hex``); export const progressBar3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressBar4 = screen.unpackPNG(hex``); export const progressBar4 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDial0 = screen.unpackPNG(hex``); export const progressDial0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDial1 = screen.unpackPNG(hex``); export const progressDial1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDial2 = screen.unpackPNG(hex``); export const progressDial2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDial3 = screen.unpackPNG(hex``); export const progressDial3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDial4 = screen.unpackPNG(hex``); export const progressDial4 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDots0 = screen.unpackPNG(hex``); export const progressDots0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDots1 = screen.unpackPNG(hex``); export const progressDots1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDots2 = screen.unpackPNG(hex``); export const progressDots2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressDots3 = screen.unpackPNG(hex``); export const progressDots3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressHourglass0 = screen.unpackPNG(hex``); export const progressHourglass0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressHourglass1 = screen.unpackPNG(hex``); export const progressHourglass1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressHourglass2 = screen.unpackPNG(hex``); export const progressHourglass2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressTimer0 = screen.unpackPNG(hex``); export const progressTimer0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressTimer1 = screen.unpackPNG(hex``); export const progressTimer1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressTimer2 = screen.unpackPNG(hex``); export const progressTimer2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressTimer3 = screen.unpackPNG(hex``); export const progressTimer3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressTimer4 = screen.unpackPNG(hex``); export const progressTimer4 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressWaterLevel0 = screen.unpackPNG(hex``); export const progressWaterLevel0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressWaterLevel1 = screen.unpackPNG(hex``); export const progressWaterLevel1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressWaterLevel2 = screen.unpackPNG(hex``); export const progressWaterLevel2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const progressWaterLevel3 = screen.unpackPNG(hex``); export const progressWaterLevel3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemAccept1 = screen.unpackPNG(hex``); export const systemAccept1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemAccept2 = screen.unpackPNG(hex``); export const systemAccept2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemAlert = screen.unpackPNG(hex``); export const systemAlert = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemBox = screen.unpackPNG(hex``); export const systemBox = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemBusy0 = screen.unpackPNG(hex``); export const systemBusy0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemBusy1 = screen.unpackPNG(hex``); export const systemBusy1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemDecline1 = screen.unpackPNG(hex``); export const systemDecline1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemDecline2 = screen.unpackPNG(hex``); export const systemDecline2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemDotEmpty = screen.unpackPNG(hex``); export const systemDotEmpty = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemDotFull = screen.unpackPNG(hex``); export const systemDotFull = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemEv3small = screen.unpackPNG(hex``); export const systemEv3small = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemPlay = screen.unpackPNG(hex``); export const systemPlay = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider0 = screen.unpackPNG(hex``); export const systemSlider0 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider1 = screen.unpackPNG(hex``); export const systemSlider1 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider2 = screen.unpackPNG(hex``); export const systemSlider2 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider3 = screen.unpackPNG(hex``); export const systemSlider3 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider4 = screen.unpackPNG(hex``); export const systemSlider4 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider5 = screen.unpackPNG(hex``); export const systemSlider5 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider6 = screen.unpackPNG(hex``); export const systemSlider6 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider7 = screen.unpackPNG(hex``); export const systemSlider7 = screen.unpackPNG(hex``);
//% fixedInstance jres blockIdentity=brick._imagePicker //% fixedInstance jres blockIdentity=brick.__imagePicker
export const systemSlider8 = screen.unpackPNG(hex``); export const systemSlider8 = screen.unpackPNG(hex``);
} }

View File

@ -74,6 +74,11 @@ namespace sensors.internal {
} }
export function getActiveSensors(): Sensor[] {
init();
return sensorInfos.filter(si => si.sensor && si.sensor.isActive()).map(si => si.sensor);
}
function readUartInfo(port: number, mode: number) { function readUartInfo(port: number, mode: number) {
let buf = output.createBuffer(UartCtlOff.Size) let buf = output.createBuffer(UartCtlOff.Size)
buf[UartCtlOff.Port] = port buf[UartCtlOff.Port] = port
@ -196,7 +201,7 @@ namespace sensors.internal {
private lowThreshold: number; private lowThreshold: number;
private highThreshold: number; private highThreshold: number;
private level: number; private level: number;
private state: ThresholdState; public state: ThresholdState;
constructor(id: number, min = 0, max = 100, lowThreshold = 20, highThreshold = 80) { constructor(id: number, min = 0, max = 100, lowThreshold = 20, highThreshold = 80) {
this.id = id; this.id = id;
@ -209,6 +214,7 @@ namespace sensors.internal {
} }
public setLevel(level: number) { public setLevel(level: number) {
if (this == null) return
this.level = this.clampValue(level); this.level = this.clampValue(level);
if (this.level >= this.highThreshold) { if (this.level >= this.highThreshold) {

View File

@ -14,7 +14,7 @@ enum Output {
//% block="C+D" //% block="C+D"
CD = Output.C | Output.D, CD = Output.C | Output.D,
//% block="A+D" //% block="A+D"
AD = Output.B | Output.C, AD = Output.A | Output.D,
//% block="All" //% block="All"
ALL = 0x0f ALL = 0x0f
} }
@ -30,8 +30,8 @@ enum MoveUnit {
Rotations, Rotations,
//% block="degrees" //% block="degrees"
Degrees, Degrees,
//% block="seconds" //% block="milliseconds"
Seconds MilliSeconds
} }
namespace motors { namespace motors {
@ -53,32 +53,56 @@ namespace motors {
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0) motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
if (!motorMM) control.fail("no motor file") if (!motorMM) control.fail("no motor file")
resetMotors() resetAllMotors()
let buf = output.createBuffer(1) const buf = output.createBuffer(1)
buf[0] = DAL.opProgramStart buf[0] = DAL.opProgramStart
writePWM(buf) writePWM(buf)
} }
function writePWM(buf: Buffer): void { /**
* Sends a command to the motors device
* @param buf the command buffer
*/
//%
export function writePWM(buf: Buffer): void {
init() init()
pwmMM.write(buf) pwmMM.write(buf)
} }
function readPWM(buf: Buffer): void { /**
* Sends and receives a message from the motors device
* @param buf message buffer
*/
//%
export function readPWM(buf: Buffer): void {
init() init()
pwmMM.read(buf); pwmMM.read(buf);
} }
function mkCmd(out: Output, cmd: number, addSize: number) { /**
* Allocates a message buffer
* @param out ports
* @param cmd command id
* @param addSize required additional bytes
*/
//%
export function mkCmd(out: Output, cmd: number, addSize: number) {
const b = output.createBuffer(2 + addSize) const b = output.createBuffer(2 + addSize)
b.setNumber(NumberFormat.UInt8LE, 0, cmd) b.setNumber(NumberFormat.UInt8LE, 0, cmd)
b.setNumber(NumberFormat.UInt8LE, 1, out) b.setNumber(NumberFormat.UInt8LE, 1, out)
return b return b
} }
function resetMotors() { function outputToName(out: Output): string {
reset(Output.ALL) let r = "";
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (out & (1 << i)) {
if (r.length > 0) r += "+";
r += "ABCD"[i];
}
}
return r;
} }
/** /**
@ -92,9 +116,18 @@ namespace motors {
writePWM(b) writePWM(b)
} }
/**
* Resets all motors
*/
//% group="Motion"
export function resetAllMotors() {
reset(Output.ALL)
}
//% fixedInstances //% fixedInstances
export class Motor extends control.Component { export class MotorBase extends control.Component {
protected _port: Output; protected _port: Output;
protected _portName: string;
protected _brake: boolean; protected _brake: boolean;
private _initialized: boolean; private _initialized: boolean;
private _init: () => void; private _init: () => void;
@ -104,6 +137,7 @@ namespace motors {
constructor(port: Output, init: () => void, setSpeed: (speed: number) => void, move: (steps: boolean, stepsOrTime: number, speed: number) => void) { constructor(port: Output, init: () => void, setSpeed: (speed: number) => void, move: (steps: boolean, stepsOrTime: number, speed: number) => void) {
super(); super();
this._port = port; this._port = port;
this._portName = outputToName(this._port);
this._brake = false; this._brake = false;
this._initialized = false; this._initialized = false;
this._init = init; this._init = init;
@ -125,7 +159,7 @@ namespace motors {
* Sets the automatic brake on or off when the motor is off * Sets the automatic brake on or off when the motor is off
* @param brake a value indicating if the motor should break when off * @param brake a value indicating if the motor should break when off
*/ */
//% blockId=outputMotorSetBrakeMode block="set `icons.motorLarge` %motor|brake %brake" //% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
//% brake.fieldEditor=toggleonoff //% brake.fieldEditor=toggleonoff
//% weight=60 blockGap=8 //% weight=60 blockGap=8
//% group="Motion" //% group="Motion"
@ -137,7 +171,7 @@ namespace motors {
/** /**
* Reverses the motor polarity * Reverses the motor polarity
*/ */
//% blockId=motorSetReversed block="set `icons.motorLarge` %motor|reversed %reversed" //% blockId=motorSetReversed block="set %motor|reversed %reversed"
//% reversed.fieldEditor=toggleonoff //% reversed.fieldEditor=toggleonoff
//% weight=59 //% weight=59
//% group="Motion" //% group="Motion"
@ -170,7 +204,7 @@ namespace motors {
* Sets the speed of the motor. * Sets the speed of the motor.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50 * @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/ */
//% blockId=motorSetSpeed block="set speed of `icons.motorLarge` %motor|to %speed|%" //% blockId=motorSetSpeed block="set speed of %motor|to %speed|%"
//% on.fieldEditor=toggleonoff //% on.fieldEditor=toggleonoff
//% weight=99 blockGap=8 //% weight=99 blockGap=8
//% speed.min=-100 speed.max=100 //% speed.min=-100 speed.max=100
@ -190,7 +224,7 @@ namespace motors {
* @param unit the meaning of the value * @param unit the meaning of the value
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50 * @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/ */
//% blockId=motorMove block="move `icons.motorLarge` %motor|for %value|%unit|at %speed|%" //% blockId=motorMove block="move %motor|for %value|%unit|at %speed|%"
//% weight=98 blockGap=8 //% weight=98 blockGap=8
//% speed.min=-100 speed.max=100 //% speed.min=-100 speed.max=100
//% group="Motion" //% group="Motion"
@ -220,10 +254,38 @@ namespace motors {
this._move(useSteps, stepsOrTime, speed); this._move(useSteps, stepsOrTime, speed);
} }
/**
* Returns a value indicating if the motor is still running a previous command.
*/
//%
isReady(): boolean {
this.init();
const buf = mkCmd(this._port, DAL.opOutputTest, 2);
readPWM(buf)
const flags = buf.getNumber(NumberFormat.UInt8LE, 2);
// TODO: FIX with ~ support
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const flag = 1 << i;
if ((this._port & flag) && (flags & flag))
return false;
}
return true;
}
/**
* Pauses the execution until the previous command finished.
* @param timeOut optional maximum pausing time in milliseconds
*/
//% blockId=motorPauseUntilRead block="%motor|pause until ready"
//% group="Motion"
pauseUntilReady(timeOut?: number) {
pauseUntil(() => this.isReady(), timeOut);
}
} }
//% fixedInstances //% fixedInstances
export class SingleMotor extends Motor { export class Motor extends MotorBase {
private _large: boolean; private _large: boolean;
constructor(port: Output, large: boolean) { constructor(port: Output, large: boolean) {
@ -248,7 +310,7 @@ namespace motors {
b.setNumber(NumberFormat.Int8LE, 2, speed) b.setNumber(NumberFormat.Int8LE, 2, speed)
writePWM(b) writePWM(b)
if (speed) { if (speed) {
writePWM(mkCmd(this._port, DAL.opOutputStart, 0)) writePWM(mkCmd(this._port, DAL.opOutputStart, 0))
} }
} }
@ -267,7 +329,7 @@ namespace motors {
* Gets motor actual speed. * Gets motor actual speed.
* @param motor the port which connects to the motor * @param motor the port which connects to the motor
*/ */
//% blockId=motorSpeed block="`icons.motorLarge` %motor|speed" //% blockId=motorSpeed block="%motor|speed"
//% weight=72 blockGap=8 //% weight=72 blockGap=8
//% group="Sensors" //% group="Sensors"
speed(): number { speed(): number {
@ -276,27 +338,15 @@ namespace motors {
} }
/** /**
* Gets motor step count. * Gets motor ration angle.
* @param motor the port which connects to the motor * @param motor the port which connects to the motor
*/ */
//% blockId=motorCount block="`icons.motorLarge` %motor|count" //% blockId=motorTachoCount block="%motor|angle"
//% weight=71 blockGap=8
//% group="Sensors"
count(): number {
this.init();
return getMotorData(this._port).count;
}
/**
* Gets motor tacho count.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="`icons.motorLarge` %motor|tacho count"
//% weight=70 //% weight=70
//% group="Sensors" //% group="Sensors"
tachoCount(): number { angle(): number {
this.init(); this.init();
return getMotorData(this._port).tachoCount; return getMotorData(this._port).count;
} }
/** /**
@ -313,37 +363,49 @@ namespace motors {
} }
} }
} }
/**
* Returns the status of the motor
*/
//%
toString(): string {
return `${this._large ? "" : "M"}${this._portName} ${this.speed()}% ${this.angle()}>`;
}
} }
//% whenUsed fixedInstance block="large A" //% whenUsed fixedInstance block="large A"
export const largeA = new SingleMotor(Output.A, true); export const largeA = new Motor(Output.A, true);
//% whenUsed fixedInstance block="large B" //% whenUsed fixedInstance block="large B"
export const largeB = new SingleMotor(Output.B, true); export const largeB = new Motor(Output.B, true);
//% whenUsed fixedInstance block="large C" //% whenUsed fixedInstance block="large C"
export const largeC = new SingleMotor(Output.C, true); export const largeC = new Motor(Output.C, true);
//% whenUsed fixedInstance block="large D" //% whenUsed fixedInstance block="large D"
export const largeD = new SingleMotor(Output.D, true); export const largeD = new Motor(Output.D, true);
//% whenUsed fixedInstance block="medium A" //% whenUsed fixedInstance block="medium A"
export const mediumA = new SingleMotor(Output.A, false); export const mediumA = new Motor(Output.A, false);
//% whenUsed fixedInstance block="medium B" //% whenUsed fixedInstance block="medium B"
export const mediumB = new SingleMotor(Output.B, false); export const mediumB = new Motor(Output.B, false);
//% whenUsed fixedInstance block="medium C" //% whenUsed fixedInstance block="medium C"
export const mediumC = new SingleMotor(Output.C, false); export const mediumC = new Motor(Output.C, false);
//% whenUsed fixedInstance block="medium D" //% whenUsed fixedInstance block="medium D"
export const mediumD = new SingleMotor(Output.D, false); export const mediumD = new Motor(Output.D, false);
//% fixedInstances //% fixedInstances
export class SynchedMotorPair extends Motor { export class SynchedMotorPair extends MotorBase {
private wheelRadius: number;
private baseLength: number;
constructor(ports: Output) { constructor(ports: Output) {
super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed)); super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
this.wheelRadius = 3;
this.baseLength = 12;
this.markUsed(); this.markUsed();
} }
@ -380,18 +442,82 @@ namespace motors {
} }
/** /**
* Turns the motor and the follower motor by a number of rotations * The Move Tank block can make a robot drive forward, backward, turn, or stop.
* @param value the move quantity, eg: 2 * Use the Move Tank block for robot vehicles that have two Large Motors,
* @param unit the meaning of the value * with one motor driving the left side of the vehicle and the other the right side.
* @param steering the ratio of power sent to the follower motor, from ``-100`` to ``100`` * You can make the two motors go at different speeds or in different directions
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50 * to make your robot turn.
* @param value the amount of movement, eg: 2
* @param unit
* @param speedLeft the speed on the left motor, eg: 50
* @param speedRight the speed on the right motor, eg: 50
*/ */
//% blockId=motorPairTurn block="steer %chassis|%steering|%|at speed %speed|%|by %value|%unit" //% blockId=motorPairTank block="tank %chassis|left %speedLeft|%|right %speedRight|%|for %value|%unit"
//% weight=9 blockGap=8 //% weight=9 blockGap=8
//% steering.min=-100 steering=100 //% speedLeft.min=-100 speedLeft=100
//% speedRight.min=-100 speedRight=100
//% inlineInputMode=inline //% inlineInputMode=inline
//% group="Chassis" //% group="Chassis"
steer(steering: number, speed: number, value: number, unit: MoveUnit) { tank(speedLeft: number, speedRight: number, value: number, unit: MoveUnit) {
this.init();
speedLeft = Math.clamp(-100, 100, speedLeft >> 0);
speedRight = Math.clamp(-100, 100, speedRight >> 0);
const speed = Math.abs(speedLeft) > Math.abs(speedRight) ? speedLeft : speedRight;
const turnRatio = speedLeft == speed
? (100 - speedRight / speedLeft * 100)
: (speedLeft / speedRight * 100 - 100);
this.steer(turnRatio, speed, value, unit);
}
/**
* Makes a differential drive robot move with a given speed (%) and rotation rate (deg/s)
* using a unicycle model.
* @param speed speed of the center point between motors, eg: 10
* @param rotationSpeed rotation of the robot around the center point, eg: 30
* @param value the amount of movement, eg: 2
* @param unit
*/
//% blockId=motorDrive block="drive %chassis|at %speed|cm/s|turning %rotationSpeed|deg/s|for %value|%unit"
//% inlineInputMode=inline
//% group="Chassis"
//% weight=8 blockGap=8
drive(speed: number, rotationSpeed: number, value: number, unit: MoveUnit) {
this.init();
// speed is expressed in %
const R = this.wheelRadius; // cm
const L = this.baseLength; // cm
const PI = 3.14;
const maxw = 170 / 60 * 2 * PI; // rad / s
const maxv = maxw * R; // cm / s
// speed is cm / s
const v = speed; // cm / s
const w = rotationSpeed / 360 * 2 * PI; // rad / s
const vr = (2 * v + w * L) / (2 * R); // rad / s
const vl = (2 * v - w * L) / (2 * R); // rad / s
const sr = vr / maxw * 100; // %
const sl = vl / maxw * 100; // %
this.tank(sr, sl, value, unit)
}
/**
* Turns the motor and the follower motor by a number of rotations
* @param turnRatio the ratio of power sent to the follower motor, from ``-200`` to ``200``, eg: 0
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
*/
//% blockId=motorPairTurn block="steer %chassis turn by|%turnRatio|at speed %speed|%|for %value|%unit"
//% weight=6 blockGap=8
//% turnRatio.min=-200 turnRatio=200
//% inlineInputMode=inline
//% group="Chassis"
steer(turnRatio: number, speed: number, value: number, unit: MoveUnit) {
this.init(); this.init();
speed = Math.clamp(-100, 100, speed >> 0); speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) { if (!speed) {
@ -399,7 +525,7 @@ namespace motors {
return; return;
} }
const turnRatio = Math.clamp(-200, 200, steering + 100 >> 0); turnRatio = Math.clamp(-200, 200, turnRatio >> 0);
let useSteps: boolean; let useSteps: boolean;
let stepsOrTime: number; let stepsOrTime: number;
switch (unit) { switch (unit) {
@ -412,7 +538,7 @@ namespace motors {
useSteps = true; useSteps = true;
break; break;
default: default:
stepsOrTime = value; stepsOrTime = value >> 0;
useSteps = false; useSteps = false;
break; break;
} }
@ -424,30 +550,33 @@ namespace motors {
stepsOrTime: stepsOrTime, stepsOrTime: stepsOrTime,
useBrake: this._brake useBrake: this._brake
}); });
}
/**
* Sets the wheels radius and base length of a directional drive robot
* @param wheelRadius
* @param baseLength
*/
//% group="Chassis"
setDimensions(wheelRadius: number, baseLength: number): void {
this.wheelRadius = wheelRadius;
this.baseLength = baseLength;
} }
/** /**
* The Move Tank block can make a robot drive forward, backward, turn, or stop. * Returns the name(s) of the motor
* Use the Move Tank block for robot vehicles that have two Large Motors,
* with one motor driving the left side of the vehicle and the other the right side.
* You can make the two motors go at different speeds or in different directions
* to make your robot turn.
* @param value the amount of movement, eg: 2
* @param unit
* @param speedLeft the speed on the left motor, eg: 50
* @param speedRight the speed on the right motor, eg: 50
*/ */
//% blockId=motorPairTank block="tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit" //%
//% weight=9 blockGap=8 toString(): string {
//% speedLeft.min=-100 speedLeft=100 this.init();
//% speedRight.min=-100 speedRight=100
//% inlineInputMode=inline let r = outputToName(this._port);
//% group="Chassis" for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
tank(speedLeft: number, speedRight: number, value: number, unit: MoveUnit) { if (this._port & (1 << i)) {
speedLeft = Math.clamp(speedLeft >> 0, -100, 100); r += ` ${getMotorData(1 << i).actualSpeed}%`
speedRight = Math.clamp(speedRight >> 0, -100, 100); }
const steering = (speedRight * 100 / speedLeft) >> 0; }
this.steer(speedLeft, steering, value, unit); return r;
} }
} }
@ -464,8 +593,8 @@ namespace motors {
export const largeCD = new SynchedMotorPair(Output.CD); export const largeCD = new SynchedMotorPair(Output.CD);
function reset(out: Output) { function reset(out: Output) {
let b = mkCmd(out, DAL.opOutputReset, 0) writePWM(mkCmd(out, DAL.opOutputReset, 0))
writePWM(b) writePWM(mkCmd(out, DAL.opOutputClearCount, 0))
} }
function outOffset(out: Output) { function outOffset(out: Output) {
@ -476,7 +605,7 @@ namespace motors {
return 0 return 0
} }
interface MotorData { export interface MotorData {
actualSpeed: number; // -100..+100 actualSpeed: number; // -100..+100
tachoCount: number; tachoCount: number;
count: number; count: number;
@ -485,7 +614,7 @@ namespace motors {
// only a single output at a time // only a single output at a time
function getMotorData(out: Output): MotorData { function getMotorData(out: Output): MotorData {
init() init()
let buf = motorMM.slice(outOffset(out), MotorDataOff.Size) const buf = motorMM.slice(outOffset(out), MotorDataOff.Size)
return { return {
actualSpeed: buf.getNumber(NumberFormat.Int8LE, MotorDataOff.Speed), actualSpeed: buf.getNumber(NumberFormat.Int8LE, MotorDataOff.Speed),
tachoCount: buf.getNumber(NumberFormat.Int32LE, MotorDataOff.TachoCounts), tachoCount: buf.getNumber(NumberFormat.Int32LE, MotorDataOff.TachoCounts),
@ -493,6 +622,11 @@ namespace motors {
} }
} }
export function getAllMotorData(): MotorData[] {
init();
return [Output.A, Output.B, Output.C, Output.D].map(out => getMotorData(out));
}
interface SyncOptions { interface SyncOptions {
useSteps?: boolean; useSteps?: boolean;
speed: number; speed: number;

View File

@ -10,6 +10,7 @@
"linux.cpp", "linux.cpp",
"mmap.cpp", "mmap.cpp",
"control.cpp", "control.cpp",
"console.ts",
"serialnumber.cpp", "serialnumber.cpp",
"buttons.ts", "buttons.ts",
"png.cpp", "png.cpp",

View File

@ -1,4 +1,6 @@
namespace brick { namespace brick {
export const LINE_HEIGHT = 12;
//% shim=screen::_setPixel //% shim=screen::_setPixel
function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { } function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { }
@ -78,15 +80,6 @@ namespace brick {
} }
} }
/**
* Sets a pixel on or off
* @param on a value indicating if the pixel should be on or off
* @param x the starting position's x coordinate, eg: 0
* @param y the starting position's x coordinate, eg: 0
*/
//% blockId=screen_setpixel block="`icons.brickDisplay` set pixel %on| at x: %x| y: %y"
//% weight=98 group="Screen"
//% x.min=0 x.max=178 y.min=0 y.max=128 on.fieldEditor=toggleonoff
export function setPixel(on: boolean, x: number, y: number) { export function setPixel(on: boolean, x: number, y: number) {
x |= 0 x |= 0
y |= 0 y |= 0
@ -95,14 +88,20 @@ namespace brick {
} }
/** /**
* Show text on the screen. * Show text on the screen at a specific line.
* @param text the text to print on the screen, eg: "Hello world" * @param text the text to print on the screen, eg: "Hello world"
* @param x the starting position's x coordinate, eg: 0 * @param line the line number to print the text at, eg: 0
* @param y the starting position's x coordinate, eg: 0
*/ */
//% blockId=screen_print block="`icons.brickDisplay` print %text| at x: %x| y: %y" //% blockId=screen_print block="print %text| at line %line"
//% weight=99 group="Screen" inlineInputMode="inline" blockGap=8 //% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
//% x.min=0 x.max=178 y.min=0 y.max=128 //% line.min=0 line.max=9
export function printLine(text: string, line: number) {
const NUM_LINES = 9;
const offset = 5;
const y = offset + (Math.clamp(0, NUM_LINES, line) / (NUM_LINES + 2)) * DAL.LCD_HEIGHT;
brick.print(text, offset, y);
}
export function print(text: string, x: number, y: number, mode = Draw.Normal) { export function print(text: string, x: number, y: number, mode = Draw.Normal) {
x |= 0 x |= 0
y |= 0 y |= 0
@ -137,8 +136,8 @@ namespace brick {
* Shows an image on screen * Shows an image on screen
* @param image image to draw * @param image image to draw
*/ */
//% blockId=screen_show_image block="`icons.brickDisplay` show image %image=screen_image_picker" //% blockId=screen_show_image block="show image %image=screen_image_picker"
//% weight=95 group="Screen" blockGap=8 //% weight=100 group="Screen" blockGap=8
export function showImage(image: Image, delay: number = 400) { export function showImage(image: Image, delay: number = 400) {
if (!image) return; if (!image) return;
image.draw(0, 0, Draw.Normal); image.draw(0, 0, Draw.Normal);
@ -156,15 +155,15 @@ namespace brick {
//% image.fieldOptions.columns=6 //% image.fieldOptions.columns=6
//% image.fieldOptions.hasSearchBar=true //% image.fieldOptions.hasSearchBar=true
//% group="Screen" weight=0 blockHidden=1 //% group="Screen" weight=0 blockHidden=1
export function _imagePicker(image: Image): Image { export function __imagePicker(image: Image): Image {
return image; return image;
} }
/** /**
* Clears the screen * Clears the screen
*/ */
//% blockId=screen_clear_screen block="`icons.brickDisplay` clear screen" //% blockId=screen_clear_screen block="clear screen"
//% weight=94 group="Screen" blockGap=8 //% weight=90 group="Screen"
export function clearScreen() { export function clearScreen() {
screen.clear(); screen.clear();
} }
@ -208,5 +207,36 @@ namespace brick {
setLineCore(x, x1, y, mode); setLineCore(x, x1, y, mode);
} }
/**
* Prints the port states on the screen
*/
//% blockId=brickPrintPorts block="print ports"
//% weight=1 group="Screen"
export function printPorts() {
clearScreen();
// motors
const datas = motors.getAllMotorData();
for(let i = 0; i < datas.length; ++i) {
const data = datas[i];
if (!data.actualSpeed && !data.count) continue;
const x = i * 52;
print(`${data.actualSpeed}%`, x, brick.LINE_HEIGHT)
print(`${data.count}>`, x, 2 * brick.LINE_HEIGHT)
console.logValue(`speed.` + "ABCD"[i], data.actualSpeed);
console.logValue(`angle.` + "ABCD"[i], data.count);
}
// sensors
const sis = sensors.internal.getActiveSensors();
for(let i =0; i < sis.length; ++i) {
const si = sis[i];
const x = (si.port() - 1) * 52;
const v = si._query();
print(`${v}`, x, 9 * brick.LINE_HEIGHT)
console.logValue(`sensor.` + si.port(), v);
}
}
} }

View File

@ -2,7 +2,7 @@ screen.clear()
brick.print("PXT!", 10, 30, Draw.Quad) brick.print("PXT!", 10, 30, Draw.Quad)
brick.drawRect(40, 40, 20, 10, Draw.Fill) brick.drawRect(40, 40, 20, 10, Draw.Fill)
brick.setStatusLight(LightsPattern.Orange) brick.setLight(LightsPattern.Orange)
brick.heart.doubled().draw(100, 50, Draw.Double | Draw.Transparent) brick.heart.doubled().draw(100, 50, Draw.Double | Draw.Transparent)
@ -12,7 +12,7 @@ brick.buttonEnter.onEvent(ButtonEvent.Click, () => {
brick.buttonLeft.onEvent(ButtonEvent.Click, () => { brick.buttonLeft.onEvent(ButtonEvent.Click, () => {
brick.drawRect(10, 70, 20, 10, Draw.Fill) brick.drawRect(10, 70, 20, 10, Draw.Fill)
brick.setStatusLight(LightsPattern.Red) brick.setLight(LightsPattern.Red)
brick.setFont(brick.microbitFont()) brick.setFont(brick.microbitFont())
}) })

View File

@ -1,30 +1,30 @@
//% color="#68C3E2" weight=100 //% color="#68C3E2" weight=100 icon="\uf106"
//% groups='["Light", "Buttons", "Screen"]' //% groups='["Buttons", "Screen"]'
//% labelLineWidth=0 //% labelLineWidth=0
namespace brick { namespace brick {
} }
//% color="#C8509B" weight=95 icon="\uf192" //% color="#C8509B" weight=95 icon="\uf10f"
//% labelLineWidth=0 //% labelLineWidth=0
//% groups='["Ultrasonic Sensor", "Touch Sensor", "Color Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Gyro Sensor"]' //% groups='["Ultrasonic Sensor", "Touch Sensor", "Color Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Gyro Sensor"]'
//% groupIcons='["\uf101","\uf103","\uf102","","","\uf104"]' //% groupIcons='["\uf101","\uf103","\uf102","","","\uf104"]'
namespace sensors { namespace sensors {
} }
//% color="#A5CA18" weight=90 icon="\uf185" //% color="#A5CA18" weight=90 icon="\uf10d"
//% groups='["Motion", "Sensors", "Chassis"]' //% groups='["Motion", "Sensors", "Chassis"]'
//% labelLineWidth=0 //% labelLineWidth=0
namespace motors { namespace motors {
} }
//% color="#D67923" weight=80 //% color="#D67923" weight=80 icon="\uf10e"
namespace music { namespace music {
} }
//% color="#48150C" //% color="#5F3109" icon="\uf107"
namespace control { namespace control {
} }

View File

@ -1,10 +1,10 @@
{ {
"sensors.GyroSensor.angle|block": "`icons.gyroSensor` %sensor|angle", "sensors.GyroSensor.angle|block": "%sensor|angle",
"sensors.GyroSensor.rate|block": "`icons.gyroSensor` %sensor|rotation rate", "sensors.GyroSensor.rate|block": "%sensor|rotation rate",
"sensors.gyro1|block": "1", "sensors.gyro1|block": "gyro 1",
"sensors.gyro2|block": "2", "sensors.gyro2|block": "gyro 2",
"sensors.gyro3|block": "3", "sensors.gyro3|block": "gyro 3",
"sensors.gyro4|block": "4", "sensors.gyro4|block": "gyro 4",
"{id:category}Sensors": "Sensors", "{id:category}Sensors": "Sensors",
"{id:group}Gyro Sensor": "Gyro Sensor" "{id:group}Gyro Sensor": "Gyro Sensor"
} }

View File

@ -24,11 +24,9 @@ namespace sensors {
* @param sensor the gyroscope to query the request * @param sensor the gyroscope to query the request
*/ */
//% help=input/gyro/angle //% help=input/gyro/angle
//% block="`icons.gyroSensor` %sensor|angle" //% block="%sensor|angle"
//% blockId=gyroGetAngle //% blockId=gyroGetAngle
//% parts="gyroscope" //% parts="gyroscope"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=65 blockGap=8 //% weight=65 blockGap=8
//% group="Gyro Sensor" //% group="Gyro Sensor"
@ -42,11 +40,9 @@ namespace sensors {
* @param sensor the gyroscope to query the request * @param sensor the gyroscope to query the request
*/ */
//% help=input/gyro/rate //% help=input/gyro/rate
//% block="`icons.gyroSensor` %sensor|rotation rate" //% block="%sensor|rotation rate"
//% blockId=gyroGetRate //% blockId=gyroGetRate
//% parts="gyroscope" //% parts="gyroscope"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=65 blockGap=8 //% weight=65 blockGap=8
//% group="Gyro Sensor" //% group="Gyro Sensor"
@ -56,15 +52,15 @@ namespace sensors {
} }
} }
//% fixedInstance whenUsed block="1" jres=icons.port1 //% fixedInstance whenUsed block="gyro 1" jres=icons.port1
export const gyro1: GyroSensor = new GyroSensor(1) export const gyro1: GyroSensor = new GyroSensor(1)
//% fixedInstance whenUsed block="2" weight=95 jres=icons.port2 //% fixedInstance whenUsed block="gyro 2" weight=95 jres=icons.port2
export const gyro2: GyroSensor = new GyroSensor(2) export const gyro2: GyroSensor = new GyroSensor(2)
//% fixedInstance whenUsed block="3" jres=icons.port3 //% fixedInstance whenUsed block="gyro 3" jres=icons.port3
export const gyro3: GyroSensor = new GyroSensor(3) export const gyro3: GyroSensor = new GyroSensor(3)
//% fixedInstance whenUsed block="4" jres=icons.port4 //% fixedInstance whenUsed block="gyro 4" jres=icons.port4
export const gyro4: GyroSensor = new GyroSensor(4) export const gyro4: GyroSensor = new GyroSensor(4)
} }

View File

@ -1,17 +1,17 @@
{ {
"InfraredSensorEvent.ObjectDetected|block": "object detected", "InfraredSensorEvent.ObjectDetected|block": "object detected",
"InfraredSensorEvent.ObjectNear|block": "object near", "InfraredSensorEvent.ObjectNear|block": "object near",
"sensors.InfraredSensor.onEvent|block": "on `icons.infraredSensor` %sensor|%event", "sensors.InfraredSensor.onEvent|block": "on %sensor|%event",
"sensors.InfraredSensor.pauseUntil|block": "pause until `icons.infraredSensor` %sensor| %event", "sensors.InfraredSensor.pauseUntil|block": "pause until %sensor| %event",
"sensors.InfraredSensor.proximity|block": "`icons.infraredSensor` %sensor|proximity", "sensors.InfraredSensor.proximity|block": "%sensor|proximity",
"sensors.InfraredSensor.remoteCommand|block": "`icons.infraredSensor` %sensor|remote command", "sensors.InfraredSensor.remoteCommand|block": "%sensor|remote command",
"sensors.RemoteInfraredBeaconButton.isPressed|block": "`icons.infraredSensor` %button|is pressed", "sensors.RemoteInfraredBeaconButton.isPressed|block": "%button|is pressed",
"sensors.RemoteInfraredBeaconButton.onEvent|block": "on `icons.infraredSensor` %button|%event", "sensors.RemoteInfraredBeaconButton.onEvent|block": "on %button|%event",
"sensors.RemoteInfraredBeaconButton.wasPressed|block": "`icons.infraredSensor` %button|was pressed", "sensors.RemoteInfraredBeaconButton.wasPressed|block": "%button|was pressed",
"sensors.infraredSensor1|block": "1", "sensors.infraredSensor1|block": "infrared 1",
"sensors.infraredSensor2|block": "2", "sensors.infraredSensor2|block": "infrared 2",
"sensors.infraredSensor3|block": "3", "sensors.infraredSensor3|block": "infrared 3",
"sensors.infraredSensor4|block": "4", "sensors.infraredSensor4|block": "infrared 4",
"sensors.remoteButtonBottomLeft|block": "bottom-left", "sensors.remoteButtonBottomLeft|block": "bottom-left",
"sensors.remoteButtonBottomRight|block": "bottom-right", "sensors.remoteButtonBottomRight|block": "bottom-right",
"sensors.remoteButtonCenter|block": "center", "sensors.remoteButtonCenter|block": "center",

View File

@ -93,7 +93,7 @@ namespace sensors {
* @param button the remote button to query the request * @param button the remote button to query the request
*/ */
//% help=input/remote-infrared-beacon/is-pressed //% help=input/remote-infrared-beacon/is-pressed
//% block="`icons.infraredSensor` %button|is pressed" //% block="%button|is pressed"
//% blockId=remoteButtonIsPressed //% blockId=remoteButtonIsPressed
//% parts="remote" //% parts="remote"
//% blockNamespace=sensors //% blockNamespace=sensors
@ -108,11 +108,11 @@ namespace sensors {
* @param button the remote button to query the request * @param button the remote button to query the request
*/ */
//% help=input/remote-infrared-beacon/was-pressed //% help=input/remote-infrared-beacon/was-pressed
//% block="`icons.infraredSensor` %button|was pressed" //% block="%button|was pressed"
//% blockId=remotebuttonWasPressed //% blockId=remotebuttonWasPressed
//% parts="remote" //% parts="remote"
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=80 blockGap=8 //% weight=80
//% group="Remote Infrared Beacon" //% group="Remote Infrared Beacon"
wasPressed() { wasPressed() {
return this.button.wasPressed(); return this.button.wasPressed();
@ -125,7 +125,7 @@ namespace sensors {
* @param body code to run when the event is raised * @param body code to run when the event is raised
*/ */
//% help=input/remote-infrared-beacon/on-event //% help=input/remote-infrared-beacon/on-event
//% blockId=remotebuttonEvent block="on `icons.infraredSensor` %button|%event" //% blockId=remotebuttonEvent block="on %button|%event"
//% parts="remote" //% parts="remote"
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=99 blockGap=8 //% weight=99 blockGap=8
@ -189,11 +189,9 @@ namespace sensors {
* @param handler the code to run when detected * @param handler the code to run when detected
*/ */
//% help=input/infrared/on //% help=input/infrared/on
//% block="on `icons.infraredSensor` %sensor|%event" //% block="on %sensor|%event"
//% blockId=infraredOn //% blockId=infraredOn
//% parts="infraredsensor" //% parts="infraredsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=100 blockGap=8 //% weight=100 blockGap=8
//% group="Infrared Sensor" //% group="Infrared Sensor"
@ -205,11 +203,9 @@ namespace sensors {
* Waits for the event to occur * Waits for the event to occur
*/ */
//% help=input/ultrasonic/wait //% help=input/ultrasonic/wait
//% block="pause until `icons.infraredSensor` %sensor| %event" //% block="pause until %sensor| %event"
//% blockId=infraredwait //% blockId=infraredwait
//% parts="infraredsensor" //% parts="infraredsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=99 blockGap=8 //% weight=99 blockGap=8
//% group="Infrared Sensor" //% group="Infrared Sensor"
@ -222,11 +218,9 @@ namespace sensors {
* @param sensor the infrared sensor * @param sensor the infrared sensor
*/ */
//% help=input/infrared/proximity //% help=input/infrared/proximity
//% block="`icons.infraredSensor` %sensor|proximity" //% block="%sensor|proximity"
//% blockId=infraredGetProximity //% blockId=infraredGetProximity
//% parts="infrared" //% parts="infrared"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=65 blockGap=8 //% weight=65 blockGap=8
//% group="Infrared Sensor" //% group="Infrared Sensor"
@ -240,13 +234,11 @@ namespace sensors {
* @param sensor the infrared sensor * @param sensor the infrared sensor
*/ */
//% help=input/infrared/remote-command //% help=input/infrared/remote-command
//% block="`icons.infraredSensor` %sensor|remote command" //% block="%sensor|remote command"
//% blockId=infraredGetRemoteCommand //% blockId=infraredGetRemoteCommand
//% parts="infrared" //% parts="infrared"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=65 blockGap=8 //% weight=65
//% group="Infrared Sensor" //% group="Infrared Sensor"
remoteCommand(): number { remoteCommand(): number {
this._setMode(IrSensorMode.RemoteControl) this._setMode(IrSensorMode.RemoteControl)
@ -260,16 +252,16 @@ namespace sensors {
} }
} }
//% fixedInstance whenUsed block="1" jres=icons.port1 //% fixedInstance whenUsed block="infrared 1" jres=icons.port1
export const infraredSensor1: InfraredSensor = new InfraredSensor(1) export const infraredSensor1: InfraredSensor = new InfraredSensor(1)
//% fixedInstance whenUsed block="2" jres=icons.port2 //% fixedInstance whenUsed block="infrared 2" jres=icons.port2
export const infraredSensor2: InfraredSensor = new InfraredSensor(2) export const infraredSensor2: InfraredSensor = new InfraredSensor(2)
//% fixedInstance whenUsed block="3" jres=icons.port3 //% fixedInstance whenUsed block="infrared 3" jres=icons.port3
export const infraredSensor3: InfraredSensor = new InfraredSensor(3) export const infraredSensor3: InfraredSensor = new InfraredSensor(3)
//% fixedInstance whenUsed block="4" jres=icons.port4 //% fixedInstance whenUsed block="infrared 4" jres=icons.port4
export const infraredSensor4: InfraredSensor = new InfraredSensor(4) export const infraredSensor4: InfraredSensor = new InfraredSensor(4)

View File

@ -2,8 +2,6 @@
"Sound.buffer": "Returns the underlaying Buffer object.", "Sound.buffer": "Returns the underlaying Buffer object.",
"Sound.play": "Play sound.", "Sound.play": "Play sound.",
"music": "Generation of music tones.", "music": "Generation of music tones.",
"music._soundPicker": "A sound",
"music._soundPicker|param|sound": "the sound",
"music.beat": "Return the duration of a beat in milliseconds (the beat fraction).", "music.beat": "Return the duration of a beat in milliseconds (the beat fraction).",
"music.beat|param|fraction": "the fraction of the current whole note, eg: BeatFraction.Half", "music.beat|param|fraction": "the fraction of the current whole note, eg: BeatFraction.Half",
"music.changeTempoBy": "Change the tempo up or down by some amount of beats per minute (bpm).", "music.changeTempoBy": "Change the tempo up or down by some amount of beats per minute (bpm).",

View File

@ -20,7 +20,6 @@
"Note.GSharp|block": "G#", "Note.GSharp|block": "G#",
"SoundOutputDestination.Pin|block": "pin", "SoundOutputDestination.Pin|block": "pin",
"SoundOutputDestination.Speaker|block": "speaker", "SoundOutputDestination.Speaker|block": "speaker",
"music._soundPicker|block": "%sound",
"music.beat|block": "%fraction|beat", "music.beat|block": "%fraction|beat",
"music.changeTempoBy|block": "change tempo by %value|(bpm)", "music.changeTempoBy|block": "change tempo by %value|(bpm)",
"music.noteFrequency|block": "%note", "music.noteFrequency|block": "%note",

View File

@ -170,7 +170,7 @@ void playTone(int frequency, int ms) {
//% blockId=music_stop_all_sounds block="stop all sounds" //% blockId=music_stop_all_sounds block="stop all sounds"
//% parts="headphone" //% parts="headphone"
//% blockNamespace=music //% blockNamespace=music
//% weight=76 blockGap=8 //% weight=97
void stopAllSounds() { void stopAllSounds() {
if (currentSample) { if (currentSample) {
samplePtr = currentSample->length; samplePtr = currentSample->length;

View File

@ -32,7 +32,7 @@ declare namespace music {
//% blockId=music_stop_all_sounds block="stop all sounds" //% blockId=music_stop_all_sounds block="stop all sounds"
//% parts="headphone" //% parts="headphone"
//% blockNamespace=music //% blockNamespace=music
//% weight=76 blockGap=8 shim=music::stopAllSounds //% weight=97 shim=music::stopAllSounds
function stopAllSounds(): void; function stopAllSounds(): void;
/** Makes a sound bound to a buffer in WAV format. */ /** Makes a sound bound to a buffer in WAV format. */

View File

@ -264,7 +264,7 @@ namespace music {
* @param sound the sound to play * @param sound the sound to play
*/ */
//% blockId=music_play_sound_effect_until_done block="play sound effect %sound|until done" //% blockId=music_play_sound_effect_until_done block="play sound effect %sound|until done"
//% weight=98 //% weight=98 blockGap=8
export function playSoundEffectUntilDone(sound: Sound) { export function playSoundEffectUntilDone(sound: Sound) {
if (!sound) return; if (!sound) return;
sound.play(); sound.play();
@ -276,7 +276,7 @@ namespace music {
*/ */
//% blockId=music_sound_picker block="%sound" shim=TD_ID //% blockId=music_sound_picker block="%sound" shim=TD_ID
//% weight=0 blockHidden=1 //% weight=0 blockHidden=1
export function _soundPicker(sound: Sound): Sound { export function __soundPicker(sound: Sound): Sound {
return sound; return sound;
} }
@ -285,7 +285,7 @@ namespace music {
* @param sound the sound to play * @param sound the sound to play
*/ */
//% blockId=music_play_sound_effect block="play sound effect %sound" //% blockId=music_play_sound_effect block="play sound effect %sound"
//% weight=99 //% weight=99 blockGap=8
export function playSoundEffect(sound: Sound) { export function playSoundEffect(sound: Sound) {
if (!sound || numSoundsPlaying >= soundsLimit) return; if (!sound || numSoundsPlaying >= soundsLimit) return;
numSoundsPlaying++; numSoundsPlaying++;

35
libs/tests/README.md Normal file
View File

@ -0,0 +1,35 @@
# tests
A unit test framework
## Defining tests
Tests are registered as event handlers. They will automatically run once ``on start`` is finished.
```blocks
tests.test("lgB set speed 10", () => {
motors.largeB.setSpeed(10);
loops.pause(100)
tests.assertClose("speedB", 10, motors.largeB.speed(), 2)
});
```
## Assertions
The library has various asserts that will register fault. Note that since exceptions are not available, assertion failure **do not** stop the program execution.
* **assert** checks a boolean condition
```blocks
tests.assert("speed positive", motors.largeB.speed() > 0)
```
* **assert close** checks that a numberical value is within a particular range
```blocks
tests.assertClose("speed", motors.largeB.speed(), 10, 2)
```
```package
tests
```

View File

@ -0,0 +1,9 @@
{
"tests": "Unit tests framework",
"tests.assert": "Checks a boolean condition",
"tests.assertClose": "Checks that 2 values are close to each other",
"tests.assertClose|param|actual": "what the value was",
"tests.assertClose|param|expected": "what the value should be",
"tests.assertClose|param|tolerance": "the acceptable error margin",
"tests.test": "Registers a test to run"
}

View File

@ -0,0 +1,6 @@
{
"tests.assert|block": "assert %message|%condition",
"tests.test|block": "test %name",
"tests|block": "tests",
"{id:category}Tests": "Tests"
}

14
libs/tests/pxt.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "tests",
"description": "A unit test library",
"files": [
"README.md",
"tests.ts"
],
"testFiles": [
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

94
libs/tests/tests.ts Normal file
View File

@ -0,0 +1,94 @@
/**
* Unit tests framework
*/
//% weight=100 color=#0fbc11 icon=""
namespace tests {
class Test {
name: string;
handler: () => void;
errors: string[];
constructor(name: string, handler: () => void) {
this.name = name;
this.handler = handler;
this.errors = [];
}
reset() {
motors.stopAllMotors();
motors.resetAllMotors();
}
run() {
// clear state
this.reset();
console.log(`> ${this.name}`)
this.handler()
if (this.errors.length)
console.log('')
// ensure clean state after test
this.reset();
}
}
let _tests: Test[] = undefined;
let _currentTest: Test = undefined;
function run() {
if (!_tests) return;
const start = control.millis();
console.sendToScreen();
console.log(`${_tests.length} tests found`)
console.log(` `)
for (let i = 0; i < _tests.length; ++i) {
const t = _currentTest = _tests[i];
t.run();
_currentTest = undefined;
}
console.log(` `)
console.log(`${_tests.length} tests, ${_tests.map(t => t.errors.length).reduce((p, c) => p + c, 0)} errs in ${Math.ceil((control.millis() - start) / 1000)}s`)
}
/**
* Registers a test to run
*/
//% blockId=testtest block="test %name"
export function test(name: string, handler: () => void): void {
if (!name || !handler) return;
if (!_tests) {
_tests = [];
control.runInBackground(function () {
// should run after on start
loops.pause(100)
run()
})
}
_tests.push(new Test(name, handler));
}
/**
* Checks a boolean condition
*/
//% blockId=testAssert block="assert %message|%condition"
export function assert(message: string, condition: boolean) {
if (!condition) {
console.log(`!!! ${message || ''}`)
if (_currentTest)
_currentTest.errors.push(message);
}
}
/**
* Checks that 2 values are close to each other
* @param expected what the value should be
* @param actual what the value was
* @param tolerance the acceptable error margin
*/
export function assertClose(name: string, expected: number, actual: number, tolerance: number) {
assert(`${name} ${expected} != ${actual} +-${tolerance}`, Math.abs(expected - actual) <= tolerance);
}
}

View File

@ -2,14 +2,14 @@
"TouchSensorEvent.Bumped|block": "bumped", "TouchSensorEvent.Bumped|block": "bumped",
"TouchSensorEvent.Pressed|block": "pressed", "TouchSensorEvent.Pressed|block": "pressed",
"TouchSensorEvent.Released|block": "released", "TouchSensorEvent.Released|block": "released",
"sensors.TouchSensor.isPressed|block": "`icons.touchSensor` %sensor|is pressed", "sensors.TouchSensor.isPressed|block": "%sensor|is pressed",
"sensors.TouchSensor.onEvent|block": "on `icons.touchSensor` %sensor|%event", "sensors.TouchSensor.onEvent|block": "on %sensor|%event",
"sensors.TouchSensor.pauseUntil|block": "pause until `icons.touchSensor` %sensor|%event", "sensors.TouchSensor.pauseUntil|block": "pause until %sensor|%event",
"sensors.TouchSensor.wasPressed|block": "`icons.touchSensor` %sensor|was pressed", "sensors.TouchSensor.wasPressed|block": "%sensor|was pressed",
"sensors.touchSensor1|block": "1", "sensors.touchSensor1|block": "touch 1",
"sensors.touchSensor2|block": "2", "sensors.touchSensor2|block": "touch 2",
"sensors.touchSensor3|block": "3", "sensors.touchSensor3|block": "touch 3",
"sensors.touchSensor4|block": "4", "sensors.touchSensor4|block": "touch 4",
"{id:category}Sensors": "Sensors", "{id:category}Sensors": "Sensors",
"{id:group}Touch Sensor": "Touch Sensor" "{id:group}Touch Sensor": "Touch Sensor"
} }

View File

@ -42,10 +42,8 @@ namespace sensors {
* @param body code to run when the event is raised * @param body code to run when the event is raised
*/ */
//% help=input/touch-sensor/on-event //% help=input/touch-sensor/on-event
//% blockId=touchEvent block="on `icons.touchSensor` %sensor|%event" //% blockId=touchEvent block="on %sensor|%event"
//% parts="touch" //% parts="touch"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=99 blockGap=8 //% weight=99 blockGap=8
//% group="Touch Sensor" //% group="Touch Sensor"
@ -59,10 +57,8 @@ namespace sensors {
* @param event the kind of button gesture that needs to be detected * @param event the kind of button gesture that needs to be detected
*/ */
//% help=input/touch-sensor/pause-until //% help=input/touch-sensor/pause-until
//% blockId=touchWaitUntil block="pause until `icons.touchSensor` %sensor|%event" //% blockId=touchWaitUntil block="pause until %sensor|%event"
//% parts="touch" //% parts="touch"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=98 blockGap=8 //% weight=98 blockGap=8
//% group="Touch Sensor" //% group="Touch Sensor"
@ -75,13 +71,11 @@ namespace sensors {
* @param sensor the port to query the request * @param sensor the port to query the request
*/ */
//% help=input/touch-sensor/is-pressed //% help=input/touch-sensor/is-pressed
//% block="`icons.touchSensor` %sensor|is pressed" //% block="%sensor|is pressed"
//% blockId=touchIsPressed //% blockId=touchIsPressed
//% parts="touch" //% parts="touch"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=81 blockGap=8 //% weight=81
//% group="Touch Sensor" //% group="Touch Sensor"
isPressed() { isPressed() {
return this.button.isPressed(); return this.button.isPressed();
@ -92,11 +86,9 @@ namespace sensors {
* @param sensor the port to query the request * @param sensor the port to query the request
*/ */
//% help=input/touch-sensor/was-pressed //% help=input/touch-sensor/was-pressed
//% block="`icons.touchSensor` %sensor|was pressed" //% block="%sensor|was pressed"
//% blockId=touchWasPressed //% blockId=touchWasPressed
//% parts="touch" //% parts="touch"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=81 blockGap=8 //% weight=81 blockGap=8
//% group="Touch Sensor" //% group="Touch Sensor"
@ -105,12 +97,12 @@ namespace sensors {
} }
} }
//% whenUsed block="1" weight=95 fixedInstance jres=icons.port1 //% whenUsed block="touch 1" weight=95 fixedInstance jres=icons.port1
export const touchSensor1: TouchSensor = new TouchSensor(1) export const touchSensor1: TouchSensor = new TouchSensor(1)
//% whenUsed block="2" weight=95 fixedInstance jres=icons.port2 //% whenUsed block="touch 2" weight=95 fixedInstance jres=icons.port2
export const touchSensor2: TouchSensor = new TouchSensor(2) export const touchSensor2: TouchSensor = new TouchSensor(2)
//% whenUsed block="3" weight=95 fixedInstance jres=icons.port3 //% whenUsed block="touch 3" weight=95 fixedInstance jres=icons.port3
export const touchSensor3: TouchSensor = new TouchSensor(3) export const touchSensor3: TouchSensor = new TouchSensor(3)
//% whenUsed block="4" weight=95 fixedInstance jres=icons.port4 //% whenUsed block="touch 4" weight=95 fixedInstance jres=icons.port4
export const touchSensor4: TouchSensor = new TouchSensor(4) export const touchSensor4: TouchSensor = new TouchSensor(4)
} }

View File

@ -2,13 +2,13 @@
"UltrasonicSensorEvent.ObjectDetected|block": "object detected", "UltrasonicSensorEvent.ObjectDetected|block": "object detected",
"UltrasonicSensorEvent.ObjectFar|block": "object far", "UltrasonicSensorEvent.ObjectFar|block": "object far",
"UltrasonicSensorEvent.ObjectNear|block": "object near", "UltrasonicSensorEvent.ObjectNear|block": "object near",
"sensors.UltraSonicSensor.distance|block": "`icons.ultrasonicSensor` %sensor|distance", "sensors.UltraSonicSensor.distance|block": "%sensor|distance",
"sensors.UltraSonicSensor.onEvent|block": "on `icons.ultrasonicSensor` %sensor|%event", "sensors.UltraSonicSensor.onEvent|block": "on %sensor|%event",
"sensors.UltraSonicSensor.pauseUntil|block": "pause until `icons.ultrasonicSensor` %sensor| %event", "sensors.UltraSonicSensor.pauseUntil|block": "pause until %sensor| %event",
"sensors.ultrasonic1|block": "1", "sensors.ultrasonic1|block": "ultrasonic 1",
"sensors.ultrasonic2|block": "2", "sensors.ultrasonic2|block": "ultrasonic 2",
"sensors.ultrasonic3|block": "3", "sensors.ultrasonic3|block": "ultrasonic 3",
"sensors.ultrasonic4|block": "4", "sensors.ultrasonic4|block": "ultrasonic 4",
"{id:category}Sensors": "Sensors", "{id:category}Sensors": "Sensors",
"{id:group}Ultrasonic Sensor": "Ultrasonic Sensor" "{id:group}Ultrasonic Sensor": "Ultrasonic Sensor"
} }

View File

@ -25,7 +25,7 @@ namespace sensors {
} }
_query(): number { _query(): number {
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff; return ((this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff) / 10) >> 0; // range is 0..2550, in 0.1 cm increments.
} }
_update(prev: number, curr: number) { _update(prev: number, curr: number) {
@ -43,10 +43,8 @@ namespace sensors {
*/ */
//% help=input/ultrasonic/on //% help=input/ultrasonic/on
//% blockId=ultrasonicOn //% blockId=ultrasonicOn
//% block="on `icons.ultrasonicSensor` %sensor|%event" //% block="on %sensor|%event"
//% parts="ultrasonicsensor" //% parts="ultrasonicsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=100 blockGap=8 //% weight=100 blockGap=8
//% group="Ultrasonic Sensor" //% group="Ultrasonic Sensor"
@ -58,11 +56,9 @@ namespace sensors {
* Waits for the event to occur * Waits for the event to occur
*/ */
//% help=input/ultrasonic/wait //% help=input/ultrasonic/wait
//% block="pause until `icons.ultrasonicSensor` %sensor| %event" //% block="pause until %sensor| %event"
//% blockId=ultrasonicWait //% blockId=ultrasonicWait
//% parts="ultrasonicsensor" //% parts="ultrasonicsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=99 blockGap=8 //% weight=99 blockGap=8
//% group="Ultrasonic Sensor" //% group="Ultrasonic Sensor"
@ -75,30 +71,28 @@ namespace sensors {
* @param sensor the ultrasonic sensor port * @param sensor the ultrasonic sensor port
*/ */
//% help=input/ultrasonic/distance //% help=input/ultrasonic/distance
//% block="`icons.ultrasonicSensor` %sensor|distance" //% block="%sensor|distance"
//% blockId=sonarGetDistance //% blockId=sonarGetDistance
//% parts="ultrasonicsensor" //% parts="ultrasonicsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors //% blockNamespace=sensors
//% weight=65 blockGap=8 //% weight=65
//% group="Ultrasonic Sensor" //% group="Ultrasonic Sensor"
distance(): number { distance(): number {
// it supposedly also has an inch mode, but we stick to cm // it supposedly also has an inch mode, but we stick to cm
this._setMode(0) this._setMode(0)
return this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff; // range is 0..255 return this._query();
} }
} }
//% fixedInstance whenUsed block="1" jres=icons.port1 //% fixedInstance whenUsed block="ultrasonic 1" jres=icons.port1
export const ultrasonic1: UltraSonicSensor = new UltraSonicSensor(1) export const ultrasonic1: UltraSonicSensor = new UltraSonicSensor(1)
//% fixedInstance whenUsed block="2" jres=icons.port2 //% fixedInstance whenUsed block="ultrasonic 2" jres=icons.port2
export const ultrasonic2: UltraSonicSensor = new UltraSonicSensor(2) export const ultrasonic2: UltraSonicSensor = new UltraSonicSensor(2)
//% fixedInstance whenUsed block="3" jres=icons.port3 //% fixedInstance whenUsed block="ultrasonic 3" jres=icons.port3
export const ultrasonic3: UltraSonicSensor = new UltraSonicSensor(3) export const ultrasonic3: UltraSonicSensor = new UltraSonicSensor(3)
//% fixedInstance whenUsed block="4" jres=icons.port4 //% fixedInstance whenUsed block="ultrasonic 4" jres=icons.port4
export const ultrasonic4: UltraSonicSensor = new UltraSonicSensor(4) export const ultrasonic4: UltraSonicSensor = new UltraSonicSensor(4)
} }

4683
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "pxt-ev3", "name": "pxt-ev3",
"version": "0.0.41", "version": "0.0.49",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode", "description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true, "private": true,
"keywords": [ "keywords": [
@ -45,7 +45,7 @@
}, },
"dependencies": { "dependencies": {
"pxt-common-packages": "0.14.13", "pxt-common-packages": "0.14.13",
"pxt-core": "3.0.2" "pxt-core": "3.0.5"
}, },
"scripts": { "scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis" "test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -15,7 +15,8 @@
"libs/ultrasonic-sensor", "libs/ultrasonic-sensor",
"libs/infrared-sensor", "libs/infrared-sensor",
"libs/gyro-sensor", "libs/gyro-sensor",
"libs/ev3" "libs/ev3",
"libs/tests"
], ],
"simulator": { "simulator": {
"autoRun": true, "autoRun": true,
@ -52,8 +53,10 @@
"serial": { "serial": {
"vendorId": "0x0694", "vendorId": "0x0694",
"productId": "0x0005", "productId": "0x0005",
"rawHID": true "rawHID": true,
}, "useEditor": true,
"log": true
},
"runtime": { "runtime": {
"mathBlocks": true, "mathBlocks": true,
"loopsBlocks": true, "loopsBlocks": true,
@ -129,16 +132,23 @@
"logic": "#1E5AA8", "logic": "#1E5AA8",
"math": "#9DC3F7", "math": "#9DC3F7",
"variables": "#B40000", "variables": "#B40000",
"text": "#F0890A", "text": "#FCAC00",
"advanced": "#969696", "advanced": "#969696",
"functions": "#064597", "functions": "#19325A",
"arrays": "#890058" "arrays": "#901F76"
},
"blockIcons": {
"loops": "\uf10b",
"logic": "\uf10a",
"math": "\uf10c",
"variables": "\uf111",
"text": "\uf110",
"functions": "\uf108",
"arrays": "\uf109"
}, },
"monacoColors": { "monacoColors": {
"editor.background": "#ecf6ff" "editor.background": "#ecf6ff"
}, }
"simAnimationEnter": "horizontal flip in",
"simAnimationExit": "horizontal flip out"
}, },
"ignoreDocsErrors": true "ignoreDocsErrors": true
} }

View File

@ -23,20 +23,6 @@ namespace pxsim {
} }
export class EV3Board extends CoreBoard { export class EV3Board extends CoreBoard {
// state & update logic for component services
// neopixelState: CommonNeoPixelState;
buttonState: EV3ButtonState;
slideSwitchState: SlideSwitchState;
lightSensorState: AnalogSensorState;
thermometerState: AnalogSensorState;
thermometerUnitState: number;
microphoneState: AnalogSensorState;
edgeConnectorState: EdgeConnectorState;
capacitiveSensorState: CapacitiveSensorState;
accelerometerState: AccelerometerState;
touchButtonState: TouchButtonState;
irState: InfraredState;
view: SVGSVGElement; view: SVGSVGElement;
outputState: EV3OutputState; outputState: EV3OutputState;
@ -86,11 +72,6 @@ namespace pxsim {
// TODO // TODO
break; break;
} }
case "irpacket": {
let ev = <SimulatorInfraredPacketMessage>msg;
this.irState.receive(new RefBuffer(ev.packet));
break;
}
} }
} }
@ -120,6 +101,9 @@ namespace pxsim {
document.body.innerHTML = ""; // clear children document.body.innerHTML = ""; // clear children
document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement); document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
this.inputNodes = [];
this.outputNodes = [];
return Promise.resolve(); return Promise.resolve();
} }
@ -127,26 +111,33 @@ namespace pxsim {
return svg.toDataUri(new XMLSerializer().serializeToString(this.view)); return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
} }
//defaultNeopixelPin() {
// return this.edgeConnectorState.getPin(CPlayPinName.D8);
//}
getDefaultPitchPin() {
return this.edgeConnectorState.getPin(CPlayPinName.D6);
}
getBrickNode() { getBrickNode() {
return this.brickNode; return this.brickNode;
} }
motorUsed(port:number, large: boolean) {
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
if (!this.outputNodes[motorPort])
this.outputNodes[motorPort] = new MotorNode(motorPort, large);
}
}
}
getMotor(port: number, large?: boolean): MotorNode[] { getMotor(port: number, large?: boolean): MotorNode[] {
if (port == 0xFF) return this.getMotors(); // Return all motors const r = [];
const motorPort = this.motorMap[port]; for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (this.outputNodes[motorPort] == undefined) { const p = 1 << i;
this.outputNodes[motorPort] = large ? if (port & p) {
new LargeMotorNode(motorPort) : new MediumMotorNode(motorPort); const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (outputNode)
r.push(outputNode);
}
} }
return [this.outputNodes[motorPort]]; return r;
} }
getMotors() { getMotors() {
@ -154,7 +145,7 @@ namespace pxsim {
} }
getSensor(port: number, type: number): SensorNode { getSensor(port: number, type: number): SensorNode {
if (this.inputNodes[port] == undefined) { if (!this.inputNodes[port]) {
switch (type) { switch (type) {
case DAL.DEVICE_TYPE_GYRO: this.inputNodes[port] = new GyroSensorNode(port); break; case DAL.DEVICE_TYPE_GYRO: this.inputNodes[port] = new GyroSensorNode(port); break;
case DAL.DEVICE_TYPE_COLOR: this.inputNodes[port] = new ColorSensorNode(port); break; case DAL.DEVICE_TYPE_COLOR: this.inputNodes[port] = new ColorSensorNode(port); break;

View File

@ -38,7 +38,7 @@ namespace pxsim {
data[AnalogOff.InConn + port] = node.isUart() ? DAL.CONN_INPUT_UART : DAL.CONN_INPUT_DUMB; data[AnalogOff.InConn + port] = node.isUart() ? DAL.CONN_INPUT_UART : DAL.CONN_INPUT_DUMB;
if (node.isAnalog() && node.hasData()) { if (node.isAnalog() && node.hasData()) {
//data[AnalogOff.InPin6 + 2 * port] = node.getValue(); //data[AnalogOff.InPin6 + 2 * port] = node.getValue();
util.map16Bit(data, AnalogOff.InPin6 + 2 * port, node.getValue()) util.map16Bit(data, AnalogOff.InPin6 + 2 * port, Math.floor(node.getValue()));
} }
} }
} }

View File

@ -3,12 +3,13 @@
namespace pxsim { namespace pxsim {
export enum ColorSensorMode { export enum ColorSensorMode {
None = -1,
Reflected = 0, Reflected = 0,
Ambient = 1, Ambient = 1,
Colors = 2, Colors = 2,
RefRaw = 3, RefRaw = 3,
RgbRaw = 4, RgbRaw = 4,
ColorCal = 5 ColorCal = 5,
} }
export enum ThresholdState { export enum ThresholdState {
@ -20,23 +21,20 @@ namespace pxsim {
export class ColorSensorNode extends UartSensorNode { export class ColorSensorNode extends UartSensorNode {
id = NodeType.ColorSensor; id = NodeType.ColorSensor;
private color: number; private color: number = 50;
constructor(port: number) { constructor(port: number) {
super(port); super(port);
this.mode = -1;
} }
getDeviceType() { getDeviceType() {
return DAL.DEVICE_TYPE_COLOR; return DAL.DEVICE_TYPE_COLOR;
} }
setColor(color: number) { setColor(color: number) {
this.color = color; this.color = color;
this.changed = true; this.setChangedState();
this.valueChanged = true;
runtime.queueDisplayUpdate();
} }
getValue() { getValue() {

View File

@ -81,7 +81,6 @@ namespace pxsim.control {
export function dmesg(s: string) { export function dmesg(s: string) {
console.log("DMESG: " + s) console.log("DMESG: " + s)
} }
} }
namespace pxsim.output { namespace pxsim.output {

View File

@ -22,20 +22,14 @@ namespace pxsim {
setAngle(angle: number) { setAngle(angle: number) {
if (this.angle != angle) { if (this.angle != angle) {
this.angle = angle; this.angle = angle;
this.changed = true; this.setChangedState();
this.valueChanged = true;
runtime.queueDisplayUpdate();
} }
} }
setRate(rate: number) { setRate(rate: number) {
if (this.rate != rate) { if (this.rate != rate) {
this.rate = rate; this.rate = rate;
this.changed = true; this.setChangedState();
this.valueChanged = true;
runtime.queueDisplayUpdate();
} }
} }

View File

@ -2,8 +2,8 @@
namespace pxsim.motors { namespace pxsim.motors {
export function __motorUsed(port: number, large: boolean) { export function __motorUsed(port: number, large: boolean) {
console.log("MOTOR INIT " + port); //console.log("MOTOR INIT " + port);
const motors = ev3board().getMotor(port, large); ev3board().motorUsed(port, large);
runtime.queueDisplayUpdate(); runtime.queueDisplayUpdate();
} }
} }
@ -11,7 +11,7 @@ namespace pxsim.motors {
namespace pxsim.sensors { namespace pxsim.sensors {
export function __sensorUsed(port: number, type: number) { export function __sensorUsed(port: number, type: number) {
console.log("SENSOR INIT " + port + ", type: " + type); //console.log("SENSOR INIT " + port + ", type: " + type);
const sensor = ev3board().getSensor(port, type); const sensor = ev3board().getSensor(port, type);
runtime.queueDisplayUpdate(); runtime.queueDisplayUpdate();
} }

View File

@ -12,8 +12,11 @@ namespace pxsim {
namespace pxsim.output { namespace pxsim.output {
export function setLights(pattern: number) { export function setLights(pattern: number) {
const lightState = ev3board().getBrickNode().lightState; const brickState = ev3board().getBrickNode();
lightState.lightPattern = pattern; const lightState = brickState.lightState;
runtime.queueDisplayUpdate(); if (lightState.lightPattern != pattern) {
lightState.lightPattern = pattern;
brickState.setChangedState();
}
} }
} }

View File

@ -15,11 +15,20 @@ namespace pxsim {
MMapMethods.register("/dev/lms_motor", { MMapMethods.register("/dev/lms_motor", {
data, data,
beforeMemRead: () => { beforeMemRead: () => {
console.log("motor before read"); const outputs = ev3board().outputNodes;
// console.log("motor before read");
for (let port = 0; port < DAL.NUM_OUTPUTS; ++port) { for (let port = 0; port < DAL.NUM_OUTPUTS; ++port) {
data[MotorDataOff.TachoCounts * port] = 0; // Tacho count const output = outputs[port];
data[MotorDataOff.Speed * port] = 50; // Speed const speed = output ? outputs[port].getSpeed() : 0;
data[MotorDataOff.TachoSensor * port] = 0; // Count const angle = output ? outputs[port].getAngle() : 0;
const tci = MotorDataOff.TachoCounts + port * MotorDataOff.Size;
const tsi = MotorDataOff.TachoSensor + port * MotorDataOff.Size;
data[tci] = data[tci + 1] = data[tci + 2] = data[tci + 3] = 0; // Tacho count
data[MotorDataOff.Speed + port * MotorDataOff.Size] = speed; // Speed
data[tsi] = angle & 0xff; // Count
data[tsi + 1] = (angle >> 8) & 0xff; // Count
data[tsi + 2] = (angle >> 16) & 0xff; // Count
data[tsi + 3] = (angle >> 24); // Count
} }
}, },
read: buf => { read: buf => {

182
sim/state/motornode.ts Normal file
View File

@ -0,0 +1,182 @@
namespace pxsim {
export class MotorNode extends BaseNode {
isOutput = true;
private rotationsPerMilliSecond: number;
// current state
private angle: number = 0;
private tacho: number = 0;
private speed: number = 0;
private polarity: number = 1; // -1, 1 or -1
private started: boolean;
private speedCmd: DAL;
private speedCmdValues: number[];
private speedCmdTacho: number;
private speedCmdTime: number;
private _synchedMotor: MotorNode; // non-null if master motor
constructor(port: number, large: boolean) {
super(port);
this.setLarge(large);
}
getSpeed() {
return this.speed * (this.polarity == 0 ? -1 : 1);
}
getAngle() {
return this.angle;
}
// returns the slave motor if any
getSynchedMotor() {
return this._synchedMotor;
}
setSpeedCmd(cmd: DAL, values: number[]) {
this.speedCmd = cmd;
this.speedCmdValues = values;
this.speedCmdTacho = this.angle;
this.speedCmdTime = pxsim.U.now();
delete this._synchedMotor;
}
setSyncCmd(motor: MotorNode, cmd: DAL, values: number[]) {
this.setSpeedCmd(cmd, values);
this._synchedMotor = motor;
}
clearSpeedCmd() {
delete this.speedCmd;
delete this._synchedMotor;
}
setLarge(large: boolean) {
this.id = large ? NodeType.LargeMotor : NodeType.MediumMotor;
// large 170 rpm (https://education.lego.com/en-us/products/ev3-large-servo-motor/45502)
this.rotationsPerMilliSecond = (large ? 170 : 250) / 60000;
}
setPolarity(polarity: number) {
// Either 1 or 255 (reverse)
/*
-1 : Motor will run backward
0 : Motor will run opposite direction
1 : Motor will run forward
*/
this.polarity = polarity;
}
reset() {
// not sure what reset does...
}
clearCount() {
this.tacho = 0;
this.angle = 0;
}
stop() {
this.started = false;
this.clearSpeedCmd();
}
start() {
this.started = true;
}
updateState(elapsed: number) {
console.log(`motor: ${elapsed}ms - ${this.speed}% - ${this.angle}> - ${this.tacho}|`)
const interval = Math.min(20, elapsed);
let t = 0;
while (t < elapsed) {
let dt = interval;
if (t + dt > elapsed) dt = elapsed - t;
this.updateStateStep(dt);
t += dt;
}
}
private updateStateStep(elapsed: number) {
// compute new speed
switch (this.speedCmd) {
case DAL.opOutputSpeed:
case DAL.opOutputPower:
// assume power == speed
// TODO: PID
this.speed = this.speedCmdValues[0];
break;
case DAL.opOutputTimeSpeed:
case DAL.opOutputTimePower:
case DAL.opOutputStepPower:
case DAL.opOutputStepSpeed: {
// ramp up, run, ramp down, <brake> using time
const speed = this.speedCmdValues[0];
const step1 = this.speedCmdValues[1];
const step2 = this.speedCmdValues[2];
const step3 = this.speedCmdValues[3];
const brake = this.speedCmdValues[4];
const dstep = (this.speedCmd == DAL.opOutputTimePower || this.speedCmd == DAL.opOutputTimeSpeed)
? pxsim.U.now() - this.speedCmdTime
: this.tacho - this.speedCmdTacho;
if (dstep < step1) // rampup
this.speed = speed * dstep / step1;
else if (dstep < step1 + step2) // run
this.speed = speed;
else if (dstep < step1 + step2 + step3)
this.speed = speed * (step1 + step2 + step3 - dstep) / (step1 + step2 + step3);
else {
if (brake) this.speed = 0;
this.clearSpeedCmd();
}
break;
}
case DAL.opOutputStepSync:
case DAL.opOutputTimeSync: {
if (!this._synchedMotor) // handled in other motor code
break;
const otherMotor = this._synchedMotor;
const speed = this.speedCmdValues[0];
const turnRatio = this.speedCmdValues[1];
const stepsOrTime = this.speedCmdValues[2];
const brake = this.speedCmdValues[3];
const dstep = this.speedCmd == DAL.opOutputTimeSync
? pxsim.U.now() - this.speedCmdTime
: this.tacho - this.speedCmdTacho;
// 0 is special case, run infinite
if (!stepsOrTime || dstep < stepsOrTime)
this.speed = speed;
else {
if (brake) this.speed = 0;
this.clearSpeedCmd();
}
// send synched motor state
otherMotor.speed = Math.floor(this.speed * turnRatio / 100);
if (!this._synchedMotor)
otherMotor.clearSpeedCmd();
break;
}
}
this.speed = Math.round(this.speed); // integer only
// compute delta angle
const rotations = this.getSpeed() / 100 * this.rotationsPerMilliSecond * elapsed;
const deltaAngle = Math.round(rotations * 360);
if (deltaAngle) {
this.angle += deltaAngle;
this.tacho += Math.abs(deltaAngle);
this.setChangedState();
}
// if the motor was stopped or there are no speed commands,
// let it coast to speed 0
if (this.speed && !(this.started || this.speedCmd)) {
// decay speed 5% per tick
this.speed = Math.round(Math.max(0, Math.abs(this.speed) - 10) * Math.sign(this.speed));
}
}
}
}

View File

@ -1,73 +0,0 @@
namespace pxsim {
export class MotorNode extends BaseNode {
isOutput = true;
public angle: number = 0;
private speed: number;
private large: boolean;
private rotation: number;
private polarity: boolean;
constructor(port: number) {
super(port);
}
setSpeed(speed: number) {
if (this.speed != speed) {
this.speed = speed;
this.changed = true;
runtime.queueDisplayUpdate();
}
}
setLarge(large: boolean) {
this.large = large;
}
getSpeed() {
return this.speed;
}
stepSpeed(speed: number, angle: number, brake: boolean) {
// TODO: implement
}
setPolarity(polarity: number) {
// Either 1 or 255 (reverse)
this.polarity = polarity === 255;
// TODO: implement
}
reset() {
// TODO: implement
}
stop() {
// TODO: implement
}
start() {
// TODO: implement
runtime.queueDisplayUpdate();
}
}
export class MediumMotorNode extends MotorNode {
id = NodeType.MediumMotor;
constructor(port: number) {
super(port);
}
}
export class LargeMotorNode extends MotorNode {
id = NodeType.LargeMotor;
constructor(port: number) {
super(port);
}
}
}

View File

@ -32,5 +32,17 @@ namespace pxsim {
this.changed = false; this.changed = false;
return res; return res;
} }
setChangedState() {
this.changed = true;
}
/**
* Updates any internal state according to the elapsed time since the last call to `updateState`
* @param elapsed
*/
updateState(elapsed: number) {
}
} }
} }

View File

@ -34,24 +34,51 @@ namespace pxsim {
motors.forEach(motor => motor.reset()); motors.forEach(motor => motor.reset());
return 2; return 2;
} }
case DAL.opOutputStepSpeed: { case DAL.opOutputClearCount:
const port = buf.data[1];
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.clearCount());
break;
case DAL.opOutputStepPower:
case DAL.opOutputStepSpeed:
case DAL.opOutputTimePower:
case DAL.opOutputTimeSpeed: {
// step speed // step speed
const port = buf.data[1]; const port = buf.data[1];
const speed = buf.data[2]; const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2); // signed byte
// note that b[3] is padding // note that b[3] is padding
const step1 = buf.data[4]; const step1 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 4);
const step2 = buf.data[5]; // angle const step2 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 8);
const step3 = buf.data[6]; const step3 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 12);
const brake = buf.data[7]; const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 16);
//console.log(buf); //console.log(buf);
const motors = ev3board().getMotor(port); const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.stepSpeed(speed, step2, brake === 1)); motors.forEach(motor => motor.setSpeedCmd(cmd, [speed, step1, step2, step3, brake]));
return 2;
}
case DAL.opOutputStepSync:
case DAL.opOutputTimeSync: {
const port = buf.data[1];
const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2); // signed byte
// note that b[3] is padding
const turnRatio = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int16LE, 4);
// b[6], b[7] is padding
const stepsOrTime = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 8);
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 12);
const motors = ev3board().getMotor(port);
for (const motor of motors) {
const otherMotor = motors.filter(m => m.port != motor.port)[0];
motor.setSyncCmd(
motor.port < otherMotor.port ? otherMotor : undefined,
cmd, [speed, turnRatio, stepsOrTime, brake]);
}
return 2; return 2;
} }
case DAL.opOutputStop: { case DAL.opOutputStop: {
// stop // stop
const port = buf.data[1]; const port = buf.data[1];
const brake = buf.data[2]; const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port); const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.stop()); motors.forEach(motor => motor.stop());
return 2; return 2;
@ -59,9 +86,9 @@ namespace pxsim {
case DAL.opOutputSpeed: { case DAL.opOutputSpeed: {
// setSpeed // setSpeed
const port = buf.data[1]; const port = buf.data[1];
const speed = buf.data[2]; const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port); const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.setSpeed(speed)); motors.forEach(motor => motor.setSpeedCmd(cmd, [speed]));
return 2; return 2;
} }
case DAL.opOutputStart: { case DAL.opOutputStart: {
@ -74,7 +101,7 @@ namespace pxsim {
case DAL.opOutputPolarity: { case DAL.opOutputPolarity: {
// reverse // reverse
const port = buf.data[1]; const port = buf.data[1];
const polarity = buf.data[2]; const polarity = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port); const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.setPolarity(polarity)); motors.forEach(motor => motor.setPolarity(polarity));
return 2; return 2;
@ -86,6 +113,9 @@ namespace pxsim {
motors.forEach(motor => motor.setLarge(large)); motors.forEach(motor => motor.setLarge(large));
return 2; return 2;
} }
default:
console.warn('unknown cmd: ' + cmd);
break;
} }
console.log("pwm write"); console.log("pwm write");

View File

@ -7,6 +7,7 @@ namespace pxsim {
export class EV3ScreenState { export class EV3ScreenState {
changed: boolean = true;
points: Uint8Array; points: Uint8Array;
constructor() { constructor() {
this.points = new Uint8Array(visuals.SCREEN_WIDTH * visuals.SCREEN_HEIGHT) this.points = new Uint8Array(visuals.SCREEN_WIDTH * visuals.SCREEN_HEIGHT)
@ -23,13 +24,13 @@ namespace pxsim {
setPixel(x: number, y: number, v: number) { setPixel(x: number, y: number, v: number) {
this.applyMode(OFF(x, y), v) this.applyMode(OFF(x, y), v)
runtime.queueDisplayUpdate(); this.changed = true;
} }
clear() { clear() {
for (let i = 0; i < this.points.length; ++i) for (let i = 0; i < this.points.length; ++i)
this.points[i] = 0; this.points[i] = 0;
runtime.queueDisplayUpdate(); this.changed = true;
} }
blitLineCore(x: number, y: number, w: number, buf: RefBuffer, mode: Draw, offset = 0) { blitLineCore(x: number, y: number, w: number, buf: RefBuffer, mode: Draw, offset = 0) {
@ -58,7 +59,7 @@ namespace pxsim {
} }
} }
runtime.queueDisplayUpdate(); this.changed = true;
} }
clearLine(x: number, y: number, w: number) { clearLine(x: number, y: number, w: number) {
@ -72,6 +73,12 @@ namespace pxsim {
off++ off++
} }
} }
didChange() {
const res = this.changed;
this.changed = false;
return res;
}
} }
} }

View File

@ -5,6 +5,7 @@ namespace pxsim {
protected mode: number; protected mode: number;
protected valueChanged: boolean; protected valueChanged: boolean;
protected modeChanged: boolean;
constructor(port: number) { constructor(port: number) {
super(port); super(port);
@ -24,6 +25,8 @@ namespace pxsim {
setMode(mode: number) { setMode(mode: number) {
this.mode = mode; this.mode = mode;
this.changed = true;
this.modeChanged = true;
} }
getMode() { getMode() {
@ -43,6 +46,17 @@ namespace pxsim {
this.valueChanged = false; this.valueChanged = false;
return res; return res;
} }
modeChange() {
const res = this.modeChanged;
this.modeChanged = false;
return res;
}
setChangedState() {
this.changed = true;
this.valueChanged = false;
}
} }
export class AnalogSensorNode extends SensorNode { export class AnalogSensorNode extends SensorNode {

View File

@ -14,8 +14,7 @@ namespace pxsim {
public setPressed(pressed: boolean) { public setPressed(pressed: boolean) {
this.pressed.push(pressed); this.pressed.push(pressed);
this.changed = true; this.setChangedState();
this.valueChanged = true;
} }
public isPressed() { public isPressed() {

View File

@ -88,10 +88,10 @@ namespace pxsim {
const inputNodes = ev3board().getInputNodes(); const inputNodes = ev3board().getInputNodes();
for (let port = 0; port < DAL.NUM_INPUTS; port++) { for (let port = 0; port < DAL.NUM_INPUTS; port++) {
const node = inputNodes[port]; const node = inputNodes[port];
if (node) { if (node && node.isUart()) {
// Actual // Actual
const index = 0; //UartOff.Actual + port * 2; const index = 0; //UartOff.Actual + port * 2;
data[UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index] = node.getValue(); util.map16Bit(data, UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index, Math.floor(node.getValue()))
// Status // Status
data[UartOff.Status + port] = node.valueChange() ? UartStatus.UART_PORT_CHANGED : UartStatus.UART_DATA_READY; data[UartOff.Status + port] = node.valueChange() ? UartStatus.UART_PORT_CHANGED : UartStatus.UART_DATA_READY;
} }

View File

@ -4,7 +4,7 @@ namespace pxsim {
export class UltrasonicSensorNode extends UartSensorNode { export class UltrasonicSensorNode extends UartSensorNode {
id = NodeType.UltrasonicSensor; id = NodeType.UltrasonicSensor;
private distance: number = 50; private distance: number = 127; // in cm
constructor(port: number) { constructor(port: number) {
super(port); super(port);
@ -17,15 +17,12 @@ namespace pxsim {
setDistance(distance: number) { setDistance(distance: number) {
if (this.distance != distance) { if (this.distance != distance) {
this.distance = distance; this.distance = distance;
this.changed = true; this.setChangedState();
this.valueChanged = true;
runtime.queueDisplayUpdate();
} }
} }
getValue() { getValue() {
return this.distance; return this.distance * 10; // convert to 0.1 cm
} }
} }
} }

View File

@ -1,7 +1,7 @@
namespace pxsim.util { namespace pxsim.util {
export function map16Bit(buffer: Uint8Array, index: number, value: number) { export function map16Bit(buffer: Uint8Array, index: number, value: number) {
buffer[index] = (value >> 8) & 0xff; buffer[index] = value & 0xFF;
buffer[index+1] = value & 0xff; buffer[index + 1] = (value >> 8) & 0xFF;
} }
} }

View File

@ -1,69 +1,87 @@
<svg id="e13b39e7-895d-4931-9be9-7aecaf709784" data-name="46d2506d-be16-4175-90b0-aabf357ea333" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 79.56 153.61"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82 156">
<defs> <defs>
<clipPath id="7dd75c25-af24-4f64-9ea1-f270fbd17872"> <clipPath id="clip-path" transform="translate(0.98 1.9)">
<path d="M12.82.54a17.07,17.07,0,0,1,20.77,12.3,1.53,1.53,0,0,1,1.6-.24l.21.19,10.41,10.1c.31.3.47.3.47,1.14V35.81h6.79a4,4,0,0,1,4,4V65.63a4,4,0,0,1-4,4H46.15v11.8l13.68-.1h.61s7.76,9.81,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78H69.17v7.57A4,4,0,0,1,67,151.75V153h0a.63.63,0,0,1-.61.61H40.08a.6.6,0,0,1-.64-.56V152a4,4,0,0,1-2.89-3.85v-7.57H30.07a5.78,5.78,0,0,1-5.78-5.78v-8.18h-6.8a4,4,0,0,1-4-4V96.84a4,4,0,0,1,4-4h6.8V92l.75-.78v-10L14.13,70.71c-.25-.27-.39-.25-.39-.67V34.27l.37-.39A17.06,17.06,0,0,1,12.82.54Z" style="fill: none"/> <path d="M79,17.05A17.07,17.07,0,0,1,64.92,33.86l.36.39V70c0,.42-.14.4-.39.68L54,81.22v10l.75.78v.79h6.79a4,4,0,0,1,4,4v25.82a4,4,0,0,1-4,4H54.75v8.18A5.79,5.79,0,0,1,49,140.61h-6.5v7.57a4,4,0,0,1-2.9,3.84v1a.6.6,0,0,1-.6.6H12.69a.6.6,0,0,1-.6-.6h0v-1.29a4,4,0,0,1-2.17-3.55v-7.57H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59l13.68.1,0-11.8H26a4,4,0,0,1-4-4V39.79a4,4,0,0,1,4-4h6.78l0-11.77c0-.84.15-.84.46-1.14l10.41-10.1.22-.19a1.53,1.53,0,0,1,1.59.23A17.07,17.07,0,0,1,79,17.05Z" style="fill: none"/>
</clipPath> </clipPath>
<clipPath id="9d9a0722-eca6-4c98-bf03-49097e654f0e"> <clipPath id="clip-path-2" transform="translate(0.98 1.9)">
<path d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78V92l9.26-9.58a2.29,2.29,0,0,1,2.38-1c4.4,0,23.86-.15,23.86-.15h.65s7.76,9.8,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/> <path d="M49,140.61H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59s19.46.14,23.87.14a2.27,2.27,0,0,1,2.37,1L54.75,92v42.79A5.79,5.79,0,0,1,49,140.61Z" style="fill: none"/>
</clipPath> </clipPath>
<clipPath id="0f29df77-bfe3-4749-8432-7a5b3681f927"> <clipPath id="clip-path-3" transform="translate(0.98 1.9)">
<path d="M35.85,135.26a5.78,5.78,0,0,1-5.78-5.78h0V92.6h0a5.78,5.78,0,0,1,5.77-5.79H57.17S64.08,96,72.73,107.22v22.25h0A5.78,5.78,0,0,1,67,135.26H35.85Z" style="fill: none"/> <path d="M43.18,135.24H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.21C15,95.94,21.86,86.8,21.86,86.8H43.18A5.78,5.78,0,0,1,49,92.58h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/>
</clipPath> </clipPath>
</defs> </defs>
<title>Large Motor</title> <title>Large Motor</title>
<g style="clip-path: url(#7dd75c25-af24-4f64-9ea1-f270fbd17872)"> <g style="isolation: isolate">
<g> <g id="e13b39e7-895d-4931-9be9-7aecaf709784">
<path id="1614620b-40ec-4523-b90a-428aad6bd045" data-name="eadcf40d-5b1f-4f82-8d01-d628e9e4bcbb" d="M40.49,64.16V41.26A5.48,5.48,0,0,1,46,35.8h5.62A5.48,5.48,0,0,1,57,41.26v22.9h0a5.45,5.45,0,0,1-5.46,5.46H46A5.45,5.45,0,0,1,40.49,64.16Z" style="fill: #a8a9a8"/> <g style="clip-path: url(#clip-path)">
<circle id="1a43435f-56ec-4b4e-a3ae-b40d13b2f218" data-name="93612c19-30fe-4c1d-bb05-67002522431d" cx="51.72" cy="64.32" r="3.79" style="fill: #fff"/> <g id="Large_Motor" data-name="Large Motor">
<circle id="43fd443a-13b5-48ef-af02-32d5de3e95b1" data-name="aee14a95-6af8-4dce-8543-2eda3545de29" cx="51.72" cy="41.11" r="3.79" style="fill: #fff"/> <path id="LM_back2" data-name="LM back2" d="M39,153.62H12.69a.6.6,0,0,1-.6-.6h0v-5.3a.6.6,0,0,1,.6-.6H39a.6.6,0,0,1,.6.6V153A.6.6,0,0,1,39,153.62Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<path id="7bfb4e09-fb97-433a-8104-a0426c319fe6" data-name="1d2e9494-7cc7-48f0-9562-12d1d83b7bb0" d="M39.44,153v-5.29a.61.61,0,0,1,.61-.61H66.34a.61.61,0,0,1,.61.61h0V153h0a.63.63,0,0,1-.61.61H40.08a.61.61,0,0,1-.64-.58Z" style="fill: #a8a9a8"/> <g>
<path id="ded8d34f-3eb8-4d18-bbd7-80e03d734fef" data-name="ccd9d8b0-00ff-4f0b-bfdc-94153d199419" d="M36.58,151.58V136.89a.61.61,0,0,1,.61-.61H68.51a.61.61,0,0,1,.61.61h0v14.69a.61.61,0,0,1-.61.61H37.19a.61.61,0,0,1-.61-.61Z" style="fill: #a8a9a8"/> <image width="36" height="20" transform="translate(9 136)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAVCAYAAADB5CeuAAAACXBIWXMAAAsSAAALEgHS3X78AAAAp0lEQVRIS+3WsW3DMBBG4Y+GSmcGp/cAHsuzeIasFPdWVpD6c0FKcBWwOxV8wIEgSIAPvOL+EhFKKcUBiIiAgjO+25rJildErJMqdMdVlcwg8MQDv5P6Q1fc5ErRujW1TfmoLPa3T//dymJI9TKkehlSvQypXoZUL5vUNhAz2R1Oao75w9IOMmpRHVZqSpjxo3KRw+YwQ2lx+EsVykqfK+aIWGhSR+MNDy9ID5XUCOsAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="a30c6f9d-adb5-4251-ad04-1bd8bfaab7af" data-name="e8b462c5-81f0-4111-9486-bb5fc1221c77" d="M55.44,59.6V57.8c0-.87-.81-.46-.81-.46l-1.19.59c-.38.18-.33-.34-.33-.34V55c0-.72.82-.77.82-.77h1.25s.26-.2.26-1.38-.29-1.32-.29-1.32H53.9a.68.68,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1-.05-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H48.56s-.3.18-.3,1.32.3,1.38.3,1.38h1.38a.73.73,0,0,1,.73.77v2.62c0,.52-.36.34-.36.34l-1-.59c-.68-.38-1.23,0-1.23.46v1.8s0,1,1.23.1a5.08,5.08,0,0,1,3-.7,4.18,4.18,0,0,1,2.35.7C55.37,60.14,55.44,59.6,55.44,59.6Z" style="fill: #fff"/> <path id="LM_back1-2" data-name="LM back1-2" d="M41.86,152.17H10.53a.6.6,0,0,1-.6-.6v-14.7a.6.6,0,0,1,.6-.6H41.86a.6.6,0,0,1,.6.6h0v14.7A.6.6,0,0,1,41.86,152.17Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<path id="c7805530-90df-4e25-868f-c9475cacc294" data-name="51a46c0a-a8a3-4420-b297-1d82512a0b32" d="M47.79,46.14c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.84,5.72,5.72,0,0,0,2.76-.84.85.85,0,0,1,.31-.09.71.71,0,0,1,.76.65V47.9s-.24,1-1.07.6a5,5,0,0,0-2.76-.77A6.46,6.46,0,0,0,49,48.5a.68.68,0,0,1-.24.08.83.83,0,0,1-1-.61s0,0,0-.07S47.76,46.94,47.79,46.14Z" style="fill: #fff"/>
<path id="f8fcbff1-7e8c-4c85-8f0e-222f81e36654" data-name="be4fbc2c-74e0-495a-bfad-cf557a83a287" d="M13.75,70V34.27L33.21,13.56c0-.84,1.4-1.36,2-1l.21.19,10.42,10.1c.31.3.46.3.46,1.14l-.19,59-18.57.67-13.4-13C13.89,70.44,13.75,70.46,13.75,70Z" style="fill: #a8a9a8"/>
<path id="1dc809f3-4ab0-4e83-b8a0-ab178c9c843d" data-name="4d02192d-b81d-48fe-8732-1ddf5bded46b" d="M35.44,82.8l10.67-.57v-8l-4.43-4.7H30.05a1.6,1.6,0,0,0-1.16.4L25.34,73.6c-.28.27-.3,1.12-.3,1.12V92Z" style="fill: #a8a9a8"/>
<g id="e0794b99-de5a-4337-b43b-7cdb158e4272" data-name="233cb9b7-9706-41a9-a0a4-7b51b61b2ff5">
<path id="c86708aa-fcc1-4e15-8d85-6e5415398b0c" data-name="6ed465b7-465a-4389-8ebc-d8f92a4226dd" d="M13.49,121.2V98.29A5.46,5.46,0,0,1,19,92.84h5.62A5.45,5.45,0,0,1,30,98.3v22.9h0a5.48,5.48,0,0,1-5.46,5.46H19A5.48,5.48,0,0,1,13.49,121.2Z" style="fill: #a8a9a8"/>
<circle id="0355a228-ed20-4633-ba10-c36cf551e228" data-name="955ad5d6-af09-42ad-8d56-9ff62c40912d" cx="18.35" cy="121.35" r="3.79" style="fill: #fff"/>
<circle id="73922eab-afce-48dd-9bae-80da92a8b392" data-name="75a0ff09-d40c-46e3-aaf0-c5878f2efdac" cx="18.35" cy="98.14" r="3.79" style="fill: #fff"/>
<path id="e9f7b6a6-dcdd-4d55-a7bb-ad3951bb1e39" data-name="eeb5a6b6-ef4a-4f05-95cd-8b1c3eb419c4" d="M22.07,116.64v-1.8c0-.86-.81-.46-.81-.46L20,115c-.38.18-.33-.34-.33-.34V112c0-.72.82-.76.82-.76h1.25s.32-.26.29-1.39-.29-1.32-.29-1.32H20.53a.67.67,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1,0-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H15.19s-.3.18-.3,1.32.3,1.39.3,1.39h1.38a.71.71,0,0,1,.73.69v2.69c0,.52-.36.34-.36.34l-1-.58c-.68-.39-1.23,0-1.23.46v1.8s0,1,1.23.1a5.09,5.09,0,0,1,3-.71,4.16,4.16,0,0,1,2.35.71C22,117.17,22.07,116.64,22.07,116.64Z" style="fill: #fff"/>
<path id="82138da6-9b2a-4b9a-952e-568841e5ed52" data-name="80481c02-bb01-43aa-bd64-15ab30fbbb5e" d="M14.44,103.18c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.83,5.78,5.78,0,0,0,2.77-.83.7.7,0,0,1,1,.25.67.67,0,0,1,.1.31v1.76s-.24,1-1.07.59a5,5,0,0,0-2.77-.77,6.59,6.59,0,0,0-2.85.77.68.68,0,0,1-.24.08.83.83,0,0,1-1-.63v0S14.44,104,14.44,103.18Z" style="fill: #fff"/>
</g>
<path id="aa62b88a-4cd5-4c76-acaf-e85a7fd45341" data-name="6b63d025-68d6-47eb-ba62-60017f4a1dd4" d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78h0V92l9.26-9.57a2.29,2.29,0,0,1,2.38-1c4.4,0,23.87-.14,23.87-.14h.64s7.76,9.8,18.13,23.86v29.64h0a5.79,5.79,0,0,1-5.79,5.79Z" style="fill: #a8a9a8"/>
<g>
<g id="1172473e-5b0c-4dd6-85c5-28f5593b9312" data-name="a0498222-7e69-48db-90b7-05f2ce124a03">
<g style="clip-path: url(#9d9a0722-eca6-4c98-bf03-49097e654f0e)">
<g id="da9d77a3-c9b0-4118-a8e3-c65ec5cbe7d4" data-name="8270fb93-0862-4480-a5b8-675cc77ac153">
<path id="77253f44-139e-4b90-82df-465a787cab9c" data-name="bb0a4ec3-45a5-4c91-b064-db5494e447e6" d="M78.56,142.76l1-7.69V103.85L66.49,87.72v14.86L42.7,133.66,35.55,141Z" style="fill: #f2f2f2"/>
</g>
</g> </g>
</g> <g id="LM_rightside" data-name="LM rightside">
<path id="9d05c10c-72eb-4f9f-8517-e403ec636357" data-name="74e07bdd-431b-40c1-b0f3-71128f2a0c08" d="M35.85,135.56a5.78,5.78,0,0,1-5.78-5.78h0V92.9h0a5.78,5.78,0,0,1,5.77-5.79H57.17s6.91,9.15,15.56,20.41v22.25h0A5.78,5.78,0,0,1,67,135.56H35.85Z" style="fill: #a8a9a8"/> <path id="LM_rightside5" data-name="LM rightside5" d="M33.08,69.61H27.47A5.45,5.45,0,0,1,22,64.15h0V41.25a5.45,5.45,0,0,1,5.46-5.46h5.61a5.46,5.46,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,33.08,69.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<g id="6df8eb1f-3187-427f-8d99-bc7a37e6cc25" data-name="9bbfb3ec-86c2-4d27-8275-76f2f9737a7f"> <circle id="LM_rightside4" data-name="LM rightside4" cx="28.3" cy="66.21" r="3.79" style="fill: #fff"/>
<g id="e4d7143c-1fb6-45a4-a205-1c920b8a823c" data-name="7e017c33-f09b-4d39-803f-1bedf1ebf566"> <circle id="LM_rightside3" data-name="LM rightside3" cx="28.3" cy="43" r="3.79" style="fill: #fff"/>
<g id="3a4e493d-cd17-48c4-97b6-da7ee3c4898e" data-name="69e3df75-72a6-4499-baab-f65ec194e193"> <path id="LM_rightside2" data-name="LM rightside2" d="M24.4,59.69A4.16,4.16,0,0,1,26.74,59a5.23,5.23,0,0,1,3,.71c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.85-1.23-.46l-1,.58s-.36.19-.36-.33V55s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.39-.29-1.32-.29-1.32H29.1s-.73,0-.73-.65v-1.3s-.23-.36-1.17-.3a3.56,3.56,0,0,0-1.4.3v1.3a.66.66,0,0,1-.66.65c-.63,0-1.26,0-1.26,0s-.25.19-.29,1.32.29,1.39.29,1.39h1.26s.8.05.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.41-.82.46,0,1.8,0,1.8S23.67,60.12,24.4,59.69Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
<g style="clip-path: url(#0f29df77-bfe3-4749-8432-7a5b3681f927)"> <path id="LM_rightside1" data-name="LM rightside1" d="M31.25,47.89a.84.84,0,0,1-1,.68.85.85,0,0,1-.24-.09,6.63,6.63,0,0,0-2.85-.77,5.11,5.11,0,0,0-2.77.77c-.83.4-1.07-.59-1.07-.59V46.13a.72.72,0,0,1,.76-.66.67.67,0,0,1,.31.1,5.79,5.79,0,0,0,2.77.84,7.12,7.12,0,0,0,2.85-.84s1.18-.24,1.2.56S31.25,47.89,31.25,47.89Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
<g id="7cada08f-1e90-4ef2-8ae4-3f2adfd3e953" data-name="b7007e32-15c9-40b3-a885-a7231fd104d1"> </g>
<path id="02e64b51-bd56-4079-9736-8a269cb37029" data-name="c599a4f4-ae5d-411c-902d-dbcde04a2d6e" d="M60.44,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112Zm-3.61,0a1.09,1.09,0,1,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112ZM46,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6A1.08,1.08,0,0,1,47,140.65h0a1.07,1.07,0,0,1-1-1.11V112Z" style="fill: #6a6a6a"/> <g>
<image width="36" height="76" transform="translate(32 12)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAABNCAYAAAAhOa00AAAACXBIWXMAAAsSAAALEgHS3X78AAACAElEQVRoQ+3avVHDMBiA4VdcypgREmo4RggLpKKHAZiIAWgoKZggrEAGCBsQpxeFpNgJ/vkkK5YLvXe6g2Cb53zE9oVPaa3xSSmlALTvjh4pn2MrpQpgab/dAYdL4MQoC3oAnuxLH8AW2Gmty9YdAxKhaqAXYAUozJnaAm/AJiZs1rfBGegBKOyP7oAFMLfbRYN1ojpAYM7WtX3dbR8F1orqAdVz27n9BsMaUR4gV1yY1vpkYX7BGvgE9oD2WL/AO3CPfROFrKs6MOAMnVcAt3b57lsV6QzV194eYw0UfWelacUGRYFdAjQYNsPcy54xV+rwv4P/Bb8jZ5gr8oK4IFcQzF2nVOdWw/KG9d77IuUFGwsFHrAxUSCEjY0CASwFCnpgqVDQAUuJghZYahQY2Ao4YJ77v08eXRJWUHvenwoKaneVKaGOZZS0jJKWUdIySlpGScsoaRklLaOkZZS0jJKWUdIySlpGScsoaRklLaOkTQml3RdTQZXAD+bD2EmgSuALMwmyg/SoEtgAr9RGU1KiGkGQDtUKgjSoThCMj+oFwbgoEQjGQ4lBMA7KCwSXR3mDoPonpBsFccWYUwgCgUEdMAN/YDBzzKjJvG0nQe7W4Q2CCnFDhVgCj1TznCHtGDDVqLTWxylXW4wzdWDA/GfjnOcZMijddGBhouHTsfsDst/cEY24RzwAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="LM_level4-2" data-name="LM level4-2" d="M64.91,70.7,51.5,83.63,32.93,83,32.76,24c0-.84.16-.84.46-1.13l10.42-10.1.21-.19c.59-.4,2,.11,2,.95L65.29,34.25V70C65.3,70.44,65.16,70.43,64.91,70.7Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
</g>
<g>
<image width="25" height="27" transform="translate(32 69)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAAACXBIWXMAAAsSAAALEgHS3X78AAABYUlEQVRIS+2WvVXDMBRG7/NxGVf0CRVFchghWSAVA2QAxmEGGgZgguzgHlPTELv/KCSD/BMs2wkV9xwf2/LPlZ70JJkk2piZdQojUd8PAWuXm1kGrIBF3wcDVEAhqWw/aIi8ZAcccLKxFMAzcGzL0voikDwCWyBjPGt8JMysKfMtMuAeeAE+Ac04TsArsAcySUj6blGGq82aaS0JyXARqYDCzHJJSnzItrh+ucW1bi4ZsCQYUAmu0w9M75dzNCqc4KxLLivpkPjzJcL1Kwl/xL9oMnXC1lkdw6SBk+IyOPf3Qz9Z8DOzD73bIAXegCfiloUV8ABs/HV07qWSKjPLiSP3xwY3m+yIlKVwflXsofSVeseFHCJlKSPxlTqZ2TEoHpSNFtVIKsfIJotgnGyWCHplW3pG8GwRdGQVbqXu35zMJZAVONEdgayzr5uL33wugBvgQ1IFVxDVmJmF+Xk1UZsvIeCPQjWq5BoAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="LM_level3-2" data-name="LM level3-2" d="M54,92V74.7a2.19,2.19,0,0,0-.3-1.12c-.28-.27-3.52-3.62-3.52-3.62a1.55,1.55,0,0,0-1.16-.4c-.75,0-11.62,0-11.62,0L33,74.26v8l10.66.57Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
</g>
<g id="LM_leftside" data-name="LM leftside">
<path id="LM_leftside5" data-name="LM leftside5" d="M60.08,126.64H54.47A5.45,5.45,0,0,1,49,121.18h0V98.28a5.46,5.46,0,0,1,5.46-5.46h5.61a5.47,5.47,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,60.08,126.64Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<circle id="LM_leftside4" data-name="LM leftside4" cx="61.67" cy="123.24" r="3.79" style="fill: #fff"/>
<circle id="LM_leftside3" data-name="LM leftside3" cx="61.67" cy="100.03" r="3.79" style="fill: #fff"/>
<path id="LM_leftside2" data-name="LM leftside2" d="M57.77,116.72a4.15,4.15,0,0,1,2.34-.7,5.14,5.14,0,0,1,3,.7c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.84-1.23-.46l-1,.58s-.36.19-.36-.33V112s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.38-.29-1.33-.29-1.33H62.47s-.73,0-.73-.64v-1.3s-.23-.36-1.17-.31a3.57,3.57,0,0,0-1.4.31v1.3a.66.66,0,0,1-.66.64H57.25s-.25.2-.29,1.33.29,1.38.29,1.38h1.26s.8,0,.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.4-.82.46,0,1.8,0,1.8S57,117.16,57.77,116.72Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
<path id="LM_leftside1" data-name="LM leftside1" d="M64.62,104.92a.83.83,0,0,1-1,.68.68.68,0,0,1-.24-.08,6.46,6.46,0,0,0-2.85-.77,5,5,0,0,0-2.77.77c-.83.4-1.07-.6-1.07-.6v-1.76a.71.71,0,0,1,.76-.65.8.8,0,0,1,.31.09,5.79,5.79,0,0,0,2.77.84,7,7,0,0,0,2.85-.84s1.18-.24,1.2.56S64.62,104.92,64.62,104.92Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
</g>
<g>
<image width="58" height="64" transform="translate(0 81)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAABBCAYAAABvnNwUAAAACXBIWXMAAAsSAAALEgHS3X78AAACFElEQVRoQ+2bQY7TMBSGvxcqsaFbJDbDrEEcYeYCcwYOwIlYsWLDjg0nGG6A1ANkbgCNhGb1WNgBpxPbaUjT+smf5E3lxPlq/25S5YmqshQiIrk+S6EzLlxmHDOKiGyB18CLXN8F6IAW6I6RXkTWi94C73HCp6YFvgI7oFXVfaY/sIBsIPoBuAG26SMWoZ/ZHfAZuJ8ivMl1SHEgess6ouDGeQtc4WMjInlhVZ3VcAPeAd+AX4Ceqf0EvgDv8Cs11hpm4Hfda1xG11q6MbbAG9+S1zFLliMGWIHBF++jNcrRmfUnu8Gd/Bo32Lnp9w4gnt+jZM+4IU0hKzxZ9sJyGiMpPFmWy8ppiqjwJNkLzWmK/no7oBWRnapqdje+8Jym2BLcdEDmp6eQnKYYrMDczJaS00lEZQvMaZZR2YJzmuSJrIGcRhmbWVM5DRnIWsxpyF9ZqzkNacB2TkP6mTWb05DGek5DGtxfn6aXb0+Du1G+wrgo/Mus2aUbknsQMEWVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUovq8le5TLwanBvbj4A2VKRwtjjvLr+g2fAb1wFxUvgFfB89NCy2APfgU/AD1V9BNio6l5E7oOOpb9u0It+5KC4aQNwINyxTonZqWiJVHENytNk3eLBU9ERqc97UovnXwArGj2U8vx34WFJ/AG0vvY6LSeZMAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="LM_level2_grey-2" data-name="LM level2 grey-2" d="M49,140.61H6.31a5.78,5.78,0,0,1-5.78-5.78h0V105.19C10.89,91.13,18.65,81.32,18.65,81.32h.6s19.46.15,23.86.15a2.29,2.29,0,0,1,2.38,1L54.75,92v42.8A5.78,5.78,0,0,1,49,140.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
</g>
<g id="LM_total" data-name="LM total">
<g id="LM_whitepart" data-name="LM whitepart">
<g style="clip-path: url(#clip-path-2)">
<g id="LM_whitepart_combined" data-name="LM whitepart combined">
<path id="LM_whitepart_top" data-name="LM whitepart top" d="M43.48,141l-7.15-7.37L12.54,102.56V87.7L-.52,103.84v31.22l1,7.69Z" transform="translate(0.98 1.9)" style="fill: #f1f1f1"/>
</g> </g>
</g> </g>
</g> </g>
<circle id="a5a29926-0401-43af-b098-0c7439485e7c" data-name="bcdc8e69-90f7-4365-8f7a-7c1567704161" cx="29.69" cy="135.22" r="2.24" style="fill: #9a9a9a"/> <g>
<circle id="313e9fdc-7bbb-4d49-9d19-2415450fb691" data-name="b7785d8e-a2e4-41a7-9c34-4d9f6aebd906" cx="58.36" cy="84.71" r="2.24" style="fill: #9a9a9a"/> <image width="47" height="52" transform="translate(5 87)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAA1CAYAAAAHz2g0AAAACXBIWXMAAAsSAAALEgHS3X78AAABnElEQVRoQ+2azXHCMBBG3zIccQuBM5MWoIHUkAJSUQrIJQWkAlIJ6SDYd+UgGWTjHxQHr5TRm9HFIzz75G8Z8KwYY5iKiMjYnr/AdBQrUwVEpADWwGps70Qq4GiMKf2LkwRc8XvgGStxT47AG3DwJZb9+4fxin8BdkAx/InJbHFPWUQuEsaY4IUt9gn4AE6AmWl9A+/AIy49CwJxDbvBxmaOk/cpgAe8fgsWwN5k69acxdc0vvGCBFzud9jT39C6mQY3C7Sado/O6V9xk4By7ge5SQD93PcyKhBj7n0GBWLNvU+vQArFQ49AzE3bpu8JRNu0ba4EYm/aNg2BVHLvcxZIKfc+/hNIJvc+C0gv9z4LF501iUWnpo7QCvtHIaniodkDycTGZ/THXOxkAW2ygDZZQJssoE0W0CYLaJMFtMkC2mQBbbKANv9K4PdDE/PSqLMWqIAvoDEJEiElts7qfEV59iFknbD1PQFFPbexBDDGlCJy4EJs70hL4BN4pW/cpiVRcf/5nxA6Z4WgY+BpxgmsEDqntWDixFYM/ACNRtJx4q2GRAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<g id="1eb2ae58-2419-47d4-86bf-4f26a7f0cf61" data-name="KNOB"> <path id="LM_top_grey-2" data-name="LM top grey-2" d="M43.18,135.54H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.51C15,96.24,21.86,87.1,21.86,87.1H43.18A5.78,5.78,0,0,1,49,92.88h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<g id="26377a78-5df7-4bea-8daa-669ea612118c" data-name="5301e639-3e28-489d-96ec-91bda6d459b7"> </g>
<circle id="13e05316-9dcc-4be0-9e0e-4b1df48fad92" data-name="f1bc8b30-bdb2-4084-8c02-e322d1c0b9bf" cx="17.06" cy="17.06" r="17.06" style="fill: #b72b1c"/> <g id="LM_top" data-name="LM top">
<g style="clip-path: url(#clip-path-3)">
<g id="LM_top_total" data-name="LM top total">
<path id="LM_top_lines" data-name="LM top lines" d="M18.6,112.05v27.47a1.08,1.08,0,0,1-1.12,1,1.06,1.06,0,0,1-1.05-1V112.05A1.09,1.09,0,0,1,18.6,112Zm3.61,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.62,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.61,0v27.47a1.09,1.09,0,0,1-2.17.07V112.05a1.09,1.09,0,0,1,2.17-.07Zm3.62,0v27.47A1.1,1.1,0,0,1,32,140.64a1.08,1.08,0,0,1-1.11-1V112.05a1.09,1.09,0,0,1,2.17-.07Z" transform="translate(0.98 1.9)" style="fill: #6a6a6a"/>
</g>
</g> </g>
</g>
<circle id="LM_detail_2" data-name="LM detail 2" cx="50.32" cy="137.11" r="2.24" style="fill: #9a9a9a"/>
<circle id="LM_detail_1" data-name="LM detail 1" cx="21.66" cy="86.59" r="2.24" style="fill: #9a9a9a"/>
<g id="hole">
<g> <g>
<circle id="58277aee-b73f-4f42-94ef-3b959dc26807" data-name="ce4b2c57-9b3b-4aed-902e-31d9f366dc61" cx="20.12" cy="28.12" r="3.79" style="fill: #3c3c3c"/> <image width="38" height="38" transform="translate(44)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAnCAYAAACMo1E1AAAACXBIWXMAAAsSAAALEgHS3X78AAACRklEQVRYR9WYPXLbMBBGHzgpxRwhTJ3ER0gukMauUvl0btIlhU9gdSkzQ9dWbmBTtTYFdiUA/BGh0ILyzWBAUiTx+C0JYdeJCDlyzrlgdwU02pu2wEb7vSR3IMDNvUahUpgGuNHetAF+aG/aA+dAzoJzztV4gI/EMHOdM+AW2IhIxwxNwqlbNfAZuMXDpTBhmE3pTQ24Be6ANdAdc/HN2A+JW7d4wJphmFTpOTX+Pu/wD7YCWufcpIuDzinYF2K36t6Jp6kjdvFhFFBEooaH+ArcA8/ADh+mJdtO732vY9Uph4iQgjngCviuFx8b5F/bs451hUYxbBWxauCDtqXCOKXp8UbC+cLxp16qvTAS3lLhTJuF9xNBeC2sK84bzlQ2bYXzJ5VOtA1+5n/PvHnsNdT7twmdswmyhEKDGltchF9rKcdMPYMMrjSYyRGwVJQPaaiIpSJek5V0MHrv4D9w7pK0Bf5oT5UeKCghWeJXyQEZvfQ8uljnTHuD7J0r7Zgp4gg/iNKAvQgaXOnQRh+DZWWVbtgPT5RzsJfvhs49apuV8C4sy8jiyF38Mj0AfAt8A37zOinhbLCh7KvjfOHd4d/xO2AtA4l1BKcfx/4CDuFdWh0+4/+l/aARvVqJiHTOuQfd3bJsOULwIGu8AS3wZFNHqtEq00QhB/LXfcIJlabRKpM62HKYnLccsiPrQxlwOlgIlVWjyy0eGlhazbTjcKRwSEZ1cxYcRLXgIeca4Fq3f7JAyRUy4EIlRWuYdo5cKNNJcOfSX2jmA7RYwAeRAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<circle id="980a54d2-804e-408d-8655-debf2e5856d0" data-name="6a44a65a-f04b-4564-81a2-4eefa438c5f1" cx="28.27" cy="14" r="3.79" style="fill: #3c3c3c"/> <circle id="LM_red-2" data-name="LM red-2" cx="62.96" cy="18.95" r="17.06" style="fill: #d42715"/>
<circle id="9b2e3cd0-e4bd-42ce-b558-4f4bc46eaf23" data-name="d5fea8d1-47c1-448e-adb6-8519e2516216" cx="14.15" cy="5.85" r="3.79" style="fill: #3c3c3c"/> </g>
<circle id="06781f30-70ec-4914-81c3-4083325e7c18" data-name="1faf9727-3903-4be3-a3d3-4ab2d5d92b1f" cx="6" cy="19.97" r="3.79" style="fill: #3c3c3c"/> <g id="LM_details_in_red" data-name="LM details in red">
<path id="f1de636f-1fd2-4419-ae5c-d6a853c5834d" data-name="9e52c672-4ec8-4d92-9380-e44b4f69f848" d="M13.56,16.89a2.91,2.91,0,0,0,.7,2.26,12,12,0,0,1,1.2-.39c.69-.18.95.59.95.59l.33,1.21s.33.24,1.41-.08,1.2-.62,1.2-.62-.17-.6-.32-1.22a.66.66,0,0,1,.45-.8l1.26-.34a3.63,3.63,0,0,0-.07-1.43c-.19-.93-.6-1.05-.6-1.05l-1.25.34c-.65.17-.82-.54-.82-.54l-.36-1.33s-.25-.24-1.35,0-1.26.64-1.26.64.14.61.35,1.34a.7.7,0,0,1-.48.88l-.07,0Z" style="fill: #242424"/> <circle id="LM_detail_red_hole4" data-name="LM detail red hole4" cx="59.9" cy="30.01" r="3.79" style="fill: #393939"/>
<path id="e8012624-d689-4ee7-a77a-99e9713ea199" data-name="3179af54-0c25-42bc-9804-f3575e0b1b0b" d="M17.44,24.25a.47.47,0,0,1-.56-.32l-.55-2.06h0a.45.45,0,0,1,.32-.55h0l1.63-.23,1.44-.6a.47.47,0,0,1,.56.32l.55,2.05h0a.45.45,0,0,1-.32.55h0L18.94,24Z" style="fill: #3c3c3c"/> <circle id="LM_detail_red_hole3" data-name="LM detail red hole3" cx="51.75" cy="15.89" r="3.79" style="fill: #393939"/>
<path id="31a64bab-6b92-41d0-afe9-392c1fae8ddb" data-name="841a583e-aa25-4a7e-8907-fbe64ce57066" d="M9.88,17.31a.46.46,0,0,1,.33-.56l2-.55h0a.47.47,0,0,1,.56.33L13,18.16l.6,1.45a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.57-1.53L9.84,17.3Z" style="fill: #3c3c3c"/> <circle id="LM_detail_red_hole2" data-name="LM detail red hole2" cx="65.87" cy="7.74" r="3.79" style="fill: #393939"/>
<path id="2b1c6b9e-6fda-40be-8bb8-318c30b7dca4" data-name="42515acc-688f-41c7-884c-80b8a90e09c0" d="M16.82,9.8a.47.47,0,0,1,.56.32l.55,2.05h0a.46.46,0,0,1-.33.55L16,13l-1.45.61a.45.45,0,0,1-.55-.32h0l-.53-2.1h0a.45.45,0,0,1,.31-.56h0l1.53-.57,1.55-.25Z" style="fill: #3c3c3c"/> <circle id="LM_detail_red_hole1" data-name="LM detail red hole1" cx="74.02" cy="21.86" r="3.79" style="fill: #393939"/>
<path id="be04cab5-eaac-4d03-a1ee-dbe70dd5bf90" data-name="8690082b-7091-4631-a199-b79823698dd6" d="M24.33,16.74a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.22-1.64-.6-1.44a.45.45,0,0,1,.31-.56h0l2-.55h0a.46.46,0,0,1,.56.33l.57,1.53.25,1.55Z" style="fill: #3c3c3c"/> <path id="LM_detail_hole" data-name="LM detail hole" d="M64.2,16.41a.72.72,0,0,1-.55-.91c.21-.72.36-1.33.36-1.33s-.17-.35-1.27-.64-1.35-.06-1.35-.06L61,14.8s-.16.72-.81.54L59,15s-.4.13-.6,1.06a3.44,3.44,0,0,0-.06,1.43l1.25.33a.68.68,0,0,1,.46.81c-.16.61-.33,1.21-.33,1.21s.12.3,1.2.62,1.42.09,1.42.09l.32-1.22s.26-.76,1-.59q.6.17,1.2.39a2.92,2.92,0,0,0,.7-2.25C64.91,16.65,64.2,16.41,64.2,16.41Z" transform="translate(0.98 1.9)" style="fill: #1f1f1f"/>
<path id="LM_detail_red4" data-name="LM detail red4" d="M61.64,24.23,60.09,24l-1.53-.57a.46.46,0,0,1-.33-.56h0l.55-2.05a.46.46,0,0,1,.56-.32l1.45.6,1.63.22a.46.46,0,0,1,.33.56h0l-.55,2.05a.44.44,0,0,1-.55.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
<path id="LM_detail_red3" data-name="LM detail red3" d="M69.15,17.3l-.26,1.55-.57,1.52a.45.45,0,0,1-.55.33h0l-2.06-.55a.46.46,0,0,1-.32-.56L66,18.15l.22-1.63a.45.45,0,0,1,.55-.33h0l2,.55a.46.46,0,0,1,.32.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
<path id="LM_detail_red2" data-name="LM detail red2" d="M62.21,9.79l1.55.25,1.53.57a.46.46,0,0,1,.33.55h0l-.55,2.05a.46.46,0,0,1-.56.32l-1.45-.6-1.63-.22a.47.47,0,0,1-.33-.56h0l.55-2.05a.44.44,0,0,1,.55-.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
<path id="LM_detail_red1" data-name="LM detail red1" d="M54.7,16.72,55,15.17l.57-1.52a.45.45,0,0,1,.55-.33h0l2.06.55a.46.46,0,0,1,.32.56l-.61,1.44-.22,1.63a.45.45,0,0,1-.55.33h0l-2-.55a.46.46,0,0,1-.32-.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
</g> </g>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,70 +1,88 @@
namespace pxsim { namespace pxsim {
export const LARGE_MOTOR_SVG = `<svg id="e13b39e7-895d-4931-9be9-7aecaf709784" data-name="46d2506d-be16-4175-90b0-aabf357ea333" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 79.56 153.61"> export const LARGE_MOTOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82 156">
<defs> <defs>
<clipPath id="7dd75c25-af24-4f64-9ea1-f270fbd17872"> <clipPath id="clip-path" transform="translate(0.98 1.9)">
<path d="M12.82.54a17.07,17.07,0,0,1,20.77,12.3,1.53,1.53,0,0,1,1.6-.24l.21.19,10.41,10.1c.31.3.47.3.47,1.14V35.81h6.79a4,4,0,0,1,4,4V65.63a4,4,0,0,1-4,4H46.15v11.8l13.68-.1h.61s7.76,9.81,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78H69.17v7.57A4,4,0,0,1,67,151.75V153h0a.63.63,0,0,1-.61.61H40.08a.6.6,0,0,1-.64-.56V152a4,4,0,0,1-2.89-3.85v-7.57H30.07a5.78,5.78,0,0,1-5.78-5.78v-8.18h-6.8a4,4,0,0,1-4-4V96.84a4,4,0,0,1,4-4h6.8V92l.75-.78v-10L14.13,70.71c-.25-.27-.39-.25-.39-.67V34.27l.37-.39A17.06,17.06,0,0,1,12.82.54Z" style="fill: none"/> <path d="M79,17.05A17.07,17.07,0,0,1,64.92,33.86l.36.39V70c0,.42-.14.4-.39.68L54,81.22v10l.75.78v.79h6.79a4,4,0,0,1,4,4v25.82a4,4,0,0,1-4,4H54.75v8.18A5.79,5.79,0,0,1,49,140.61h-6.5v7.57a4,4,0,0,1-2.9,3.84v1a.6.6,0,0,1-.6.6H12.69a.6.6,0,0,1-.6-.6h0v-1.29a4,4,0,0,1-2.17-3.55v-7.57H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59l13.68.1,0-11.8H26a4,4,0,0,1-4-4V39.79a4,4,0,0,1,4-4h6.78l0-11.77c0-.84.15-.84.46-1.14l10.41-10.1.22-.19a1.53,1.53,0,0,1,1.59.23A17.07,17.07,0,0,1,79,17.05Z" style="fill: none"/>
</clipPath> </clipPath>
<clipPath id="9d9a0722-eca6-4c98-bf03-49097e654f0e"> <clipPath id="clip-path-2" transform="translate(0.98 1.9)">
<path d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78V92l9.26-9.58a2.29,2.29,0,0,1,2.38-1c4.4,0,23.86-.15,23.86-.15h.65s7.76,9.8,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/> <path d="M49,140.61H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59s19.46.14,23.87.14a2.27,2.27,0,0,1,2.37,1L54.75,92v42.79A5.79,5.79,0,0,1,49,140.61Z" style="fill: none"/>
</clipPath> </clipPath>
<clipPath id="0f29df77-bfe3-4749-8432-7a5b3681f927"> <clipPath id="clip-path-3" transform="translate(0.98 1.9)">
<path d="M35.85,135.26a5.78,5.78,0,0,1-5.78-5.78h0V92.6h0a5.78,5.78,0,0,1,5.77-5.79H57.17S64.08,96,72.73,107.22v22.25h0A5.78,5.78,0,0,1,67,135.26H35.85Z" style="fill: none"/> <path d="M43.18,135.24H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.21C15,95.94,21.86,86.8,21.86,86.8H43.18A5.78,5.78,0,0,1,49,92.58h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/>
</clipPath> </clipPath>
</defs> </defs>
<title>Large Motor</title> <title>Large Motor</title>
<g style="clip-path: url(#7dd75c25-af24-4f64-9ea1-f270fbd17872)"> <g style="isolation: isolate">
<g> <g id="e13b39e7-895d-4931-9be9-7aecaf709784">
<path id="1614620b-40ec-4523-b90a-428aad6bd045" data-name="eadcf40d-5b1f-4f82-8d01-d628e9e4bcbb" d="M40.49,64.16V41.26A5.48,5.48,0,0,1,46,35.8h5.62A5.48,5.48,0,0,1,57,41.26v22.9h0a5.45,5.45,0,0,1-5.46,5.46H46A5.45,5.45,0,0,1,40.49,64.16Z" style="fill: #a8a9a8"/> <g style="clip-path: url(#clip-path)">
<circle id="1a43435f-56ec-4b4e-a3ae-b40d13b2f218" data-name="93612c19-30fe-4c1d-bb05-67002522431d" cx="51.72" cy="64.32" r="3.79" style="fill: #fff"/> <g id="Large_Motor" data-name="Large Motor">
<circle id="43fd443a-13b5-48ef-af02-32d5de3e95b1" data-name="aee14a95-6af8-4dce-8543-2eda3545de29" cx="51.72" cy="41.11" r="3.79" style="fill: #fff"/> <path id="LM_back2" data-name="LM back2" d="M39,153.62H12.69a.6.6,0,0,1-.6-.6h0v-5.3a.6.6,0,0,1,.6-.6H39a.6.6,0,0,1,.6.6V153A.6.6,0,0,1,39,153.62Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<path id="7bfb4e09-fb97-433a-8104-a0426c319fe6" data-name="1d2e9494-7cc7-48f0-9562-12d1d83b7bb0" d="M39.44,153v-5.29a.61.61,0,0,1,.61-.61H66.34a.61.61,0,0,1,.61.61h0V153h0a.63.63,0,0,1-.61.61H40.08a.61.61,0,0,1-.64-.58Z" style="fill: #a8a9a8"/> <g>
<path id="ded8d34f-3eb8-4d18-bbd7-80e03d734fef" data-name="ccd9d8b0-00ff-4f0b-bfdc-94153d199419" d="M36.58,151.58V136.89a.61.61,0,0,1,.61-.61H68.51a.61.61,0,0,1,.61.61h0v14.69a.61.61,0,0,1-.61.61H37.19a.61.61,0,0,1-.61-.61Z" style="fill: #a8a9a8"/> <image width="36" height="20" transform="translate(9 136)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAVCAYAAADB5CeuAAAACXBIWXMAAAsSAAALEgHS3X78AAAAp0lEQVRIS+3WsW3DMBBG4Y+GSmcGp/cAHsuzeIasFPdWVpD6c0FKcBWwOxV8wIEgSIAPvOL+EhFKKcUBiIiAgjO+25rJildErJMqdMdVlcwg8MQDv5P6Q1fc5ErRujW1TfmoLPa3T//dymJI9TKkehlSvQypXoZUL5vUNhAz2R1Oao75w9IOMmpRHVZqSpjxo3KRw+YwQ2lx+EsVykqfK+aIWGhSR+MNDy9ID5XUCOsAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="a30c6f9d-adb5-4251-ad04-1bd8bfaab7af" data-name="e8b462c5-81f0-4111-9486-bb5fc1221c77" d="M55.44,59.6V57.8c0-.87-.81-.46-.81-.46l-1.19.59c-.38.18-.33-.34-.33-.34V55c0-.72.82-.77.82-.77h1.25s.26-.2.26-1.38-.29-1.32-.29-1.32H53.9a.68.68,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1-.05-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H48.56s-.3.18-.3,1.32.3,1.38.3,1.38h1.38a.73.73,0,0,1,.73.77v2.62c0,.52-.36.34-.36.34l-1-.59c-.68-.38-1.23,0-1.23.46v1.8s0,1,1.23.1a5.08,5.08,0,0,1,3-.7,4.18,4.18,0,0,1,2.35.7C55.37,60.14,55.44,59.6,55.44,59.6Z" style="fill: #fff"/> <path id="LM_back1-2" data-name="LM back1-2" d="M41.86,152.17H10.53a.6.6,0,0,1-.6-.6v-14.7a.6.6,0,0,1,.6-.6H41.86a.6.6,0,0,1,.6.6h0v14.7A.6.6,0,0,1,41.86,152.17Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<path id="c7805530-90df-4e25-868f-c9475cacc294" data-name="51a46c0a-a8a3-4420-b297-1d82512a0b32" d="M47.79,46.14c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.84,5.72,5.72,0,0,0,2.76-.84.85.85,0,0,1,.31-.09.71.71,0,0,1,.76.65V47.9s-.24,1-1.07.6a5,5,0,0,0-2.76-.77A6.46,6.46,0,0,0,49,48.5a.68.68,0,0,1-.24.08.83.83,0,0,1-1-.61s0,0,0-.07S47.76,46.94,47.79,46.14Z" style="fill: #fff"/>
<path id="f8fcbff1-7e8c-4c85-8f0e-222f81e36654" data-name="be4fbc2c-74e0-495a-bfad-cf557a83a287" d="M13.75,70V34.27L33.21,13.56c0-.84,1.4-1.36,2-1l.21.19,10.42,10.1c.31.3.46.3.46,1.14l-.19,59-18.57.67-13.4-13C13.89,70.44,13.75,70.46,13.75,70Z" style="fill: #a8a9a8"/>
<path id="1dc809f3-4ab0-4e83-b8a0-ab178c9c843d" data-name="4d02192d-b81d-48fe-8732-1ddf5bded46b" d="M35.44,82.8l10.67-.57v-8l-4.43-4.7H30.05a1.6,1.6,0,0,0-1.16.4L25.34,73.6c-.28.27-.3,1.12-.3,1.12V92Z" style="fill: #a8a9a8"/>
<g id="e0794b99-de5a-4337-b43b-7cdb158e4272" data-name="233cb9b7-9706-41a9-a0a4-7b51b61b2ff5">
<path id="c86708aa-fcc1-4e15-8d85-6e5415398b0c" data-name="6ed465b7-465a-4389-8ebc-d8f92a4226dd" d="M13.49,121.2V98.29A5.46,5.46,0,0,1,19,92.84h5.62A5.45,5.45,0,0,1,30,98.3v22.9h0a5.48,5.48,0,0,1-5.46,5.46H19A5.48,5.48,0,0,1,13.49,121.2Z" style="fill: #a8a9a8"/>
<circle id="0355a228-ed20-4633-ba10-c36cf551e228" data-name="955ad5d6-af09-42ad-8d56-9ff62c40912d" cx="18.35" cy="121.35" r="3.79" style="fill: #fff"/>
<circle id="73922eab-afce-48dd-9bae-80da92a8b392" data-name="75a0ff09-d40c-46e3-aaf0-c5878f2efdac" cx="18.35" cy="98.14" r="3.79" style="fill: #fff"/>
<path id="e9f7b6a6-dcdd-4d55-a7bb-ad3951bb1e39" data-name="eeb5a6b6-ef4a-4f05-95cd-8b1c3eb419c4" d="M22.07,116.64v-1.8c0-.86-.81-.46-.81-.46L20,115c-.38.18-.33-.34-.33-.34V112c0-.72.82-.76.82-.76h1.25s.32-.26.29-1.39-.29-1.32-.29-1.32H20.53a.67.67,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1,0-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H15.19s-.3.18-.3,1.32.3,1.39.3,1.39h1.38a.71.71,0,0,1,.73.69v2.69c0,.52-.36.34-.36.34l-1-.58c-.68-.39-1.23,0-1.23.46v1.8s0,1,1.23.1a5.09,5.09,0,0,1,3-.71,4.16,4.16,0,0,1,2.35.71C22,117.17,22.07,116.64,22.07,116.64Z" style="fill: #fff"/>
<path id="82138da6-9b2a-4b9a-952e-568841e5ed52" data-name="80481c02-bb01-43aa-bd64-15ab30fbbb5e" d="M14.44,103.18c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.83,5.78,5.78,0,0,0,2.77-.83.7.7,0,0,1,1,.25.67.67,0,0,1,.1.31v1.76s-.24,1-1.07.59a5,5,0,0,0-2.77-.77,6.59,6.59,0,0,0-2.85.77.68.68,0,0,1-.24.08.83.83,0,0,1-1-.63v0S14.44,104,14.44,103.18Z" style="fill: #fff"/>
</g>
<path id="aa62b88a-4cd5-4c76-acaf-e85a7fd45341" data-name="6b63d025-68d6-47eb-ba62-60017f4a1dd4" d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78h0V92l9.26-9.57a2.29,2.29,0,0,1,2.38-1c4.4,0,23.87-.14,23.87-.14h.64s7.76,9.8,18.13,23.86v29.64h0a5.79,5.79,0,0,1-5.79,5.79Z" style="fill: #a8a9a8"/>
<g>
<g id="1172473e-5b0c-4dd6-85c5-28f5593b9312" data-name="a0498222-7e69-48db-90b7-05f2ce124a03">
<g style="clip-path: url(#9d9a0722-eca6-4c98-bf03-49097e654f0e)">
<g id="da9d77a3-c9b0-4118-a8e3-c65ec5cbe7d4" data-name="8270fb93-0862-4480-a5b8-675cc77ac153">
<path id="77253f44-139e-4b90-82df-465a787cab9c" data-name="bb0a4ec3-45a5-4c91-b064-db5494e447e6" d="M78.56,142.76l1-7.69V103.85L66.49,87.72v14.86L42.7,133.66,35.55,141Z" style="fill: #f2f2f2"/>
</g>
</g> </g>
</g> <g id="LM_rightside" data-name="LM rightside">
<path id="9d05c10c-72eb-4f9f-8517-e403ec636357" data-name="74e07bdd-431b-40c1-b0f3-71128f2a0c08" d="M35.85,135.56a5.78,5.78,0,0,1-5.78-5.78h0V92.9h0a5.78,5.78,0,0,1,5.77-5.79H57.17s6.91,9.15,15.56,20.41v22.25h0A5.78,5.78,0,0,1,67,135.56H35.85Z" style="fill: #a8a9a8"/> <path id="LM_rightside5" data-name="LM rightside5" d="M33.08,69.61H27.47A5.45,5.45,0,0,1,22,64.15h0V41.25a5.45,5.45,0,0,1,5.46-5.46h5.61a5.46,5.46,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,33.08,69.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<g id="6df8eb1f-3187-427f-8d99-bc7a37e6cc25" data-name="9bbfb3ec-86c2-4d27-8275-76f2f9737a7f"> <circle id="LM_rightside4" data-name="LM rightside4" cx="28.3" cy="66.21" r="3.79" style="fill: #fff"/>
<g id="e4d7143c-1fb6-45a4-a205-1c920b8a823c" data-name="7e017c33-f09b-4d39-803f-1bedf1ebf566"> <circle id="LM_rightside3" data-name="LM rightside3" cx="28.3" cy="43" r="3.79" style="fill: #fff"/>
<g id="3a4e493d-cd17-48c4-97b6-da7ee3c4898e" data-name="69e3df75-72a6-4499-baab-f65ec194e193"> <path id="LM_rightside2" data-name="LM rightside2" d="M24.4,59.69A4.16,4.16,0,0,1,26.74,59a5.23,5.23,0,0,1,3,.71c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.85-1.23-.46l-1,.58s-.36.19-.36-.33V55s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.39-.29-1.32-.29-1.32H29.1s-.73,0-.73-.65v-1.3s-.23-.36-1.17-.3a3.56,3.56,0,0,0-1.4.3v1.3a.66.66,0,0,1-.66.65c-.63,0-1.26,0-1.26,0s-.25.19-.29,1.32.29,1.39.29,1.39h1.26s.8.05.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.41-.82.46,0,1.8,0,1.8S23.67,60.12,24.4,59.69Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
<g style="clip-path: url(#0f29df77-bfe3-4749-8432-7a5b3681f927)"> <path id="LM_rightside1" data-name="LM rightside1" d="M31.25,47.89a.84.84,0,0,1-1,.68.85.85,0,0,1-.24-.09,6.63,6.63,0,0,0-2.85-.77,5.11,5.11,0,0,0-2.77.77c-.83.4-1.07-.59-1.07-.59V46.13a.72.72,0,0,1,.76-.66.67.67,0,0,1,.31.1,5.79,5.79,0,0,0,2.77.84,7.12,7.12,0,0,0,2.85-.84s1.18-.24,1.2.56S31.25,47.89,31.25,47.89Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
<g id="7cada08f-1e90-4ef2-8ae4-3f2adfd3e953" data-name="b7007e32-15c9-40b3-a885-a7231fd104d1"> </g>
<path id="02e64b51-bd56-4079-9736-8a269cb37029" data-name="c599a4f4-ae5d-411c-902d-dbcde04a2d6e" d="M60.44,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112Zm-3.61,0a1.09,1.09,0,1,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112ZM46,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6A1.08,1.08,0,0,1,47,140.65h0a1.07,1.07,0,0,1-1-1.11V112Z" style="fill: #6a6a6a"/> <g>
<image width="36" height="76" transform="translate(32 12)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAABNCAYAAAAhOa00AAAACXBIWXMAAAsSAAALEgHS3X78AAACAElEQVRoQ+3avVHDMBiA4VdcypgREmo4RggLpKKHAZiIAWgoKZggrEAGCBsQpxeFpNgJ/vkkK5YLvXe6g2Cb53zE9oVPaa3xSSmlALTvjh4pn2MrpQpgab/dAYdL4MQoC3oAnuxLH8AW2Gmty9YdAxKhaqAXYAUozJnaAm/AJiZs1rfBGegBKOyP7oAFMLfbRYN1ojpAYM7WtX3dbR8F1orqAdVz27n9BsMaUR4gV1yY1vpkYX7BGvgE9oD2WL/AO3CPfROFrKs6MOAMnVcAt3b57lsV6QzV194eYw0UfWelacUGRYFdAjQYNsPcy54xV+rwv4P/Bb8jZ5gr8oK4IFcQzF2nVOdWw/KG9d77IuUFGwsFHrAxUSCEjY0CASwFCnpgqVDQAUuJghZYahQY2Ao4YJ77v08eXRJWUHvenwoKaneVKaGOZZS0jJKWUdIySlpGScsoaRklLaOkZZS0jJKWUdIySlpGScsoaRklLaOkTQml3RdTQZXAD+bD2EmgSuALMwmyg/SoEtgAr9RGU1KiGkGQDtUKgjSoThCMj+oFwbgoEQjGQ4lBMA7KCwSXR3mDoPonpBsFccWYUwgCgUEdMAN/YDBzzKjJvG0nQe7W4Q2CCnFDhVgCj1TznCHtGDDVqLTWxylXW4wzdWDA/GfjnOcZMijddGBhouHTsfsDst/cEY24RzwAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="LM_level4-2" data-name="LM level4-2" d="M64.91,70.7,51.5,83.63,32.93,83,32.76,24c0-.84.16-.84.46-1.13l10.42-10.1.21-.19c.59-.4,2,.11,2,.95L65.29,34.25V70C65.3,70.44,65.16,70.43,64.91,70.7Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
</g>
<g>
<image width="25" height="27" transform="translate(32 69)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAAACXBIWXMAAAsSAAALEgHS3X78AAABYUlEQVRIS+2WvVXDMBRG7/NxGVf0CRVFchghWSAVA2QAxmEGGgZgguzgHlPTELv/KCSD/BMs2wkV9xwf2/LPlZ70JJkk2piZdQojUd8PAWuXm1kGrIBF3wcDVEAhqWw/aIi8ZAcccLKxFMAzcGzL0voikDwCWyBjPGt8JMysKfMtMuAeeAE+Ac04TsArsAcySUj6blGGq82aaS0JyXARqYDCzHJJSnzItrh+ucW1bi4ZsCQYUAmu0w9M75dzNCqc4KxLLivpkPjzJcL1Kwl/xL9oMnXC1lkdw6SBk+IyOPf3Qz9Z8DOzD73bIAXegCfiloUV8ABs/HV07qWSKjPLiSP3xwY3m+yIlKVwflXsofSVeseFHCJlKSPxlTqZ2TEoHpSNFtVIKsfIJotgnGyWCHplW3pG8GwRdGQVbqXu35zMJZAVONEdgayzr5uL33wugBvgQ1IFVxDVmJmF+Xk1UZsvIeCPQjWq5BoAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="LM_level3-2" data-name="LM level3-2" d="M54,92V74.7a2.19,2.19,0,0,0-.3-1.12c-.28-.27-3.52-3.62-3.52-3.62a1.55,1.55,0,0,0-1.16-.4c-.75,0-11.62,0-11.62,0L33,74.26v8l10.66.57Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
</g>
<g id="LM_leftside" data-name="LM leftside">
<path id="LM_leftside5" data-name="LM leftside5" d="M60.08,126.64H54.47A5.45,5.45,0,0,1,49,121.18h0V98.28a5.46,5.46,0,0,1,5.46-5.46h5.61a5.47,5.47,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,60.08,126.64Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<circle id="LM_leftside4" data-name="LM leftside4" cx="61.67" cy="123.24" r="3.79" style="fill: #fff"/>
<circle id="LM_leftside3" data-name="LM leftside3" cx="61.67" cy="100.03" r="3.79" style="fill: #fff"/>
<path id="LM_leftside2" data-name="LM leftside2" d="M57.77,116.72a4.15,4.15,0,0,1,2.34-.7,5.14,5.14,0,0,1,3,.7c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.84-1.23-.46l-1,.58s-.36.19-.36-.33V112s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.38-.29-1.33-.29-1.33H62.47s-.73,0-.73-.64v-1.3s-.23-.36-1.17-.31a3.57,3.57,0,0,0-1.4.31v1.3a.66.66,0,0,1-.66.64H57.25s-.25.2-.29,1.33.29,1.38.29,1.38h1.26s.8,0,.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.4-.82.46,0,1.8,0,1.8S57,117.16,57.77,116.72Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
<path id="LM_leftside1" data-name="LM leftside1" d="M64.62,104.92a.83.83,0,0,1-1,.68.68.68,0,0,1-.24-.08,6.46,6.46,0,0,0-2.85-.77,5,5,0,0,0-2.77.77c-.83.4-1.07-.6-1.07-.6v-1.76a.71.71,0,0,1,.76-.65.8.8,0,0,1,.31.09,5.79,5.79,0,0,0,2.77.84,7,7,0,0,0,2.85-.84s1.18-.24,1.2.56S64.62,104.92,64.62,104.92Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
</g>
<g>
<image width="58" height="64" transform="translate(0 81)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAABBCAYAAABvnNwUAAAACXBIWXMAAAsSAAALEgHS3X78AAACFElEQVRoQ+2bQY7TMBSGvxcqsaFbJDbDrEEcYeYCcwYOwIlYsWLDjg0nGG6A1ANkbgCNhGb1WNgBpxPbaUjT+smf5E3lxPlq/25S5YmqshQiIrk+S6EzLlxmHDOKiGyB18CLXN8F6IAW6I6RXkTWi94C73HCp6YFvgI7oFXVfaY/sIBsIPoBuAG26SMWoZ/ZHfAZuJ8ivMl1SHEgess6ouDGeQtc4WMjInlhVZ3VcAPeAd+AX4Ceqf0EvgDv8Cs11hpm4Hfda1xG11q6MbbAG9+S1zFLliMGWIHBF++jNcrRmfUnu8Gd/Bo32Lnp9w4gnt+jZM+4IU0hKzxZ9sJyGiMpPFmWy8ppiqjwJNkLzWmK/no7oBWRnapqdje+8Jym2BLcdEDmp6eQnKYYrMDczJaS00lEZQvMaZZR2YJzmuSJrIGcRhmbWVM5DRnIWsxpyF9ZqzkNacB2TkP6mTWb05DGek5DGtxfn6aXb0+Du1G+wrgo/Mus2aUbknsQMEWVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUovq8le5TLwanBvbj4A2VKRwtjjvLr+g2fAb1wFxUvgFfB89NCy2APfgU/AD1V9BNio6l5E7oOOpb9u0It+5KC4aQNwINyxTonZqWiJVHENytNk3eLBU9ERqc97UovnXwArGj2U8vx34WFJ/AG0vvY6LSeZMAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="LM_level2_grey-2" data-name="LM level2 grey-2" d="M49,140.61H6.31a5.78,5.78,0,0,1-5.78-5.78h0V105.19C10.89,91.13,18.65,81.32,18.65,81.32h.6s19.46.15,23.86.15a2.29,2.29,0,0,1,2.38,1L54.75,92v42.8A5.78,5.78,0,0,1,49,140.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
</g>
<g id="LM_total" data-name="LM total">
<g id="LM_whitepart" data-name="LM whitepart">
<g style="clip-path: url(#clip-path-2)">
<g id="LM_whitepart_combined" data-name="LM whitepart combined">
<path id="LM_whitepart_top" data-name="LM whitepart top" d="M43.48,141l-7.15-7.37L12.54,102.56V87.7L-.52,103.84v31.22l1,7.69Z" transform="translate(0.98 1.9)" style="fill: #f1f1f1"/>
</g> </g>
</g> </g>
</g> </g>
<circle id="a5a29926-0401-43af-b098-0c7439485e7c" data-name="bcdc8e69-90f7-4365-8f7a-7c1567704161" cx="29.69" cy="135.22" r="2.24" style="fill: #9a9a9a"/> <g>
<circle id="313e9fdc-7bbb-4d49-9d19-2415450fb691" data-name="b7785d8e-a2e4-41a7-9c34-4d9f6aebd906" cx="58.36" cy="84.71" r="2.24" style="fill: #9a9a9a"/> <image width="47" height="52" transform="translate(5 87)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAA1CAYAAAAHz2g0AAAACXBIWXMAAAsSAAALEgHS3X78AAABnElEQVRoQ+2azXHCMBBG3zIccQuBM5MWoIHUkAJSUQrIJQWkAlIJ6SDYd+UgGWTjHxQHr5TRm9HFIzz75G8Z8KwYY5iKiMjYnr/AdBQrUwVEpADWwGps70Qq4GiMKf2LkwRc8XvgGStxT47AG3DwJZb9+4fxin8BdkAx/InJbHFPWUQuEsaY4IUt9gn4AE6AmWl9A+/AIy49CwJxDbvBxmaOk/cpgAe8fgsWwN5k69acxdc0vvGCBFzud9jT39C6mQY3C7Sado/O6V9xk4By7ge5SQD93PcyKhBj7n0GBWLNvU+vQArFQ49AzE3bpu8JRNu0ba4EYm/aNg2BVHLvcxZIKfc+/hNIJvc+C0gv9z4LF501iUWnpo7QCvtHIaniodkDycTGZ/THXOxkAW2ygDZZQJssoE0W0CYLaJMFtMkC2mQBbbKANv9K4PdDE/PSqLMWqIAvoDEJEiElts7qfEV59iFknbD1PQFFPbexBDDGlCJy4EJs70hL4BN4pW/cpiVRcf/5nxA6Z4WgY+BpxgmsEDqntWDixFYM/ACNRtJx4q2GRAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<g id="1eb2ae58-2419-47d4-86bf-4f26a7f0cf61" data-name="KNOB"> <path id="LM_top_grey-2" data-name="LM top grey-2" d="M43.18,135.54H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.51C15,96.24,21.86,87.1,21.86,87.1H43.18A5.78,5.78,0,0,1,49,92.88h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
<g id="26377a78-5df7-4bea-8daa-669ea612118c" data-name="5301e639-3e28-489d-96ec-91bda6d459b7"> </g>
<circle id="13e05316-9dcc-4be0-9e0e-4b1df48fad92" data-name="f1bc8b30-bdb2-4084-8c02-e322d1c0b9bf" cx="17.06" cy="17.06" r="17.06" style="fill: #b72b1c"/> <g id="LM_top" data-name="LM top">
<g style="clip-path: url(#clip-path-3)">
<g id="LM_top_total" data-name="LM top total">
<path id="LM_top_lines" data-name="LM top lines" d="M18.6,112.05v27.47a1.08,1.08,0,0,1-1.12,1,1.06,1.06,0,0,1-1.05-1V112.05A1.09,1.09,0,0,1,18.6,112Zm3.61,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.62,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.61,0v27.47a1.09,1.09,0,0,1-2.17.07V112.05a1.09,1.09,0,0,1,2.17-.07Zm3.62,0v27.47A1.1,1.1,0,0,1,32,140.64a1.08,1.08,0,0,1-1.11-1V112.05a1.09,1.09,0,0,1,2.17-.07Z" transform="translate(0.98 1.9)" style="fill: #6a6a6a"/>
</g>
</g> </g>
</g>
<circle id="LM_detail_2" data-name="LM detail 2" cx="50.32" cy="137.11" r="2.24" style="fill: #9a9a9a"/>
<circle id="LM_detail_1" data-name="LM detail 1" cx="21.66" cy="86.59" r="2.24" style="fill: #9a9a9a"/>
<g id="hole">
<g> <g>
<circle id="58277aee-b73f-4f42-94ef-3b959dc26807" data-name="ce4b2c57-9b3b-4aed-902e-31d9f366dc61" cx="20.12" cy="28.12" r="3.79" style="fill: #3c3c3c"/> <image width="38" height="38" transform="translate(44)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAnCAYAAACMo1E1AAAACXBIWXMAAAsSAAALEgHS3X78AAACRklEQVRYR9WYPXLbMBBGHzgpxRwhTJ3ER0gukMauUvl0btIlhU9gdSkzQ9dWbmBTtTYFdiUA/BGh0ILyzWBAUiTx+C0JYdeJCDlyzrlgdwU02pu2wEb7vSR3IMDNvUahUpgGuNHetAF+aG/aA+dAzoJzztV4gI/EMHOdM+AW2IhIxwxNwqlbNfAZuMXDpTBhmE3pTQ24Be6ANdAdc/HN2A+JW7d4wJphmFTpOTX+Pu/wD7YCWufcpIuDzinYF2K36t6Jp6kjdvFhFFBEooaH+ArcA8/ADh+mJdtO732vY9Uph4iQgjngCviuFx8b5F/bs451hUYxbBWxauCDtqXCOKXp8UbC+cLxp16qvTAS3lLhTJuF9xNBeC2sK84bzlQ2bYXzJ5VOtA1+5n/PvHnsNdT7twmdswmyhEKDGltchF9rKcdMPYMMrjSYyRGwVJQPaaiIpSJek5V0MHrv4D9w7pK0Bf5oT5UeKCghWeJXyQEZvfQ8uljnTHuD7J0r7Zgp4gg/iNKAvQgaXOnQRh+DZWWVbtgPT5RzsJfvhs49apuV8C4sy8jiyF38Mj0AfAt8A37zOinhbLCh7KvjfOHd4d/xO2AtA4l1BKcfx/4CDuFdWh0+4/+l/aARvVqJiHTOuQfd3bJsOULwIGu8AS3wZFNHqtEq00QhB/LXfcIJlabRKpM62HKYnLccsiPrQxlwOlgIlVWjyy0eGlhazbTjcKRwSEZ1cxYcRLXgIeca4Fq3f7JAyRUy4EIlRWuYdo5cKNNJcOfSX2jmA7RYwAeRAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<circle id="980a54d2-804e-408d-8655-debf2e5856d0" data-name="6a44a65a-f04b-4564-81a2-4eefa438c5f1" cx="28.27" cy="14" r="3.79" style="fill: #3c3c3c"/> <circle id="LM_red-2" data-name="LM red-2" cx="62.96" cy="18.95" r="17.06" style="fill: #d42715"/>
<circle id="9b2e3cd0-e4bd-42ce-b558-4f4bc46eaf23" data-name="d5fea8d1-47c1-448e-adb6-8519e2516216" cx="14.15" cy="5.85" r="3.79" style="fill: #3c3c3c"/> </g>
<circle id="06781f30-70ec-4914-81c3-4083325e7c18" data-name="1faf9727-3903-4be3-a3d3-4ab2d5d92b1f" cx="6" cy="19.97" r="3.79" style="fill: #3c3c3c"/> <g id="LM_details_in_red" data-name="LM details in red">
<path id="f1de636f-1fd2-4419-ae5c-d6a853c5834d" data-name="9e52c672-4ec8-4d92-9380-e44b4f69f848" d="M13.56,16.89a2.91,2.91,0,0,0,.7,2.26,12,12,0,0,1,1.2-.39c.69-.18.95.59.95.59l.33,1.21s.33.24,1.41-.08,1.2-.62,1.2-.62-.17-.6-.32-1.22a.66.66,0,0,1,.45-.8l1.26-.34a3.63,3.63,0,0,0-.07-1.43c-.19-.93-.6-1.05-.6-1.05l-1.25.34c-.65.17-.82-.54-.82-.54l-.36-1.33s-.25-.24-1.35,0-1.26.64-1.26.64.14.61.35,1.34a.7.7,0,0,1-.48.88l-.07,0Z" style="fill: #242424"/> <circle id="LM_detail_red_hole4" data-name="LM detail red hole4" cx="59.9" cy="30.01" r="3.79" style="fill: #393939"/>
<path id="e8012624-d689-4ee7-a77a-99e9713ea199" data-name="3179af54-0c25-42bc-9804-f3575e0b1b0b" d="M17.44,24.25a.47.47,0,0,1-.56-.32l-.55-2.06h0a.45.45,0,0,1,.32-.55h0l1.63-.23,1.44-.6a.47.47,0,0,1,.56.32l.55,2.05h0a.45.45,0,0,1-.32.55h0L18.94,24Z" style="fill: #3c3c3c"/> <circle id="LM_detail_red_hole3" data-name="LM detail red hole3" cx="51.75" cy="15.89" r="3.79" style="fill: #393939"/>
<path id="31a64bab-6b92-41d0-afe9-392c1fae8ddb" data-name="841a583e-aa25-4a7e-8907-fbe64ce57066" d="M9.88,17.31a.46.46,0,0,1,.33-.56l2-.55h0a.47.47,0,0,1,.56.33L13,18.16l.6,1.45a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.57-1.53L9.84,17.3Z" style="fill: #3c3c3c"/> <circle id="LM_detail_red_hole2" data-name="LM detail red hole2" cx="65.87" cy="7.74" r="3.79" style="fill: #393939"/>
<path id="2b1c6b9e-6fda-40be-8bb8-318c30b7dca4" data-name="42515acc-688f-41c7-884c-80b8a90e09c0" d="M16.82,9.8a.47.47,0,0,1,.56.32l.55,2.05h0a.46.46,0,0,1-.33.55L16,13l-1.45.61a.45.45,0,0,1-.55-.32h0l-.53-2.1h0a.45.45,0,0,1,.31-.56h0l1.53-.57,1.55-.25Z" style="fill: #3c3c3c"/> <circle id="LM_detail_red_hole1" data-name="LM detail red hole1" cx="74.02" cy="21.86" r="3.79" style="fill: #393939"/>
<path id="be04cab5-eaac-4d03-a1ee-dbe70dd5bf90" data-name="8690082b-7091-4631-a199-b79823698dd6" d="M24.33,16.74a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.22-1.64-.6-1.44a.45.45,0,0,1,.31-.56h0l2-.55h0a.46.46,0,0,1,.56.33l.57,1.53.25,1.55Z" style="fill: #3c3c3c"/> <path id="LM_detail_hole" data-name="LM detail hole" d="M64.2,16.41a.72.72,0,0,1-.55-.91c.21-.72.36-1.33.36-1.33s-.17-.35-1.27-.64-1.35-.06-1.35-.06L61,14.8s-.16.72-.81.54L59,15s-.4.13-.6,1.06a3.44,3.44,0,0,0-.06,1.43l1.25.33a.68.68,0,0,1,.46.81c-.16.61-.33,1.21-.33,1.21s.12.3,1.2.62,1.42.09,1.42.09l.32-1.22s.26-.76,1-.59q.6.17,1.2.39a2.92,2.92,0,0,0,.7-2.25C64.91,16.65,64.2,16.41,64.2,16.41Z" transform="translate(0.98 1.9)" style="fill: #1f1f1f"/>
<path id="LM_detail_red4" data-name="LM detail red4" d="M61.64,24.23,60.09,24l-1.53-.57a.46.46,0,0,1-.33-.56h0l.55-2.05a.46.46,0,0,1,.56-.32l1.45.6,1.63.22a.46.46,0,0,1,.33.56h0l-.55,2.05a.44.44,0,0,1-.55.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
<path id="LM_detail_red3" data-name="LM detail red3" d="M69.15,17.3l-.26,1.55-.57,1.52a.45.45,0,0,1-.55.33h0l-2.06-.55a.46.46,0,0,1-.32-.56L66,18.15l.22-1.63a.45.45,0,0,1,.55-.33h0l2,.55a.46.46,0,0,1,.32.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
<path id="LM_detail_red2" data-name="LM detail red2" d="M62.21,9.79l1.55.25,1.53.57a.46.46,0,0,1,.33.55h0l-.55,2.05a.46.46,0,0,1-.56.32l-1.45-.6-1.63-.22a.47.47,0,0,1-.33-.56h0l.55-2.05a.44.44,0,0,1,.55-.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
<path id="LM_detail_red1" data-name="LM detail red1" d="M54.7,16.72,55,15.17l.57-1.52a.45.45,0,0,1,.55-.33h0l2.06.55a.46.46,0,0,1,.32.56l-.61,1.44-.22,1.63a.45.45,0,0,1-.55.33h0l-2-.55a.46.46,0,0,1-.32-.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
</g> </g>
</g> </g>
</g> </g>

View File

@ -1,28 +1,33 @@
<svg id="svg7610" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 44.14 44.15"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48">
<defs> <defs>
<linearGradient id="motor-linear-gradient" x1="-313.1" y1="551.59" x2="-313.1" y2="551.43" gradientTransform="matrix(44.14, 0, 0, -44.15, 13843.7, 24396.04)" gradientUnits="userSpaceOnUse"> <linearGradient id="linear-gradient" x1="-427.2" y1="440.79" x2="-427.2" y2="440.63" gradientTransform="matrix(44.14, 0, 0, -44.15, 18878.72, 19502.57)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8a9a8"/> <stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#545554"/> <stop offset="1" stop-color="#535453"/>
</linearGradient> </linearGradient>
</defs> </defs>
<title>MediumMotor</title> <title>MediumMotor</title>
<g id="Medium_Motor" data-name="Medium Motor"> <g style="isolation: isolate">
<g id="Group_4" data-name="Group 4"> <g id="svg7610">
<path id="Path_1" data-name="Path 1" d="M4.07,1.5h39a2.36,2.36,0,0,1,2.57,2V43.82c0,1-1.1,1.82-2.57,1.82h-39c-1.47,0-2.57-.81-2.57-1.82V3.47A2.36,2.36,0,0,1,4.07,1.5Z" transform="translate(-1.5 -1.49)" style="fill: url(#motor-linear-gradient)"/> <g id="Medium_Motor" data-name="Medium Motor">
</g> <g id="medmotor_box" data-name="medmotor box">
<g id="Group_7" data-name="Group 7"> <path id="medmotor_box_wgradient" data-name="medmotor box wgradient" d="M2.57,0h39a2.36,2.36,0,0,1,2.57,2V42.33c0,1-1.1,1.82-2.57,1.82h-39C1.1,44.15,0,43.34,0,42.33V2A2.36,2.36,0,0,1,2.57,0Z" transform="translate(2 1.84)" style="fill: url(#linear-gradient)"/>
<g id="g7596"> </g>
<path id="Union_1-2" data-name="Union 1-2" d="M1.5,22.74a6.21,6.21,0,0,1,6.22-6.22h8.8V7.72A6.22,6.22,0,0,1,22.74,1.5H24.4a6.21,6.21,0,0,1,6.22,6.22v8.8h8.8a6.21,6.21,0,0,1,6.22,6.22V24.4a6.22,6.22,0,0,1-6.22,6.22h-8.8v8.8a6.22,6.22,0,0,1-6.22,6.22H22.74a6.23,6.23,0,0,1-6.22-6.22h0v-8.8H7.72A6.22,6.22,0,0,1,1.5,24.4h0Z" transform="translate(-1.5 -1.49)" style="fill: #a8a9a8"/> <g id="medmotor_star" data-name="medmotor star">
<g>
<image width="48" height="48" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAxCAYAAACcXioiAAAACXBIWXMAAAsSAAALEgHS3X78AAACfElEQVRoQ+2aPXLbMBBG32ZUijlCnDo/R1AukCoHyAFS5Sw5g5t0bnwCu0ofpTZ9A1usvSmwoClRJEEAI4gzejMcqliS+2FB4AMoUVVyISICrIErO3dpgBpoNONDJde9RKTCJf4R+Ga/u9TADbAFalXdkYEsAiz5L8B3nICxCmyBa+Auh4jVVMAUneR/ABugAuRIaIUT9w4TJyLJIpIEWJ+/wrX8Bng7fgViMRtcRR5F5G/KO/FmKiCANa5Vq6nADhXwwY7DrjaLHALgeJcZQ4D32MtulYwil4AYfOXOogKxRLe8p7SAZC4CSnMRUJrBmThwbBbSRxIh4HFDs3VPwIQl7oXj/M1U3BBr3PUAY3aiEZGjVnzPjQZY4mOscbNqjIgGeLDzGINWvBUQaImHSOlGIUZu2IqbgAr4CtwCT8AL7sbndLzgcrvF5Vq1jQ98An5bwNSNSh9PuFw/A+KH0RhLXIo9K96dB1L68SnZs+JLnchaK75UAWA9ZskCgGULUFiugAZ4BJquAB0IPjcUZz9ugNoL8IqSNplOxA74Z0ezUlU1p3eNG5787tocUuaQOZXfAfe4XB9UVVcAqroTkTsLaghzoZ5TuFGPb+jWzA3Z6dBk/Hrgp53nVEJx7vKXnUMq0XBgp/cWNFaJbe+yYXzCoS14SINL/g9hAnors96KbM5Gqy0Dg+MHUNxjo+6z1Hmg5SKgNBcBpSktIGrk6VJSQOsopwLHKCVAed2sqmPnAMgnICaB9sv9VOAYOQTEWPEdGZKHRAFWeu8Q74Fnpjemnnm1xEndBxI/dEOUFe9Z4hSy/FcCZlnxniVOIZsACPhKYaR2my5ZBZTgP7HrUIs43RAaAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="medmotor_cut-2" data-name="medmotor cut-2" d="M0,21.25A6.21,6.21,0,0,1,6.22,15H15V6.23A6.22,6.22,0,0,1,21.24,0H22.9a6.22,6.22,0,0,1,6.22,6.22V15h8.8a6.21,6.21,0,0,1,6.22,6.22v1.66a6.21,6.21,0,0,1-6.22,6.22h-8.8v8.8a6.21,6.21,0,0,1-6.22,6.22H21.24A6.22,6.22,0,0,1,15,37.93v-8.8H6.22A6.22,6.22,0,0,1,0,22.92H0Z" transform="translate(2 1.84)" style="fill: #a8aaa8"/>
</g>
<circle id="medmotor_hole_4" data-name="medmotor hole 4" cx="39.77" cy="24" r="4.85" style="fill: #393939"/>
<circle id="medmotor_hole_3" data-name="medmotor hole 3" cx="8.37" cy="24" r="4.85" style="fill: #393939"/>
<circle id="medmotor_hole_2" data-name="medmotor hole 2" cx="24.15" cy="8.22" r="4.85" style="fill: #393939"/>
<circle id="medmotor_hole_1" data-name="medmotor hole 1" cx="24.15" cy="39.62" r="4.85" style="fill: #393939"/>
</g>
<g id="medmotor_red" data-name="medmotor red">
<circle cx="24.3" cy="24" r="6.75" style="fill: #d42715"/>
<circle cx="24.3" cy="24" r="6.63" style="fill: none;stroke: #a20800;stroke-width: 0.25px"/>
</g>
<path id="medmotor_Hole" data-name="medmotor Hole" d="M20.59,19.46s-.05,1-.77,1-1.46,0-1.46,0a2.38,2.38,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.21.21,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29A6.88,6.88,0,0,0,24,26.15V24.48a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H24.6a.65.65,0,0,1-.75-.51.49.49,0,0,1,0-.17,11.22,11.22,0,0,1,0-1.64,4.78,4.78,0,0,0-3.25,0C20.58,18.74,20.59,19.46,20.59,19.46Z" transform="translate(2 1.84)"/>
</g> </g>
<circle id="Ellipse_1" data-name="Ellipse 1" cx="37.77" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_2" data-name="Ellipse 2" cx="6.37" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_1-2" data-name="Ellipse 1-2" cx="22.15" cy="6.38" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_2-2" data-name="Ellipse 2-2" cx="22.15" cy="37.78" r="4.85" style="fill: #3c3c3c"/>
</g> </g>
<g id="Ellipse_4" data-name="Ellipse 4">
<circle id="ellipse7603" cx="22.3" cy="22.16" r="6.75" style="fill: #b72b1c"/>
<circle id="ellipse7605" cx="22.3" cy="22.16" r="6.63" style="fill: none;stroke: #8b1104;stroke-width: 0.25px"/>
</g>
<path id="Hole" d="M23.9,26.72s.56-.86,1.18-.51,1.27.73,1.27.73a2.37,2.37,0,0,0,1.24-1.23c.63-1.1.49-1.57.49-1.57l-1.41-.81a.64.64,0,0,1-.32-.86l.06-.1.84-1.44a3.9,3.9,0,0,0-1.39-1.13,6,6,0,0,0-1.6-.59l-.83,1.44a.73.73,0,0,1-1,.24,11.15,11.15,0,0,0-1.32-.6s-.54.13-1.2,1.2-.51,1.6-.51,1.6l1.46.84a.64.64,0,0,1,.4.82.55.55,0,0,1-.08.15,11.25,11.25,0,0,1-.82,1.43A4.83,4.83,0,0,0,23.19,28C23.56,27.35,23.9,26.72,23.9,26.72Z" transform="translate(-1.5 -1.49)"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,30 +1,35 @@
namespace pxsim.visuals { namespace pxsim.visuals {
export const MEDIUM_MOTOR_SVG = `<svg id="svg7610" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 44.14 44.15"> export const MEDIUM_MOTOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48">
<defs> <defs>
<linearGradient id="motor-linear-gradient" x1="-313.1" y1="551.59" x2="-313.1" y2="551.43" gradientTransform="matrix(44.14, 0, 0, -44.15, 13843.7, 24396.04)" gradientUnits="userSpaceOnUse"> <linearGradient id="linear-gradient" x1="-427.2" y1="440.79" x2="-427.2" y2="440.63" gradientTransform="matrix(44.14, 0, 0, -44.15, 18878.72, 19502.57)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8a9a8"/> <stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#545554"/> <stop offset="1" stop-color="#535453"/>
</linearGradient> </linearGradient>
</defs> </defs>
<title>Medium Motor</title> <title>MediumMotor</title>
<g id="Medium_Motor" data-name="Medium Motor"> <g style="isolation: isolate">
<g id="Group_4" data-name="Group 4"> <g id="svg7610">
<path id="Path_1" data-name="Path 1" d="M4.07,1.5h39a2.36,2.36,0,0,1,2.57,2V43.82c0,1-1.1,1.82-2.57,1.82h-39c-1.47,0-2.57-.81-2.57-1.82V3.47A2.36,2.36,0,0,1,4.07,1.5Z" transform="translate(-1.5 -1.49)" style="fill: url(#motor-linear-gradient)"/> <g id="Medium_Motor" data-name="Medium Motor">
</g> <g id="medmotor_box" data-name="medmotor box">
<g id="Group_7" data-name="Group 7"> <path id="medmotor_box_wgradient" data-name="medmotor box wgradient" d="M2.57,0h39a2.36,2.36,0,0,1,2.57,2V42.33c0,1-1.1,1.82-2.57,1.82h-39C1.1,44.15,0,43.34,0,42.33V2A2.36,2.36,0,0,1,2.57,0Z" transform="translate(2 1.84)" style="fill: url(#linear-gradient)"/>
<g id="g7596"> </g>
<path id="Union_1-2" data-name="Union 1-2" d="M1.5,22.74a6.21,6.21,0,0,1,6.22-6.22h8.8V7.72A6.22,6.22,0,0,1,22.74,1.5H24.4a6.21,6.21,0,0,1,6.22,6.22v8.8h8.8a6.21,6.21,0,0,1,6.22,6.22V24.4a6.22,6.22,0,0,1-6.22,6.22h-8.8v8.8a6.22,6.22,0,0,1-6.22,6.22H22.74a6.23,6.23,0,0,1-6.22-6.22h0v-8.8H7.72A6.22,6.22,0,0,1,1.5,24.4h0Z" transform="translate(-1.5 -1.49)" style="fill: #a8a9a8"/> <g id="medmotor_star" data-name="medmotor star">
<g>
<image width="48" height="48" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAxCAYAAACcXioiAAAACXBIWXMAAAsSAAALEgHS3X78AAACfElEQVRoQ+2aPXLbMBBG32ZUijlCnDo/R1AukCoHyAFS5Sw5g5t0bnwCu0ofpTZ9A1usvSmwoClRJEEAI4gzejMcqliS+2FB4AMoUVVyISICrIErO3dpgBpoNONDJde9RKTCJf4R+Ga/u9TADbAFalXdkYEsAiz5L8B3nICxCmyBa+Auh4jVVMAUneR/ABugAuRIaIUT9w4TJyLJIpIEWJ+/wrX8Bng7fgViMRtcRR5F5G/KO/FmKiCANa5Vq6nADhXwwY7DrjaLHALgeJcZQ4D32MtulYwil4AYfOXOogKxRLe8p7SAZC4CSnMRUJrBmThwbBbSRxIh4HFDs3VPwIQl7oXj/M1U3BBr3PUAY3aiEZGjVnzPjQZY4mOscbNqjIgGeLDzGINWvBUQaImHSOlGIUZu2IqbgAr4CtwCT8AL7sbndLzgcrvF5Vq1jQ98An5bwNSNSh9PuFw/A+KH0RhLXIo9K96dB1L68SnZs+JLnchaK75UAWA9ZskCgGULUFiugAZ4BJquAB0IPjcUZz9ugNoL8IqSNplOxA74Z0ezUlU1p3eNG5787tocUuaQOZXfAfe4XB9UVVcAqroTkTsLaghzoZ5TuFGPb+jWzA3Z6dBk/Hrgp53nVEJx7vKXnUMq0XBgp/cWNFaJbe+yYXzCoS14SINL/g9hAnors96KbM5Gqy0Dg+MHUNxjo+6z1Hmg5SKgNBcBpSktIGrk6VJSQOsopwLHKCVAed2sqmPnAMgnICaB9sv9VOAYOQTEWPEdGZKHRAFWeu8Q74Fnpjemnnm1xEndBxI/dEOUFe9Z4hSy/FcCZlnxniVOIZsACPhKYaR2my5ZBZTgP7HrUIs43RAaAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="medmotor_cut-2" data-name="medmotor cut-2" d="M0,21.25A6.21,6.21,0,0,1,6.22,15H15V6.23A6.22,6.22,0,0,1,21.24,0H22.9a6.22,6.22,0,0,1,6.22,6.22V15h8.8a6.21,6.21,0,0,1,6.22,6.22v1.66a6.21,6.21,0,0,1-6.22,6.22h-8.8v8.8a6.21,6.21,0,0,1-6.22,6.22H21.24A6.22,6.22,0,0,1,15,37.93v-8.8H6.22A6.22,6.22,0,0,1,0,22.92H0Z" transform="translate(2 1.84)" style="fill: #a8aaa8"/>
</g>
<circle id="medmotor_hole_4" data-name="medmotor hole 4" cx="39.77" cy="24" r="4.85" style="fill: #393939"/>
<circle id="medmotor_hole_3" data-name="medmotor hole 3" cx="8.37" cy="24" r="4.85" style="fill: #393939"/>
<circle id="medmotor_hole_2" data-name="medmotor hole 2" cx="24.15" cy="8.22" r="4.85" style="fill: #393939"/>
<circle id="medmotor_hole_1" data-name="medmotor hole 1" cx="24.15" cy="39.62" r="4.85" style="fill: #393939"/>
</g>
<g id="medmotor_red" data-name="medmotor red">
<circle cx="24.3" cy="24" r="6.75" style="fill: #d42715"/>
<circle cx="24.3" cy="24" r="6.63" style="fill: none;stroke: #a20800;stroke-width: 0.25px"/>
</g>
<path id="medmotor_Hole" data-name="medmotor Hole" d="M20.59,19.46s-.05,1-.77,1-1.46,0-1.46,0a2.38,2.38,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.21.21,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29A6.88,6.88,0,0,0,24,26.15V24.48a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H24.6a.65.65,0,0,1-.75-.51.49.49,0,0,1,0-.17,11.22,11.22,0,0,1,0-1.64,4.78,4.78,0,0,0-3.25,0C20.58,18.74,20.59,19.46,20.59,19.46Z" transform="translate(2 1.84)"/>
</g> </g>
<circle id="Ellipse_1" data-name="Ellipse 1" cx="37.77" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_2" data-name="Ellipse 2" cx="6.37" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_1-2" data-name="Ellipse 1-2" cx="22.15" cy="6.38" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_2-2" data-name="Ellipse 2-2" cx="22.15" cy="37.78" r="4.85" style="fill: #3c3c3c"/>
</g> </g>
<g id="Ellipse_4" data-name="Ellipse 4">
<circle id="ellipse7603" cx="22.3" cy="22.16" r="6.75" style="fill: #b72b1c"/>
<circle id="ellipse7605" cx="22.3" cy="22.16" r="6.63" style="fill: none;stroke: #8b1104;stroke-width: 0.25px"/>
</g>
<path id="Hole" d="M22.09,21s-.05,1-.77,1H19.86a2.37,2.37,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.2.2,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29,6.88,6.88,0,0,0,1.68-.29V26a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H26.1a.65.65,0,0,1-.75-.51.5.5,0,0,1,0-.17,11.36,11.36,0,0,1,0-1.65,4.9,4.9,0,0,0-3.25,0C22.08,20.23,22.09,21,22.09,21Z" transform="translate(-1.5 -1.49)"/>
</g> </g>
</svg>`; </svg>`;
} }

View File

@ -1,5 +1,9 @@
/// <reference path="./layoutView.ts" /> /// <reference path="./layoutView.ts" />
namespace pxsim {
export const GAME_LOOP_FPS = 32;
}
namespace pxsim.visuals { namespace pxsim.visuals {
const EV3_STYLE = ` const EV3_STYLE = `
@ -29,13 +33,21 @@ namespace pxsim.visuals {
font-family:"Lucida Console", Monaco, monospace; font-family:"Lucida Console", Monaco, monospace;
font-size:8px; font-size:8px;
fill:#fff; fill:#fff;
pointer-events: none; user-select: none; pointer-events: none;
user-select: none;
} }
.sim-text.small { .sim-text.small {
font-size:6px; font-size:6px;
} }
.sim-text.large {
font-size:30px;
}
.sim-text.number {
font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro;
font-weight: bold;
}
.sim-text.inverted { .sim-text.inverted {
fill:#000; fill:#5A5A5A;
} }
/* Color Grid */ /* Color Grid */
@ -95,16 +107,9 @@ namespace pxsim.visuals {
private layoutView: LayoutView; private layoutView: LayoutView;
private controlGroup: ViewContainer;
private selectedNode: NodeType;
private selectedPort: number;
private controlView: View;
private cachedControlNodes: { [index: string]: View[] } = {}; private cachedControlNodes: { [index: string]: View[] } = {};
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {}; private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
private closeGroup: ViewContainer;
private closeIconView: View;
private screenCanvas: HTMLCanvasElement; private screenCanvas: HTMLCanvasElement;
private screenCanvasCtx: CanvasRenderingContext2D; private screenCanvasCtx: CanvasRenderingContext2D;
private screenCanvasData: ImageData; private screenCanvasData: ImageData;
@ -136,6 +141,17 @@ namespace pxsim.visuals {
this.board.updateSubscribers.push(() => this.updateState()); this.board.updateSubscribers.push(() => this.updateState());
this.updateState(); this.updateState();
} }
Runtime.messagePosted = (msg) => {
switch (msg.type || "") {
case "status": {
const state = (msg as pxsim.SimulatorStateMessage).state;
if (state == "killed") this.kill();
if (state == "running") this.begin();
break;
}
}
}
} }
public getView(): SVGAndSize<SVGSVGElement> { public getView(): SVGAndSize<SVGSVGElement> {
@ -167,86 +183,8 @@ namespace pxsim.visuals {
this.layoutView.updateTheme(theme); this.layoutView.updateTheme(theme);
} }
public updateState() { private getControlForNode(id: NodeType, port: number, useCache = true) {
this.updateVisibleNodes(); if (useCache && this.cachedControlNodes[id] && this.cachedControlNodes[id][port]) {
this.updateScreen();
}
private updateVisibleNodes() {
const inputNodes = ev3board().getInputNodes();
inputNodes.forEach((node, index) => {
const view = this.getDisplayViewForNode(node.id, index);
if (view) {
this.layoutView.setInput(index, view);
view.updateState();
}
});
this.getDisplayViewForNode(ev3board().getBrickNode().id, -1).updateState();
const outputNodes = ev3board().getMotors();
outputNodes.forEach((node, index) => {
const view = this.getDisplayViewForNode(node.id, index);
if (view) {
this.layoutView.setOutput(index, view);
view.updateState();
}
});
const selected = this.layoutView.getSelected();
if (selected && (selected.getId() !== this.selectedNode || selected.getPort() !== this.selectedPort)) {
this.selectedNode = selected.getId();
this.selectedPort = selected.getPort();
this.controlGroup.clear();
const control = this.getControlForNode(this.selectedNode, selected.getPort());
if (control) {
this.controlView = control;
this.controlGroup.addView(control);
}
this.closeIconView.setVisible(true);
} else if (!selected) {
this.controlGroup.clear();
this.controlView = undefined;
this.selectedNode = undefined;
this.selectedPort = undefined;
this.closeIconView.setVisible(false);
}
this.resize();
}
public resize() {
const bounds = this.element.getBoundingClientRect();
this.width = bounds.width;
this.height = bounds.height;
this.layoutView.layout(bounds.width, bounds.height);
if (this.selectedNode) {
const scale = this.width / this.closeIconView.getInnerWidth() / 10;
// Translate close icon
this.closeIconView.scale(Math.max(0, Math.min(1, scale)));
const closeIconWidth = this.closeIconView.getWidth();
const closeIconHeight = this.closeIconView.getHeight();
const closeCoords = this.layoutView.getCloseIconCoords(closeIconWidth, closeIconHeight);
this.closeIconView.translate(closeCoords.x, closeCoords.y);
}
if (this.controlView) {
const h = this.controlView.getInnerHeight();
const w = this.controlView.getInnerWidth();
const bh = this.layoutView.getModuleBounds().height - this.closeIconView.getHeight();
const bw = this.layoutView.getModuleBounds().width - (this.width * MODULE_INNER_PADDING_RATIO * 2);
this.controlView.scale(Math.min(bh / h, bw / w), false);
const controlCoords = this.layoutView.getSelectedCoords();
this.controlView.translate(controlCoords.x, controlCoords.y);
}
this.updateScreen();
}
private getControlForNode(id: NodeType, port: number) {
if (this.cachedControlNodes[id] && this.cachedControlNodes[id][port]) {
return this.cachedControlNodes[id][port]; return this.cachedControlNodes[id][port];
} }
@ -275,9 +213,10 @@ namespace pxsim.visuals {
} }
case NodeType.MediumMotor: case NodeType.MediumMotor:
case NodeType.LargeMotor: { case NodeType.LargeMotor: {
// const state = ev3board().getMotor(port)[0]; // TODO: figure out if the motor is in "input" or "output" mode
// view = new MotorInputControl(this.element, this.defs, state, port); const state = ev3board().getMotors()[port];
// break; view = new MotorReporterControl(this.element, this.defs, state, port);
break;
} }
} }
@ -323,6 +262,10 @@ namespace pxsim.visuals {
return undefined; return undefined;
} }
private getCloseIconView() {
return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
}
private buildDom() { private buildDom() {
this.wrapper = document.createElement('div'); this.wrapper = document.createElement('div');
this.wrapper.style.display = 'inline'; this.wrapper.style.display = 'inline';
@ -337,25 +280,10 @@ namespace pxsim.visuals {
this.layoutView = new LayoutView(); this.layoutView = new LayoutView();
this.layoutView.inject(this.element); this.layoutView.inject(this.element);
this.controlGroup = new ViewContainer();
this.controlGroup.inject(this.element);
this.closeGroup = new ViewContainer();
this.closeGroup.inject(this.element);
// Add EV3 module element // Add EV3 module element
this.layoutView.setBrick(new BrickView(-1)); this.layoutView.setBrick(new BrickView(-1));
this.closeIconView = new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
this.closeIconView.registerClick(() => {
this.layoutView.clearSelected();
this.updateState();
})
this.closeGroup.addView(this.closeIconView);
this.closeIconView.setVisible(false);
this.resize(); this.resize();
this.updateState();
// Add Screen canvas to board // Add Screen canvas to board
this.buildScreenCanvas(); this.buildScreenCanvas();
@ -369,6 +297,17 @@ namespace pxsim.visuals {
}); });
} }
public resize() {
if (!this.element) return;
this.width = document.body.offsetWidth;
this.height = document.body.offsetHeight;
this.layoutView.layout(this.width, this.height);
this.updateState();
let state = ev3board().screenState;
this.updateScreenStep(state);
}
private buildScreenCanvas() { private buildScreenCanvas() {
this.screenCanvas = document.createElement("canvas"); this.screenCanvas = document.createElement("canvas");
this.screenCanvas.id = "board-screen-canvas"; this.screenCanvas.id = "board-screen-canvas";
@ -394,18 +333,124 @@ namespace pxsim.visuals {
this.screenCanvasTemp.style.display = 'none'; this.screenCanvasTemp.style.display = 'none';
} }
private updateScreen() { private kill() {
let state = ev3board().screenState; this.running = false;
if (this.lastAnimationIds.length > 0) {
this.lastAnimationIds.forEach(animationId => {
cancelAnimationFrame(animationId);
})
}
// 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);
}
private static previousSelectedInputs: number[];
private static previousSeletedOutputs: number[];
private static isPreviousInputSelected(index: number, id: number) {
if (EV3View.previousSelectedInputs && EV3View.previousSelectedInputs[index] == id) {
EV3View.previousSelectedInputs[index] = undefined;
return true;
}
return false;
}
private static isPreviousOutputSelected(index: number, id: number) {
if (EV3View.previousSeletedOutputs && EV3View.previousSeletedOutputs[index] == id) {
EV3View.previousSeletedOutputs[index] = undefined;
return true;
}
return false;
}
private begin() {
this.running = true;
this.updateState();
}
private running: boolean = false;
private lastAnimationIds: number[] = [];
public updateState() {
if (this.lastAnimationIds.length > 0) {
this.lastAnimationIds.forEach(animationId => {
cancelAnimationFrame(animationId);
})
}
if (!this.running) return;
const fps = GAME_LOOP_FPS;
let now;
let then = pxsim.U.now();
let interval = 1000 / fps;
let delta;
let that = this;
function loop() {
const animationId = requestAnimationFrame(loop);
that.lastAnimationIds.push(animationId);
now = pxsim.U.now();
delta = now - then;
if (delta > interval) {
then = now;
that.updateStateStep(delta);
}
}
loop();
}
private updateStateStep(elapsed: number) {
const inputNodes = ev3board().getInputNodes();
inputNodes.forEach((node, index) => {
node.updateState(elapsed);
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 control = isSelected ? this.getControlForNode(node.id, index, !node.modeChange()) : undefined;
const closeIcon = control ? this.getCloseIconView() : undefined;
this.layoutView.setInput(index, view, control, closeIcon);
view.updateState();
if (control) control.updateState();
}
});
const brickNode = ev3board().getBrickNode();
if (brickNode.didChange()) {
this.getDisplayViewForNode(brickNode.id, -1).updateState();
}
const outputNodes = ev3board().getMotors();
outputNodes.forEach((node, index) => {
node.updateState(elapsed);
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 control = isSelected ? this.getControlForNode(node.id, index) : undefined;
const closeIcon = control ? this.getCloseIconView() : undefined;
this.layoutView.setOutput(index, view, control, closeIcon);
view.updateState();
if (control) control.updateState();
}
});
let state = ev3board().screenState;
if (state.didChange()) {
this.updateScreenStep(state);
}
}
private updateScreenStep(state: EV3ScreenState) {
const bBox = this.layoutView.getBrick().getScreenBBox(); const bBox = this.layoutView.getBrick().getScreenBBox();
if (!bBox || bBox.width == 0) return; if (!bBox || bBox.width == 0) return;
const scale = bBox.width / SCREEN_WIDTH; const scale = (bBox.width - 4) / SCREEN_WIDTH;
this.screenScaledWidth = bBox.width; this.screenScaledWidth = (bBox.width - 4);
this.screenScaledHeight = this.screenScaledWidth / SCREEN_WIDTH * SCREEN_HEIGHT; this.screenScaledHeight = this.screenScaledWidth / SCREEN_WIDTH * SCREEN_HEIGHT;
this.screenCanvas.style.top = `${bBox.top}px`; this.screenCanvas.style.top = `${bBox.top + 2}px`;
this.screenCanvas.style.left = `${bBox.left}px`; this.screenCanvas.style.left = `${bBox.left + 2}px`;
this.screenCanvas.width = this.screenScaledWidth; this.screenCanvas.width = this.screenScaledWidth;
this.screenCanvas.height = this.screenScaledHeight; this.screenCanvas.height = this.screenScaledHeight;

View File

@ -1,17 +1,20 @@
/// <reference path="./nodes/staticView.ts" /> /// <reference path="./nodes/moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export const CONTROL_WIDTH = 87.5; export const CONTROL_WIDTH = 87.5;
export const CONTROL_HEIGHT = 175; export const CONTROL_HEIGHT = 175;
export const CONTROL_TEXT_COLOR = '#000';
export abstract class ControlView<T extends BaseNode> extends SimView<T> implements LayoutElement { export abstract class ControlView<T extends BaseNode> extends SimView<T> implements LayoutElement {
private background: SVGSVGElement; protected content: SVGSVGElement;
abstract getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement): SVGElement; abstract getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement): SVGElement;
constructor(protected parent: SVGSVGElement, protected globalDefs: SVGDefsElement, protected state: T, protected port: number) { constructor(protected parent: SVGSVGElement, protected globalDefs: SVGDefsElement, protected state: T, protected port: number) {
super(state); super(state);
} }
getInnerWidth(): number { getInnerWidth(): number {
@ -34,18 +37,39 @@ namespace pxsim.visuals {
return false; return false;
} }
buildDom(width: number): SVGElement { buildDom(): SVGElement {
this.background = svg.elt("svg", { height: "100%", width: "100%"}) as SVGSVGElement; this.content = svg.elt("svg", { viewBox: `0 0 ${this.getInnerWidth()} ${this.getInnerHeight()}` }) as SVGSVGElement;
this.background.appendChild(this.getInnerView(this.parent, this.globalDefs)); this.content.appendChild(this.getInnerView(this.parent, this.globalDefs));
return this.background; return this.content;
}
public resize(width: number, height: number) {
super.resize(width, height);
this.updateDimensions(width, height);
}
private updateDimensions(width: number, height: number) {
if (this.content) {
const currentWidth = this.getInnerWidth();
const currentHeight = this.getInnerHeight();
const newHeight = currentHeight / currentWidth * width;
const newWidth = currentWidth / currentHeight * height;
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);
} else {
this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`);
// translate to the middle (height)
this.translate(0, height / 2 - newHeight / 2);
}
}
} }
onComponentVisible() { onComponentVisible() {
}
getWeight() {
return 0;
} }
} }
} }

View File

@ -17,6 +17,17 @@ namespace pxsim.visuals {
return this.closeGroup; return this.closeGroup;
} }
buildDom(): SVGElement {
this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement;
this.content.appendChild(this.getInnerView());
return this.content;
}
public resize(width: number, height: number) {
super.resize(width, height);
}
public getInnerHeight() { public getInnerHeight() {
return 32; return 32;
} }

View File

@ -7,35 +7,44 @@ namespace pxsim.visuals {
getInnerView() { getInnerView() {
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
this.group.setAttribute("transform", `translate(17, ${35 + this.getHeight() / 4}) scale(5)`) this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`)
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey']; const colorIds = ['red', 'yellow', 'blue', 'green', undefined, 'grey'];
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00']; const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', undefined, '#6c2d00'];
const colorValue = [5, 4, 2, 3, 1, 7]; const colorValue = [5, 4, 2, 3, 1, 7];
let cy = -4; let cy = -4;
for (let c = 0; c < colorIds.length; c++) { for (let c = 0; c < colorIds.length; c++) {
const cx = c % 2 == 0 ? 2.2 : 8.2; const cx = c % 2 == 0 ? 2.2 : 8.2;
if (c % 2 == 0) cy += 5; if (c % 2 == 0) cy += 5;
const circle = pxsim.svg.child(this.group, "circle", { 'class': 'sim-color-grid-circle', 'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` }); if (colorIds[c]) {
circle.addEventListener(pointerEvents.down, ev => { const circle = pxsim.svg.child(this.group, "circle", { 'class': 'sim-color-grid-circle', 'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` });
this.setColor(colorValue[c]); circle.addEventListener(pointerEvents.down, ev => {
}) this.setColor(colorValue[c]);
})
}
} }
const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' }); const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` }); pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle', 'cx': 2.2, 'cy': '11', '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.5px` }); pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '11', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` });
whiteCircleWrapper.addEventListener(pointerEvents.down, ev => { whiteCircleWrapper.addEventListener(pointerEvents.down, ev => {
this.setColor(6); this.setColor(6);
}) })
return this.group; return this.group;
} }
getInnerWidth() {
return 10.2;
}
getInnerHeight() {
return 15;
}
private setColor(color: number) { private setColor(color: number) {
const state = this.state; const state = this.state;
state.setColor(color); state.setColor(color);
} }
} }
} }

View File

@ -4,30 +4,78 @@ namespace pxsim.visuals {
export class ColorWheelControl extends ControlView<ColorSensorNode> { export class ColorWheelControl extends ControlView<ColorSensorNode> {
private group: SVGGElement; private group: SVGGElement;
private colorGradient: SVGLinearGradientElement;
private defs: SVGDefsElement;
private static COLOR_DARK = 1; getInnerView(parent: SVGSVGElement) {
private static COLOR_LIGHT = 99; this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
getInnerView() {
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
this.group.setAttribute("transform", `translate(12, ${this.getHeight() / 2 - 15}) scale(2.5)`) this.group.setAttribute("transform", `translate(12, 0) scale(2)`)
let gc = "gradient-color";
this.colorGradient = svg.linearGradient(this.defs, gc, true);
svg.setGradientValue(this.colorGradient, "50%");
svg.setGradientColors(this.colorGradient, "black", "white");
const circle = pxsim.svg.child(this.group, "g"); const circle = pxsim.svg.child(this.group, "g");
const lightHalf = pxsim.svg.child(circle, "path", { 'class': 'sim-color-wheel-half', 'd': 'M19,28.76a11.71,11.71,0,1,1,4.58-.92A11.74,11.74,0,0,1,19,28.76Z', 'transform': 'translate(-6.5 -4.5)', 'style': `fill: #fff;stroke: #000;stroke-miterlimit: 10` }); const innerCircle = pxsim.svg.child(circle, "circle",
pxsim.svg.child(circle, "path", { 'd': 'M19,28.52a11.42,11.42,0,0,0,4.48-.9,11.75,11.75,0,0,0,3.67-2.47,11.55,11.55,0,0,0,2.46-3.67,11.48,11.48,0,0,0,0-9,11.41,11.41,0,0,0-6.13-6.13,11.48,11.48,0,0,0-9,0,11.41,11.41,0,0,0-6.13,6.13,11.48,11.48,0,0,0,0,9,11.55,11.55,0,0,0,2.46,3.67,11.75,11.75,0,0,0,3.67,2.47,11.42,11.42,0,0,0,4.48.9M19,29A12,12,0,1,1,31,17,12,12,0,0,1,19,29Z', 'transform': 'translate(-6.5 -4.5)', 'style': `fill: #fff;stroke: #000;stroke-miterlimit: 10` }); {cursor: '-webkit-grab',
lightHalf.addEventListener(pointerEvents.down, ev => { fill: `url(#${gc})`,
this.setColor(ColorWheelControl.COLOR_LIGHT); r: 17,
}) cx: 13,
const darkHalf = pxsim.svg.child(this.group, "path", { 'class': 'sim-color-wheel-half', 'd': 'M19,5c.16,8.54,0,14.73,0,24A12,12,0,0,1,19,5Z', 'transform': 'translate(-6.5 -4.5)' }); cy: 20,
darkHalf.addEventListener(pointerEvents.down, ev => { stroke: 'black',
this.setColor(ColorWheelControl.COLOR_DARK); 'stroke-width': 2
}) });
let pt = parent.createSVGPoint();
let captured = false;
touchEvents(circle,
ev => {
if (captured && (ev as MouseEvent).clientX) {
ev.preventDefault();
this.setColor(pt, parent, ev as MouseEvent);
}
},
ev => {
captured = true;
if ((ev as MouseEvent).clientX) {
this.setColor(pt, parent, ev as MouseEvent);
}
},
ev => {
captured = false;
},
ev => {
captured = false;
}
)
return this.group; return this.group;
} }
private setColor(color: number) { getInnerWidth() {
return CONTROL_WIDTH;
}
getInnerHeight() {
return CONTROL_WIDTH;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue();
svg.setGradientValue(this.colorGradient, percentage + "%");
}
private setColor(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
const width = CONTROL_WIDTH;
let cur = svg.cursorPoint(pt, parent, ev);
let t = Math.max(0, Math.min(1, (width + this.left / this.scaleFactor - cur.x / this.scaleFactor) / width));
const state = this.state; const state = this.state;
state.setColor(color); state.setColor((1-t)*100);
} }
} }

View File

@ -7,33 +7,38 @@ namespace pxsim.visuals {
private gradient: SVGLinearGradientElement; private gradient: SVGLinearGradientElement;
private slider: SVGGElement; private slider: SVGGElement;
private static SLIDER_HANDLE_HEIGHT = 31; private reporter: SVGTextElement;
private isVisible = false; private static SLIDER_HANDLE_HEIGHT = 26;
private static SLIDER_SIDE_PADDING = 6;
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) { getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
let gid = "gradient-slider-" + this.getId(); let gid = "gradient-slider-" + this.getId();
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
this.gradient = createGradient(gid, this.getGradientDefinition()); this.gradient = createGradient(gid, this.getGradientDefinition());
this.gradient.setAttribute('x1', '-438.37'); this.gradient.setAttribute('x1', '0%');
this.gradient.setAttribute('y1', '419.43'); this.gradient.setAttribute('y1', '0%');
this.gradient.setAttribute('x2', '-438.37'); this.gradient.setAttribute('x2', '0%');
this.gradient.setAttribute('y2', '418.43'); this.gradient.setAttribute('y2', '100%');
this.gradient.setAttribute('gradientTransform', 'matrix(50, 0, 0, -110, 21949.45, 46137.67)'); // this.gradient.setAttribute('gradientTransform', 'matrix(50, 0, 0, -110, 21949.45, 46137.67)');
this.gradient.setAttribute('gradientUnits', 'userSpaceOnUse'); // this.gradient.setAttribute('gradientUnits', 'userSpaceOnUse');
globalDefs.appendChild(this.gradient); globalDefs.appendChild(this.gradient);
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
const sliderGroup = pxsim.svg.child(this.group, "g"); const reporterGroup = pxsim.svg.child(this.group, "g");
sliderGroup.setAttribute("transform", `translate(0, ${10 + this.getTopPadding()})`) reporterGroup.setAttribute("transform", `translate(31, 42)`);
this.reporter = pxsim.svg.child(reporterGroup, "text", { 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': this.getLeftPadding(), 'y': 2, 'width': this.getWidth() - this.getLeftPadding() * 2, 'height': this.getContentHeight(), 'style': `fill: url(#${gid})` }); const sliderGroup = pxsim.svg.child(this.group, "g");
sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 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.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement; this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
const sliderInner = pxsim.svg.child(this.slider, "g"); const sliderInner = pxsim.svg.child(this.slider, "g");
pxsim.svg.child(sliderInner, "rect", { 'width': this.getWidth(), 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT, 'rx': '2', 'ry': '2', 'style': 'fill: #f12a21' }); pxsim.svg.child(sliderInner, "rect", { 'width': this.getSliderWidth(), 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT, 'rx': '2', 'ry': '2', 'style': 'fill: #f12a21' });
pxsim.svg.child(sliderInner, "rect", { 'x': '0.5', 'y': '0.5', 'width': this.getWidth() - 1, 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT - 1, 'rx': '1.5', 'ry': '1.5', 'style': 'fill: none;stroke: #b32e29' }); pxsim.svg.child(sliderInner, "rect", { 'x': '0.5', 'y': '0.5', 'width': this.getSliderWidth() - 1, 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT - 1, 'rx': '1.5', 'ry': '1.5', 'style': 'fill: none;stroke: #b32e29' });
const dragSurface = svg.child(this.group, "rect", { const dragSurface = svg.child(this.group, "rect", {
x: 0, x: 0,
@ -66,52 +71,61 @@ namespace pxsim.visuals {
return this.group; return this.group;
} }
private getLeftPadding() { getInnerHeight() {
return this.getInnerWidth() * 0.12; return 192;
} }
private getTopPadding() { getInnerWidth() {
return this.getInnerHeight() / 4; return 111;
} }
private getContentHeight() { private getReporterHeight() {
return this.getInnerHeight() * 0.6; return 50;
} }
onBoardStateChanged() { private getSliderHeight() {
if (!this.isVisible) { return 110;
}
private getSliderWidth() {
return 62;
}
updateState() {
if (!this.visible) {
return; return;
} }
const node = this.state; const node = this.state;
const percentage = node.getValue(); const percentage = node.getValue() / 10; /* convert back to cm */
const y = this.getContentHeight() * percentage / 100; const y = this.getSliderHeight() * percentage / this.getMax();
this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`); this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
} // Update reporter text
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
onComponentVisible() {
super.onComponentVisible();
this.isVisible = true;
this.onBoardStateChanged();
}
onComponentHidden() {
this.isVisible = false;
} }
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) { private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev); let cur = svg.cursorPoint(pt, parent, ev);
const height = this.getContentHeight(); //DistanceSliderControl.SLIDER_HEIGHT; const height = this.getSliderHeight();
let t = Math.max(0, Math.min(1, (this.getTopPadding() + height + this.top / this.scaleFactor - cur.y / this.scaleFactor) / height)) const bBox = this.content.getBoundingClientRect();
let t = Math.max(0, Math.min(1, (DistanceSliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
const state = this.state; const state = this.state;
state.setDistance((1 - t) * (100)); state.setDistance((1 - t) * (this.getMax()));
}
private getMin() {
return 0;
}
private getMax() {
return 250; //cm
} }
private getGradientDefinition(): LinearGradientDefinition { private getGradientDefinition(): LinearGradientDefinition {
return { return {
stops: [ stops: [
{ offset: 0, color: '#626262' }, { offset: 0, color: '#626262' },
{ offset: 1, color: "#ddd" } { offset: 100, color: "#ddd" }
] ]
}; };
} }

View File

@ -0,0 +1,60 @@
namespace pxsim.visuals {
export class MotorReporterControl extends ControlView<MotorNode> {
private group: SVGGElement;
private circleBar: SVGCircleElement;
private reporter: SVGTextElement;
getInnerView() {
this.group = svg.elt("g") as SVGGElement;
const outerCircle = pxsim.svg.child(this.group, "circle", {
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 1s linear;`,
'stroke': '#a8aaa8', 'stroke-width': '1rem' }) as SVGCircleElement;
this.circleBar = pxsim.svg.child(this.group, "circle", {
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent; transition: stroke-dashoffset 1s linear;`,
'stroke': '#f12a21', 'stroke-width': '1rem' }) as SVGCircleElement;
this.reporter = pxsim.svg.child(this.group, "text", {
'x': this.getWidth() / 2, 'y': this.getHeight() / 2,
'text-anchor': 'middle', 'alignment-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number' }) as SVGTextElement;
return this.group;
}
getInnerWidth() {
return 200;
}
getInnerHeight() {
return 200;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const speed = node.getSpeed();
this.updateSpeed(speed);
// Update reporter
this.reporter.textContent = `${speed}`;
}
private updateSpeed(speed: number) {
let c = Math.PI * (90 * 2);
speed = Math.abs(speed);
let pct = ((100 - speed) / 100) * c;
this.circleBar.setAttribute('stroke-dashoffset', `${pct}`);
}
}
}

View File

@ -6,8 +6,6 @@ namespace pxsim.visuals {
private group: SVGGElement; private group: SVGGElement;
private slider: SVGGElement; private slider: SVGGElement;
private isVisible = false;
private static SLIDER_WIDTH = 70; private static SLIDER_WIDTH = 70;
private static SLIDER_HEIGHT = 78; private static SLIDER_HEIGHT = 78;
@ -60,8 +58,8 @@ namespace pxsim.visuals {
return this.getInnerHeight() / 4; return this.getInnerHeight() / 4;
} }
onBoardStateChanged() { updateState() {
if (!this.isVisible) { if (!this.visible) {
return; return;
} }
const node = this.state; const node = this.state;
@ -71,20 +69,11 @@ namespace pxsim.visuals {
this.slider.setAttribute("transform", `translate(${x}, ${y})`); this.slider.setAttribute("transform", `translate(${x}, ${y})`);
} }
onComponentVisible() {
super.onComponentVisible();
this.isVisible = true;
this.onBoardStateChanged();
}
onComponentHidden() {
this.isVisible = false;
}
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) { private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev); let cur = svg.cursorPoint(pt, parent, ev);
const width = CONTROL_WIDTH; //DistanceSliderControl.SLIDER_HEIGHT; const width = CONTROL_WIDTH; //DistanceSliderControl.SLIDER_HEIGHT;
let t = Math.max(0, Math.min(1, (width + this.left / this.scaleFactor - cur.x / this.scaleFactor) / width)) const bBox = this.content.getBoundingClientRect();
let t = Math.max(0, Math.min(1, (width + bBox.left / this.scaleFactor - cur.x / this.scaleFactor) / width))
const state = this.state; const state = this.state;
state.setAngle((1 - t) * (100)); state.setAngle((1 - t) * (100));

View File

@ -1,67 +1,56 @@
/// <reference path="./view.ts" /> /// <reference path="./view.ts" />
/// <reference path="./nodes/staticView.ts" /> /// <reference path="./nodes/moduleView.ts" />
/// <reference path="./nodes/portView.ts" /> /// <reference path="./nodes/portView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export const DEFAULT_WIDTH = 350;
export const DEFAULT_HEIGHT = 700;
export const BRICK_HEIGHT_RATIO = 1 / 3; export const BRICK_HEIGHT_RATIO = 1 / 3;
export const MODULE_AND_WIRING_HEIGHT_RATIO = 1 / 3; // For inputs and outputs export const MODULE_AND_WIRING_HEIGHT_RATIO = 1 / 3; // For inputs and outputs
export const MODULE_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO * 3 / 4; export const MODULE_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO * 4 / 5;
export const WIRING_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO / 4; export const WIRING_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO / 5;
export const MODULE_INNER_PADDING_RATIO = 1 / 35; export const MODULE_INNER_PADDING_RATIO = 1 / 35;
export const MAX_MODULE_WIDTH = 100;
export interface LayoutElement extends View { export interface LayoutElement extends View {
getId(): number; getId(): number;
getPort(): number; getPort(): number;
getPaddingRatio(): number; getPaddingRatio(): number;
getWiringRatio(): number; getWiringRatio(): number;
setSelected(selected: boolean): void;
} }
export class LayoutView extends ViewContainer { export class LayoutView extends ViewContainer {
private inputs: LayoutElement[] = []; private inputs: LayoutElement[] = [];
private outputs: LayoutElement[] = []; private outputs: LayoutElement[] = [];
private inputContainers: ViewContainer[] = [];
private outputContainers: ViewContainer[] = [];
private inputControls: View[] = [];
private outputControls: View[] = [];
private inputCloseIcons: View[] = [];
private outputCloseIcons: View[] = [];
private inputWires: WireView[] = []; private inputWires: WireView[] = [];
private outputWires: WireView[] = []; private outputWires: WireView[] = [];
private selected: number;
private selectedIsInput: boolean;
private brick: BrickView; private brick: BrickView;
private offsets: number[]; private offsets: number[];
private contentGroup: SVGGElement; private contentGroup: SVGGElement;
private scrollGroup: SVGGElement; private scrollGroup: SVGGElement;
private renderedViews: Map<boolean> = {}; private renderedViews: Map<boolean> = {};
private childScaleFactor: number;
private totalLength: number;
private height: number;
private hasDimensions = false; private hasDimensions = false;
constructor() { constructor() {
super(); super();
this.outputs = [ this.outputContainers = [new ViewContainer(), new ViewContainer, new ViewContainer(), new ViewContainer()];
new PortView(0, 'A'), this.inputContainers = [new ViewContainer(), new ViewContainer, new ViewContainer(), new ViewContainer()];
new PortView(1, 'B'),
new PortView(2, 'C'),
new PortView(3, 'D')
];
this.brick = new BrickView(0); this.brick = new BrickView(0);
this.inputs = [
new PortView(0, '1'),
new PortView(1, '2'),
new PortView(2, '3'),
new PortView(3, '4')
];
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) { for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
this.outputWires[port] = new WireView(port); this.outputWires[port] = new WireView(port);
} }
@ -72,8 +61,7 @@ namespace pxsim.visuals {
public layout(width: number, height: number) { public layout(width: number, height: number) {
this.hasDimensions = true; this.hasDimensions = true;
this.width = width; this.resize(width, height);
this.height = height;
this.scrollGroup.setAttribute("width", width.toString()); this.scrollGroup.setAttribute("width", width.toString());
this.scrollGroup.setAttribute("height", height.toString()); this.scrollGroup.setAttribute("height", height.toString());
this.position(); this.position();
@ -81,6 +69,7 @@ namespace pxsim.visuals {
public setBrick(brick: BrickView) { public setBrick(brick: BrickView) {
this.brick = brick; this.brick = brick;
this.brick.inject(this.scrollGroup);
this.position(); this.position();
} }
@ -88,55 +77,112 @@ namespace pxsim.visuals {
return this.brick; return this.brick;
} }
public setInput(port: number, child: LayoutElement) { public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
if (this.inputs[port]) { if (this.inputs[port] != view || this.inputControls[port] != control) {
// Remove current input if (this.inputs[port]) {
this.inputs[port].dispose(); // Remove current input
this.inputs[port].dispose();
}
this.inputs[port] = view;
if (this.inputControls[port]) {
this.inputControls[port].dispose();
}
this.inputControls[port] = control;
this.inputCloseIcons[port] = closeIcon;
this.inputContainers[port].clear();
this.inputContainers[port].addView(view);
if (control) this.inputContainers[port].addView(control);
if (view.hasClick()) view.registerClick((ev: any) => {
view.setSelected(true);
runtime.queueDisplayUpdate();
}, true);
if (control && closeIcon) {
this.inputContainers[port].addView(closeIcon);
closeIcon.registerClick(() => {
// Clear selection
view.setSelected(false);
runtime.queueDisplayUpdate();
})
}
} }
this.inputs[port] = child;
this.position(); this.position();
} }
public setOutput(port: number, child: LayoutElement) { public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
if (this.outputs[port]) { if (this.outputs[port] != view || this.outputControls[port] != control) {
// Remove current input if (this.outputs[port]) {
this.outputs[port].dispose(); // Remove current output
this.outputs[port].dispose();
}
this.outputs[port] = view;
if (this.outputControls[port]) {
this.outputControls[port].dispose();
}
this.outputControls[port] = control;
this.outputCloseIcons[port] = closeIcon;
this.outputContainers[port].clear();
this.outputContainers[port].addView(view);
if (control) this.outputContainers[port].addView(control);
if (view.hasClick()) view.registerClick((ev: any) => {
view.setSelected(true);
runtime.queueDisplayUpdate();
}, true)
if (control && closeIcon) {
this.outputContainers[port].addView(closeIcon);
closeIcon.registerClick(() => {
// Clear selection
view.setSelected(false);
runtime.queueDisplayUpdate();
})
}
} }
this.outputs[port] = child;
this.position(); this.position();
} }
public onClick(index: number, input: boolean, ev: any) { protected buildDom() {
this.setSelected(index, input);
}
public clearSelected() {
this.selected = undefined;
this.selectedIsInput = undefined;
}
public setSelected(index: number, input?: boolean) {
if (index !== this.selected || input !== this.selectedIsInput) {
this.selected = index;
this.selectedIsInput = input;
const node = this.getSelected();
if (node) node.setSelected(true);
//this.redoPositioning();
runtime.queueDisplayUpdate();
}
}
public getSelected() {
if (this.selected !== undefined) {
return this.selectedIsInput ? this.inputs[this.selected] : this.outputs[this.selected];
}
return undefined;
}
protected buildDom(width: number) {
this.contentGroup = svg.elt("g") as SVGGElement; this.contentGroup = svg.elt("g") as SVGGElement;
this.scrollGroup = svg.child(this.contentGroup, "g") as SVGGElement; this.scrollGroup = svg.child(this.contentGroup, "g") as SVGGElement;
this.inputs = [];
this.outputs = [];
this.inputControls = [];
this.outputControls = [];
// Inject all wires
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
this.outputWires[port].inject(this.scrollGroup);
}
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
this.inputWires[port].inject(this.scrollGroup);
}
// Inject all view containers
for (let i = 0; i < 4; i++) {
this.inputContainers[i].inject(this.scrollGroup);
this.outputContainers[i].inject(this.scrollGroup);
}
// Inject all ports
this.setInput(0, new PortView(0, 'A'));
this.setInput(1, new PortView(1, 'B'));
this.setInput(2, new PortView(2, 'C'));
this.setInput(3, new PortView(3, 'D'));
this.setOutput(0, new PortView(0, '1'));
this.setOutput(1, new PortView(1, '2'));
this.setOutput(2, new PortView(2, '3'));
this.setOutput(3, new PortView(3, '4'));
return this.contentGroup; return this.contentGroup;
} }
@ -171,34 +217,63 @@ namespace pxsim.visuals {
this.offsets = []; this.offsets = [];
const selectedNode = this.getSelected(); const contentWidth = this.width;
if (!contentWidth) return;
const contentHeight = this.height;
if (!contentHeight) return;
const contentWidth = this.width || DEFAULT_WIDTH; const noConnections = this.outputs.concat(this.inputs).filter(m => m.getId() != NodeType.Port).length == 0;
const contentHeight = this.height || DEFAULT_HEIGHT;
if (noConnections) {
// No connections render the entire board
this.brick.resize(contentWidth, contentHeight);
this.brick.translate(0, 0);
// Hide all other connections
this.outputs.concat(this.inputs).forEach(m => m.setVisible(false));
return;
} else {
this.outputs.concat(this.inputs).forEach(m => m.setVisible(true));
}
const moduleHeight = this.getModuleHeight(); const moduleHeight = this.getModuleHeight();
const brickHeight = this.getBrickHeight(); const brickHeight = this.getBrickHeight();
this.brick.inject(this.scrollGroup);
const brickWidth = this.brick.getInnerWidth() / this.brick.getInnerHeight() * brickHeight; const brickWidth = this.brick.getInnerWidth() / this.brick.getInnerHeight() * brickHeight;
const brickPadding = (contentWidth - brickWidth) / 2; const brickPadding = (contentWidth - brickWidth) / 2;
const modulePadding = contentWidth / 35; const modulePadding = this.getModulePadding();
const moduleSpacing = contentWidth / 4; const moduleSpacing = contentWidth / 4;
const moduleWidth = moduleSpacing - (modulePadding * 2); const moduleWidth = this.getInnerModuleWidth();
let currentX = modulePadding; let currentX = this.getModulePadding();
let currentY = 0; let currentY = 0;
this.outputs.forEach((n, i) => { this.outputs.forEach((n, i) => {
const outputPadding = moduleWidth * n.getPaddingRatio(); this.outputContainers[i].translate(currentX, currentY);
const outputWidth = moduleWidth - outputPadding * 2; if (this.outputs[i]) {
n.inject(this.scrollGroup, outputWidth); const view = this.outputs[i];
n.resize(outputWidth); const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
const nHeight = n.getHeight() / n.getWidth() * outputWidth; const desiredOutputWidth = this.getInnerModuleWidth() - outputPadding * 2;
n.translate(currentX + outputPadding, currentY + moduleHeight - nHeight); const outputWidth = Math.min(desiredOutputWidth, MAX_MODULE_WIDTH);
n.setSelected(n == selectedNode); const outputHeight = this.getModuleHeight();
if (n.hasClick()) n.registerClick((ev: any) => {
this.onClick(i, false, ev); // Translate and resize view
}) view.resize(outputWidth, outputHeight);
const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
// Resize control
const control = this.outputControls[i];
if (control) {
control.resize(this.getInnerModuleWidth(), outputHeight);
// Translate close icon
const closeIcon = this.outputCloseIcons[i];
if (closeIcon) {
const closeIconWidth = closeIcon.getWidth();
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, 0);
}
}
}
currentX += moduleSpacing; currentX += moduleSpacing;
}) })
@ -206,7 +281,7 @@ namespace pxsim.visuals {
currentY = moduleHeight; currentY = moduleHeight;
const wireBrickSpacing = brickWidth / 5; const wireBrickSpacing = brickWidth / 5;
const wiringYPadding = 10; const wiringYPadding = 5;
let wireStartX = 0; let wireStartX = 0;
let wireEndX = brickPadding + wireBrickSpacing; let wireEndX = brickPadding + wireBrickSpacing;
let wireEndY = currentY + this.getWiringHeight() + wiringYPadding; let wireEndY = currentY + this.getWiringHeight() + wiringYPadding;
@ -214,7 +289,6 @@ namespace pxsim.visuals {
// Draw output lines // Draw output lines
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) { for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
if (!this.outputWires[port].isRendered()) this.outputWires[port].inject(this.scrollGroup);
this.outputWires[port].updateDimensions(wireStartX + moduleSpacing * this.outputs[port].getWiringRatio(), wireStartY, wireEndX, wireEndY); this.outputWires[port].updateDimensions(wireStartX + moduleSpacing * this.outputs[port].getWiringRatio(), wireStartY, wireEndX, wireEndY);
this.outputWires[port].setSelected(this.outputs[port].getId() == NodeType.Port); this.outputWires[port].setSelected(this.outputs[port].getId() == NodeType.Port);
wireStartX += moduleSpacing; wireStartX += moduleSpacing;
@ -225,22 +299,39 @@ namespace pxsim.visuals {
currentY += this.getWiringHeight(); currentY += this.getWiringHeight();
// Render the brick in the middle // Render the brick in the middle
this.brick.resize(brickWidth); this.brick.resize(brickWidth, brickHeight);
this.brick.translate(currentX, currentY); this.brick.translate(currentX, currentY);
currentX = modulePadding; currentX = modulePadding;
currentY += brickHeight + this.getWiringHeight(); currentY += brickHeight + this.getWiringHeight();
this.inputs.forEach((n, i) => { this.inputs.forEach((n, i) => {
const inputPadding = moduleWidth * n.getPaddingRatio(); this.inputContainers[i].translate(currentX, currentY);
const inputWidth = moduleWidth - inputPadding * 2; if (this.inputs[i]) {
n.inject(this.scrollGroup, inputWidth); const view = this.inputs[i];
n.resize(inputWidth); const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
n.translate(currentX + inputPadding, currentY); const desiredInputWidth = this.getInnerModuleWidth() - inputPadding * 2;
n.setSelected(n == selectedNode); const inputWidth = Math.min(desiredInputWidth, MAX_MODULE_WIDTH);
if (n.hasClick()) n.registerClick((ev: any) => { const inputHeight = this.getModuleHeight();
this.onClick(i, true, ev);
}) // Translate and resize view
view.resize(inputWidth, inputHeight);
view.translate(inputPadding + ((desiredInputWidth - inputWidth) / 2), 0, true);
// Resize control
const control = this.inputControls[i];
if (control) {
control.resize(this.getInnerModuleWidth(), inputHeight);
// Translate and resize close icon
const closeIcon = this.inputCloseIcons[i];
if (closeIcon) {
const closeIconWidth = closeIcon.getWidth();
const closeIconHeight = closeIcon.getHeight();
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, this.getModuleHeight() - closeIconHeight);
}
}
}
currentX += moduleSpacing; currentX += moduleSpacing;
}) })
@ -251,7 +342,6 @@ namespace pxsim.visuals {
// Draw input lines // Draw input lines
for (let port = 0; port < DAL.NUM_INPUTS; port++) { for (let port = 0; port < DAL.NUM_INPUTS; port++) {
if (!this.inputWires[port].isRendered()) this.inputWires[port].inject(this.scrollGroup);
this.inputWires[port].updateDimensions(wireStartX, wireStartY, wireEndX, wireEndY); this.inputWires[port].updateDimensions(wireStartX, wireStartY, wireEndX, wireEndY);
this.inputWires[port].setSelected(this.inputs[port].getId() == NodeType.Port); this.inputWires[port].setSelected(this.inputs[port].getId() == NodeType.Port);
wireStartX += moduleSpacing; wireStartX += moduleSpacing;
@ -259,33 +349,12 @@ namespace pxsim.visuals {
} }
} }
public getSelectedCoords() {
const selected = this.getSelected();
if (!selected) return undefined;
const port = this.getSelected().getPort();
return {
x: this.getSelected().getPort() * this.width / 4 + this.width * MODULE_INNER_PADDING_RATIO,
y: this.selectedIsInput ? this.getModuleHeight() + 2 * this.getWiringHeight() + this.getBrickHeight() : this.getModuleHeight() / 4
}
}
public getCloseIconCoords(closeIconWidth: number, closeIconHeight: number) {
return {
x: this.getSelected().getPort() * this.width / 4 + this.getModuleBounds().width / 2 - closeIconWidth / 2,
y: this.selectedIsInput ? this.getModuleHeight() + 2 * this.getWiringHeight() + this.getBrickHeight() + this.getModuleHeight() - closeIconHeight : 0
}
}
public getModuleHeight() {
return (this.height || DEFAULT_HEIGHT) * MODULE_HEIGHT_RATIO;
}
public getBrickHeight() { public getBrickHeight() {
return (this.height || DEFAULT_HEIGHT) * BRICK_HEIGHT_RATIO; return this.height * BRICK_HEIGHT_RATIO;
} }
public getWiringHeight() { public getWiringHeight() {
return (this.height || DEFAULT_HEIGHT) * WIRING_HEIGHT_RATIO; return this.height * WIRING_HEIGHT_RATIO;
} }
public getModuleBounds() { public getModuleBounds() {
@ -294,5 +363,17 @@ namespace pxsim.visuals {
height: this.getModuleHeight() height: this.getModuleHeight()
} }
} }
public getModulePadding() {
return this.getModuleBounds().width / 35;
}
public getInnerModuleWidth() {
return this.getModuleBounds().width - (this.getModulePadding() * 2);
}
public getModuleHeight() {
return this.height * MODULE_HEIGHT_RATIO;
}
} }
} }

View File

@ -1,8 +1,8 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class BrickView extends StaticModuleView implements LayoutElement { export class BrickView extends ModuleView implements LayoutElement {
private static EV3_SCREEN_ID = "ev3_screen"; private static EV3_SCREEN_ID = "ev3_screen";
private static EV3_LIGHT_ID = "btn_color"; private static EV3_LIGHT_ID = "btn_color";
@ -35,19 +35,15 @@ namespace pxsim.visuals {
return false; return false;
} }
public shouldUpdateState() {
return true;
}
public updateState() { public updateState() {
this.updateLight(); this.updateLight();
} }
public updateThemeCore() { public updateThemeCore() {
let theme = this.theme; let theme = this.theme;
svg.fill(this.buttons[0], theme.buttonUps[0]); // svg.fill(this.buttons[0], theme.buttonUps[0]);
svg.fill(this.buttons[1], theme.buttonUps[1]); // svg.fill(this.buttons[1], theme.buttonUps[1]);
svg.fill(this.buttons[2], theme.buttonUps[2]); // svg.fill(this.buttons[2], theme.buttonUps[2]);
} }
private lastLightPattern: number = -1; private lastLightPattern: number = -1;

View File

@ -1,7 +1,7 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class ColorSensorView extends StaticModuleView implements LayoutElement { export class ColorSensorView extends ModuleView implements LayoutElement {
private control: ColorGridControl; private control: ColorGridControl;
@ -10,7 +10,12 @@ namespace pxsim.visuals {
} }
public getPaddingRatio() { public getPaddingRatio() {
return 1 / 8; return 1 / 4;
}
public updateState() {
super.updateState();
// TODO: show different color modes
} }
} }
} }

View File

@ -1,14 +1,14 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class GyroSensorView extends StaticModuleView implements LayoutElement { export class GyroSensorView extends ModuleView implements LayoutElement {
constructor(port: number) { constructor(port: number) {
super(GYRO_SVG, "gyro", NodeType.GyroSensor, port); super(GYRO_SVG, "gyro", NodeType.GyroSensor, port);
} }
public getPaddingRatio() { public getPaddingRatio() {
return 1 / 4; return 0.3;
} }
} }
} }

View File

@ -1,62 +1,34 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class LargeMotorView extends StaticModuleView implements LayoutElement { export class LargeMotorView extends ModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "1eb2ae58-2419-47d4-86bf-4f26a7f0cf61"; private static ROTATING_ECLIPSE_ID = "hole";
private lastMotorAnimationId: any;
constructor(port: number) { constructor(port: number) {
super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port); super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port);
} }
updateState() { updateState() {
super.updateState();
const motorState = ev3board().getMotors()[this.port]; const motorState = ev3board().getMotors()[this.port];
if (!motorState) return; if (!motorState) return;
const speed = motorState.getSpeed(); const speed = motorState.getSpeed();
if (this.lastMotorAnimationId) cancelAnimationFrame(this.lastMotorAnimationId);
if (!speed) return; if (!speed) return;
this.playMotorAnimation(motorState); this.setMotorAngle(motorState.getAngle());
} }
private playMotorAnimation(state: MotorNode) { private setMotorAngle(angle: number) {
// Max medium motor RPM is 170 according to http://www.cs.scranton.edu/~bi/2015s-html/cs358/EV3-Motor-Guide.docx
const rotationsPerMinute = 170; // 170 rpm at speed 100
const rotationsPerSecond = rotationsPerMinute / 60;
const fps = MOTOR_ROTATION_FPS;
const rotationsPerFrame = rotationsPerSecond / fps;
let now;
let then = Date.now();
let interval = 1000 / fps;
let delta;
let that = this;
function draw() {
that.lastMotorAnimationId = requestAnimationFrame(draw);
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - (delta % interval);
that.playMotorAnimationStep(state.angle);
const rotations = state.getSpeed() / 100 * rotationsPerFrame;
const angle = rotations * 360;
state.angle += angle;
}
}
draw();
}
private playMotorAnimationStep(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(LargeMotorView.ROTATING_ECLIPSE_ID)) const holeEl = this.content.getElementById(this.normalizeId(LargeMotorView.ROTATING_ECLIPSE_ID))
const width = 34; const width = 125.92;
const height = 34; const height = 37.9;
const transform = `rotate(${angle} ${width / 2} ${height / 2})`; const transform = `rotate(${angle} ${width / 2} ${height / 2})`;
holeEl.setAttribute("transform", transform); holeEl.setAttribute("transform", transform);
} }
getWiringRatio() { getWiringRatio() {
return 0.62; return 0.37;
} }
} }
} }

View File

@ -1,67 +1,39 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export const MOTOR_ROTATION_FPS = 32; export const MOTOR_ROTATION_FPS = 32;
export class MediumMotorView extends StaticModuleView implements LayoutElement { export class MediumMotorView extends ModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "Hole"; private static ROTATING_ECLIPSE_ID = "medmotor_Hole";
private hasPreviousAngle: boolean; private hasPreviousAngle: boolean;
private previousAngle: number; private previousAngle: number;
private lastMotorAnimationId: any;
constructor(port: number) { constructor(port: number) {
super(MEDIUM_MOTOR_SVG, "medium-motor", NodeType.MediumMotor, port); super(MEDIUM_MOTOR_SVG, "medium-motor", NodeType.MediumMotor, port);
} }
public getPaddingRatio() { public getPaddingRatio() {
return 1 / 10; return 1 / 5;
} }
updateState() { updateState() {
super.updateState();
const motorState = ev3board().getMotors()[this.port]; const motorState = ev3board().getMotors()[this.port];
if (!motorState) return; if (!motorState) return;
const speed = motorState.getSpeed(); const speed = motorState.getSpeed();
if (this.lastMotorAnimationId) cancelAnimationFrame(this.lastMotorAnimationId);
if (!speed) return; if (!speed) return;
this.playMotorAnimation(motorState); this.setMotorAngle(motorState.getAngle());
} }
private playMotorAnimation(state: MotorNode) { private setMotorAngle(angle: number) {
// Max medium motor RPM is 250 according to http://www.cs.scranton.edu/~bi/2015s-html/cs358/EV3-Motor-Guide.docx
const rotationsPerMinute = 250; // 250 rpm at speed 100
const rotationsPerSecond = rotationsPerMinute / 60;
const fps = MOTOR_ROTATION_FPS;
const rotationsPerFrame = rotationsPerSecond / fps;
let now;
let then = Date.now();
let interval = 1000 / fps;
let delta;
let that = this;
function draw() {
that.lastMotorAnimationId = requestAnimationFrame(draw);
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - (delta % interval);
that.playMotorAnimationStep(state.angle);
const rotations = state.getSpeed() / 100 * rotationsPerFrame;
const angle = rotations * 360;
state.angle += angle;
}
}
draw();
}
private playMotorAnimationStep(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(MediumMotorView.ROTATING_ECLIPSE_ID)) const holeEl = this.content.getElementById(this.normalizeId(MediumMotorView.ROTATING_ECLIPSE_ID))
const width = 47.9; const width = 44.45;
const height = 47.2; const height = 44.45;
const transform = `translate(-1.5 -1.49) rotate(${angle} ${width / 2} ${height / 2})`; const transform = `translate(2 1.84) rotate(${angle} ${width / 2} ${height / 2})`;
holeEl.setAttribute("transform", transform); holeEl.setAttribute("transform", transform);
} }
} }

View File

@ -1,10 +1,11 @@
namespace pxsim.visuals { namespace pxsim.visuals {
export class StaticModuleView extends View implements LayoutElement { export class ModuleView extends View implements LayoutElement {
protected content: SVGSVGElement; protected content: SVGSVGElement;
protected controlShown: boolean; protected controlShown: boolean;
protected selected: boolean;
protected opacity: number;
constructor(protected xml: string, protected prefix: string, protected id: NodeType, protected port: NodeType) { constructor(protected xml: string, protected prefix: string, protected id: NodeType, protected port: NodeType) {
super(); super();
@ -45,9 +46,8 @@ namespace pxsim.visuals {
return 0.5; return 0.5;
} }
protected buildDom(width: number): SVGElement { protected buildDom(): SVGElement {
this.content = svg.parseString(this.xml); this.content = svg.parseString(this.xml);
this.updateDimensions(width);
this.buildDomCore(); this.buildDomCore();
this.attachEvents(); this.attachEvents();
if (this.hasClick()) if (this.hasClick())
@ -82,15 +82,17 @@ namespace pxsim.visuals {
public attachEvents() { public attachEvents() {
} }
public resize(width: number) { public resize(width: number, height: number) {
this.updateDimensions(width); super.resize(width, height);
this.updateDimensions(width, height);
} }
private updateDimensions(width: number) { private updateDimensions(width: number, height: number) {
if (this.content) { if (this.content) {
const currentWidth = this.getInnerWidth(); const currentWidth = this.getInnerWidth();
const currentHeight = this.getInnerHeight(); const currentHeight = this.getInnerHeight();
const newHeight = currentHeight / currentWidth * width; const newHeight = currentHeight / currentWidth * width;
const newWidth = currentWidth / currentHeight * height;
this.content.setAttribute('width', `${width}`); this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`); this.content.setAttribute('height', `${newHeight}`);
} }
@ -101,23 +103,28 @@ namespace pxsim.visuals {
} }
public setSelected(selected: boolean) { public setSelected(selected: boolean) {
this.selected = selected; super.setSelected(selected);
this.updateOpacity();
}
public updateState() {
this.updateOpacity(); this.updateOpacity();
} }
protected updateOpacity() { protected updateOpacity() {
if (this.rendered) { if (this.rendered) {
const opacity = this.selected ? "0.5" : "1"; const opacity = this.selected ? 0.2 : 1;
if (this.hasClick()) { if (this.hasClick() && this.opacity != opacity) {
this.setOpacity(opacity); this.opacity = opacity;
this.setOpacity(this.opacity);
if (this.selected) this.content.style.cursor = ""; if (this.selected) this.content.style.cursor = "";
else this.content.style.cursor = "pointer"; else this.content.style.cursor = "pointer";
} }
} }
} }
protected setOpacity(opacity: string) { protected setOpacity(opacity: number) {
this.element.setAttribute("opacity", opacity); this.element.setAttribute("opacity", `${opacity}`);
} }
} }
} }

View File

@ -1,8 +1,8 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class PortView extends StaticModuleView implements LayoutElement { export class PortView extends ModuleView implements LayoutElement {
constructor(port: NodeType, private label: string) { constructor(port: NodeType, private label: string) {
super(PORT_SVG, "port", NodeType.Port, port); super(PORT_SVG, "port", NodeType.Port, port);

View File

@ -1,7 +1,7 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class TouchSensorView extends StaticModuleView implements LayoutElement { export class TouchSensorView extends ModuleView implements LayoutElement {
private static RECT_ID = ["touch_gradient4", "touch_gradient3", "touch_gradient2", "touch_gradient1"]; private static RECT_ID = ["touch_gradient4", "touch_gradient3", "touch_gradient2", "touch_gradient1"];
private static TOUCH_GRADIENT_UNPRESSED = ["linear-gradient-2", "linear-gradient-3", "linear-gradient-4", "linear-gradient-5"]; private static TOUCH_GRADIENT_UNPRESSED = ["linear-gradient-2", "linear-gradient-3", "linear-gradient-4", "linear-gradient-5"];
@ -17,7 +17,7 @@ namespace pxsim.visuals {
} }
public getPaddingRatio() { public getPaddingRatio() {
return 1 / 10; return 1 / 4;
} }
public hasClick() { public hasClick() {

View File

@ -1,7 +1,7 @@
/// <reference path="./staticView.ts" /> /// <reference path="./moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class UltrasonicSensorView extends StaticModuleView implements LayoutElement { export class UltrasonicSensorView extends ModuleView implements LayoutElement {
constructor(port: number) { constructor(port: number) {
super(ULTRASONIC_SVG, "ultrasonic", NodeType.UltrasonicSensor, port); super(ULTRASONIC_SVG, "ultrasonic", NodeType.UltrasonicSensor, port);

View File

@ -3,14 +3,19 @@ namespace pxsim.visuals {
protected element: SVGGElement; protected element: SVGGElement;
protected rendered = false; protected rendered = false;
protected visible = false; protected visible = false;
protected selected: boolean;
protected width: number = 0; protected width: number = 0;
protected height: number = 0;
protected left: number = 0; protected left: number = 0;
protected top: number = 0; protected top: number = 0;
protected scaleFactor: number = 1; protected scaleFactor: number = 1;
private changed: boolean;
private hover: boolean = false;
protected theme: IBoardTheme; protected theme: IBoardTheme;
protected abstract buildDom(width: number): SVGElement; protected abstract buildDom(): SVGElement;
public abstract getInnerWidth(): number; public abstract getInnerWidth(): number;
public abstract getInnerHeight(): number; public abstract getInnerHeight(): number;
@ -61,10 +66,6 @@ namespace pxsim.visuals {
} }
} }
public shouldUpdateState() {
return true;
}
public updateState() { public updateState() {
} }
@ -87,9 +88,26 @@ namespace pxsim.visuals {
} }
private onClickHandler: (ev: any) => void; private onClickHandler: (ev: any) => void;
public registerClick(handler: (ev: any) => void) { public registerClick(handler: (ev: any) => void, zoom?: boolean) {
this.onClickHandler = handler; this.onClickHandler = handler;
this.getView().addEventListener(pointerEvents.up, this.onClickHandler); if (zoom) {
this.getView().addEventListener(pointerEvents.up, (ev: any) => {
if (!this.getSelected()) {
this.onClickHandler(ev);
this.setHover(false);
}
});
this.getView().addEventListener(pointerEvents.move, () => {
if (!this.getSelected()) {
this.setHover(true);
}
});
this.getView().addEventListener(pointerEvents.leave, () => {
this.setHover(false);
});
} else {
this.getView().addEventListener(pointerEvents.up, this.onClickHandler);
}
} }
public dispose() { public dispose() {
@ -102,7 +120,7 @@ namespace pxsim.visuals {
this.element = svg.elt("g") as SVGGElement; this.element = svg.elt("g") as SVGGElement;
View.track(this); View.track(this);
const content = this.buildDom(this.width); const content = this.buildDom();
if (content) { if (content) {
this.element.appendChild(content); this.element.appendChild(content);
} }
@ -112,16 +130,30 @@ namespace pxsim.visuals {
return this.element; return this.element;
} }
public resize(width: number) { public resize(width: number, height: number) {
this.width = width; this.width = width;
this.height = height;
} }
private updateTransform() { private updateTransform() {
if (this.rendered) { if (this.rendered) {
let transform = `translate(${this.left} ${this.top})`; let left = this.left;
let top = this.top;
let scaleFactor = this.scaleFactor;
if (this.hover) {
const hoverScaleFactor = scaleFactor + 0.05;
// Scale around center of module
const centerX = this.getWidth() / 2;
const centerY = this.getHeight() / 2;
left = left - centerX * (hoverScaleFactor - 1);
top = top - centerY * (hoverScaleFactor - 1);
scaleFactor = hoverScaleFactor;
}
if (this.scaleFactor !== 1) { let transform = `translate(${left} ${top})`;
transform += ` scale(${this.scaleFactor})`;
if (scaleFactor !== 1) {
transform += ` scale(${scaleFactor})`;
} }
this.element.setAttribute("transform", transform); this.element.setAttribute("transform", transform);
@ -153,6 +185,40 @@ namespace pxsim.visuals {
delete View.allViews[id]; delete View.allViews[id];
} }
} }
///////// HOVERED STATE /////////////
public getHover() {
return this.hover;
}
public setHover(hover: boolean) {
if (this.hover != hover) {
this.hover = hover;
this.updateTransform();
}
}
///////// SELECTED STATE /////////////
public getSelected() {
return this.selected;
}
public setSelected(selected: boolean) {
this.selected = selected;
this.setChangedState();
}
protected setChangedState() {
this.changed = true;
}
public didChange() {
const res = this.changed;
this.changed = false;
return res;
}
} }
export abstract class SimView<T extends BaseNode> extends View implements LayoutElement { export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
@ -179,23 +245,8 @@ namespace pxsim.visuals {
public setSelected(selected: boolean) { } public setSelected(selected: boolean) { }
protected getView() { protected getView() {
if (!this.rendered) {
this.subscribe();
}
return super.getView(); return super.getView();
} }
protected onBoardStateChanged() {
// To be implemented by sub class
}
protected subscribe() {
board().updateSubscribers.push(() => {
if (this.state.didChange()) {
this.onBoardStateChanged();
}
});
}
} }
export class ViewContainer extends View { export class ViewContainer extends View {
@ -241,7 +292,7 @@ namespace pxsim.visuals {
}); });
} }
protected buildDom(width: number): SVGElement { protected buildDom(): SVGElement {
return undefined; return undefined;
} }
} }

View File

@ -1,11 +1,10 @@
/// <reference path="./nodes/staticView.ts" /> /// <reference path="./nodes/moduleView.ts" />
namespace pxsim.visuals { namespace pxsim.visuals {
export class WireView extends View implements LayoutElement { export class WireView extends View implements LayoutElement {
private wire: SVGSVGElement; private wire: SVGSVGElement;
private path: SVGPathElement; private path: SVGPathElement;
private selected: boolean;
private hasDimensions: boolean; private hasDimensions: boolean;
protected startX: number; protected startX: number;
@ -30,7 +29,7 @@ namespace pxsim.visuals {
this.updatePath(); this.updatePath();
} }
buildDom(width: number): SVGElement { buildDom(): SVGElement {
this.wire = svg.elt("svg", { height: "100%", width: "100%" }) as SVGSVGElement; this.wire = svg.elt("svg", { height: "100%", width: "100%" }) as SVGSVGElement;
this.path = pxsim.svg.child(this.wire, "path", { this.path = pxsim.svg.child(this.wire, "path", {
'd': '', 'd': '',
@ -45,8 +44,8 @@ namespace pxsim.visuals {
updatePath() { updatePath() {
if (!this.hasDimensions) return; if (!this.hasDimensions) return;
const height = this.endY - this.startY; const height = this.endY - this.startY;
const quarterHeight = height / 4; const thirdHeight = height / 3;
const middleHeight = this.port == 1 || this.port == 2 ? quarterHeight : quarterHeight * 2; const middleHeight = this.port == 1 || this.port == 2 ? thirdHeight : thirdHeight * 2;
let d = `M${this.startX} ${this.startY}`; let d = `M${this.startX} ${this.startY}`;
d += ` L${this.startX} ${this.startY + middleHeight}`; d += ` L${this.startX} ${this.startY + middleHeight}`;
d += ` L${this.endX} ${this.startY + middleHeight}`; d += ` L${this.endX} ${this.startY + middleHeight}`;
@ -79,7 +78,7 @@ namespace pxsim.visuals {
} }
public setSelected(selected: boolean) { public setSelected(selected: boolean) {
this.selected = selected; super.setSelected(selected);
this.updateOpacity(); this.updateOpacity();
} }

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:none;stroke:#717171;}
</style>
<title>color</title>
<g id="menu_icn_expansions" transform="translate(-9596 11554)">
<circle id="Ellipse_34" class="st0" cx="9607.5" cy="-11542" r="9"/>
<polygon class="st1" points="9613.5,-11543 9608.5,-11543 9608.5,-11548 9606.5,-11548 9606.5,-11543 9601.5,-11543 9601.5,-11541
9606.5,-11541 9606.5,-11536 9608.5,-11536 9608.5,-11541 9613.5,-11541 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 802 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<polygon class="st0" points="22.8,6.8 20.8,4.9 11.5,14.2 2.2,4.9 0.2,6.8 10.5,17.1 10.5,17.1 11.5,18.1 11.8,17.8 11.8,17.8 "/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<polygon class="st0" points="22.6,16.1 12.5,6 12.5,6 11.5,5 11.2,5.3 11.2,5.3 0.4,16.1 2.3,18 11.5,8.8 20.7,18 "/>
</svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<path id="menu_icn_brick" class="st0" d="M19.7,22.5H3.3v-22h16.5V22.5z M6,4.2v8.2h11V4.2H6z"/>
</svg>

After

Width:  |  Height:  |  Size: 520 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="menu_icn_controls" transform="translate(-9597 11323)">
<rect id="Rectangle_253_2_" x="9600.5" y="-11319" class="st0" width="2" height="16"/>
<rect id="Rectangle_254_2_" x="9607.5" y="-11319" class="st0" width="2" height="16"/>
<rect id="Rectangle_255" x="9614.5" y="-11319" class="st0" width="2" height="16"/>
<rect id="Rectangle_256" x="9598.5" y="-11309" class="st0" width="6" height="5"/>
<rect id="Rectangle_257" x="9605.5" y="-11318" class="st0" width="6" height="5"/>
<rect id="Rectangle_258" x="9612.5" y="-11313" class="st0" width="6" height="5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 999 B

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:0.3;}
</style>
<title>color</title>
<g id="menu_icn_FX" transform="translate(-9079.886 11883.081)">
<path id="Path_166" class="st0" d="M9086.9-11874.7h-1.8c0-0.5,0-0.6,0.5-0.6c0.9,0,1.5-0.4,1.8-1.2c0.4-0.9,1-1.7,1.5-2.6
c0.5-0.7,1.4-1.3,2.3-1.4c0.6-0.1,1.3,0.1,1.6,0.6c0.2,0.3,0.2,0.6,0.1,1c-0.2,0.3-0.5,0.4-0.9,0.3c-0.3,0-0.5-0.3-0.5-0.6
c0-0.1,0-0.1,0-0.2c0-0.2,0.1-0.4,0.1-0.6c-0.2,0-0.5-0.1-0.7,0c-0.4,0.2-0.7,0.4-1,0.9c-0.4,0.6-0.7,1.4-1,2.1
c-0.2,0.5-0.4,1.2-0.5,1.7h1.4c-0.1,0.2-0.1,0.4-0.2,0.6h-1.4c-0.3,1.1-0.5,2-0.9,3c-0.4,2-1.2,3.9-2,5.8c-0.4,0.9-1,1.6-1.6,2.2
c-0.5,0.5-1.2,0.8-1.9,0.8c-0.4,0.1-0.9-0.1-1.3-0.3c-0.2-0.2-0.3-0.6-0.2-1c0.2-0.3,0.5-0.5,1-0.4c0.3,0,0.4,0.3,0.4,0.6
c0,0.1-0.1,0.2-0.1,0.2c-0.1,0.1-0.1,0.2-0.1,0.3c0.1,0,0.2,0.1,0.4,0.1c0.5,0,1.1-0.3,1.4-0.8c0.3-0.5,0.6-1.1,0.7-1.6
c0.8-3,1.7-6,2.6-8.9C9086.9-11874.6,9086.9-11874.6,9086.9-11874.7z"/>
<path id="Path_167" class="st0" d="M9098.6-11862.8c0.1-0.1,0.1-0.2,0.2-0.3c1.1-0.6,1.8-1.8,2-3c0.4-2,0.4-4-0.1-6
c-0.3-1.1-1-1.9-1.8-2.6c-0.1-0.1-0.2-0.2-0.2-0.3c1.9,0.7,3.7,3.1,3.7,6.1C9102.5-11866.3,9101-11863.8,9098.6-11862.8z"/>
<path id="Path_168" class="st0" d="M9093.6-11874.9c0,0.1-0.1,0.2-0.1,0.2c-1.1,0.6-1.8,1.8-2,3c-0.4,2-0.4,4,0.1,6.1
c0.3,1.1,1,1.9,1.8,2.6c0.1,0.1,0.2,0.1,0.2,0.3c-2.2-1.1-3.7-3.4-3.6-6C9089.9-11872,9091.9-11874.4,9093.6-11874.9z"/>
<path id="Path_169" class="st0" d="M9098.3-11867.1c0,0,0.1,0,0.2,0.1c0,0,0,0,0,0.1c-0.3,0.5-0.6,1-1.2,1.3c-0.2,0.2-0.6,0.3-1,0
c-0.1-0.1-0.2-0.2-0.2-0.4c-0.2-0.6-0.3-1.2-0.4-1.8c0-0.1,0-0.1-0.1-0.2c-0.1,0.2-0.2,0.4-0.4,0.6c-0.3,0.4-0.6,1-1.1,1.4
c-0.2,0.2-0.4,0.4-0.7,0.5c-0.3,0.1-0.6,0-0.9-0.3v-0.1c0-0.2,0-0.4,0.2-0.5c0.1-0.1,0.3-0.1,0.5,0l0.1,0.1
c0.3,0.2,0.4,0.3,0.6-0.1c0.4-0.6,0.9-1.4,1.4-2c0-0.1,0-0.2,0-0.3c-0.1-0.4-0.2-0.9-0.3-1.3c-0.2-0.7-0.5-1-1.3-1h-0.1
c-0.1-0.1,0-0.2,0.1-0.2c0.4-0.1,1-0.2,1.4-0.2c0.1,0,0.2,0,0.3,0.1c0.3,0.4,0.5,0.8,0.6,1.4c0,0.1,0.1,0.3,0.1,0.4
c0.1-0.2,0.3-0.5,0.5-0.7c0.3-0.3,0.6-0.7,1-1c0.2-0.1,0.4-0.2,0.6-0.2c0.3,0,0.5,0.2,0.5,0.5v0.1c-0.1,0.3-0.3,0.4-0.6,0.3l0,0
c-0.1,0-0.1,0-0.2,0c-0.3-0.2-0.7-0.1-1,0.2l0,0c-0.2,0.3-0.5,0.7-0.7,1.1c0,0.1,0,0.1,0,0.2c0.2,0.7,0.3,1.5,0.5,2.3
c0,0.1,0,0.1,0.1,0.2c0.1,0.3,0.3,0.4,0.5,0.1C9097.9-11866.6,9098-11866.9,9098.3-11867.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{enable-background:new ;}
</style>
<title>color</title>
<g id="menu_icn_matrices" transform="translate(-9597 11374)">
<rect id="Rectangle_253" x="9603.4" y="-11368.5" class="st0" width="14.8" height="1.8"/>
<rect id="Rectangle_253-2" x="9603.4" y="-11363.9" class="st0" width="14.8" height="1.8"/>
<rect id="Rectangle_254" x="9603.4" y="-11359.3" class="st0" width="14.8" height="1.8"/>
<g class="st1">
<path class="st0" d="M9600.2-11366.7h-1v-3.6h-0.8v-0.6c0.5,0,0.9-0.3,1-0.9h0.8V-11366.7z"/>
</g>
<g class="st1">
<path class="st0" d="M9601.4-11361.1v0.9h-3.6v-0.4c0-1.2,0.8-1.7,1.5-2.1c1-0.5,1-0.7,1-1c0-0.3-0.2-0.6-0.7-0.6
c-0.5,0-0.8,0.4-0.8,0.9h-1c0-0.9,0.6-1.8,1.8-1.8c1.2,0,1.7,0.8,1.7,1.5c0,1-0.7,1.3-1.5,1.8c-0.5,0.3-0.9,0.6-0.9,0.9H9601.4z"
/>
</g>
<g class="st1">
<path class="st0" d="M9599.6-11353.6c-1.2,0-1.8-0.8-1.9-1.7h1c0,0.5,0.3,0.8,0.9,0.8c0.6,0,0.9-0.3,0.9-0.7
c0-0.4-0.3-0.7-0.8-0.7h-0.3v-0.8h0.2c0.6,0,0.8-0.3,0.8-0.6c0-0.4-0.3-0.6-0.7-0.6c-0.5,0-0.8,0.4-0.8,0.8h-1
c0-0.9,0.6-1.7,1.7-1.7c1.1,0,1.7,0.7,1.7,1.4c0,0.4-0.2,0.8-0.7,1c0.6,0.2,0.8,0.7,0.8,1.2
C9601.5-11354.5,9600.9-11353.6,9599.6-11353.6z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="menu_icn_logic" transform="translate(1634.03 -5.889)">
<path id="Path_161" class="st0" d="M-1632.4,20.8h2.6c1.5-0.1,2.8-1.1,3.3-2.6c1.1-2.6,3-7.3,6.1-7.3s3.5,0,3.5,0V8.3l4.1,4.3
l-4.1,3.9v-2.8h-2.8c-1.5,0.4-2.7,1.4-3.2,2.8c-1.2,2.6-2.4,7-6.4,7.1c-1.8,0-3,0-3,0V20.8z"/>
<path id="Path_162" class="st0" d="M-1632.4,11v2.8h3.1c0,0,1.8,0,2.8,2.4c0.4-1,0.9-1.9,1.5-2.8c0,0-2-2.3-4.2-2.3
S-1632.4,11-1632.4,11z"/>
<path id="Path_163" class="st0" d="M-1622.8,18.3c0,0,1.3,2.6,2.8,2.6h3.2v-2.6l4.2,4.1l-4.2,4.1v-2.8h-3.6c0,0-2,0-3.9-2.5
C-1623.7,20.3-1623.2,19.3-1622.8,18.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1018 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:0.7;}
</style>
<title>color</title>
<g id="menu_icn_loops" transform="translate(-9080.903 10401.478)">
<path id="Path_141" class="st0" d="M9084.5-10385.8c-1-1.4-2.1-2.7-3.1-4.2h2.1c0-2.4,1-4.8,2.7-6.5c1.2-1.2,2.6-2,4.3-2.3
c3-0.7,6.1,0.2,8.3,2.4c-0.5,0.5-1,1-1.4,1.4c-2.6-2.2-5.4-2.6-8.4-1c-2.2,1.2-3.4,3.5-3.4,5.9h2.1
C9086.5-10388.6,9085.4-10387.1,9084.5-10385.8z"/>
<path id="Path_142" class="st0" d="M9099.4-10390h-2.1c1.1-1.4,2.1-2.7,3.1-4.2c1.1,1.4,2.1,2.7,3.1,4.2h-2.1
c0,2.5-1.1,4.9-2.9,6.6c-1.2,1.1-2.6,1.9-4.2,2.2c-3,0.7-6.1-0.2-8.3-2.4l1.4-1.4c2.5,2.2,5.4,2.7,8.4,1
C9098.1-10385.2,9099.4-10387.5,9099.4-10390z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;}
.st1{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="menu_icn_math" transform="translate(-9078.442 10583.172)">
<path id="Path_153" class="st0" d="M9087.2-10563.2c-0.2,0.2-0.6,0.6-0.8,0.8l-2.6-2.6c-1,1-1.8,1.8-2.6,2.6
c-0.2-0.2-0.6-0.6-0.8-0.8l2.6-2.6c-1-1-1.8-1.8-2.6-2.6c0.2-0.2,0.6-0.6,0.8-0.8l2.6,2.6l2.6-2.6c0.2,0.2,0.6,0.6,0.8,0.8
l-2.6,2.6C9085.4-10564.9,9086.3-10564,9087.2-10563.2z"/>
<path id="Path_154" class="st0" d="M9079.3-10576.3v-1.1h3.7v-3.7c0.4,0,0.7,0,1.1,0v3.7h3.7v1.1h-3.7v3.7h-1.2v-3.7H9079.3z"/>
<path id="Path_155" class="st1" d="M9100.5-10566.3h-9.4v-2h9.4V-10566.3z"/>
<path id="Path_156" class="st1" d="M9100.5-10564.3v2h-9.4v-2H9100.5z"/>
<path id="Path_157" class="st1" d="M9100.5-10578v2h-9.4v-2H9100.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="menu_icn_motor" transform="translate(-9078.587 10279.652)">
<path id="Path_159" class="st0" d="M9095.2-10271.5c-0.8,0.8-1.7,1.7-2.5,2.5h6.4v-6.4l-2,2c-1.5-1.8-3.6-3-6-3.3
c-2-0.3-4.1,0.2-5.8,1.3c-4.3,2.6-5.6,8.2-3,12.5s8.2,5.6,12.5,3c1.5-0.9,2.6-2.2,3.4-3.7c-0.8-0.3-1.6-0.7-2.4-1
c-0.8,1.6-2.3,2.8-4,3.3c-1.3,0.4-2.7,0.3-4-0.2c-3.3-1.3-5-5-3.8-8.3c0.7-2,2.4-3.5,4.4-4
C9091-10274.6,9093.7-10273.6,9095.2-10271.5z"/>
<path id="Path_160" class="st0" d="M9088.2-10267.6c0,1.1,0.9,1.9,1.9,1.9s1.9-0.9,1.9-1.9c0-1.1-0.9-1.9-1.9-1.9
C9089-10269.6,9088.2-10268.7,9088.2-10267.6C9088.2-10267.6,9088.2-10267.6,9088.2-10267.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<path id="menu_icn_music" class="st0" d="M13.9,12.8c1.7,0.3,3.3,1.2,4.3,2.6c0.5,0,1,0,1,0s1.5-4.4-1.8-7.5s-9.7-3.1-12.1,0
c-1.7,2.1-2.3,5-1.4,7.5c0.3,0,0.5,0,0.8,0c0,0,1.7-2.5,4.4-2.6C9,17.2,9,21,9,21c-1.9-0.4-3.5-1.6-4.4-3.4C2.6,17.2,1,15.4,1,13.2
C1,6.7,5.9,3,11.5,3s10.8,4.3,10.4,10.2c-0.2,4.1-3.6,4.4-3.6,4.4c-0.8,1.8-2.4,3.1-4.3,3.4C13.9,16.5,13.9,12.8,13.9,12.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 799 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<path class="st0" d="M11.5,1.8c-5.4,0-9.8,4.4-9.8,9.8c0,5.4,4.4,9.8,9.8,9.8s9.8-4.4,9.8-9.8C21.2,6.1,16.9,1.8,11.5,1.8z
M11.5,18.3c-3.8,0-6.8-3.1-6.8-6.8s3.1-6.8,6.8-6.8s6.8,3.1,6.8,6.8S15.3,18.3,11.5,18.3z"/>
<circle class="st0" cx="11.5" cy="11.5" r="3.9"/>
</svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{enable-background:new ;}
.st1{fill:#FFFFFF;}
</style>
<title>color</title>
<g class="st0">
<path class="st1" d="M13.3,5.4v16H9.5v-16H4.4V1.9h14.2v3.5H13.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 549 B

Some files were not shown because too many files have changed in this diff Show More