Compare commits

..

116 Commits

Author SHA1 Message Date
59ca9cf463 0.0.52 2018-01-04 13:04:23 -08:00
7da811246c Behaviors Driven Robotics (#178)
* adding avoid crash behavior

* more implementation

* add another simple behavior

* fixed synched motors

* bump common packages
2018-01-04 12:55:30 -08:00
69f8453947 Merge pull request #177 from Microsoft/sounds_bug_fix
Fix stopAllSounds simulator bug
2018-01-04 12:52:17 -08:00
39ba9b81af Initial work 2018-01-04 12:20:31 -08:00
51a14596cd 0.0.51 2018-01-04 11:41:49 -08:00
8518c446cd Merge pull request #176 from Microsoft/iefonticons
Fix font icons on IE, and add package icon on Windows
2018-01-04 11:40:42 -08:00
2f69df0d9d Fix font icons on IE, and add package icon on Windows 2018-01-04 11:39:20 -08:00
1789d0ce21 Lowercase menubar titles. 2018-01-04 10:42:37 -08:00
d0809510c4 Merge pull request #174 from Microsoft/addbetatag
Add beta tag.
2018-01-04 10:14:43 -08:00
6b9c0eaf65 Add beta tag. #150 2018-01-04 10:14:11 -08:00
52bdf94233 Merge pull request #173 from Microsoft/blockiconfont
Add block icon font
2018-01-04 09:57:11 -08:00
fd50ed8f7c Add block icon font 2018-01-04 09:52:12 -08:00
ba0eb93b0f use common packages tests implementation (#171) 2018-01-04 09:23:28 -08:00
9a4ed45797 0.0.50 2018-01-04 09:19:33 -08:00
820fdf3a3c Merge pull request #172 from Microsoft/fixwinicons
Fix check and cancel icons on windows
2018-01-04 09:18:30 -08:00
348d5ffc26 Fix check and cancel icons on windows 2018-01-04 09:17:30 -08:00
95e47a0b25 Merge pull request #169 from Microsoft/screenimgeditor
Initial screen field editor.
2018-01-04 08:37:56 -08:00
c3312ed5d1 Merge pull request #170 from Microsoft/clearcounts
moving motor blocks around
2018-01-04 08:37:39 -08:00
b7cdc7d0fe moving blocks around 2018-01-04 08:25:02 -08:00
dde5a35cd9 Initial screen field editor. Adding Lego designer assets to legoresources 2018-01-03 17:20:35 -08:00
be398d84ee Merge pull request #135 from Microsoft/field_ports
Add an output port field editor
2018-01-03 17:07:34 -08:00
4445acce7a Split editor extensions into multiple module loaded files. Re-using editor/deploy.ts for cmds. Fixing field ports 2018-01-03 16:18:39 -08:00
d6d8b0655b Merge branch 'master' into field_ports 2018-01-03 15:12:35 -08:00
216aa1ddaf Fix save icon 2018-01-03 15:07:45 -08:00
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
a1b059171b Add a output port field editor 2017-12-24 17:46:58 -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
142 changed files with 13551 additions and 1354 deletions

View File

@ -2,7 +2,7 @@
import * as fs from 'fs';
require("./editor")
require("./editor/deploy")
declare namespace pxt.editor {
function deployCoreAsync(resp: pxtc.CompileResult, disconnect?: boolean): Promise<void>;

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont";
src: url("iconfont.eot?e05611aaee246c1da118a83eaf515de9?#iefix") format("embedded-opentype"),
url("iconfont.woff2?e05611aaee246c1da118a83eaf515de9") format("woff2"),
url("iconfont.woff?e05611aaee246c1da118a83eaf515de9") format("woff");
src: url("iconfont.eot?73552ec404b3a3d3769a3f04fa58c2c4?#iefix") format("embedded-opentype"),
url("iconfont.woff2?73552ec404b3a3d3769a3f04fa58c2c4") format("woff2"),
url("iconfont.woff?73552ec404b3a3d3769a3f04fa58c2c4") format("woff");
}
.icon {
@ -28,3 +28,63 @@ url("iconfont.woff?e05611aaee246c1da118a83eaf515de9") format("woff");
.icon-gyro:before {
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-advancedcollapsed:before {
content: "\f112";
}
.icon-advancedexpanded:before {
content: "\f113";
}
.icon-cancel:before {
content: "\f114";
}
.icon-check:before {
content: "\f115";
}
.icon-download:before {
content: "\f116";
}
.icon-save:before {
content: "\f117";
}
.icon-blocks:before {
content: "\f118";
}

Binary file not shown.

View File

@ -19,6 +19,66 @@
<glyph glyph-name="gyro"
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" />
<glyph glyph-name="addpackage"
unicode="&#xF105;"
horiz-adv-x="40" d=" M20 35.7C11.3 35.7 4.3 28.7 4.3 20S11.3 4.3 20 4.3S35.7 11.3 35.7 20S28.7 35.7 20 35.7zM30.4 18.3H21.7V9.6H18.3V18.3H9.6V21.7H18.3V30.4H21.7V21.7H30.4V18.3z" />
<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="advancedcollapsed"
unicode="&#xF112;"
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="&#xF113;"
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" />
<glyph glyph-name="cancel"
unicode="&#xF114;"
horiz-adv-x="40" d=" M33 29.6L29.4 33.2L20.2 24L11 33.2L7.3 29.6L16.5 20.3L7.3 11.1L11 7.3L20.2 16.5L29.4 7.3L33 11.1L23.8 20.3z" />
<glyph glyph-name="check"
unicode="&#xF115;"
horiz-adv-x="40" d=" M33.7 32.9L15.3 14.4L7.5 22.3L3.8 18.4L11.7 10.8L11.7 10.8L15.3 7.1L37.4 29.2z" />
<glyph glyph-name="download"
unicode="&#xF116;"
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="&#xF117;"
horiz-adv-x="40" d=" M25 34.5C25 33.6 25 32.8 25 31.9C25 31 24.7 30.9 24 30.9C23.5 30.9 22.8 30.9 22.4 30.9C22.1 30.9 21.4 31 21.4 31.7V31.9V36.9C21.4 37.3 21.6 37.8 22.3 38C22.3 38 22.3 38 22.4 38C23 38 23.7 38 24.2 38C24.7 38 25.2 37.6 25.2 37.1C25.2 37.1 25.2 37.1 25.2 36.9C25 36.3 25 35.2 25 34.5z M37.6 31.2C35.8 32.9 34.3 34.5 32.5 36.3C31 37.8 31 37.8 28.5 37.8C27.5 37.8 27.3 37.6 27.3 36.6V28.4C27.3 27.4 27.1 27.2 26.1 27.2H14.3C13.2 27.2 13 27.4 13 28.4V36.4C13 37.6 12.9 37.8 11.7 37.8H3.8C2.6 37.8 2.4 37.6 2.4 36.4V19.9C2.4 14.3 2.4 8.9 2.4 3.2C2.4 2 2.6 1.8 3.7 1.8H36.9C37.9 1.8 38.1 2.2 38.1 3.2V29.6C38.3 30.5 37.9 30.9 37.6 31.2zM33.7 6.7H7.1V23H33.7V6.7z" />
<glyph glyph-name="blocks"
unicode="&#xF118;"
horiz-adv-x="40" d=" M10.9 23H39V16.9H10.9V23z M39.2 27.1L39.2 33L0.9 33L0.9 31.1L0.9 27.1L0.9 12.9L0.9 7L39.2 7L39.2 12.9L6.9 12.9L6.9 27.1z" />
</font>
</defs>
</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.

133
editor/deploy.ts Normal file
View File

@ -0,0 +1,133 @@
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
import UF2 = pxtc.UF2;
export let ev3: pxt.editor.Ev3Wrapper
export function debug() {
return initAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
function hf2Async() {
return pxt.HF2.mkPacketIOAsync()
.then(h => {
let w = new pxt.editor.Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})
}
let noHID = false
let initPromise: Promise<pxt.editor.Ev3Wrapper>
export function initAsync() {
if (initPromise)
return initPromise
let canHID = false
if (pxt.U.isNodeJS) {
canHID = true
} else {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && !forceHexDownload)
canHID = true
}
if (noHID)
canHID = false
if (canHID) {
initPromise = hf2Async()
.catch(err => {
initPromise = null
noHID = true
return Promise.reject(err)
})
} else {
noHID = true
initPromise = Promise.reject(new Error("no HID"))
}
return initPromise
}
// this comes from aux/pxt.lms
const rbfTemplate = `
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
`
export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
let w: pxt.editor.Ev3Wrapper
let filename = resp.downloadFileBaseName || "pxt"
filename = filename.replace(/^lego-/, "")
let fspath = "../prjs/BrkProg_SAVE/"
let elfPath = fspath + filename + ".elf"
let rbfPath = fspath + filename + ".rbf"
let rbfHex = rbfTemplate
.replace(/\s+/g, "")
.replace("XX", pxt.U.toHex(pxt.U.stringToUint8Array(elfPath)))
let rbfBIN = pxt.U.fromHex(rbfHex)
pxt.HF2.write16(rbfBIN, 4, rbfBIN.length)
let origElfUF2 = UF2.parseFile(pxt.U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()])))
let mkFile = (ext: string, data: Uint8Array = null) => {
let f = UF2.newBlockFile()
f.filename = "Projects/" + filename + ext
if (data)
UF2.writeBytes(f, 0, data)
return f
}
let elfUF2 = mkFile(".elf")
for (let b of origElfUF2) {
UF2.writeBytes(elfUF2, b.targetAddr, b.data)
}
let r = UF2.concatFiles([elfUF2, mkFile(".rbf", rbfBIN)])
let data = UF2.serializeFile(r)
resp.outfiles[pxtc.BINARY_UF2] = btoa(data)
let saveUF2Async = () => {
if (isCli || !pxt.commands.saveOnlyAsync) {
return Promise.resolve()
} else {
return pxt.commands.saveOnlyAsync(resp)
}
}
if (noHID) return saveUF2Async()
return initAsync()
.then(w_ => {
w = w_
if (w.isStreaming)
pxt.U.userError("please stop the program first")
return w.stopAsync()
})
.then(() => w.rmAsync(elfPath))
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
.then(() => w.flashAsync(rbfPath, rbfBIN))
.then(() => w.runAsync(rbfPath))
.then(() => {
if (isCli)
return w.disconnectAsync()
else
return Promise.resolve()
//return Promise.delay(1000).then(() => w.dmesgAsync())
}).catch(e => {
// if we failed to initalize, retry
if (noHID)
return saveUF2Async()
else
return Promise.reject(e)
})
}

View File

@ -1,152 +1,120 @@
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
import { deployCoreAsync, initAsync } from "./deploy";
import { FieldPorts } from "./field_ports";
import { FieldImages } from "./field_images";
pxt.editor.initExtensionsAsync = function(opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...')
updateBlocklyShape();
const res: pxt.editor.ExtensionResult = {
fieldEditors: [{
selector: "ports",
editor: FieldPorts
}, {
selector: "images",
editor: FieldImages
}],
deployCoreAsync
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
/**
* 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;
}
// When require()d from node, bind the global pxt namespace
namespace pxt {
export const dummyExport = 1;
}
eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")
namespace pxt.editor {
import UF2 = pxtc.UF2;
export let ev3: Ev3Wrapper
export function debug() {
return initAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
// this comes from aux/pxt.lms
const rbfTemplate = `
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
`
function hf2Async() {
return pxt.HF2.mkPacketIOAsync()
.then(h => {
let w = new Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})
}
let noHID = false
let initPromise: Promise<Ev3Wrapper>
function initAsync() {
if (initPromise)
return initPromise
let canHID = false
if (U.isNodeJS) {
canHID = true
} else {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (Cloud.isLocalHost() && Cloud.localToken && !forceHexDownload)
canHID = true
}
if (noHID)
canHID = false
if (canHID) {
initPromise = hf2Async()
.catch(err => {
initPromise = null
noHID = true
return Promise.reject(err)
})
} else {
noHID = true
initPromise = Promise.reject(new Error("no HID"))
}
return initPromise
}
export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
let w: Ev3Wrapper
let filename = resp.downloadFileBaseName || "pxt"
filename = filename.replace(/^lego-/, "")
let fspath = "../prjs/BrkProg_SAVE/"
let elfPath = fspath + filename + ".elf"
let rbfPath = fspath + filename + ".rbf"
let rbfHex = rbfTemplate
.replace(/\s+/g, "")
.replace("XX", U.toHex(U.stringToUint8Array(elfPath)))
let rbfBIN = U.fromHex(rbfHex)
HF2.write16(rbfBIN, 4, rbfBIN.length)
let origElfUF2 = UF2.parseFile(U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()])))
let mkFile = (ext: string, data: Uint8Array = null) => {
let f = UF2.newBlockFile()
f.filename = "Projects/" + filename + ext
if (data)
UF2.writeBytes(f, 0, data)
return f
}
let elfUF2 = mkFile(".elf")
for (let b of origElfUF2) {
UF2.writeBytes(elfUF2, b.targetAddr, b.data)
}
let r = UF2.concatFiles([elfUF2, mkFile(".rbf", rbfBIN)])
let data = UF2.serializeFile(r)
resp.outfiles[pxtc.BINARY_UF2] = btoa(data)
let saveUF2Async = () => {
if (isCli || !pxt.commands.saveOnlyAsync) {
return Promise.resolve()
} else {
return pxt.commands.saveOnlyAsync(resp)
}
}
if (noHID) return saveUF2Async()
return initAsync()
.then(w_ => {
w = w_
if (w.isStreaming)
U.userError("please stop the program first")
return w.stopAsync()
})
.then(() => w.rmAsync(elfPath))
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
.then(() => w.flashAsync(rbfPath, rbfBIN))
.then(() => w.runAsync(rbfPath))
.then(() => {
if (isCli)
return w.disconnectAsync()
else
return Promise.resolve()
//return Promise.delay(1000).then(() => w.dmesgAsync())
}).catch(e => {
// if we failed to initalize, retry
if (noHID)
return saveUF2Async()
else
return Promise.reject(e)
})
}
initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...')
const res: pxt.editor.ExtensionResult = {
deployCoreAsync,
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
}
// namespace pxt {
// export const dummyExport = 1;
// }
// eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")

114
editor/field_images.ts Normal file
View File

@ -0,0 +1,114 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldImagesOptions extends pxtblockly.FieldImageDropdownOptions {
}
export class FieldImages extends pxtblockly.FieldImageDropdown implements Blockly.FieldCustom {
public isFieldCustom_ = true;
constructor(text: string, options: FieldImagesOptions, validator?: Function) {
super(text, options, validator);
}
/**
* Create a dropdown menu under the text.
* @private
*/
public showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const options = this.getOptions();
for (let i = 0, option: any; option = options[i]; i++) {
let content = (options[i] as any)[0]; // Human-readable text or image.
const value = (options[i] as any)[1]; // Language-neutral value.
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content.alt;
if ((this as any).columns_) {
button.style.width = (((this as any).width_ / (this as any).columns_) - 8) + 'px';
//button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
} else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
}
let backgroundColor = this.sourceBlock_.getColour();
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = this.sourceBlock_.getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = this.sourceBlock_.getColourTertiary();
Blockly.bindEvent_(button, 'click', this, (this as any).buttonClick_);
Blockly.bindEvent_(button, 'mouseup', this, (this as any).buttonClick_);
// These are applied manually instead of using the :hover pseudoclass
// because Android has a bad long press "helper" menu and green highlight
// that we must prevent with ontouchstart preventDefault
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
e.preventDefault();
});
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', this.id);
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
let buttonImg = document.createElement('img');
buttonImg.src = content.src;
//buttonImg.alt = icon.alt;
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
button.appendChild(buttonImg);
contentDiv.appendChild(button);
}
contentDiv.style.width = (this as any).width_ + 'px';
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Calculate positioning based on the field position.
var scale = this.sourceBlock_.workspace.scale;
var bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
var position = this.fieldGroup_.getBoundingClientRect();
var primaryX = position.left + bBox.width / 2;
var primaryY = position.top + bBox.height;
var secondaryX = primaryX;
var secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
(this as any).onHide_.bind(this));
}
}

147
editor/field_ports.ts Normal file
View File

@ -0,0 +1,147 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldPortsOptions extends Blockly.FieldCustomDropdownOptions {
columns?: string;
width?: string;
}
export class FieldPorts extends Blockly.FieldDropdown implements Blockly.FieldCustom {
public isFieldCustom_ = true;
// Width in pixels
private width_: number;
// Columns in grid
private columns_: number;
private savedPrimary_: string;
constructor(text: string, options: FieldPortsOptions, validator?: Function) {
super(options.data);
this.columns_ = parseInt(options.columns) || 4;
this.width_ = parseInt(options.width) || 300;
}
/**
* Create a dropdown menu under the text.
* @private
*/
public showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const options = this.getOptions();
for (let i = 0, option: any; option = options[i]; i++) {
let content = (options[i] as any)[0]; // Human-readable text or image.
const value = (options[i] as any)[1]; // Language-neutral value.
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content.alt;
if (this.columns_) {
button.style.width = ((this.width_ / this.columns_) - 8) + 'px';
button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
} else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
}
let backgroundColor = this.sourceBlock_.getColour();
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = this.sourceBlock_.getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = this.sourceBlock_.getColourTertiary();
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
// These are applied manually instead of using the :hover pseudoclass
// because Android has a bad long press "helper" menu and green highlight
// that we must prevent with ontouchstart preventDefault
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
e.preventDefault();
});
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', this.id);
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
let buttonImg = document.createElement('img');
buttonImg.src = content.src;
//buttonImg.alt = icon.alt;
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
button.appendChild(buttonImg);
contentDiv.appendChild(button);
}
contentDiv.style.width = this.width_ + 'px';
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Calculate positioning based on the field position.
var scale = this.sourceBlock_.workspace.scale;
var bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
var position = this.fieldGroup_.getBoundingClientRect();
var primaryX = position.left + bBox.width / 2;
var primaryY = position.top + bBox.height;
var secondaryX = primaryX;
var secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
}
/**
* Callback for when a button is clicked inside the drop-down.
* Should be bound to the FieldIconMenu.
* @param {Event} e DOM event for the click/touch
* @private
*/
private buttonClick_ = function (e: any) {
let value = e.target.getAttribute('data-value');
this.setValue(value);
Blockly.DropDownDiv.hide();
};
/**
* Callback for when the drop-down is hidden.
*/
private onHide_ = function () {
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
};
}

View File

@ -1,12 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"declaration": true,
"out": "../built/editor.js",
"module": "commonjs",
"outDir": "../built/editor",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false
"sourceMap": false,
"allowSyntheticDefaultImports": true,
"declaration": true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
{
"behaviors": "Behavior drive blocks",
"behaviors.Behavior": "A behavior",
"behaviors.BehaviorManager": "A manager for behaviors",
"behaviors.BehaviorManager.add": "Adds a new behavior to the behavior manager",
"behaviors.BehaviorManager.add|param|behavior": "the behavior to add",
"behaviors.BehaviorManager.start": "Starts the behavior control loop",
"behaviors.BehaviorManager.stop": "Stops the execution loop",
"behaviors.addBehavior": "Adds the behavior and starts it",
"behaviors.addBehavior|param|behavior": "a behavior",
"behaviors.avoidCrash": "A behavior that stops all motors if the sensor distance get too short",
"behaviors.driveForward": "A behavior that turns on the motors to the specified speed",
"behaviors.driveForward|param|motors": "@param speed the desired speed, eg: 50"
}

View File

@ -0,0 +1,7 @@
{
"behaviors.addBehavior|block": "add behavior %behavior",
"behaviors.avoidCrash|block": "avoid crash using %ultrasonic",
"behaviors.driveForward|block": "drive %motors|forward at %speed|%",
"behaviors|block": "behaviors",
"{id:category}Behaviors": "Behaviors"
}

6
libs/behaviors/pxt.json Normal file
View File

@ -0,0 +1,6 @@
{
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/behaviors",
"dependencies": {
"core": "file:../ev3"
}
}

View File

@ -0,0 +1,56 @@
namespace behaviors {
class AvoidCrashBehavior extends behaviors.Behavior {
private ultrasonic: sensors.UltraSonicSensor;
constructor(ultrasonic: sensors.UltraSonicSensor) {
super();
this.ultrasonic = ultrasonic;
}
shouldRun(): boolean {
return this.ultrasonic.distance() < 5;
}
run(): void {
motors.stopAllMotors();
this.active = false;
}
}
/**
* A behavior that stops all motors if the sensor distance get too short
*/
//% blockId=behaviorsAvoidCrash block="avoid crash using %ultrasonic"
export function avoidCrash(ultrasonic: sensors.UltraSonicSensor) : behaviors.Behavior {
return new AvoidCrashBehavior(ultrasonic);
}
class DriveForwardBehavior extends behaviors.Behavior {
private motors: motors.MotorBase;
private speed: number;
constructor(motors: motors.MotorBase, speed: number) {
super();
this.motors = motors;
this.speed = speed;
}
shouldRun(): boolean {
return true;
}
run(): void {
this.motors.setSpeed(this.speed);
pauseUntil(() => !this.active);
this.motors.setSpeed(0);
}
}
/**
* A behavior that turns on the motors to the specified speed
* @param motors
* @param speed the desired speed, eg: 50
*/
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed|%"
export function driveForward(motors: motors.MotorBase, speed: number): behaviors.Behavior {
return new DriveForwardBehavior(motors, speed);
}
}

View File

@ -56,6 +56,7 @@ namespace sensors {
constructor(port: number) {
super(port)
this._setMode(ColorSensorMode.None);
this.thresholdDetector = new sensors.internal.ThresholdDetector(this.id());
}
@ -68,6 +69,13 @@ namespace sensors {
}
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)
}
@ -103,6 +111,7 @@ namespace sensors {
//% blockId=colorOnColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=100 blockGap=8
//% group="Color Sensor"
onColorDetected(color: ColorSensorColor, handler: () => void) {
@ -122,6 +131,7 @@ namespace sensors {
//% blockId=colorPauseForColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Color Sensor"
pauseForColor(color: ColorSensorColor) {
@ -141,6 +151,7 @@ namespace sensors {
//% blockId=colorGetColor
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99
//% group="Color Sensor"
color(): ColorSensorColor {
@ -158,6 +169,7 @@ namespace sensors {
//% blockId=colorOnLightChanged
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=89 blockGap=8
//% group="Color Sensor"
onLightChanged(mode: LightIntensityMode, condition: LightCondition, handler: () => void) {
@ -174,6 +186,7 @@ namespace sensors {
//% blockId=colorPauseForLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=88 blockGap=8
//% group="Color Sensor"
pauseForLight(mode: LightIntensityMode, condition: LightCondition) {
@ -191,6 +204,7 @@ namespace sensors {
//% blockId=colorLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=87
//% group="Color Sensor"
light(mode: LightIntensityMode) {

View File

@ -21,8 +21,6 @@
"brick.Button.pauseUntil": "Waits until the event is raised",
"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._imagePicker": "An image",
"brick._imagePicker|param|image": "the image",
"brick.buttonDown": "Down button on the EV3 Brick.",
"brick.buttonEnter": "Enter button on the EV3 Brick.",
"brick.buttonLeft": "Left button on the EV3 Brick.",
@ -31,16 +29,12 @@
"brick.clearScreen": "Clears the screen",
"brick.lightPattern": "Pattern block.",
"brick.lightPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
"brick.print": "Show text on the screen.",
"brick.print|param|text": "the text to print on the screen, eg: \"Hello world\"",
"brick.print|param|x": "the starting position's x coordinate, eg: 0",
"brick.print|param|y": "the starting position's x coordinate, eg: 0",
"brick.setPixel": "Sets a pixel on or off",
"brick.setPixel|param|on": "a value indicating if the pixel should be on or off",
"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.printLine": "Show text on the screen at a specific line.",
"brick.printLine|param|line": "the line number to print the text at, eg: 0",
"brick.printLine|param|text": "the text to print on the screen, eg: \"Hello world\"",
"brick.printPorts": "Prints the port states on the screen",
"brick.setLight": "Set lights.",
"brick.setLight|param|pattern": "the lights pattern to use.",
"brick.showImage": "Shows an image on screen",
"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.",
@ -58,9 +52,10 @@
"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|value": "Component specific code indicating the cause of the event.",
"motors.Motor.angle": "Gets motor ration angle.",
"motors.Motor.clearCount": "Clears the motor count",
"motors.Motor.angle": "Gets motor angle.",
"motors.Motor.clearCounts": "Clears the motor count",
"motors.Motor.speed": "Gets motor actual speed.",
"motors.Motor.tacho": "Gets motor tachometer count.",
"motors.Motor.toString": "Returns the status of the motor",
"motors.MotorBase.isReady": "Returns a value indicating if the motor is still running a previous command.",
"motors.MotorBase.move": "Moves the motor by a number of rotations, degress or seconds",
@ -76,9 +71,15 @@
"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|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|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.",

View File

@ -24,22 +24,21 @@
"Output.CD|block": "C+D",
"Output.C|block": "C",
"Output.D|block": "D",
"brick.Button.isPressed|block": "`icons.brickButtons` %button|is pressed",
"brick.Button.onEvent|block": "on `icons.brickButtons` %button|%event",
"brick.Button.pauseUntil|block": "pause until `icons.brickButtons` %button|%event",
"brick.Button.wasPressed|block": "`icons.brickButtons` %button|was pressed",
"brick._imagePicker|block": "%image",
"brick.Button.isPressed|block": "%button|is pressed",
"brick.Button.onEvent|block": "on %button|%event",
"brick.Button.pauseUntil|block": "pause until %button|%event",
"brick.Button.wasPressed|block": "%button|was pressed",
"brick.buttonDown|block": "down",
"brick.buttonEnter|block": "enter",
"brick.buttonLeft|block": "left",
"brick.buttonRight|block": "right",
"brick.buttonUp|block": "up",
"brick.clearScreen|block": "`icons.brickDisplay` clear screen",
"brick.clearScreen|block": "clear screen",
"brick.lightPattern|block": "%pattern",
"brick.print|block": "`icons.brickDisplay` print %text| at x: %x| y: %y",
"brick.setPixel|block": "`icons.brickDisplay` set pixel %on| at x: %x| y: %y",
"brick.setStatusLight|block": "set `icons.brickButtons` to %pattern=led_pattern",
"brick.showImage|block": "`icons.brickDisplay` show image %image=screen_image_picker",
"brick.printLine|block": "print %text| at line %line",
"brick.printPorts|block": "print ports",
"brick.setLight|block": "set light to %pattern=led_pattern",
"brick.showImage|block": "show image %image=screen_image_picker",
"brick|block": "brick",
"console.logValue|block": "console|log value %name|= %value",
"console.log|block": "console|log %text",
@ -48,14 +47,17 @@
"control.raiseEvent|block": "raise event|from %src|with value %value",
"control|block": "control",
"motors.Motor.angle|block": "%motor|angle",
"motors.Motor.clearCounts|block": "%motor|clear counts",
"motors.Motor.speed|block": "%motor|speed",
"motors.Motor.tacho|block": "%motor|tacho",
"motors.MotorBase.move|block": "move %motor|for %value|%unit|at %speed|%",
"motors.MotorBase.pauseUntilReady|block": "%motor|pause until ready",
"motors.MotorBase.setBrake|block": "set %motor|brake %brake",
"motors.MotorBase.setReversed|block": "set %motor|reversed %reversed",
"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.tank|block": "tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit",
"motors.SynchedMotorPair.drive|block": "drive %chassis|at %speed|cm/s|turning %rotationSpeed|deg/s|for %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.largeAD|block": "large A+D",
"motors.largeA|block": "large A",

View File

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

View File

@ -59,7 +59,6 @@ namespace console {
namespace console.screen {
const maxLines = 100;
const screenLines = 8;
const lineHeight = 12;
let lines: string[];
let scrollPosition = 0;
@ -67,8 +66,8 @@ namespace console.screen {
if (!lines) {
lines = [];
console.addListener(log);
brick.buttonUp.onEvent(ButtonEvent.Click, () => scroll(1))
brick.buttonDown.onEvent(ButtonEvent.Click, () => scroll(-1))
brick.buttonUp.onEvent(ButtonEvent.Click, () => scroll(-1))
brick.buttonDown.onEvent(ButtonEvent.Click, () => scroll(1))
}
}
@ -78,7 +77,7 @@ namespace console.screen {
for (let i = 0; i < screenLines; ++i) {
const line = lines[i + scrollPosition];
if (line)
brick.print(line, 0, 4 + i * lineHeight)
brick.print(line, 0, 4 + i * brick.LINE_HEIGHT)
}
}

View File

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

View File

@ -14,7 +14,7 @@ enum Output {
//% block="C+D"
CD = Output.C | Output.D,
//% block="A+D"
AD = Output.B | Output.C,
AD = Output.A | Output.D,
//% block="All"
ALL = 0x0f
}
@ -75,9 +75,9 @@ namespace motors {
* @param buf message buffer
*/
//%
export function readPWM(buf: Buffer): number {
export function readPWM(buf: Buffer): void {
init()
return pwmMM.read(buf);
pwmMM.read(buf);
}
/**
@ -109,7 +109,7 @@ namespace motors {
* Stops all motors
*/
//% blockId=motorStopAll block="stop all motors"
//% weight=97
//% weight=5
//% group="Motion"
export function stopAllMotors() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
@ -258,12 +258,19 @@ namespace motors {
/**
* Returns a value indicating if the motor is still running a previous command.
*/
//%
//% group="Sensors"
isReady(): boolean {
this.init();
const r = readPWM(mkCmd(this._port, DAL.opOutputTest, 0))
// 0 = ready, 1 = busy
return r == 0;
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;
}
/**
@ -271,6 +278,7 @@ namespace motors {
* @param timeOut optional maximum pausing time in milliseconds
*/
//% blockId=motorPauseUntilRead block="%motor|pause until ready"
//% weight=97
//% group="Motion"
pauseUntilReady(timeOut?: number) {
pauseUntil(() => this.isReady(), timeOut);
@ -323,7 +331,8 @@ namespace motors {
* @param motor the port which connects to the motor
*/
//% blockId=motorSpeed block="%motor|speed"
//% weight=72 blockGap=8
//% weight=72
//% blockGap=8
//% group="Sensors"
speed(): number {
this.init();
@ -331,10 +340,10 @@ namespace motors {
}
/**
* Gets motor ration angle.
* Gets motor angle.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|angle"
//% blockId=motorAngle block="%motor|angle"
//% weight=70
//% group="Sensors"
angle(): number {
@ -342,11 +351,28 @@ namespace motors {
return getMotorData(this._port).count;
}
/**
* Gets motor tachometer count.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|tacho"
//% weight=69
//% blockGap=8
//% group="Sensors"
tacho(): number {
this.init();
return getMotorData(this._port).tachoCount;
}
/**
* Clears the motor count
*/
//% group="Motion"
clearCount() {
//% blockId=motorClearCount block="%motor|clear counts"
//% weight=68
//% blockGap=8
//% group="Sensors"
clearCounts() {
this.init();
const b = mkCmd(this._port, DAL.opOutputClearCount, 0)
writePWM(b)
@ -392,9 +418,13 @@ namespace motors {
//% fixedInstances
export class SynchedMotorPair extends MotorBase {
private wheelRadius: number;
private baseLength: number;
constructor(ports: Output) {
super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
this.wheelRadius = 3;
this.baseLength = 12;
this.markUsed();
}
@ -415,7 +445,7 @@ namespace motors {
private __setSpeed(speed: number) {
syncMotors(this._port, {
speed: speed,
turnRatio: 0,
turnRatio: 100, // same speed
useBrake: !!this._brake
})
}
@ -430,53 +460,6 @@ namespace motors {
});
}
/**
* Turns the motor and the follower motor by a number of rotations
* @param value the move quantity, eg: 2
* @param unit the meaning of the value
* @param steering the ratio of power sent to the follower motor, from ``-100`` to ``100``
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/
//% blockId=motorPairTurn block="steer %chassis|%steering|%|at speed %speed|%|by %value|%unit"
//% weight=9 blockGap=8
//% steering.min=-100 steering=100
//% inlineInputMode=inline
//% group="Chassis"
steer(steering: number, speed: number, value: number, unit: MoveUnit) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
stop(this._port, this._brake);
return;
}
const turnRatio = Math.clamp(-200, 200, steering + 100 >> 0);
let useSteps: boolean;
let stepsOrTime: number;
switch (unit) {
case MoveUnit.Rotations:
stepsOrTime = (value * 360) >> 0;
useSteps = true;
break;
case MoveUnit.Degrees:
stepsOrTime = value >> 0;
useSteps = true;
break;
default:
stepsOrTime = value;
useSteps = false;
break;
}
syncMotors(this._port, {
useSteps: useSteps,
speed: speed,
turnRatio: turnRatio,
stepsOrTime: stepsOrTime,
useBrake: this._brake
});
}
/**
* The Move Tank block can make a robot drive forward, backward, turn, or stop.
* Use the Move Tank block for robot vehicles that have two Large Motors,
@ -488,17 +471,115 @@ namespace motors {
* @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"
//% blockId=motorPairTank block="tank %chassis|left %speedLeft|%|right %speedRight|%|for %value|%unit"
//% weight=9 blockGap=8
//% speedLeft.min=-100 speedLeft=100
//% speedRight.min=-100 speedRight=100
//% inlineInputMode=inline
//% group="Chassis"
tank(speedLeft: number, speedRight: number, value: number, unit: MoveUnit) {
speedLeft = Math.clamp(speedLeft >> 0, -100, 100);
speedRight = Math.clamp(speedRight >> 0, -100, 100);
const steering = (speedRight * 100 / speedLeft) >> 0;
this.steer(speedLeft, steering, value, unit);
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();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
stop(this._port, this._brake);
return;
}
turnRatio = Math.clamp(-200, 200, turnRatio >> 0);
let useSteps: boolean;
let stepsOrTime: number;
switch (unit) {
case MoveUnit.Rotations:
stepsOrTime = (value * 360) >> 0;
useSteps = true;
break;
case MoveUnit.Degrees:
stepsOrTime = value >> 0;
useSteps = true;
break;
default:
stepsOrTime = value >> 0;
useSteps = false;
break;
}
syncMotors(this._port, {
useSteps: useSteps,
speed: speed,
turnRatio: turnRatio,
stepsOrTime: stepsOrTime,
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;
}
/**
@ -506,6 +587,8 @@ namespace motors {
*/
//%
toString(): string {
this.init();
let r = outputToName(this._port);
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (this._port & (1 << i)) {
@ -529,8 +612,8 @@ namespace motors {
export const largeCD = new SynchedMotorPair(Output.CD);
function reset(out: Output) {
let b = mkCmd(out, DAL.opOutputReset, 0)
writePWM(b)
writePWM(mkCmd(out, DAL.opOutputReset, 0))
writePWM(mkCmd(out, DAL.opOutputClearCount, 0))
}
function outOffset(out: Output) {
@ -541,7 +624,7 @@ namespace motors {
return 0
}
interface MotorData {
export interface MotorData {
actualSpeed: number; // -100..+100
tachoCount: number;
count: number;
@ -550,7 +633,7 @@ namespace motors {
// only a single output at a time
function getMotorData(out: Output): MotorData {
init()
let buf = motorMM.slice(outOffset(out), MotorDataOff.Size)
const buf = motorMM.slice(outOffset(out), MotorDataOff.Size)
return {
actualSpeed: buf.getNumber(NumberFormat.Int8LE, MotorDataOff.Speed),
tachoCount: buf.getNumber(NumberFormat.Int32LE, MotorDataOff.TachoCounts),
@ -558,6 +641,11 @@ namespace motors {
}
}
export function getAllMotorData(): MotorData[] {
init();
return [Output.A, Output.B, Output.C, Output.D].map(out => getMotorData(out));
}
interface SyncOptions {
useSteps?: boolean;
speed: number;

View File

@ -1,4 +1,6 @@
namespace brick {
export const LINE_HEIGHT = 12;
//% shim=screen::_setPixel
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) {
x |= 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 x the starting position's x coordinate, eg: 0
* @param y the starting position's x coordinate, eg: 0
* @param line the line number to print the text at, eg: 0
*/
//% blockId=screen_print block="`icons.brickDisplay` print %text| at x: %x| y: %y"
//% weight=99 group="Screen" inlineInputMode="inline" blockGap=8
//% x.min=0 x.max=178 y.min=0 y.max=128
//% blockId=screen_print block="print %text| at line %line"
//% weight=98 group="Screen" inlineInputMode="inline" blockGap=8
//% 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) {
x |= 0
y |= 0
@ -137,8 +136,8 @@ namespace brick {
* Shows an image on screen
* @param image image to draw
*/
//% blockId=screen_show_image block="`icons.brickDisplay` show image %image=screen_image_picker"
//% weight=95 group="Screen" blockGap=8
//% blockId=screen_show_image block="show image %image=screen_image_picker"
//% weight=100 group="Screen" blockGap=8
export function showImage(image: Image, delay: number = 400) {
if (!image) return;
image.draw(0, 0, Draw.Normal);
@ -152,19 +151,19 @@ namespace brick {
* @param image the image
*/
//% blockId=screen_image_picker block="%image" shim=TD_ID
//% image.fieldEditor="imagedropdown"
//% image.fieldEditor="images"
//% image.fieldOptions.columns=6
//% image.fieldOptions.hasSearchBar=true
//% image.fieldOptions.width=600
//% group="Screen" weight=0 blockHidden=1
export function _imagePicker(image: Image): Image {
export function __imagePicker(image: Image): Image {
return image;
}
/**
* Clears the screen
*/
//% blockId=screen_clear_screen block="`icons.brickDisplay` clear screen"
//% weight=94 group="Screen" blockGap=8
//% blockId=screen_clear_screen block="clear screen"
//% weight=90 group="Screen"
export function clearScreen() {
screen.clear();
}
@ -208,5 +207,36 @@ namespace brick {
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.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)
@ -12,7 +12,7 @@ brick.buttonEnter.onEvent(ButtonEvent.Click, () => {
brick.buttonLeft.onEvent(ButtonEvent.Click, () => {
brick.drawRect(10, 70, 20, 10, Draw.Fill)
brick.setStatusLight(LightsPattern.Red)
brick.setLight(LightsPattern.Red)
brick.setFont(brick.microbitFont())
})

View File

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

View File

@ -28,6 +28,7 @@ namespace sensors {
//% blockId=gyroGetAngle
//% parts="gyroscope"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=65 blockGap=8
//% group="Gyro Sensor"
angle(): number {
@ -44,6 +45,7 @@ namespace sensors {
//% blockId=gyroGetRate
//% parts="gyroscope"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=65 blockGap=8
//% group="Gyro Sensor"
rate(): number {

View File

@ -112,7 +112,7 @@ namespace sensors {
//% blockId=remotebuttonWasPressed
//% parts="remote"
//% blockNamespace=sensors
//% weight=80 blockGap=8
//% weight=80
//% group="Remote Infrared Beacon"
wasPressed() {
return this.button.wasPressed();
@ -238,7 +238,7 @@ namespace sensors {
//% blockId=infraredGetRemoteCommand
//% parts="infrared"
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% weight=65
//% group="Infrared Sensor"
remoteCommand(): number {
this._setMode(IrSensorMode.RemoteControl)

View File

@ -2,8 +2,6 @@
"Sound.buffer": "Returns the underlaying Buffer object.",
"Sound.play": "Play sound.",
"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|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).",

View File

@ -20,7 +20,6 @@
"Note.GSharp|block": "G#",
"SoundOutputDestination.Pin|block": "pin",
"SoundOutputDestination.Speaker|block": "speaker",
"music._soundPicker|block": "%sound",
"music.beat|block": "%fraction|beat",
"music.changeTempoBy|block": "change tempo by %value|(bpm)",
"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"
//% parts="headphone"
//% blockNamespace=music
//% weight=76 blockGap=8
//% weight=97
void stopAllSounds() {
if (currentSample) {
samplePtr = currentSample->length;

View File

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

View File

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

View File

@ -1,9 +1,11 @@
{
"tests": "Unit tests framework",
"TestEvent": "Various test event in the execution cycle",
"tests": "A 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.assertClose|param|tolerance": "the acceptable error margin, eg: 5",
"tests.onEvent": "Registers code to be called at various points in the test execution",
"tests.test": "Registers a test to run"
}

View File

@ -1,4 +1,9 @@
{
"TestEvent.RunSetUp|block": "run setup",
"TestEvent.RunTearDown|block": "run teardown",
"TestEvent.TestSetUp|block": "test setup",
"TestEvent.TestTearDown|block": "test teardown",
"tests.assertClose|block": "assert %message|%expected|close to %actual|by %tolerance",
"tests.assert|block": "assert %message|%condition",
"tests.test|block": "test %name",
"tests|block": "tests",

View File

@ -1,14 +1,6 @@
{
"name": "tests",
"description": "A unit test library",
"files": [
"README.md",
"tests.ts"
],
"testFiles": [
],
"public": true,
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/tests",
"dependencies": {
"core": "file:../core"
"core": "file:../ev3"
}
}

View File

@ -0,0 +1,12 @@
// EV3 specific test functions
tests.onEvent(TestEvent.RunSetUp, function() {
console.sendToScreen();
})
tests.onEvent(TestEvent.TestSetUp, function() {
motors.stopAllMotors();
motors.resetAllMotors();
})
tests.onEvent(TestEvent.TestTearDown, function() {
motors.stopAllMotors();
motors.resetAllMotors();
})

View File

@ -1,89 +0,0 @@
/**
* 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()
// 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`)
for (let i = 0; i < _tests.length; ++i) {
const t = _currentTest = _tests[i];
t.run();
_currentTest = undefined;
}
console.log(`${_tests.map(t => t.errors.length).reduce((p, c) => p + c, 0)} X, ${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(` X ${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

@ -45,6 +45,7 @@ namespace sensors {
//% blockId=touchEvent block="on %sensor|%event"
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Touch Sensor"
onEvent(ev: TouchSensorEvent, body: () => void) {
@ -60,6 +61,7 @@ namespace sensors {
//% blockId=touchWaitUntil block="pause until %sensor|%event"
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=98 blockGap=8
//% group="Touch Sensor"
pauseUntil(ev: TouchSensorEvent) {
@ -75,7 +77,8 @@ namespace sensors {
//% blockId=touchIsPressed
//% parts="touch"
//% blockNamespace=sensors
//% weight=81 blockGap=8
//% sensor.fieldEditor="ports"
//% weight=81
//% group="Touch Sensor"
isPressed() {
return this.button.isPressed();
@ -90,6 +93,7 @@ namespace sensors {
//% blockId=touchWasPressed
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=81 blockGap=8
//% group="Touch Sensor"
wasPressed() {

View File

@ -25,7 +25,7 @@ namespace sensors {
}
_query(): number {
return (this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff) * 0.1; // range is 0..2550, in 0.1 cm increments.
return ((this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff) / 10) >> 0; // range is 0..2550, in 0.1 cm increments.
}
_update(prev: number, curr: number) {
@ -46,6 +46,7 @@ namespace sensors {
//% block="on %sensor|%event"
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=100 blockGap=8
//% group="Ultrasonic Sensor"
onEvent(event: UltrasonicSensorEvent, handler: () => void) {
@ -60,6 +61,7 @@ namespace sensors {
//% blockId=ultrasonicWait
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Ultrasonic Sensor"
pauseUntil(event: UltrasonicSensorEvent) {
@ -75,12 +77,13 @@ namespace sensors {
//% blockId=sonarGetDistance
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% weight=65 blockGap=8
//% sensor.fieldEditor="ports"
//% weight=65
//% group="Ultrasonic Sensor"
distance(): number {
// it supposedly also has an inch mode, but we stick to cm
this._setMode(0)
return (this.getNumber(NumberFormat.UInt16LE, 0) & 0x0fff) * 0.1; // range is 0..2550, in 0.1 cm increments.
return this._query();
}
}

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",
"version": "0.0.43",
"version": "0.0.52",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -44,8 +44,8 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.14.13",
"pxt-core": "3.0.2"
"pxt-common-packages": "0.15.3",
"pxt-core": "3.0.5"
},
"scripts": {
"test": "node node_modules/pxt-core/built/pxt.js travis"

View File

@ -16,7 +16,8 @@
"libs/infrared-sensor",
"libs/gyro-sensor",
"libs/ev3",
"libs/tests"
"libs/tests",
"libs/behaviors"
],
"simulator": {
"autoRun": true,
@ -94,6 +95,7 @@
"privacyUrl": "https://go.microsoft.com/fwlink/?LinkId=521839",
"termsOfUseUrl": "https://go.microsoft.com/fwlink/?LinkID=206977",
"githubUrl": "https://github.com/Microsoft/pxt-ev3",
"betaUrl": "https://lego.makecode.com/",
"boardName": "LEGO Mindstorms EV3 Brick",
"selectLanguage": true,
"highContrast": true,
@ -132,16 +134,23 @@
"logic": "#1E5AA8",
"math": "#9DC3F7",
"variables": "#B40000",
"text": "#F0890A",
"text": "#FCAC00",
"advanced": "#969696",
"functions": "#064597",
"arrays": "#890058"
"functions": "#19325A",
"arrays": "#901F76"
},
"blockIcons": {
"loops": "\uf10b",
"logic": "\uf10a",
"math": "\uf10c",
"variables": "\uf111",
"text": "\uf110",
"functions": "\uf108",
"arrays": "\uf109"
},
"monacoColors": {
"editor.background": "#ecf6ff"
},
"simAnimationEnter": "horizontal flip in",
"simAnimationExit": "horizontal flip out"
}
},
"ignoreDocsErrors": true
}

View File

@ -23,20 +23,6 @@ namespace pxsim {
}
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;
outputState: EV3OutputState;
@ -86,11 +72,6 @@ namespace pxsim {
// TODO
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.appendChild(this.view = viewHost.getView() as SVGSVGElement);
this.inputNodes = [];
this.outputNodes = [];
return Promise.resolve();
}
@ -127,26 +111,33 @@ namespace pxsim {
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
}
//defaultNeopixelPin() {
// return this.edgeConnectorState.getPin(CPlayPinName.D8);
//}
getDefaultPitchPin() {
return this.edgeConnectorState.getPin(CPlayPinName.D6);
}
getBrickNode() {
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[] {
if (port == 0xFF) return this.getMotors(); // Return all motors
const motorPort = this.motorMap[port];
if (this.outputNodes[motorPort] == undefined) {
this.outputNodes[motorPort] = large ?
new LargeMotorNode(motorPort) : new MediumMotorNode(motorPort);
const r = [];
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const p = 1 << i;
if (port & p) {
const motorPort = this.motorMap[p];
const outputNode = this.outputNodes[motorPort];
if (outputNode)
r.push(outputNode);
}
}
return [this.outputNodes[motorPort]];
return r;
}
getMotors() {
@ -154,7 +145,7 @@ namespace pxsim {
}
getSensor(port: number, type: number): SensorNode {
if (this.inputNodes[port] == undefined) {
if (!this.inputNodes[port]) {
switch (type) {
case DAL.DEVICE_TYPE_GYRO: this.inputNodes[port] = new GyroSensorNode(port); break;
case DAL.DEVICE_TYPE_COLOR: this.inputNodes[port] = new ColorSensorNode(port); break;

View File

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

View File

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

View File

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

View File

@ -2,8 +2,8 @@
namespace pxsim.motors {
export function __motorUsed(port: number, large: boolean) {
console.log("MOTOR INIT " + port);
const motors = ev3board().getMotor(port, large);
//console.log("MOTOR INIT " + port);
ev3board().motorUsed(port, large);
runtime.queueDisplayUpdate();
}
}
@ -11,7 +11,7 @@ namespace pxsim.motors {
namespace pxsim.sensors {
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);
runtime.queueDisplayUpdate();
}

View File

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

View File

@ -15,11 +15,20 @@ namespace pxsim {
MMapMethods.register("/dev/lms_motor", {
data,
beforeMemRead: () => {
console.log("motor before read");
const outputs = ev3board().outputNodes;
// console.log("motor before read");
for (let port = 0; port < DAL.NUM_OUTPUTS; ++port) {
data[MotorDataOff.TachoCounts * port] = 0; // Tacho count
data[MotorDataOff.Speed * port] = 50; // Speed
data[MotorDataOff.TachoSensor * port] = 0; // Count
const output = outputs[port];
const speed = output ? outputs[port].getSpeed() : 0;
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 => {

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;
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());
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
const port = buf.data[1];
const speed = buf.data[2];
// note that b[3] is padding
const step1 = buf.data[4];
const step2 = buf.data[5]; // angle
const step3 = buf.data[6];
const brake = buf.data[7];
const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2); // signed byte
// note that b[3] is padding
const step1 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 4);
const step2 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 8);
const step3 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 12);
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 16);
//console.log(buf);
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;
}
case DAL.opOutputStop: {
// stop
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);
motors.forEach(motor => motor.stop());
return 2;
@ -59,9 +86,9 @@ namespace pxsim {
case DAL.opOutputSpeed: {
// setSpeed
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);
motors.forEach(motor => motor.setSpeed(speed));
motors.forEach(motor => motor.setSpeedCmd(cmd, [speed]));
return 2;
}
case DAL.opOutputStart: {
@ -74,7 +101,7 @@ namespace pxsim {
case DAL.opOutputPolarity: {
// reverse
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);
motors.forEach(motor => motor.setPolarity(polarity));
return 2;
@ -86,6 +113,9 @@ namespace pxsim {
motors.forEach(motor => motor.setLarge(large));
return 2;
}
default:
console.warn('unknown cmd: ' + cmd);
break;
}
console.log("pwm write");

View File

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

View File

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

View File

@ -10,8 +10,6 @@ namespace pxsim.music {
}
namespace pxsim.SoundMethods {
let numSoundsPlaying = 0;
const soundsLimit = 1;
let audio: HTMLAudioElement;
export function buffer(buf: RefBuffer) {
@ -27,18 +25,15 @@ namespace pxsim.SoundMethods {
}
export function play(buf: RefBuffer, volume: number) {
if (!buf || numSoundsPlaying >= soundsLimit) {
if (!buf) {
return Promise.resolve();
}
return new Promise<void>(resolve => {
let url = "data:audio/wav;base64," + btoa(uint8ArrayToString(buf.data))
audio = new Audio(url)
audio.onended = () => {
resolve();
numSoundsPlaying--;
}
numSoundsPlaying++;
audio.play()
audio.onended = () => resolve();
audio.onpause = () => resolve();
audio.play();
})
}
@ -46,8 +41,8 @@ namespace pxsim.SoundMethods {
return new Promise<void>(resolve => {
if (audio) {
audio.pause();
numSoundsPlaying--;
}
resolve();
})
}

View File

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

View File

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

View File

@ -17,10 +17,7 @@ namespace pxsim {
setDistance(distance: number) {
if (this.distance != distance) {
this.distance = distance;
this.changed = true;
this.valueChanged = true;
runtime.queueDisplayUpdate();
this.setChangedState();
}
}

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>
<clipPath id="7dd75c25-af24-4f64-9ea1-f270fbd17872">
<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"/>
<clipPath id="clip-path" transform="translate(0.98 1.9)">
<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 id="9d9a0722-eca6-4c98-bf03-49097e654f0e">
<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"/>
<clipPath id="clip-path-2" transform="translate(0.98 1.9)">
<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 id="0f29df77-bfe3-4749-8432-7a5b3681f927">
<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"/>
<clipPath id="clip-path-3" transform="translate(0.98 1.9)">
<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>
</defs>
<title>Large Motor</title>
<g style="clip-path: url(#7dd75c25-af24-4f64-9ea1-f270fbd17872)">
<g>
<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"/>
<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"/>
<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="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"/>
<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"/>
<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="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 style="isolation: isolate">
<g id="e13b39e7-895d-4931-9be9-7aecaf709784">
<g style="clip-path: url(#clip-path)">
<g id="Large_Motor" data-name="Large Motor">
<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"/>
<g>
<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="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"/>
</g>
</g>
<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"/>
<g id="6df8eb1f-3187-427f-8d99-bc7a37e6cc25" data-name="9bbfb3ec-86c2-4d27-8275-76f2f9737a7f">
<g id="e4d7143c-1fb6-45a4-a205-1c920b8a823c" data-name="7e017c33-f09b-4d39-803f-1bedf1ebf566">
<g id="3a4e493d-cd17-48c4-97b6-da7ee3c4898e" data-name="69e3df75-72a6-4499-baab-f65ec194e193">
<g style="clip-path: url(#0f29df77-bfe3-4749-8432-7a5b3681f927)">
<g id="7cada08f-1e90-4ef2-8ae4-3f2adfd3e953" data-name="b7007e32-15c9-40b3-a885-a7231fd104d1">
<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 id="LM_rightside" data-name="LM rightside">
<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"/>
<circle id="LM_rightside4" data-name="LM rightside4" cx="28.3" cy="66.21" r="3.79" style="fill: #fff"/>
<circle id="LM_rightside3" data-name="LM rightside3" cx="28.3" cy="43" r="3.79" style="fill: #fff"/>
<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"/>
<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>
<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>
<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"/>
<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"/>
<g id="1eb2ae58-2419-47d4-86bf-4f26a7f0cf61" data-name="KNOB">
<g id="26377a78-5df7-4bea-8daa-669ea612118c" data-name="5301e639-3e28-489d-96ec-91bda6d459b7">
<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>
<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"/>
<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>
<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>
<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>
<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"/>
<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="9b2e3cd0-e4bd-42ce-b558-4f4bc46eaf23" data-name="d5fea8d1-47c1-448e-adb6-8519e2516216" cx="14.15" cy="5.85" r="3.79" style="fill: #3c3c3c"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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="LM_red-2" data-name="LM red-2" cx="62.96" cy="18.95" r="17.06" style="fill: #d42715"/>
</g>
<g id="LM_details_in_red" data-name="LM details in red">
<circle id="LM_detail_red_hole4" data-name="LM detail red hole4" cx="59.9" cy="30.01" r="3.79" style="fill: #393939"/>
<circle id="LM_detail_red_hole3" data-name="LM detail red hole3" cx="51.75" cy="15.89" r="3.79" style="fill: #393939"/>
<circle id="LM_detail_red_hole2" data-name="LM detail red hole2" cx="65.87" cy="7.74" r="3.79" style="fill: #393939"/>
<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="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>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,70 +1,88 @@
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>
<clipPath id="7dd75c25-af24-4f64-9ea1-f270fbd17872">
<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"/>
<clipPath id="clip-path" transform="translate(0.98 1.9)">
<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 id="9d9a0722-eca6-4c98-bf03-49097e654f0e">
<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"/>
<clipPath id="clip-path-2" transform="translate(0.98 1.9)">
<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 id="0f29df77-bfe3-4749-8432-7a5b3681f927">
<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"/>
<clipPath id="clip-path-3" transform="translate(0.98 1.9)">
<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>
</defs>
<title>Large Motor</title>
<g style="clip-path: url(#7dd75c25-af24-4f64-9ea1-f270fbd17872)">
<g>
<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"/>
<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"/>
<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="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"/>
<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"/>
<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="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 style="isolation: isolate">
<g id="e13b39e7-895d-4931-9be9-7aecaf709784">
<g style="clip-path: url(#clip-path)">
<g id="Large_Motor" data-name="Large Motor">
<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"/>
<g>
<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="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"/>
</g>
</g>
<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"/>
<g id="6df8eb1f-3187-427f-8d99-bc7a37e6cc25" data-name="9bbfb3ec-86c2-4d27-8275-76f2f9737a7f">
<g id="e4d7143c-1fb6-45a4-a205-1c920b8a823c" data-name="7e017c33-f09b-4d39-803f-1bedf1ebf566">
<g id="3a4e493d-cd17-48c4-97b6-da7ee3c4898e" data-name="69e3df75-72a6-4499-baab-f65ec194e193">
<g style="clip-path: url(#0f29df77-bfe3-4749-8432-7a5b3681f927)">
<g id="7cada08f-1e90-4ef2-8ae4-3f2adfd3e953" data-name="b7007e32-15c9-40b3-a885-a7231fd104d1">
<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 id="LM_rightside" data-name="LM rightside">
<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"/>
<circle id="LM_rightside4" data-name="LM rightside4" cx="28.3" cy="66.21" r="3.79" style="fill: #fff"/>
<circle id="LM_rightside3" data-name="LM rightside3" cx="28.3" cy="43" r="3.79" style="fill: #fff"/>
<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"/>
<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>
<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>
<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"/>
<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"/>
<g id="1eb2ae58-2419-47d4-86bf-4f26a7f0cf61" data-name="KNOB">
<g id="26377a78-5df7-4bea-8daa-669ea612118c" data-name="5301e639-3e28-489d-96ec-91bda6d459b7">
<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>
<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"/>
<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>
<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>
<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>
<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"/>
<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="9b2e3cd0-e4bd-42ce-b558-4f4bc46eaf23" data-name="d5fea8d1-47c1-448e-adb6-8519e2516216" cx="14.15" cy="5.85" r="3.79" style="fill: #3c3c3c"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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"/>
<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="LM_red-2" data-name="LM red-2" cx="62.96" cy="18.95" r="17.06" style="fill: #d42715"/>
</g>
<g id="LM_details_in_red" data-name="LM details in red">
<circle id="LM_detail_red_hole4" data-name="LM detail red hole4" cx="59.9" cy="30.01" r="3.79" style="fill: #393939"/>
<circle id="LM_detail_red_hole3" data-name="LM detail red hole3" cx="51.75" cy="15.89" r="3.79" style="fill: #393939"/>
<circle id="LM_detail_red_hole2" data-name="LM detail red hole2" cx="65.87" cy="7.74" r="3.79" style="fill: #393939"/>
<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="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>

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>
<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">
<stop offset="0" stop-color="#a8a9a8"/>
<stop offset="1" stop-color="#545554"/>
<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="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
</defs>
<title>MediumMotor</title>
<g id="Medium_Motor" data-name="Medium Motor">
<g id="Group_4" data-name="Group 4">
<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>
<g id="Group_7" data-name="Group 7">
<g id="g7596">
<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 style="isolation: isolate">
<g id="svg7610">
<g id="Medium_Motor" data-name="Medium Motor">
<g id="medmotor_box" data-name="medmotor box">
<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>
<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>
<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 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>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,30 +1,35 @@
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>
<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">
<stop offset="0" stop-color="#a8a9a8"/>
<stop offset="1" stop-color="#545554"/>
<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="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
</defs>
<title>Medium Motor</title>
<g id="Medium_Motor" data-name="Medium Motor">
<g id="Group_4" data-name="Group 4">
<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>
<g id="Group_7" data-name="Group 7">
<g id="g7596">
<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"/>
<title>MediumMotor</title>
<g style="isolation: isolate">
<g id="svg7610">
<g id="Medium_Motor" data-name="Medium Motor">
<g id="medmotor_box" data-name="medmotor box">
<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>
<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>
<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 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>
</svg>`;
}

View File

@ -1,5 +1,9 @@
/// <reference path="./layoutView.ts" />
namespace pxsim {
export const GAME_LOOP_FPS = 32;
}
namespace pxsim.visuals {
const EV3_STYLE = `
@ -29,13 +33,21 @@ namespace pxsim.visuals {
font-family:"Lucida Console", Monaco, monospace;
font-size:8px;
fill:#fff;
pointer-events: none; user-select: none;
pointer-events: none;
user-select: none;
}
.sim-text.small {
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 {
fill:#000;
fill:#5A5A5A;
}
/* Color Grid */
@ -95,16 +107,9 @@ namespace pxsim.visuals {
private layoutView: LayoutView;
private controlGroup: ViewContainer;
private selectedNode: NodeType;
private selectedPort: number;
private controlView: View;
private cachedControlNodes: { [index: string]: View[] } = {};
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
private closeGroup: ViewContainer;
private closeIconView: View;
private screenCanvas: HTMLCanvasElement;
private screenCanvasCtx: CanvasRenderingContext2D;
private screenCanvasData: ImageData;
@ -136,6 +141,17 @@ namespace pxsim.visuals {
this.board.updateSubscribers.push(() => 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> {
@ -167,86 +183,8 @@ namespace pxsim.visuals {
this.layoutView.updateTheme(theme);
}
public updateState() {
this.updateVisibleNodes();
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]) {
private getControlForNode(id: NodeType, port: number, useCache = true) {
if (useCache && this.cachedControlNodes[id] && this.cachedControlNodes[id][port]) {
return this.cachedControlNodes[id][port];
}
@ -275,9 +213,10 @@ namespace pxsim.visuals {
}
case NodeType.MediumMotor:
case NodeType.LargeMotor: {
// const state = ev3board().getMotor(port)[0];
// view = new MotorInputControl(this.element, this.defs, state, port);
// break;
// TODO: figure out if the motor is in "input" or "output" mode
const state = ev3board().getMotors()[port];
view = new MotorReporterControl(this.element, this.defs, state, port);
break;
}
}
@ -323,6 +262,10 @@ namespace pxsim.visuals {
return undefined;
}
private getCloseIconView() {
return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
}
private buildDom() {
this.wrapper = document.createElement('div');
this.wrapper.style.display = 'inline';
@ -337,25 +280,10 @@ namespace pxsim.visuals {
this.layoutView = new LayoutView();
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
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.updateState();
// Add Screen canvas to board
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() {
this.screenCanvas = document.createElement("canvas");
this.screenCanvas.id = "board-screen-canvas";
@ -394,9 +333,115 @@ namespace pxsim.visuals {
this.screenCanvasTemp.style.display = 'none';
}
private updateScreen() {
let state = ev3board().screenState;
private kill() {
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();
if (!bBox || bBox.width == 0) return;

View File

@ -1,17 +1,20 @@
/// <reference path="./nodes/staticView.ts" />
/// <reference path="./nodes/moduleView.ts" />
namespace pxsim.visuals {
export const CONTROL_WIDTH = 87.5;
export const CONTROL_HEIGHT = 175;
export const CONTROL_TEXT_COLOR = '#000';
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;
constructor(protected parent: SVGSVGElement, protected globalDefs: SVGDefsElement, protected state: T, protected port: number) {
super(state);
}
getInnerWidth(): number {
@ -34,18 +37,39 @@ namespace pxsim.visuals {
return false;
}
buildDom(width: number): SVGElement {
this.background = svg.elt("svg", { height: "100%", width: "100%"}) as SVGSVGElement;
this.background.appendChild(this.getInnerView(this.parent, this.globalDefs));
return this.background;
buildDom(): SVGElement {
this.content = svg.elt("svg", { viewBox: `0 0 ${this.getInnerWidth()} ${this.getInnerHeight()}` }) as SVGSVGElement;
this.content.appendChild(this.getInnerView(this.parent, this.globalDefs));
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() {
}
getWeight() {
return 0;
}
}
}

View File

@ -17,6 +17,17 @@ namespace pxsim.visuals {
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() {
return 32;
}

View File

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

View File

@ -4,30 +4,78 @@ namespace pxsim.visuals {
export class ColorWheelControl extends ControlView<ColorSensorNode> {
private group: SVGGElement;
private colorGradient: SVGLinearGradientElement;
private defs: SVGDefsElement;
private static COLOR_DARK = 1;
private static COLOR_LIGHT = 99;
getInnerView() {
getInnerView(parent: SVGSVGElement) {
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
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 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` });
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` });
lightHalf.addEventListener(pointerEvents.down, ev => {
this.setColor(ColorWheelControl.COLOR_LIGHT);
})
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)' });
darkHalf.addEventListener(pointerEvents.down, ev => {
this.setColor(ColorWheelControl.COLOR_DARK);
})
const innerCircle = pxsim.svg.child(circle, "circle",
{cursor: '-webkit-grab',
fill: `url(#${gc})`,
r: 17,
cx: 13,
cy: 20,
stroke: 'black',
'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;
}
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;
state.setColor(color);
state.setColor((1-t)*100);
}
}

View File

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

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