Compare commits

...

71 Commits

Author SHA1 Message Date
58384017f2 0.0.46 2017-12-28 09:08:27 -08:00
01f7fe633c Motorworks2 (#141)
* fixing polarity

* allocate motor on motorUsed only

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

* locale files

* enum issue

* fixing decoding of array

* implement clear count

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

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

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

* added toString on motors

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

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

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

* adding console API

* adding ready support for motors

* fix time output scale

* fixing angle
2017-12-18 14:13:38 -08:00
04275ee35c 0.0.41 2017-12-18 13:22:13 -08:00
f8d0594eca Merge pull request #107 from Microsoft/initial_sim
Initial sim implementation
2017-12-18 13:21:48 -08:00
5be3b31e00 Merge branch 'master' into initial_sim 2017-12-18 13:21:35 -08:00
84c1079e50 Fix Safari bug. 2017-12-18 13:19:49 -08:00
6320379d02 Initial sim implementation 2017-12-18 13:04:17 -08:00
b166f6034e storing test 2017-12-18 09:43:44 -08:00
d07f672b28 fixing single motor set speed 2017-12-18 09:17:19 -08:00
363e076f36 fixing motor compilation 2017-12-18 08:54:53 -08:00
8bf6f265f7 Merge pull request #105 from Microsoft/motorfixes
Motor fixes
2017-12-18 00:54:53 -08:00
217958aec3 fix polarity 2017-12-17 23:19:38 -08:00
367b1b0d1a properly setting multiple-motor options 2017-12-17 23:00:07 -08:00
6836852122 refactoring motors wiht base class (#104) 2017-12-17 22:47:13 -08:00
944098b9f9 Minor refactor fix (#102)
* Minor fix to a refactor of motor output this.brake to this._brake

* No (_this) in global context

* Minor fix
2017-12-17 21:12:18 -08:00
132 changed files with 5606 additions and 3935 deletions

BIN
docs/static/MC-LEGO-loader-eyes.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

30
docs/static/fonts/icons/iconfont.css vendored Normal file
View File

@ -0,0 +1,30 @@
@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");
}
.icon {
line-height: 1;
}
.icon:before {
font-family: iconfont !important;
font-style: normal;
font-weight: normal !important;
vertical-align: top;
}
.icon-ultrasonic:before {
content: "\f101";
}
.icon-color:before {
content: "\f102";
}
.icon-touch:before {
content: "\f103";
}
.icon-gyro:before {
content: "\f104";
}

BIN
docs/static/fonts/icons/iconfont.eot vendored Normal file

Binary file not shown.

24
docs/static/fonts/icons/iconfont.svg vendored Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<font id="iconfont" horiz-adv-x="40">
<font-face font-family="iconfont"
units-per-em="40" ascent="40"
descent="0" />
<missing-glyph horiz-adv-x="0" />
<glyph glyph-name="ultrasonic"
unicode="&#xF101;"
horiz-adv-x="40" d=" M26.8 13H13.1C15.6 14.6 17 17.3 17 20.2C17 21.8 16.6 23.3 15.8 24.7S13.8 27 12.4 27.8H27.6C26.2 27 25.1 26.1 24.2 24.7S23 21.8 23 20.2C22.9 17.3 24.3 14.6 26.8 13L26.8 13L26.8 13z M8.6 12C4.1 12 0.4 15.7 0.4 20.2S4.1 28.4 8.6 28.4S16.8 24.7 16.8 20.2C16.7 15.7 13.1 12 8.6 12zM8.6 23.4C6.8 23.4 5.4 22 5.4 20.2S6.8 16.9 8.6 16.9S11.9 18.4 11.9 20.2S10.4 23.4 8.6 23.4z M31.4 12C26.9 12 23.2 15.7 23.2 20.2S26.9 28.4 31.4 28.4S39.6 24.7 39.6 20.2C39.5 15.7 35.9 12 31.4 12zM31.4 23.4C29.6 23.4 28.1 22 28.1 20.2S29.6 16.9 31.4 16.9S34.6 18.4 34.6 20.2S33.2 23.4 31.4 23.4z" />
<glyph glyph-name="color"
unicode="&#xF102;"
horiz-adv-x="43.35195530726257" d=" M38.7 -0.1H4.7C3.4 -0.1 2.5 0.6 2.5 1.5V6.8H0.9A0.9608938547486034 0.9608938547486034 0 0 0 0 7.7V31.5A0.9608938547486034 0.9608938547486034 0 0 0 0.9 32.4H2.5V38.4C2.5 39.3 3.6 40 4.7 40H38.7C40 40 40.9 39.3 40.9 38.4V32.4H42.5A0.9608938547486034 0.9608938547486034 0 0 0 43.4 31.5V7.8A0.9608938547486034 0.9608938547486034 0 0 0 42.5 6.9H40.9V1.6C41.1 0.7 40 -0.1 38.7 -0.1zM21.7 35.1A11.374301675977657 11.374301675977657 0 0 1 13.6 15.5A4.916201117318437 4.916201117318437 0 0 1 13.4 13.7A8.268156424581006 8.268156424581006 0 0 1 29.9 13.7A4.916201117318437 4.916201117318437 0 0 1 29.7 15.5A12.379888268156424 12.379888268156424 0 0 1 33.1 23.6A11.1731843575419 11.1731843575419 0 0 1 21.7 35.1zM41.6 18.3V10.9H42.5V18.3zM1.1 18.3V10.9H2V18.3zM41.6 28.6V21.2H42.5V28.6zM1.1 28.6V21.2H2V28.6z M21.9 7.4A6.480446927374302 6.480446927374302 0 0 0 15.4 13.9A9.497206703910615 9.497206703910615 0 0 0 15.9 16.3A9.87709497206704 9.87709497206704 0 0 0 12.3 23.9A9.608938547486035 9.608938547486035 0 0 0 31.5 23.9A9.87709497206704 9.87709497206704 0 0 0 27.9 16.3A8.022346368715086 8.022346368715086 0 0 0 28.4 13.9A6.703910614525141 6.703910614525141 0 0 0 21.9 7.4zM21.9 16.3A2.4581005586592184 2.4581005586592184 0 1 1 24.4 13.9A2.837988826815643 2.837988826815643 0 0 1 21.9 16.3zM21.9 27.7A4.46927374301676 4.46927374301676 0 1 1 26.4 23.2A4.804469273743018 4.804469273743018 0 0 1 21.9 27.7z" />
<glyph glyph-name="touch"
unicode="&#xF103;"
horiz-adv-x="40" d=" M34.3 3.3H4.9C3.8 3.3 2.8 3.9 2.8 4.8V9.3H1.5C1.1 9.3 0.7 9.7 0.7 10.1V30.5C0.7 30.9 1.1 31.3 1.5 31.3H2.8V36.6C2.8 37.4 3.7 38.1 4.9 38.1H16V36.6H22.7V38.1H34.2C35.3 38.1 36.3 37.5 36.3 36.6V31.3H37.6C37.9 31.3 38.3 30.9 38.3 30.5V10.1C38.3 9.7 37.9 9.3 37.6 9.3H36.3V4.8C36.3 3.9 35.3 3.3 34.3 3.3L34.3 3.3zM14.1 16.2C14.7 15.3 15.4 14.7 16.2 14.1V5.6H22.6V14.1C23.5 14.7 24.1 15.4 24.7 16.2H33.4V22.6H24.9C24.3 23.5 23.6 24.3 22.6 24.9V33.6H16.2V24.9C15.3 24.3 14.5 23.6 13.9 22.6H5.4V16.2H14.1L14.1 16.2zM36.6 19.2V13H37.4V19.2H36.6zM1.8 19.2V13H2.6V19.2H1.8zM36.6 28.1V21.9H37.4V28.1H36.6zM1.8 28.1V21.9H2.6V28.1H1.8zM19.4 15.4C18.8 15.4 18.5 15.4 18.1 15.6V17.1C18.1 17.5 17.9 17.7 17.5 17.7L17.5 17.7H16C15.8 18.1 15.6 18.6 15.6 19.2C15.6 19.8 15.6 20.3 16 20.7H17.3C18.1 20.7 18.1 21.6 18.1 21.6L18.1 21.6C18.1 21.8 18.1 22.4 18.1 22.9C18.5 23.1 19 23.1 19.4 23.1C20 23.1 20.5 23.1 21.1 22.9C21.1 22.5 21.1 22 21.1 21.6L21.1 21.6V21.4C21.1 21 21.3 20.8 21.7 20.8C21.7 20.8 21.7 20.8 21.9 20.8L21.9 20.8H23.4C23.8 20.4 23.8 19.9 23.8 19.3S23.6 18.2 23.4 17.8L23.4 17.8C23 17.8 22.5 17.6 22.1 17.6C21.7 17.6 21.3 17.4 21.3 17V15.5C20.7 15.3 20.4 15.3 19.8 15.3C19.4 15.4 19.4 15.4 19.4 15.4z" />
<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" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
docs/static/fonts/icons/iconfont.ttf vendored Normal file

Binary file not shown.

BIN
docs/static/fonts/icons/iconfont.woff vendored Normal file

Binary file not shown.

BIN
docs/static/fonts/icons/iconfont.woff2 vendored Normal file

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

@ -68,18 +68,27 @@ 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)
}
/**
* Gets the current color mode
*/
colorMode() {
colorMode() {
return <ColorSensorMode>this.mode;
}
_query() {
if (this.mode == ColorSensorMode.Color)
if (this.mode == ColorSensorMode.Color
|| this.mode == ColorSensorMode.AmbientLightIntensity
|| this.mode == ColorSensorMode.ReflectedLightIntensity)
return this.getNumber(NumberFormat.UInt8LE, 0)
return 0
}
@ -97,11 +106,9 @@ namespace sensors {
* @param handler the code to run when detected
*/
//% help=sensors/color-sensor/on-color-detected
//% block="on `icons.colorSensor` %sensor|detected color %color"
//% block="on %sensor|detected color %color"
//% blockId=colorOnColorDetected
//% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors
//% weight=100 blockGap=8
//% group="Color Sensor"
@ -113,16 +120,33 @@ namespace sensors {
control.raiseEvent(this._id, v);
}
/**
* Waits for the given color to be detected
* @param color the color to detect
*/
//% help=sensors/color-sensor/pause-for-color
//% block="pause %sensor|for color %color"
//% blockId=colorPauseForColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=99 blockGap=8
//% group="Color Sensor"
pauseForColor(color: ColorSensorColor) {
this.setMode(ColorSensorMode.Color);
if (this.color() != color) {
const v = this._colorEventValue(<number>color);
control.waitForEvent(this._id, v);
}
}
/**
* Get the current color from the color sensor.
* @param sensor the color sensor to query the request
*/
//% help=sensors/color-sensor/color
//% block="`icons.colorSensor` %sensor| color"
//% block="%sensor| color"
//% blockId=colorGetColor
//% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors
//% weight=99
//% group="Color Sensor"
@ -137,17 +161,32 @@ namespace sensors {
* @param handler the code to run when detected
*/
//% help=sensors/color-sensor/on-light-changed
//% block="on `icons.colorSensor` %sensor|%mode|%condition"
//% block="on %sensor|%mode|%condition"
//% blockId=colorOnLightChanged
//% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors
//% weight=89 blockGap=8
//% group="Color Sensor"
onLightChanged(mode: LightIntensityMode, condition: LightCondition, handler: () => void) {
control.onEvent(this._id, <number>condition, handler);
this.setMode(<ColorSensorMode><number>mode)
control.onEvent(this._id, <number>condition, handler);
}
/**
* Waits for the given color to be detected
* @param color the color to detect
*/
//% help=sensors/color-sensor/pause-for-light
//% block="pause %sensor|for %mode|light %condition"
//% blockId=colorPauseForLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% weight=88 blockGap=8
//% group="Color Sensor"
pauseForLight(mode: LightIntensityMode, condition: LightCondition) {
this.setMode(<ColorSensorMode><number>mode)
if (this.thresholdDetector.state != <number>condition)
control.waitForEvent(this._id, <number>condition)
}
/**
@ -155,13 +194,11 @@ namespace sensors {
* @param sensor the color sensor port
*/
//% help=sensors/color-sensor/light
//% block="`icons.colorSensor` %sensor|%mode"
//% block="%sensor|%mode"
//% blockId=colorLight
//% parts="colorsensor"
//% sensor.fieldEditor="imagedropdown"
//% sensor.fieldOptions.columns=4
//% blockNamespace=sensors
//% weight=88
//% weight=87
//% group="Color Sensor"
light(mode: LightIntensityMode) {
this.setMode(<ColorSensorMode><number>mode)
@ -179,15 +216,15 @@ namespace sensors {
}
}
//% whenUsed block="1" weight=95 fixedInstance jres=icons.port1
//% whenUsed block="color 1" weight=95 fixedInstance jres=icons.port1
export const color1: ColorSensor = new ColorSensor(1)
//% whenUsed block="2" weight=90 fixedInstance jres=icons.port2
//% whenUsed block="color 2" weight=90 fixedInstance jres=icons.port2
export const color2: ColorSensor = new ColorSensor(2)
//% whenUsed block="3" weight=90 fixedInstance jres=icons.port3
//% whenUsed block="color 3" weight=90 fixedInstance jres=icons.port3
export const color3: ColorSensor = new ColorSensor(3)
//% whenUsed block="4" weight=90 fixedInstance jres=icons.port4
//% whenUsed block="color 4" weight=90 fixedInstance jres=icons.port4
export const color4: ColorSensor = new ColorSensor(4)
}

View File

@ -32,17 +32,25 @@
"brick.lightPattern": "Pattern block.",
"brick.lightPattern|param|pattern": "the lights pattern to use. eg: LightsPattern.Green",
"brick.print": "Show text on the screen.",
"brick.printPorts": "Prints the port states 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.setLight": "Set lights.",
"brick.setLight|param|pattern": "the lights pattern to use.",
"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.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.",
"console.addListener": "Adds a listener for the log messages",
"console.log": "Write a line of text to the console output.",
"console.logValue": "Write a name:value pair as a line of text to the console output.",
"console.logValue|param|name": "name of the value stream, eg: \"x\"",
"console.logValue|param|value": "to write",
"console.sendToScreen": "Sends the log messages to the brick screen and uses the brick up and down buttons to scroll.",
"control": "Program controls and events.",
"control.allocateNotifyEvent": "Allocates the next user notification event",
"control.deviceFirmwareVersion": "Determine the version of system software currently running.",
@ -51,35 +59,44 @@
"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.count": "Gets motor step count.",
"motors.Motor.move": "Moves the motor by a number of rotations, degress or seconds",
"motors.Motor.move|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.Motor.move|param|unit": "the meaning of the value",
"motors.Motor.move|param|value": "the move quantity, eg: 2",
"motors.Motor.port": "Gets the port where this motor is connected",
"motors.Motor.reset": "Resets the motor.",
"motors.Motor.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.Motor.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.Motor.setReversed": "Reverses the motor polarity",
"motors.Motor.setSpeed": "Sets the speed of the motor.",
"motors.Motor.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.Motor.speed": "Gets motor actual speed.",
"motors.Motor.tachoCount": "Gets motor tacho count.",
"motors.SynchedMotorPair.moveSteering": "Turns the motor and the follower motor by a number of rotations",
"motors.SynchedMotorPair.moveSteering|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.SynchedMotorPair.moveSteering|param|steering": "the ratio of power sent to the follower motor, from ``-100`` to ``100``",
"motors.SynchedMotorPair.moveSteering|param|unit": "the meaning of the value",
"motors.SynchedMotorPair.moveSteering|param|value": "the move quantity, eg: 2",
"motors.SynchedMotorPair.moveTank": "The Move Tank block can make a robot drive forward, backward, turn, or stop. \nUse the Move Tank block for robot vehicles that have two Large Motors, \nwith one motor driving the left side of the vehicle and the other the right side. \nYou can make the two motors go at different speeds or in different directions \nto make your robot turn.",
"motors.SynchedMotorPair.moveTank|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.moveTank|param|unit": "@param speedLeft the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.moveTank|param|value": "the amount of movement, eg: 2",
"motors.SynchedMotorPair.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.SynchedMotorPair.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.SynchedMotorPair.setSpeed": "Sets the speed of the motor.",
"motors.SynchedMotorPair.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"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",
"motors.MotorBase.move|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.move|param|unit": "the meaning of the value",
"motors.MotorBase.move|param|value": "the move quantity, eg: 2",
"motors.MotorBase.pauseUntilReady": "Pauses the execution until the previous command finished.",
"motors.MotorBase.pauseUntilReady|param|timeOut": "optional maximum pausing time in milliseconds",
"motors.MotorBase.reset": "Resets the motor(s).",
"motors.MotorBase.setBrake": "Sets the automatic brake on or off when the motor is off",
"motors.MotorBase.setBrake|param|brake": "a value indicating if the motor should break when off",
"motors.MotorBase.setReversed": "Reverses the motor polarity",
"motors.MotorBase.setSpeed": "Sets the speed of the motor.",
"motors.MotorBase.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
"motors.MotorBase.stop": "Stops the motor(s).",
"motors.SynchedMotorPair.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|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.",
"motors.SynchedMotorPair.tank|param|speedRight": "the speed on the right motor, eg: 50",
"motors.SynchedMotorPair.tank|param|unit": "@param speedLeft the speed on the left motor, eg: 50",
"motors.SynchedMotorPair.tank|param|value": "the amount of movement, eg: 2",
"motors.SynchedMotorPair.toString": "Returns the name(s) of the motor",
"motors.mkCmd": "Allocates a message buffer",
"motors.mkCmd|param|addSize": "required additional bytes",
"motors.mkCmd|param|cmd": "command id",
"motors.mkCmd|param|out": "ports",
"motors.readPWM": "Sends and receives a message from the motors device",
"motors.readPWM|param|buf": "message buffer",
"motors.resetAllMotors": "Resets all motors",
"motors.stopAllMotors": "Stops all motors",
"motors.writePWM": "Sends a command to the motors device",
"motors.writePWM|param|buf": "the command buffer",
"output.createBuffer": "Create a new zero-initialized buffer.",
"output.createBuffer|param|size": "number of bytes in the buffer",
"screen.clear": "Clear screen and reset font to normal.",

View File

@ -13,8 +13,8 @@
"LightsPattern.RedPulse|block": "Pulsing Red",
"LightsPattern.Red|block": "Red",
"MoveUnit.Degrees|block": "degrees",
"MoveUnit.MilliSeconds|block": "milliseconds",
"MoveUnit.Rotations|block": "rotations",
"MoveUnit.Seconds|block": "seconds",
"Output.AB|block": "A+B",
"Output.AD|block": "A+D",
"Output.ALL|block": "All",
@ -24,37 +24,39 @@
"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.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._imagePicker|block": "%image",
"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.printPorts|block": "print ports",
"brick.print|block": "print %text| at x: %x| y: %y",
"brick.setLight|block": "set light to %pattern=led_pattern",
"brick.setPixel|block": "set pixel %on| at x: %x| y: %y",
"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",
"console.sendToScreen|block": "send console to screen",
"console|block": "console",
"control.raiseEvent|block": "raise event|from %src|with value %value",
"control|block": "control",
"motors.Motor.count|block": "`icons.motorLarge` %motor|count",
"motors.Motor.move|block": "move `icons.motorLarge` %motor|for %value|%unit|at %speed|%",
"motors.Motor.reset|block": "reset `icons.motorLarge` %motor",
"motors.Motor.setBrake|block": "set `icons.motorLarge` %motor|brake %brake",
"motors.Motor.setReversed|block": "set `icons.motorLarge` %motor|reversed %reversed",
"motors.Motor.setSpeed|block": "set speed of `icons.motorLarge` %motor|to %speed|%",
"motors.Motor.speed|block": "`icons.motorLarge` %motor|speed",
"motors.Motor.tachoCount|block": "`icons.motorLarge` %motor|tacho count",
"motors.SynchedMotorPair.moveSteering|block": "move steering %chassis|at %speed|%|steer %turnRadio|%|by %value|%unit",
"motors.SynchedMotorPair.moveTank|block": "move tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit",
"motors.SynchedMotorPair.setBrake|block": "set `icons.motorLarge` %chassis|brake %brake",
"motors.SynchedMotorPair.setSpeed|block": "set speed of `icons.motorLarge` %motor|to %speed|%",
"motors.Motor.angle|block": "%motor|angle",
"motors.Motor.speed|block": "%motor|speed",
"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.largeAB|block": "large A+B",
"motors.largeAD|block": "large A+D",
"motors.largeA|block": "large A",
@ -71,8 +73,10 @@
"motors|block": "motors",
"output|block": "output",
"screen|block": "screen",
"sensors|block": "sensors",
"serial|block": "serial",
"{id:category}Brick": "Brick",
"{id:category}Console": "Console",
"{id:category}Control": "Control",
"{id:category}Image": "Image",
"{id:category}Images": "Images",

View File

@ -85,7 +85,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,7 +100,7 @@ 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
@ -119,7 +119,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 +133,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 +248,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

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

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

11
libs/core/input.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "pxt.h"
namespace sensors {
/**
* Mark a sensor as used
*/
//%
void __sensorUsed(int port, int type) {
}
}

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
@ -142,6 +147,11 @@ namespace sensors.internal {
this._port = port_ - 1
init()
sensorInfos[this._port].sensors.push(this)
this.markUsed();
}
markUsed() {
sensors.__sensorUsed(this._port, this._deviceType());
}
_activated() { }
@ -191,7 +201,7 @@ namespace sensors.internal {
private lowThreshold: number;
private highThreshold: number;
private level: number;
private state: ThresholdState;
public state: ThresholdState;
constructor(id: number, min = 0, max = 100, lowThreshold = 20, highThreshold = 80) {
this.id = id;

View File

@ -20,3 +20,13 @@ void target_init() {
}
}
namespace motors {
/**
* Mark a motor as used
*/
//%
void __motorUsed(int port, bool large) {
}
}

View File

@ -30,8 +30,8 @@ enum MoveUnit {
Rotations,
//% block="degrees"
Degrees,
//% block="seconds"
Seconds
//% block="milliseconds"
MilliSeconds
}
namespace motors {
@ -53,32 +53,56 @@ namespace motors {
motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
if (!motorMM) control.fail("no motor file")
resetMotors()
resetAllMotors()
let buf = output.createBuffer(1)
const buf = output.createBuffer(1)
buf[0] = DAL.opProgramStart
writePWM(buf)
}
function writePWM(buf: Buffer): void {
/**
* Sends a command to the motors device
* @param buf the command buffer
*/
//%
export function writePWM(buf: Buffer): void {
init()
pwmMM.write(buf)
}
function readPWM(buf: Buffer): void {
/**
* Sends and receives a message from the motors device
* @param buf message buffer
*/
//%
export function readPWM(buf: Buffer): void {
init()
pwmMM.read(buf);
}
function mkCmd(out: Output, cmd: number, addSize: number) {
/**
* Allocates a message buffer
* @param out ports
* @param cmd command id
* @param addSize required additional bytes
*/
//%
export function mkCmd(out: Output, cmd: number, addSize: number) {
const b = output.createBuffer(2 + addSize)
b.setNumber(NumberFormat.UInt8LE, 0, cmd)
b.setNumber(NumberFormat.UInt8LE, 1, out)
return b
}
function resetMotors() {
reset(Output.ALL)
function outputToName(out: Output): string {
let r = "";
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (out & (1 << i)) {
if (r.length > 0) r += "+";
r += "ABCD"[i];
}
}
return r;
}
/**
@ -92,58 +116,106 @@ namespace motors {
writePWM(b)
}
/**
* Resets all motors
*/
//% group="Motion"
export function resetAllMotors() {
reset(Output.ALL)
}
//% fixedInstances
export class Motor extends control.Component {
private _port: Output;
private _large: boolean;
export class MotorBase extends control.Component {
protected _port: Output;
protected _portName: string;
protected _brake: boolean;
private _initialized: boolean;
private _brake: boolean;
private _init: () => void;
private _setSpeed: (speed: number) => void;
private _move: (steps: boolean, stepsOrTime: number, speed: number) => void;
constructor(port: Output, large: boolean) {
constructor(port: Output, init: () => void, setSpeed: (speed: number) => void, move: (steps: boolean, stepsOrTime: number, speed: number) => void) {
super();
this._port = port;
this._large = large;
this._portName = outputToName(this._port);
this._brake = false;
this._initialized = false;
this._init = init;
this._setSpeed = setSpeed;
this._move = move;
}
private __init() {
/**
* Lazy initialization code
*/
protected init() {
if (!this._initialized) {
// specify motor size on this port
const b = mkCmd(this._port, DAL.opOutputSetType, 1)
b.setNumber(NumberFormat.Int8LE, 2, this._large ? 0x07 : 0x08)
writePWM(b)
this._initialized = true;
this._init();
}
}
/**
* Gets the port where this motor is connected
* Sets the automatic brake on or off when the motor is off
* @param brake a value indicating if the motor should break when off
*/
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
//% brake.fieldEditor=toggleonoff
//% weight=60 blockGap=8
//% group="Motion"
setBrake(brake: boolean) {
this.init();
this._brake = brake;
}
/**
* Reverses the motor polarity
*/
//% blockId=motorSetReversed block="set %motor|reversed %reversed"
//% reversed.fieldEditor=toggleonoff
//% weight=59
//% group="Motion"
setReversed(reversed: boolean) {
this.init();
const b = mkCmd(this._port, DAL.opOutputPolarity, 1)
b.setNumber(NumberFormat.Int8LE, 2, reversed ? 0 : 1);
writePWM(b)
}
/**
* Stops the motor(s).
*/
//%
//% group="Motion"
port(): Output {
return this._port;
stop() {
this.init();
stop(this._port, this._brake);
}
/**
* Resets the motor(s).
*/
//%
reset() {
this.init();
reset(this._port);
}
/**
* Sets the speed of the motor.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/
//% blockId=motorSetSpeed block="set speed of `icons.motorLarge` %motor|to %speed|%"
//% blockId=motorSetSpeed block="set speed of %motor|to %speed|%"
//% on.fieldEditor=toggleonoff
//% weight=99 blockGap=8
//% speed.min=-100 speed.max=100
//% group="Motion"
setSpeed(speed: number) {
this.__init();
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) { // always stop
if (!speed) // always stop
this.stop();
} else {
const b = mkCmd(this._port, DAL.opOutputSpeed, 1)
b.setNumber(NumberFormat.Int8LE, 2, speed)
writePWM(b)
}
else
this._setSpeed(speed);
}
/**
@ -152,22 +224,17 @@ namespace motors {
* @param unit the meaning of the value
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/
//% blockId=motorMove block="move `icons.motorLarge` %motor|for %value|%unit|at %speed|%"
//% blockId=motorMove block="move %motor|for %value|%unit|at %speed|%"
//% weight=98 blockGap=8
//% speed.min=-100 speed.max=100
//% group="Motion"
move(value: number, unit: MoveUnit, speed: number) {
this.output(value, unit, speed, 0);
}
private output(value: number, unit: MoveUnit, speed: number, turnRatio: number) {
this.__init();
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
this.stop();
return;
}
turnRatio = Math.clamp(-200, 200, turnRatio >> 0);
let useSteps: boolean;
let stepsOrTime: number;
switch (unit) {
@ -185,8 +252,71 @@ namespace motors {
break;
}
this._move(useSteps, stepsOrTime, speed);
}
/**
* Returns a value indicating if the motor is still running a previous command.
*/
//%
isReady(): boolean {
this.init();
const buf = mkCmd(this._port, DAL.opOutputTest, 2);
readPWM(buf)
const flags = buf.getNumber(NumberFormat.UInt8LE, 2);
// TODO: FIX with ~ support
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
const flag = 1 << i;
if ((this._port & flag) && (flags & flag))
return false;
}
return true;
}
/**
* Pauses the execution until the previous command finished.
* @param timeOut optional maximum pausing time in milliseconds
*/
//% blockId=motorPauseUntilRead block="%motor|pause until ready"
//% group="Motion"
pauseUntilReady(timeOut?: number) {
pauseUntil(() => this.isReady(), timeOut);
}
}
//% fixedInstances
export class Motor extends MotorBase {
private _large: boolean;
constructor(port: Output, large: boolean) {
super(port, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
this._large = large;
this.markUsed();
}
markUsed() {
motors.__motorUsed(this._port, this._large);
}
private __init() {
// specify motor size on this port
const b = mkCmd(outOffset(this._port), DAL.opOutputSetType, 1)
b.setNumber(NumberFormat.Int8LE, 2, this._large ? 0x07 : 0x08)
writePWM(b)
}
private __setSpeed(speed: number) {
const b = mkCmd(this._port, DAL.opOutputSpeed, 1)
b.setNumber(NumberFormat.Int8LE, 2, speed)
writePWM(b)
if (speed) {
writePWM(mkCmd(this._port, DAL.opOutputStart, 0))
}
}
private __move(steps: boolean, stepsOrTime: number, speed: number) {
step(this._port, {
useSteps: useSteps,
useSteps: steps,
step1: 0,
step2: stepsOrTime,
step3: 0,
@ -195,75 +325,28 @@ namespace motors {
})
}
/**
* Stops the motor
*/
private stop() {
this.__init();
stop(this._port);
}
/**
* Sets the automatic brake on or off when the motor is off
* @param brake a value indicating if the motor should break when off
*/
//% blockId=outputMotorSetBrakeMode block="set `icons.motorLarge` %motor|brake %brake"
//% brake.fieldEditor=toggleonoff
//% weight=60 blockGap=8
//% group="Motion"
setBrake(brake: boolean) {
this.__init();
this._brake = brake;
}
/**
* Reverses the motor polarity
*/
//% blockId=motorSetReversed block="set `icons.motorLarge` %motor|reversed %reversed"
//% reversed.fieldEditor=toggleonoff
//% weight=59
//% group="Motion"
setReversed(reversed: boolean) {
this.__init();
const b = mkCmd(this._port, DAL.opOutputPolarity, 1)
b.setNumber(NumberFormat.Int8LE, 2, reversed ? -1 : 1);
writePWM(b)
}
/**
* Gets motor actual speed.
* @param motor the port which connects to the motor
*/
//% blockId=motorSpeed block="`icons.motorLarge` %motor|speed"
//% blockId=motorSpeed block="%motor|speed"
//% weight=72 blockGap=8
//% group="Sensors"
speed(): number {
this.__init();
this.init();
return getMotorData(this._port).actualSpeed;
}
/**
* Gets motor step count.
* Gets motor ration angle.
* @param motor the port which connects to the motor
*/
//% blockId=motorCount block="`icons.motorLarge` %motor|count"
//% weight=71 blockGap=8
//% group="Sensors"
count(): number {
this.__init();
return getMotorData(this._port).count;
}
/**
* Gets motor tacho count.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="`icons.motorLarge` %motor|tacho count"
//% blockId=motorTachoCount block="%motor|angle"
//% weight=70
//% group="Sensors"
tachoCount(): number {
this.__init();
return getMotorData(this._port).tachoCount;
angle(): number {
this.init();
return getMotorData(this._port).count;
}
/**
@ -271,7 +354,7 @@ namespace motors {
*/
//% group="Motion"
clearCount() {
this.__init();
this.init();
const b = mkCmd(this._port, DAL.opOutputClearCount, 0)
writePWM(b)
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
@ -282,14 +365,11 @@ namespace motors {
}
/**
* Resets the motor.
* Returns the status of the motor
*/
//% blockId=motorReset block="reset `icons.motorLarge` %motor"
//% weight=20
//% group="Motion"
reset() {
this.__init();
reset(this._port);
//%
toString(): string {
return `${this._large ? "" : "M"}${this._portName} ${this.speed()}% ${this.angle()}>`;
}
}
@ -318,66 +398,62 @@ namespace motors {
export const mediumD = new Motor(Output.D, false);
//% fixedInstances
export class SynchedMotorPair extends control.Component {
private _ports: Output;
private _brake: boolean;
export class SynchedMotorPair extends MotorBase {
constructor(ports: Output) {
super();
this._ports = ports;
this._brake = false;
super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
this.markUsed();
}
/**
* Sets the speed of the motor.
* @param speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
*/
//% blockId=motorPairSetSpeed block="set speed of `icons.motorLarge` %motor|to %speed|%"
//% on.fieldEditor=toggleonoff
//% weight=99 blockGap=8
//% speed.min=-100 speed.max=100
//% group="Chassis"
setSpeed(speed: number) {
speed = Math.clamp(speed >> 0, -100, 100);
if (!speed) {
stop(this._ports);
return;
markUsed() {
motors.__motorUsed(this._port, true);
}
private __init() {
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (this._port & (1 << i)) {
const b = mkCmd(outOffset(1 << i), DAL.opOutputSetType, 1)
b.setNumber(NumberFormat.Int8LE, 2, 0x07) // large motor
writePWM(b)
}
}
syncMotors(this._ports, {
}
private __setSpeed(speed: number) {
syncMotors(this._port, {
speed: speed,
turnRatio: 0,
useBrake: !!this._brake
})
}
/**
* Sets the automatic brake on or off when the motor is off
* @param brake a value indicating if the motor should break when off
*/
//% blockId=motorPairSetBrakeMode block="set `icons.motorLarge` %chassis|brake %brake"
//% brake.fieldEditor=toggleonoff
//% weight=60 blockGap=8
//% group="Chassis"
setBrake(brake: boolean) {
this._brake = brake;
private __move(steps: boolean, stepsOrTime: number, speed: number) {
syncMotors(this._port, {
useSteps: steps,
speed: speed,
turnRatio: 100, // same speed
stepsOrTime: stepsOrTime,
useBrake: this._brake
});
}
/**
* 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 speed the speed from ``100`` full forward to ``-100`` full backward, eg: 50
* @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="move steering %chassis|at %speed|%|steer %turnRadio|%|by %value|%unit"
//% 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"
moveSteering(steering: number, speed: number, value: number, unit: MoveUnit) {
steer(steering: number, speed: number, value: number, unit: MoveUnit) {
this.init();
speed = Math.clamp(-100, 100, speed >> 0);
if (!speed) {
stop(this._ports);
stop(this._port, this._brake);
return;
}
@ -399,7 +475,7 @@ namespace motors {
break;
}
syncMotors(this._ports, {
syncMotors(this._port, {
useSteps: useSteps,
speed: speed,
turnRatio: turnRatio,
@ -419,17 +495,31 @@ 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="move tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit"
//% blockId=motorPairTank block="tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit"
//% weight=9 blockGap=8
//% speedLeft.min=-100 speedLeft=100
//% speedRight.min=-100 speedRight=100
//% inlineInputMode=inline
//% group="Chassis"
moveTank(speedLeft: number, speedRight: number, value: number, unit: MoveUnit) {
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.moveSteering(speedLeft, steering, value, unit);
this.steer(speedLeft, steering, value, unit);
}
/**
* Returns the name(s) of the motor
*/
//%
toString(): string {
let r = outputToName(this._port);
for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
if (this._port & (1 << i)) {
r += ` ${getMotorData(1 << i).actualSpeed}%`
}
}
return r;
}
}
@ -446,8 +536,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) {
@ -458,7 +548,7 @@ namespace motors {
return 0
}
interface MotorData {
export interface MotorData {
actualSpeed: number; // -100..+100
tachoCount: number;
count: number;
@ -467,7 +557,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),
@ -475,6 +565,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;
@ -513,9 +608,9 @@ namespace motors {
writePWM(b);
}
function stop(out: Output) {
function stop(out: Output, brake: boolean) {
const b = mkCmd(out, DAL.opOutputStop, 1)
b.setNumber(NumberFormat.UInt8LE, 2, this.brake ? 1 : 0)
b.setNumber(NumberFormat.UInt8LE, 2, brake ? 1 : 0)
writePWM(b);
}

View File

@ -10,6 +10,7 @@
"linux.cpp",
"mmap.cpp",
"control.cpp",
"console.ts",
"serialnumber.cpp",
"buttons.ts",
"png.cpp",
@ -18,6 +19,7 @@
"output.cpp",
"output.ts",
"core.ts",
"input.cpp",
"input.ts",
"shims.d.ts",
"enums.d.ts",

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 { }
@ -84,7 +86,7 @@ namespace brick {
* @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"
//% blockId=screen_setpixel block="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) {
@ -100,7 +102,7 @@ namespace brick {
* @param x the starting position's x coordinate, eg: 0
* @param y the starting position's x coordinate, eg: 0
*/
//% blockId=screen_print block="`icons.brickDisplay` print %text| at x: %x| y: %y"
//% blockId=screen_print block="print %text| at x: %x| y: %y"
//% weight=99 group="Screen" inlineInputMode="inline" blockGap=8
//% x.min=0 x.max=178 y.min=0 y.max=128
export function print(text: string, x: number, y: number, mode = Draw.Normal) {
@ -137,7 +139,7 @@ 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"
//% blockId=screen_show_image block="show image %image=screen_image_picker"
//% weight=95 group="Screen" blockGap=8
export function showImage(image: Image, delay: number = 400) {
if (!image) return;
@ -163,7 +165,7 @@ namespace brick {
/**
* Clears the screen
*/
//% blockId=screen_clear_screen block="`icons.brickDisplay` clear screen"
//% blockId=screen_clear_screen block="clear screen"
//% weight=94 group="Screen" blockGap=8
export function clearScreen() {
screen.clear();
@ -208,5 +210,29 @@ 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 x = i * 52;
const data = datas[i];
print(`${data.actualSpeed}%`, x, brick.LINE_HEIGHT)
print(`${data.count}>`, x, 2 * brick.LINE_HEIGHT)
}
// sensors
const sis = sensors.internal.getActiveSensors();
for(let i =0; i < sis.length; ++i) {
const si = sis[i];
const x = (si.port() - 1) * 52;
print(`${si._query()}`, x, 9 * brick.LINE_HEIGHT)
}
}
}

16
libs/core/shims.d.ts vendored
View File

@ -120,5 +120,21 @@ declare namespace output {
//% shim=output::createBuffer
function createBuffer(size: int32): Buffer;
}
declare namespace motors {
/**
* Mark a motor as used
*/
//% shim=motors::__motorUsed
function __motorUsed(port: int32, large: boolean): void;
}
declare namespace sensors {
/**
* Mark a sensor as used
*/
//% shim=sensors::__sensorUsed
function __sensorUsed(port: int32, type: int32): void;
}
// Auto-generated. Do not edit. Really.

View File

@ -1,81 +0,0 @@
namespace pxsim {
enum ThresholdState {
High,
Low,
Normal
}
export class AnalogSensorState {
public sensorUsed: boolean = false;
private level: number;
private state = ThresholdState.Normal;
constructor(public id: number, private min = 0, private max = 255, private lowThreshold = 64, private highThreshold = 192) {
this.level = Math.ceil((max - min) / 2);
}
public setUsed() {
if (!this.sensorUsed) {
this.sensorUsed = true;
runtime.queueDisplayUpdate();
}
}
public setLevel(level: number) {
this.level = this.clampValue(level);
if (this.level >= this.highThreshold) {
this.setState(ThresholdState.High);
}
else if (this.level <= this.lowThreshold) {
this.setState(ThresholdState.Low);
}
else {
this.setState(ThresholdState.Normal);
}
}
public getLevel(): number {
return this.level;
}
public setLowThreshold(value: number) {
this.lowThreshold = this.clampValue(value);
this.highThreshold = Math.max(this.lowThreshold + 1, this.highThreshold);
}
public setHighThreshold(value: number) {
this.highThreshold = this.clampValue(value);
this.lowThreshold = Math.min(this.highThreshold - 1, this.lowThreshold);
}
private clampValue(value: number) {
if (value < this.min) {
return this.min;
}
else if (value > this.max) {
return this.max;
}
return value;
}
private setState(state: ThresholdState) {
if (this.state === state) {
return;
}
this.state = state;
switch (state) {
case ThresholdState.High:
board().bus.queue(this.id, DAL.ANALOG_THRESHOLD_HIGH);
break;
case ThresholdState.Low:
board().bus.queue(this.id, DAL.ANALOG_THRESHOLD_LOW);
break;
case ThresholdState.Normal:
break;
}
}
}
}

View File

@ -1,177 +0,0 @@
namespace pxsim.pins {
export class CommonPin extends Pin {
used: boolean;
}
export class DigitalPin extends CommonPin {
}
export class AnalogPin extends CommonPin {
}
export function markUsed(name: CommonPin) {
if (!name.used) {
name.used = true;
runtime.queueDisplayUpdate();
}
}
}
namespace pxsim.DigitalPinMethods {
export function digitalRead(name: pins.DigitalPin): number {
return name.digitalReadPin();
}
/**
* Set a pin or connector value to either 0 or 1.
* @param value value to set on the pin, 1 eg,0
*/
export function digitalWrite(name: pins.DigitalPin, value: number): void {
name.digitalWritePin(value);
}
/**
* Configures this pin to a digital input, and generates events where the timestamp is the duration
* that this pin was either ``high`` or ``low``.
*/
export function onPulsed(name: pins.DigitalPin, pulse: number, body: RefAction): void {
// TODO
}
/**
* Returns the duration of a pulse in microseconds
* @param value the value of the pulse (default high)
* @param maximum duration in micro-seconds
*/
export function pulseIn(name: pins.DigitalPin, pulse: number, maxDuration = 2000000): number {
// TODO
return 500;
}
/**
* Configures the pull of this pin.
* @param pull one of the mbed pull configurations: PullUp, PullDown, PullNone
*/
export function setPull(name: pins.DigitalPin, pull: number): void {
name.setPull(pull);
}
/**
* Do something when a pin is pressed.
* @param body the code to run when the pin is pressed
*/
export function onPressed(name: pins.DigitalPin, body: RefAction): void {
}
/**
* Do something when a pin is released.
* @param body the code to run when the pin is released
*/
export function onReleased(name: pins.DigitalPin, body: RefAction): void {
}
/**
* Get the pin state (pressed or not). Requires to hold the ground to close the circuit.
* @param name pin used to detect the touch
*/
export function isPressed(name: pins.DigitalPin): boolean {
return name.isTouched();
}
}
namespace pxsim.AnalogPinMethods {
/**
* Read the connector value as analog, that is, as a value comprised between 0 and 1023.
*/
export function analogRead(name: pins.AnalogPin): number {
pins.markUsed(name);
return name.analogReadPin();
}
/**
* Set the connector value as analog. Value must be comprised between 0 and 1023.
* @param value value to write to the pin between ``0`` and ``1023``. eg:1023,0
*/
export function analogWrite(name: pins.AnalogPin, value: number): void {
pins.markUsed(name);
name.analogWritePin(value);
}
/**
* Configures the Pulse-width modulation (PWM) of the analog output to the given value in
* **microseconds** or `1/1000` milliseconds.
* If this pin is not configured as an analog output (using `analog write pin`), the operation has
* no effect.
* @param micros period in micro seconds. eg:20000
*/
export function analogSetPeriod(name: pins.AnalogPin, micros: number): void {
pins.markUsed(name);
name.analogSetPeriod(micros);
}
/**
* Writes a value to the servo, controlling the shaft accordingly. On a standard servo, this will
* set the angle of the shaft (in degrees), moving the shaft to that orientation. On a continuous
* rotation servo, this will set the speed of the servo (with ``0`` being full-speed in one
* direction, ``180`` being full speed in the other, and a value near ``90`` being no movement).
* @param value angle or rotation speed, eg:180,90,0
*/
export function servoWrite(name: pins.AnalogPin, value: number): void {
pins.markUsed(name);
name.servoWritePin(value);
}
/**
* Configures this IO pin as an analog/pwm output, configures the period to be 20 ms, and sets the
* pulse width, based on the value it is given **microseconds** or `1/1000` milliseconds.
* @param micros pulse duration in micro seconds, eg:1500
*/
export function servoSetPulse(name: pins.AnalogPin, micros: number): void {
pins.markUsed(name);
// TODO fix pxt
// name.servoSetPulse(micros);
}
}
namespace pxsim.PwmPinMethods {
export function analogSetPeriod(name: pins.AnalogPin, micros: number): void {
name.analogSetPeriod(micros);
}
export function servoWrite(name: pins.AnalogPin, value: number): void {
name.servoWritePin(value);
}
export function servoSetPulse(name: pins.AnalogPin, micros: number): void {
name.servoSetPulse(name.id, micros);
}
}
namespace pxsim.pins {
export function pulseDuration(): number {
// bus last event timestamp
return 500;
}
export function createBuffer(sz: number) {
return pxsim.BufferMethods.createBuffer(sz)
}
export function spiWrite(value: number): number {
// TODO
return 0;
}
export function i2cReadBuffer(address: number, size: number, repeat?: boolean): RefBuffer {
// fake reading zeros
return createBuffer(size)
}
export function i2cWriteBuffer(address: number, buf: RefBuffer, repeat?: boolean): void {
// fake - noop
}
}

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,17 +1,21 @@
//% color="#68C3E2" weight=100
//% groups='["Light", "Buttons", "Screen"]'
//% groups='["Buttons", "Screen"]'
//% labelLineWidth=0
namespace brick {
}
//% color="#C8509B" weight=95 icon="\uf192"
//% 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"
//% groups='["Motion", "Sensors", "Chassis"]'
//% labelLineWidth=0
namespace motors {
}
@ -20,7 +24,7 @@ namespace music {
}
//% color="#48150C"
//% color="#5F3109"
namespace control {
}

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.40",
"version": "0.0.46",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -40,7 +40,8 @@
"@types/bluebird": "2.0.33",
"@types/jquery": "3.2.16",
"@types/marked": "0.3.0",
"@types/node": "8.0.53"
"@types/node": "8.0.53",
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.14.13",

View File

@ -15,12 +15,13 @@
"libs/ultrasonic-sensor",
"libs/infrared-sensor",
"libs/gyro-sensor",
"libs/ev3"
"libs/ev3",
"libs/tests"
],
"simulator": {
"autoRun": true,
"streams": true,
"aspectRatio": 0.67,
"aspectRatio": 0.5,
"parts": false,
"enableTrace": true,
"boardDefinition": {
@ -52,8 +53,10 @@
"serial": {
"vendorId": "0x0694",
"productId": "0x0005",
"rawHID": true
},
"rawHID": true,
"useEditor": true,
"log": true
},
"runtime": {
"mathBlocks": true,
"loopsBlocks": true,
@ -129,10 +132,10 @@
"logic": "#1E5AA8",
"math": "#9DC3F7",
"variables": "#B40000",
"text": "#F0890A",
"text": "#FCAC00",
"advanced": "#969696",
"functions": "#064597",
"arrays": "#890058"
"functions": "#19325A",
"arrays": "#901F76"
},
"monacoColors": {
"editor.background": "#ecf6ff"

View File

@ -22,102 +22,40 @@ namespace pxsim {
D13
}
export class DalBoard extends CoreBoard implements
AccelerometerBoard,
CommonBoard,
// LightBoard,
LightSensorBoard,
MicrophoneBoard,
MusicBoard,
SlideSwitchBoard,
TemperatureBoard,
InfraredBoard,
CapTouchBoard {
// 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;
audioState: AudioState;
touchButtonState: TouchButtonState;
irState: InfraredState;
lightState: EV3LightState;
screenState: EV3ScreenState;
export class EV3Board extends CoreBoard {
view: SVGSVGElement;
outputState: EV3OutputState;
analogState: EV3AnalogState;
uartState: EV3UArtState;
motorState: EV3MotorState;
screenState: EV3ScreenState;
audioState: AudioState;
inputNodes: SensorNode[] = [];
brickNode: BrickNode;
outputNodes: MotorNode[] = [];
private motorMap: pxt.Map<number> = {
0x01: 0,
0x02: 1,
0x04: 2,
0x08: 3
}
constructor() {
super()
this.bus.setNotify(DAL.DEVICE_ID_NOTIFY, DAL.DEVICE_ID_NOTIFY_ONE);
//components
this.brickNode = new BrickNode();
this.builtinParts["buttons"] = this.buttonState = new EV3ButtonState();
this.builtinParts["light"] = this.lightState = new EV3LightState();
this.builtinParts["screen"] = this.screenState = new EV3ScreenState();
this.builtinParts["audio"] = this.audioState = new AudioState();
/*this.builtinParts["neopixel"] = this.neopixelState = new CommonNeoPixelState();
this.builtinParts["buttonpair"] = this.buttonState = new CommonButtonState();
this.builtinParts["switch"] = this.slideSwitchState = new SlideSwitchState();
this.builtinParts["lightsensor"] = this.lightSensorState = new AnalogSensorState(DAL.DEVICE_ID_LIGHT_SENSOR, 0, 255);
this.builtinParts["thermometer"] = this.thermometerState = new AnalogSensorState(DAL.DEVICE_ID_THERMOMETER, -5, 50);
this.builtinParts["soundsensor"] = this.microphoneState = new AnalogSensorState(DAL.DEVICE_ID_TOUCH_SENSOR + 1, 0, 255);
this.builtinParts["capacitivesensor"] = this.capacitiveSensorState = new CapacitiveSensorState({
0: 0,
1: 1,
2: 2,
3: 3,
6: 4,
9: 5,
10: 6,
12: 7
});
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
this.builtinParts["edgeconnector"] = this.edgeConnectorState = new EdgeConnectorState({
pins: [
pxsim.CPlayPinName.A0,
pxsim.CPlayPinName.A1,
pxsim.CPlayPinName.A2,
pxsim.CPlayPinName.A3,
pxsim.CPlayPinName.A4,
pxsim.CPlayPinName.A5,
pxsim.CPlayPinName.A6,
pxsim.CPlayPinName.A7,
pxsim.CPlayPinName.A8,
pxsim.CPlayPinName.A9,
pxsim.CPlayPinName.D4,
pxsim.CPlayPinName.D5,
pxsim.CPlayPinName.D6,
pxsim.CPlayPinName.D7,
pxsim.CPlayPinName.D8,
pxsim.CPlayPinName.D13
]
});
this.builtinParts["microservo"] = this.edgeConnectorState;
this.builtinVisuals["microservo"] = () => new visuals.MicroServoView();
this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy);
this.touchButtonState = new TouchButtonState([
pxsim.CPlayPinName.A1,
pxsim.CPlayPinName.A2,
pxsim.CPlayPinName.A3,
pxsim.CPlayPinName.A4,
pxsim.CPlayPinName.A5,
pxsim.CPlayPinName.A6,
pxsim.CPlayPinName.A7
]);
this.builtinParts["ir"] = this.irState = new InfraredState();*/
this.outputState = new EV3OutputState();
this.analogState = new EV3AnalogState();
this.uartState = new EV3UArtState();
this.motorState = new EV3MotorState();
this.screenState = new EV3ScreenState();
this.audioState = new AudioState();
}
receiveMessage(msg: SimulatorMessage) {
@ -134,11 +72,6 @@ namespace pxsim {
// TODO
break;
}
case "irpacket": {
let ev = <SimulatorInfraredPacketMessage>msg;
this.irState.receive(new RefBuffer(ev.packet));
break;
}
}
}
@ -168,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();
}
@ -175,18 +111,59 @@ namespace pxsim {
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
}
//defaultNeopixelPin() {
// return this.edgeConnectorState.getPin(CPlayPinName.D8);
//}
getBrickNode() {
return this.brickNode;
}
getDefaultPitchPin() {
return this.edgeConnectorState.getPin(CPlayPinName.D6);
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[] {
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 r;
}
getMotors() {
return this.outputNodes;
}
getSensor(port: number, type: number): SensorNode {
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;
case DAL.DEVICE_TYPE_TOUCH: this.inputNodes[port] = new TouchSensorNode(port); break;
case DAL.DEVICE_TYPE_ULTRASONIC: this.inputNodes[port] = new UltrasonicSensorNode(port); break;
}
}
return this.inputNodes[port];
}
getInputNodes() {
return this.inputNodes;
}
}
export function initRuntimeWithDalBoard() {
U.assert(!runtime.board);
let b = new DalBoard();
let b = new EV3Board();
runtime.board = b;
runtime.postError = (e) => {
// TODO
@ -194,6 +171,10 @@ namespace pxsim {
}
}
export function ev3board(): EV3Board {
return runtime.board as EV3Board;
}
if (!pxsim.initCurrentRuntime) {
pxsim.initCurrentRuntime = initRuntimeWithDalBoard;
}

68
sim/state/analog.ts Normal file
View File

@ -0,0 +1,68 @@
namespace pxsim {
enum AnalogOff {
InPin1 = 0, // int16[4]
InPin6 = 8, // int16[4]
OutPin5 = 16, // int16[4]
BatteryTemp = 24, // int16
MotorCurrent = 26, // int16
BatteryCurrent = 28, // int16
Cell123456 = 30, // int16
Pin1 = 32, // int16[300][4]
Pin6 = 2432, // int16[300][4]
Actual = 4832, // uint16[4]
LogIn = 4840, // uint16[4]
LogOut = 4848, // uint16[4]
NxtCol = 4856, // uint16[36][4] - NxtColor*4
OutPin5Low = 5144, // int16[4]
Updated = 5152, // int8[4]
InDcm = 5156, // int8[4]
InConn = 5160, // int8[4]
OutDcm = 5164, // int8[4]
OutConn = 5168, // int8[4]
Size = 5172
}
export class EV3AnalogState {
constructor() {
let data = new Uint8Array(5172)
MMapMethods.register("/dev/lms_analog", {
data,
beforeMemRead: () => {
//console.log("analog before read");
const inputNodes = ev3board().getInputNodes();
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
const node = inputNodes[port];
if (node) {
data[AnalogOff.InConn + port] = node.isUart() ? DAL.CONN_INPUT_UART : DAL.CONN_INPUT_DUMB;
if (node.isAnalog() && node.hasData()) {
//data[AnalogOff.InPin6 + 2 * port] = node.getValue();
util.map16Bit(data, AnalogOff.InPin6 + 2 * port, Math.floor(node.getValue()));
}
}
}
},
read: buf => {
let v = "vSIM"
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0
console.log("analog read");
console.log(buf.data);
return buf.data.length
},
write: buf => {
console.log("analog write");
console.log(buf);
return 2
},
ioctl: (id, buf) => {
console.log("analog ioctl");
console.log(id);
console.log(buf);
return 2;
}
})
}
}
}

27
sim/state/brick.ts Normal file
View File

@ -0,0 +1,27 @@
/// <reference path="./nodeTypes.ts"/>
namespace pxsim {
export class PortNode extends BaseNode {
id = NodeType.Port;
constructor(port: number) {
super(port);
}
}
export class BrickNode extends BaseNode {
id = NodeType.Brick;
buttonState: EV3ButtonState;
lightState: EV3LightState;
constructor() {
super(-1);
this.buttonState = new EV3ButtonState();
this.lightState = new EV3LightState();
}
}
}

42
sim/state/color.ts Normal file
View File

@ -0,0 +1,42 @@
/// <reference path="./sensor.ts"/>
namespace pxsim {
export enum ColorSensorMode {
Reflected = 0,
Ambient = 1,
Colors = 2,
RefRaw = 3,
RgbRaw = 4,
ColorCal = 5
}
export enum ThresholdState {
Normal = 1,
High = 2,
Low = 3,
}
export class ColorSensorNode extends UartSensorNode {
id = NodeType.ColorSensor;
private color: number = 50;
constructor(port: number) {
super(port);
}
getDeviceType() {
return DAL.DEVICE_TYPE_COLOR;
}
setColor(color: number) {
this.color = color;
this.setChangedState();
}
getValue() {
return this.color;
}
}
}

View File

@ -63,7 +63,6 @@ namespace pxsim.MMapMethods {
export function write(m: MMap, data: Buffer): number {
return m.impl.write(data)
}
export function read(m: MMap, data: Buffer): number {

41
sim/state/gyro.ts Normal file
View File

@ -0,0 +1,41 @@
namespace pxsim {
const enum GyroSensorMode {
None = -1,
Angle = 0,
Rate = 1,
}
export class GyroSensorNode extends UartSensorNode {
id = NodeType.GyroSensor;
private angle: number = 0;
private rate: number = 0;
constructor(port: number) {
super(port);
}
getDeviceType() {
return DAL.DEVICE_TYPE_GYRO;
}
setAngle(angle: number) {
if (this.angle != angle) {
this.angle = angle;
this.setChangedState();
}
}
setRate(rate: number) {
if (this.rate != rate) {
this.rate = rate;
this.setChangedState();
}
}
getValue() {
return this.mode == GyroSensorMode.Angle ? this.angle :
this.mode == GyroSensorMode.Rate ? this.rate : 0;
}
}
}

18
sim/state/input.ts Normal file
View File

@ -0,0 +1,18 @@
namespace pxsim.motors {
export function __motorUsed(port: number, large: boolean) {
//console.log("MOTOR INIT " + port);
ev3board().motorUsed(port, large);
runtime.queueDisplayUpdate();
}
}
namespace pxsim.sensors {
export function __sensorUsed(port: number, type: number) {
//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 = (board() as DalBoard).lightState;
lightState.lightPattern = pattern;
runtime.queueDisplayUpdate();
const brickState = ev3board().getBrickNode();
const lightState = brickState.lightState;
if (lightState.lightPattern != pattern) {
lightState.lightPattern = pattern;
brickState.setChangedState();
}
}
}

58
sim/state/motor.ts Normal file
View File

@ -0,0 +1,58 @@
namespace pxsim {
enum MotorDataOff {
TachoCounts = 0, // int32
Speed = 4, // int8
Padding = 5, // int8[3]
TachoSensor = 8, // int32
Size = 12
}
export class EV3MotorState {
constructor() {
let data = new Uint8Array(12 * DAL.NUM_OUTPUTS)
MMapMethods.register("/dev/lms_motor", {
data,
beforeMemRead: () => {
const outputs = ev3board().outputNodes;
// console.log("motor before read");
for (let port = 0; port < DAL.NUM_OUTPUTS; ++port) {
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 => {
let v = "vSIM"
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0
console.log("motor read");
console.log(buf.data);
return buf.data.length
},
write: buf => {
if (buf.data.length == 0) return 2;
const cmd = buf.data[0];
console.log("motor write");
console.log(buf);
return 2
},
ioctl: (id, buf) => {
console.log("motor ioctl");
console.log(id);
console.log(buf);
return 2;
}
});
}
}
}

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

@ -0,0 +1,141 @@
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;
constructor(port: number, large: boolean) {
super(port);
this.setLarge(large);
}
getSpeed() {
return this.speed * (this.polarity == 0 ? -1 : 1);
}
getAngle() {
return this.angle;
}
setSpeedCmd(cmd: DAL, values: number[]) {
this.speedCmd = cmd;
this.speedCmdValues = values;
this.speedCmdTacho = this.angle;
this.speedCmdTime = pxsim.U.now();
}
clearSpeedCmd() {
delete this.speedCmd;
}
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;
}
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();
}
this.speed = Math.round(this.speed); // integer only
break;
}
// 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));
}
}
}
}

48
sim/state/nodeTypes.ts Normal file
View File

@ -0,0 +1,48 @@
namespace pxsim {
export enum NodeType {
Port = 0,
Brick = 1,
TouchSensor = 2,
MediumMotor = 3,
LargeMotor = 4,
GyroSensor = 5,
ColorSensor = 6,
UltrasonicSensor = 7
}
export interface Node {
id: number;
didChange(): boolean;
}
export class BaseNode implements Node {
public id: number;
public port: number;
public isOutput = false;
private used = false;
protected changed = true;
constructor(port: number) {
this.port = port;
}
didChange() {
const res = this.changed;
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) {
}
}
}

115
sim/state/output.ts Normal file
View File

@ -0,0 +1,115 @@
namespace pxsim {
export class EV3OutputState {
constructor() {
let data = new Uint8Array(10)
MMapMethods.register("/dev/lms_pwm", {
data,
beforeMemRead: () => {
//console.log("pwm before read");
for (let i = 0; i < 10; ++i)
data[i] = 0
},
read: buf => {
let v = "vSIM"
for (let i = 0; i < buf.data.length; ++i)
buf.data[i] = v.charCodeAt(i) || 0
console.log("pwm read");
return buf.data.length
},
write: buf => {
if (buf.data.length == 0) return 2;
const cmd = buf.data[0];
switch (cmd) {
case DAL.opProgramStart: {
// init
console.log('init');
return 2;
}
case DAL.opOutputReset: {
// reset
const port = buf.data[1];
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.reset());
return 2;
}
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 = 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.setSpeedCmd(cmd, [speed, step1, step2, step3, brake]));
return 2;
}
case DAL.opOutputStop: {
// stop
const port = buf.data[1];
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.stop());
return 2;
}
case DAL.opOutputSpeed: {
// setSpeed
const port = buf.data[1];
const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.setSpeedCmd(cmd, [speed]));
return 2;
}
case DAL.opOutputStart: {
// start
const port = buf.data[1];
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.start());
return 2;
}
case DAL.opOutputPolarity: {
// reverse
const port = buf.data[1];
const polarity = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.setPolarity(polarity));
return 2;
}
case DAL.opOutputSetType: {
const port = buf.data[1];
const large = buf.data[2] == 0x07;
const motors = ev3board().getMotor(port);
motors.forEach(motor => motor.setLarge(large));
return 2;
}
default:
console.warn('unknown cmd: ' + cmd);
break;
}
console.log("pwm write");
console.log(buf);
return 2
},
ioctl: (id, buf) => {
console.log("pwm ioctl");
console.log(id);
console.log(buf);
return 2;
}
});
}
}
}

View File

@ -7,7 +7,7 @@ namespace pxsim {
export class EV3ScreenState {
shouldUpdate: boolean;
changed: boolean = true;
points: Uint8Array;
constructor() {
this.points = new Uint8Array(visuals.SCREEN_WIDTH * visuals.SCREEN_HEIGHT)
@ -24,13 +24,13 @@ namespace pxsim {
setPixel(x: number, y: number, v: number) {
this.applyMode(OFF(x, y), v)
this.shouldUpdate = true;
this.changed = true;
}
clear() {
for (let i = 0; i < this.points.length; ++i)
this.points[i] = 0;
this.shouldUpdate = true;
this.changed = true;
}
blitLineCore(x: number, y: number, w: number, buf: RefBuffer, mode: Draw, offset = 0) {
@ -59,7 +59,7 @@ namespace pxsim {
}
}
this.shouldUpdate = true;
this.changed = true;
}
clearLine(x: number, y: number, w: number) {
@ -73,6 +73,12 @@ namespace pxsim {
off++
}
}
didChange() {
const res = this.changed;
this.changed = false;
return res;
}
}
}
@ -82,12 +88,12 @@ namespace pxsim.screen {
function YY(v: number) { return v >> 16 }
export function _setPixel(x: number, y: number, mode: Draw) {
const screenState = (board() as DalBoard).screenState;
const screenState = ev3board().screenState;
screenState.setPixel(x, y, mode);
}
export function _blitLine(xw: number, y: number, buf: RefBuffer, mode: Draw) {
const screenState = (board() as DalBoard).screenState;
const screenState = ev3board().screenState;
screenState.blitLineCore(XX(xw), y, YY(xw), buf, mode)
}
@ -99,7 +105,7 @@ namespace pxsim.screen {
return ((x + 7) >> 3)
}
export function clear(): void {
const screenState = (board() as DalBoard).screenState;
const screenState = ev3board().screenState;
screenState.clear()
}
@ -240,7 +246,7 @@ namespace pxsim.ImageMethods {
}
export function draw(buf: RefBuffer, x: number, y: number, mode: Draw): void {
const screenState = (board() as DalBoard).screenState;
const screenState = ev3board().screenState;
if (!screen.isValidImage(buf))
return;

78
sim/state/sensor.ts Normal file
View File

@ -0,0 +1,78 @@
namespace pxsim {
export class SensorNode extends BaseNode {
protected mode: number;
protected valueChanged: boolean;
constructor(port: number) {
super(port);
}
public isUart() {
return true;
}
public isAnalog() {
return false;
}
public getValue() {
return 0;
}
setMode(mode: number) {
this.mode = mode;
}
getMode() {
return this.mode;
}
getDeviceType() {
return DAL.DEVICE_TYPE_NONE;
}
public hasData() {
return true;
}
valueChange() {
const res = this.valueChanged;
this.valueChanged = false;
return res;
}
setChangedState() {
this.changed = true;
this.valueChanged = false;
}
}
export class AnalogSensorNode extends SensorNode {
constructor(port: number) {
super(port);
}
public isUart() {
return false;
}
public isAnalog() {
return true;
}
}
export class UartSensorNode extends SensorNode {
constructor(port: number) {
super(port);
}
hasChanged() {
return this.changed;
}
}
}

41
sim/state/touch.ts Normal file
View File

@ -0,0 +1,41 @@
namespace pxsim {
export const TOUCH_SENSOR_ANALOG_PRESSED = 2600;
export class TouchSensorNode extends AnalogSensorNode {
id = NodeType.TouchSensor;
private pressed: boolean[];
constructor(port: number) {
super(port);
this.pressed = [];
}
public setPressed(pressed: boolean) {
this.pressed.push(pressed);
this.setChangedState();
}
public isPressed() {
return this.pressed;
}
public getValue() {
if (this.pressed.length) {
if (this.pressed.pop())
return TOUCH_SENSOR_ANALOG_PRESSED;
}
return 0;
}
getDeviceType() {
return DAL.DEVICE_TYPE_TOUCH;
}
public hasData() {
return this.pressed.length > 0;
}
}
}

156
sim/state/uart.ts Normal file
View File

@ -0,0 +1,156 @@
namespace pxsim {
enum UartOff {
TypeData = 0, // Types[8][4]
Repeat = 1792, // uint16[300][4]
Raw = 4192, // int8[32][300][4]
Actual = 42592, // uint16[4]
LogIn = 42600, // uint16[4]
Status = 42608, // int8[4]
Output = 42612, // int8[32][4]
OutputLength = 42740, // int8[4]
Size = 42744
}
enum UartStatus {
UART_PORT_CHANGED = 1,
UART_DATA_READY = 8
}
enum IO {
UART_SET_CONN = 0xc00c7500,
UART_READ_MODE_INFO = 0xc03c7501,
UART_NACK_MODE_INFO = 0xc03c7502,
UART_CLEAR_CHANGED = 0xc03c7503,
IIC_SET_CONN = 0xc00c6902,
IIC_READ_TYPE_INFO = 0xc03c6903,
IIC_SETUP = 0xc04c6905,
IIC_SET = 0xc02c6906,
TST_PIN_ON = 0xc00b7401,
TST_PIN_OFF = 0xc00b7402,
TST_PIN_READ = 0xc00b7403,
TST_PIN_WRITE = 0xc00b7404,
TST_UART_ON = 0xc0487405,
TST_UART_OFF = 0xc0487406,
TST_UART_EN = 0xc0487407,
TST_UART_DIS = 0xc0487408,
TST_UART_READ = 0xc0487409,
TST_UART_WRITE = 0xc048740a,
}
enum DevConOff {
Connection = 0, // int8[4]
Type = 4, // int8[4]
Mode = 8, // int8[4]
Size = 12
}
enum UartCtlOff {
TypeData = 0, // Types
Port = 56, // int8
Mode = 57, // int8
Size = 58
}
enum TypesOff {
Name = 0, // int8[12]
Type = 12, // int8
Connection = 13, // int8
Mode = 14, // int8
DataSets = 15, // int8
Format = 16, // int8
Figures = 17, // int8
Decimals = 18, // int8
Views = 19, // int8
RawMin = 20, // float32
RawMax = 24, // float32
PctMin = 28, // float32
PctMax = 32, // float32
SiMin = 36, // float32
SiMax = 40, // float32
InvalidTime = 44, // uint16
IdValue = 46, // uint16
Pins = 48, // int8
Symbol = 49, // int8[5]
Align = 54, // uint16
Size = 56
}
export class EV3UArtState {
constructor() {
let data = new Uint8Array(UartOff.Size);
MMapMethods.register("/dev/lms_uart", {
data,
beforeMemRead: () => {
//console.log("uart before read");
const inputNodes = ev3board().getInputNodes();
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
const node = inputNodes[port];
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()))
// Status
data[UartOff.Status + port] = node.valueChange() ? UartStatus.UART_PORT_CHANGED : UartStatus.UART_DATA_READY;
}
}
},
read: buf => {
let v = "vSIM"
// for (let i = 0; i < buf.data.length; ++i)
// buf.data[i] = v.charCodeAt(i) || 0
console.log("uart read");
console.log(buf.data);
return buf.data.length
},
write: buf => {
console.log("uart write");
console.log(buf);
return 2
},
ioctl: (id, buf) => {
switch (id) {
case IO.UART_SET_CONN: {
// Set mode
console.log("IO.UART_SET_CONN");
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
const connection = buf.data[DevConOff.Connection + port]; // CONN_NONE, CONN_INPUT_UART
const type = buf.data[DevConOff.Type + port];
const mode = buf.data[DevConOff.Mode + port];
console.log(`${port}, mode: ${mode}`)
const node = ev3board().getInputNodes()[port];
if (node) node.setMode(mode);
}
return 2;
}
case IO.UART_CLEAR_CHANGED: {
console.log("IO.UART_CLEAR_CHANGED")
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
const connection = buf.data[DevConOff.Connection + port]; // CONN_NONE, CONN_INPUT_UART
const type = buf.data[DevConOff.Type + port];
const mode = buf.data[DevConOff.Mode + port];
const node = ev3board().getInputNodes()[port];
if (node) node.setMode(mode);
}
return 2;
}
case IO.UART_READ_MODE_INFO: {
console.log("IO.UART_READ_MODE_INFO")
const port = buf.data[UartCtlOff.Port];
const mode = buf.data[UartCtlOff.Mode];
const node = ev3board().getInputNodes()[port];
if (node) buf.data[UartCtlOff.TypeData + TypesOff.Type] = node.getDeviceType(); // DEVICE_TYPE_NONE, DEVICE_TYPE_TOUCH,
return 2;
}
}
console.log("uart ioctl");
console.log(id);
console.log(buf);
return 2;
}
})
}
}
}

28
sim/state/ultrasonic.ts Normal file
View File

@ -0,0 +1,28 @@
/// <reference path="./sensor.ts"/>
namespace pxsim {
export class UltrasonicSensorNode extends UartSensorNode {
id = NodeType.UltrasonicSensor;
private distance: number = 127; // in cm
constructor(port: number) {
super(port);
}
getDeviceType() {
return DAL.DEVICE_TYPE_ULTRASONIC;
}
setDistance(distance: number) {
if (this.distance != distance) {
this.distance = distance;
this.setChangedState();
}
}
getValue() {
return this.distance * 10; // convert to 0.1 cm
}
}
}

7
sim/state/util.ts Normal file
View File

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

View File

@ -0,0 +1,32 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 33.22 34">
<defs>
<linearGradient id="linear-gradient" x1="-448.15" y1="475.99" x2="-448.15" y2="475.83" gradientTransform="matrix(32.16, 0, 0, -33.37, 14429.08, 15914.64)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
</defs>
<title>Color Sensor</title>
<g style="isolation: isolate">
<g id="svg41">
<g id="color_box" data-name="color box">
<g id="color_grey" data-name="color grey">
<path id="color_sideboxes" data-name="color sideboxes" d="M2.14,9.19H34a.7.7,0,0,1,.7.7V28a.7.7,0,0,1-.7.7H2.14a.7.7,0,0,1-.7-.7V9.89A.7.7,0,0,1,2.14,9.19Z" transform="translate(-1.44 -1.6)" style="fill: #a8aaa8"/>
<g id="color_bigbox-2" data-name="color bigbox-2">
<image width="33" height="34" transform="translate(0.06)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAjCAYAAADxG9hnAAAACXBIWXMAAAsSAAALEgHS3X78AAAAzUlEQVRYR+3YMU4DMRBG4W8Q5eYMUEMuw/3ScQDugkTPcoSsa4ZibYjcUMVQ+Ekjj+Tif7YrT2Sm/8BtayIisOCurtemYEXJzIzMFBEL7vGIJ7vMtVnxgje8q09zxAmvOONzQJ1r3gnH9jQLHuw3cmMMB3tewXIZGrVG8p056vS/MkV6pkjPFOmZIj1TpGeK9EyRninSM0V6pkjPFOmZIj2XIn81n0h+RAo+sNWNUbXV3NI+4Sueaz9iJNFouWu0iVFEHIwb0jQK1szcvgDqy2NNnOFs5AAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path d="M5,3.33h26c1,0,1.78.57,1.78,1.27V32.77c0,.7-.8,1.27-1.78,1.27H5c-1,0-1.78-.57-1.78-1.27V4.6C3.25,3.9,4.05,3.33,5,3.33Z" transform="translate(-1.44 -1.6)" style="fill: url(#linear-gradient)"/>
</g>
<rect id="color_side_black_4" data-name="color side black 4" x="31.83" y="10.38" width="0.7" height="5.58"/>
<rect id="color_side_black_3" data-name="color side black 3" x="0.84" y="10.38" width="0.7" height="5.58"/>
<rect id="color_side_black_2" data-name="color side black 2" x="31.83" y="18.34" width="0.7" height="5.58"/>
<rect id="color_side_black1" data-name="color side black1" x="0.84" y="18.34" width="0.7" height="5.58"/>
</g>
<path id="color_red" data-name="color red" d="M11.63,23.43a6.32,6.32,0,0,1,.15-1.37,8.79,8.79,0,1,1,12.54,0,6.42,6.42,0,1,1-12.55,2.7,6.61,6.61,0,0,1-.15-1.33Z" transform="translate(-1.44 -1.6)" style="fill: #d42715"/>
<path id="color_black" data-name="color black" d="M13.11,23.43a4.89,4.89,0,0,1,.34-1.81,7.4,7.4,0,1,1,10.4-1.2,7.32,7.32,0,0,1-1.18,1.18,5,5,0,1,1-9.56,1.83Z" transform="translate(-1.44 -1.6)"/>
<g id="color_sensors" data-name="color sensors">
<circle id="color_sensor_white_big" data-name="color sensor white big" cx="16.61" cy="14.43" r="3.35" style="fill: #f1f1f1"/>
<circle id="color_sensor_white_small" data-name="color sensor white small" cx="16.61" cy="21.69" r="1.81" style="fill: #f1f1f1"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,34 @@
namespace pxsim {
export const COLOR_SENSOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 33.22 34">
<defs>
<linearGradient id="linear-gradient" x1="-448.15" y1="475.99" x2="-448.15" y2="475.83" gradientTransform="matrix(32.16, 0, 0, -33.37, 14429.08, 15914.64)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
</defs>
<title>Color Sensor</title>
<g style="isolation: isolate">
<g id="svg41">
<g id="color_box" data-name="color box">
<g id="color_grey" data-name="color grey">
<path id="color_sideboxes" data-name="color sideboxes" d="M2.14,9.19H34a.7.7,0,0,1,.7.7V28a.7.7,0,0,1-.7.7H2.14a.7.7,0,0,1-.7-.7V9.89A.7.7,0,0,1,2.14,9.19Z" transform="translate(-1.44 -1.6)" style="fill: #a8aaa8"/>
<g id="color_bigbox-2" data-name="color bigbox-2">
<image width="33" height="34" transform="translate(0.06)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAjCAYAAADxG9hnAAAACXBIWXMAAAsSAAALEgHS3X78AAAAzUlEQVRYR+3YMU4DMRBG4W8Q5eYMUEMuw/3ScQDugkTPcoSsa4ZibYjcUMVQ+Ekjj+Tif7YrT2Sm/8BtayIisOCurtemYEXJzIzMFBEL7vGIJ7vMtVnxgje8q09zxAmvOONzQJ1r3gnH9jQLHuw3cmMMB3tewXIZGrVG8p056vS/MkV6pkjPFOmZIj1TpGeK9EyRninSM0V6pkjPFOmZIj2XIn81n0h+RAo+sNWNUbXV3NI+4Sueaz9iJNFouWu0iVFEHIwb0jQK1szcvgDqy2NNnOFs5AAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path d="M5,3.33h26c1,0,1.78.57,1.78,1.27V32.77c0,.7-.8,1.27-1.78,1.27H5c-1,0-1.78-.57-1.78-1.27V4.6C3.25,3.9,4.05,3.33,5,3.33Z" transform="translate(-1.44 -1.6)" style="fill: url(#linear-gradient)"/>
</g>
<rect id="color_side_black_4" data-name="color side black 4" x="31.83" y="10.38" width="0.7" height="5.58"/>
<rect id="color_side_black_3" data-name="color side black 3" x="0.84" y="10.38" width="0.7" height="5.58"/>
<rect id="color_side_black_2" data-name="color side black 2" x="31.83" y="18.34" width="0.7" height="5.58"/>
<rect id="color_side_black1" data-name="color side black1" x="0.84" y="18.34" width="0.7" height="5.58"/>
</g>
<path id="color_red" data-name="color red" d="M11.63,23.43a6.32,6.32,0,0,1,.15-1.37,8.79,8.79,0,1,1,12.54,0,6.42,6.42,0,1,1-12.55,2.7,6.61,6.61,0,0,1-.15-1.33Z" transform="translate(-1.44 -1.6)" style="fill: #d42715"/>
<path id="color_black" data-name="color black" d="M13.11,23.43a4.89,4.89,0,0,1,.34-1.81,7.4,7.4,0,1,1,10.4-1.2,7.32,7.32,0,0,1-1.18,1.18,5,5,0,1,1-9.56,1.83Z" transform="translate(-1.44 -1.6)"/>
<g id="color_sensors" data-name="color sensors">
<circle id="color_sensor_white_big" data-name="color sensor white big" cx="16.61" cy="14.43" r="3.35" style="fill: #f1f1f1"/>
<circle id="color_sensor_white_small" data-name="color sensor white small" cx="16.61" cy="21.69" r="1.81" style="fill: #f1f1f1"/>
</g>
</g>
</g>
</g>
</svg>`;
}

103
sim/visuals/assets/EV3.svg Normal file
View File

@ -0,0 +1,103 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 110.73 170.04">
<defs>
<linearGradient id="linear-gradient" x1="-374.89" y1="432.9" x2="-374.89" y2="432.82" gradientTransform="matrix(110.73, 0, 0, -106.94, 41567.45, 46425.3)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f1f1f1"/>
<stop offset="1" stop-color="#7a7a7a"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="-376" y1="450.74" x2="-376" y2="450.72" gradientTransform="matrix(100.11, 0, 0, -79.18, 37697.19, 35762.28)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="gray"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="-376.21" y1="614.94" x2="-376.21" y2="614.75" gradientTransform="matrix(98.29, 0, 0, -23.36, 37033.43, 14529.9)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
<linearGradient id="linear-gradient-black" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#6a6a6a"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-green" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#8CE300"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-red" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#D02E26"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-orange" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#F8D039"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-5" x1="-743.87" y1="1256.85" x2="-743.87" y2="1257.21" gradientTransform="matrix(3.03, 0, 0, -6.22, 2312.41, 7891.56)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#757575"/>
<stop offset="1" stop-color="#393939"/>
</linearGradient>
<clipPath id="clip-path" transform="translate(0 0)">
<rect x="86.48" y="149.58" width="12.38" height="12.38" style="fill: none"/>
</clipPath>
</defs>
<title>EV3</title>
<g id="EV3">
<g id="brick">
<path id="ev3_body_2" data-name="ev3 body 2" d="M2,31.7H108.76a2,2,0,0,1,2,2h0v103a2,2,0,0,1-2,2H2a2,2,0,0,1-2-2H0v-103a2,2,0,0,1,2-2Z" transform="translate(0 0)" style="fill: url(#linear-gradient)"/>
<path id="ev3_body_1" data-name="ev3 body 1" d="M8.19,127.57h94.35a2,2,0,0,1,2,2h0v38.53a2,2,0,0,1-2,2H8.19a2,2,0,0,1-2-2h0V129.54a2,2,0,0,1,2-2Z" transform="translate(0 0)" style="fill: #f1f1f1"/>
<path id="ev3_screen_grey" data-name="ev3 screen grey" d="M7.28,0h96.17a2,2,0,0,1,2,2h0V77.21a2,2,0,0,1-2,2H7.28a2,2,0,0,1-2-2h0V2a2,2,0,0,1,2-2Z" transform="translate(0 0)" style="fill: url(#linear-gradient-2)"/>
<path id="ev3_screenborder" data-name="ev3 screenborder" d="M18.2,10.47H92.68a3.79,3.79,0,0,1,3.79,3.79V56.12a3.8,3.8,0,0,1-3.79,3.8H18.2a3.8,3.8,0,0,1-3.79-3.8V14.26A3.79,3.79,0,0,1,18.2,10.47Z" transform="translate(0 0)" style="fill: #393939"/>
<path id="ev3_screen" data-name="ev3 screen" d="M24,12.44H87.22A2.73,2.73,0,0,1,90,15.17h0V55.52a2.73,2.73,0,0,1-2.73,2.73H24a2.73,2.73,0,0,1-2.73-2.73V15.17A2.73,2.73,0,0,1,24,12.44Z" transform="translate(0 0)" style="fill: #97b5a6"/>
<path id="ev3_grey_buttom" data-name="ev3 grey buttom" d="M6.22,146.68h98.29v21.39a2,2,0,0,1-2,2H8.19a2,2,0,0,1-2-2h0Z" transform="translate(0 0)" style="fill: url(#linear-gradient-3)"/>
</g>
<g id="buttons">
<path id="btn_grey" data-name="btn grey" d="M48.69,133.94c-5.58-5.39-14.26-14.26-14.26-14.26v-1.82H31a8.27,8.27,0,1,1,0-16.53h3.41V99.5L48.54,85.09H62.65C69,91.61,76.75,99.2,76.75,99.2v2.13H80a8.27,8.27,0,1,1,0,16.53H76.75v1.82c-7.51,7.73-14,14.26-14,14.26Z" transform="translate(0 0)" style="fill: #6a6a6a"/>
<path id="btn_color" data-name="btn color" d="M48.69,133.94c-5.58-5.39-14.26-14.26-14.26-14.26v-1.82H31a8.27,8.27,0,1,1,0-16.53h3.41V99.5L48.54,85.09H62.65C69,91.61,76.75,99.2,76.75,99.2v2.13H80a8.27,8.27,0,1,1,0,16.53H76.75v1.82c-7.51,7.73-14,14.26-14,14.26Z" transform="translate(0 0)" style="fill: url(#linear-gradient-black)"/>
<path id="btn_left" data-name="btn left" d="M30.87,103.3H41.26v12.28H30.87a6.14,6.14,0,0,1-6.14-6.14h0a6.14,6.14,0,0,1,6.14-6.14Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<path id="btn_right" data-name="btn right" d="M80,115.58H69.62V103.3H80a6.14,6.14,0,0,1,6.15,6.14h0A6.14,6.14,0,0,1,80,115.58Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<path id="btn_enter" data-name="btn enter" d="M49.45,103.3h12a.46.46,0,0,1,.46.45v11.38a.46.46,0,0,1-.46.45h-12a.46.46,0,0,1-.46-.45V103.75a.46.46,0,0,1,.46-.45Z" transform="translate(0 0)" style="fill: #393939"/>
<path id="btn_up" data-name="btn up" d="M49.15,87.37l12.74-.15,9.55,9.86L67.5,101v7.13H63.86v-4.1a3,3,0,0,0-3-3c-3,0-11.08,0-11.08,0a2.47,2.47,0,0,0-2.58,2.35v4.77H43.53V101l-3.94-4Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<path id="btn_down" data-name="btn down" d="M61.83,131.54l-12.66.1-9.58-9.85,4-3.91v-7.17h3.6v4.11a3.06,3.06,0,0,0,3,3.06c3,.05,11,0,11,0a2.6,2.6,0,0,0,2.65-2.55v-4.62h3.67v7.17l3.91,3.91Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<rect id="btn_part_4" data-name="btn part 4" x="54" y="59.76" width="3.03" height="13.8" style="fill: #393939"/>
<rect id="btn_part_3" data-name="btn part 3" x="54" y="72.96" width="3.03" height="6.07" style="fill: #9a9a9a"/>
<rect id="btn_part_2" data-name="btn part 2" x="54" y="72.96" width="3.03" height="6.22" style="fill: url(#linear-gradient-5)"/>
<rect id="btn_part_1" data-name="btn part 1" x="54" y="79.18" width="3.03" height="5.92" style="fill: gray"/>
<path id="btn_back" data-name="btn back" d="M13.2,79.18H36.86v5.71c-2.62,2.64-6,6-6,6H15.17a2,2,0,0,1-2-2Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
</g>
<g id="LEGO_logo" data-name="LEGO logo">
<rect id="logo_white_bg" data-name="logo white bg" x="86.56" y="149.66" width="12.21" height="12.21" style="fill: #fff"/>
<g id="lego">
<g style="clip-path: url(#clip-path)">
<g id="logo_part_5" data-name="logo part 5">
<path id="Path_18" data-name="Path 18" d="M86.56,161.87H98.77V149.66H86.56ZM98,154.73a4.76,4.76,0,0,1-.24,1.18c-.43,1.27-.91,2.07-2.07,2.07a1.12,1.12,0,0,1-1.16-.7l0-.15-.09.11a1.86,1.86,0,0,1-1.46.72,1.29,1.29,0,0,1-1-.43l-.07-.09-.06.06a1.6,1.6,0,0,1-1.16.42,1.32,1.32,0,0,1-1-.37l-.09,0-.07.07a1.55,1.55,0,0,1-1.11.37.87.87,0,0,1-.95-.79.37.37,0,0,1,0-.11,8.15,8.15,0,0,1,1.09-3.1,1,1,0,0,1,.92-.52.78.78,0,0,1,.57.17c.11.11.11.2.13.42l0,.28.16-.24a1.6,1.6,0,0,1,1.52-.65,1,1,0,0,1,.9.37l0,.09.06-.07a1.8,1.8,0,0,1,1.2-.39,1.53,1.53,0,0,1,1.12.37.69.69,0,0,1,.13.2l.07.11.08-.11a1.59,1.59,0,0,1,1.31-.57,1.27,1.27,0,0,1,1,.35,1.37,1.37,0,0,1,.26,1" transform="translate(0 0)" style="fill: #ffed00"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_4" data-name="logo part 4">
<path id="Path_19" data-name="Path 19" d="M86.56,161.87H98.77V149.66H86.56Zm11.75-6.66a6.73,6.73,0,0,1-.52,1.59,2.28,2.28,0,0,1-2.1,1.55,1.67,1.67,0,0,1-1.36-.54,2.18,2.18,0,0,1-1.48.54,1.73,1.73,0,0,1-1.09-.35,2.12,2.12,0,0,1-1.22.35,1.8,1.8,0,0,1-1-.3,2.19,2.19,0,0,1-1.18.3A1.28,1.28,0,0,1,87,157.14v0a8.26,8.26,0,0,1,1.18-3.34A1.37,1.37,0,0,1,89.4,153c.59,0,.79.17.9.37a2.24,2.24,0,0,1,1.46-.4,1.51,1.51,0,0,1,1,.33A2.4,2.4,0,0,1,94,153a1.74,1.74,0,0,1,1.36.45,2,2,0,0,1,1.33-.43,1.53,1.53,0,0,1,1.37.61,1.79,1.79,0,0,1,.29,1.55" transform="translate(0 0)" style="fill: #d42715"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_3" data-name="logo part 3">
<path id="Path_20" data-name="Path 20" d="M86.48,162H98.86V149.58H86.48Zm12.2-.18h-12v-12h12Z" transform="translate(0 0)" style="fill: #171714"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_2" data-name="logo part 2">
<path id="Path_21" data-name="Path 21" d="M98.25,153.29v-.09h.06s.07,0,.07.05,0,0-.07,0Zm.2.17,0-.06c0-.05,0-.07-.06-.07h0a.09.09,0,0,0,.08-.09.07.07,0,0,0-.07-.08h-.17v.3h.07v-.13h0s0,0,0,0,0,.05,0,.07l0,0Zm-.16-.39a.24.24,0,0,1,0,.48.24.24,0,0,1-.24-.24.26.26,0,0,1,.24-.24m0-.07a.31.31,0,0,0,0,.62.31.31,0,0,0,.31-.31h0a.32.32,0,0,0-.31-.31" transform="translate(0 0)" style="fill: #171714"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_1" data-name="logo part 1">
<path id="Path_22" data-name="Path 22" d="M96.7,153.33a1.73,1.73,0,0,0-1.38.61.66.66,0,0,0-.15-.22,1.47,1.47,0,0,0-1.16-.41,1.87,1.87,0,0,0-1.25.41,1.1,1.1,0,0,0-1-.41,1.7,1.7,0,0,0-1.6.7.52.52,0,0,0-.15-.46.87.87,0,0,0-.63-.2,1.1,1.1,0,0,0-1,.57,8.73,8.73,0,0,0-1.13,3.17,1,1,0,0,0,1,1h.08a1.53,1.53,0,0,0,1.18-.39,1.35,1.35,0,0,0,1,.39,1.61,1.61,0,0,0,1.23-.46,1.36,1.36,0,0,0,1.09.46,1.92,1.92,0,0,0,1.53-.77,1.2,1.2,0,0,0,1.24.75c1.2,0,1.73-.83,2.16-2.12a4.54,4.54,0,0,0,.27-1.2,1.21,1.21,0,0,0-1-1.37,1.29,1.29,0,0,0-.34,0m-8,3.28c.62-.11.79.11.77.33-.07.61-.63.76-1.12.74a.62.62,0,0,1-.69-.55v0a8.47,8.47,0,0,1,1.07-3,.72.72,0,0,1,.68-.39c.31,0,.37.15.37.33a14.2,14.2,0,0,1-1.07,2.55m2-.57c0,.11-.11.35-.17.59a3.1,3.1,0,0,1,.61-.09c.31,0,.48.14.48.38,0,.59-.66.76-1.11.76a.85.85,0,0,1-.94-.75v-.08a6,6,0,0,1,.67-2.27,1.36,1.36,0,0,1,1.53-.9c.31,0,.68.13.68.44s-.35.56-.68.59h-.5a3.58,3.58,0,0,0-.24.48c.64-.09.9,0,.79.41s-.59.52-1.12.44m3.13-1.55a.46.46,0,0,0-.37.21,6.33,6.33,0,0,0-.64,1.73c0,.28.09.35.22.35s.46-.24.55-.61c0,0-.42,0-.31-.37s.33-.44.68-.46c.7,0,.63.48.57.76a1.81,1.81,0,0,1-1.7,1.6.9.9,0,0,1-1-.79v-.18a5,5,0,0,1,.4-1.55c.37-.87.76-1.48,1.77-1.48.59,0,1.07.22,1,.76a.67.67,0,0,1-.64.68c-.11,0-.52,0-.39-.42,0-.13,0-.24-.15-.24m3.75.75a7.06,7.06,0,0,1-.59,1.61,1.41,1.41,0,0,1-1.37.86.82.82,0,0,1-.94-.86,5.11,5.11,0,0,1,.39-1.63c.31-.83.63-1.51,1.66-1.49s.94,1.05.85,1.51m-.85-.52a9.84,9.84,0,0,1-.63,1.85.33.33,0,0,1-.31.22c-.13,0-.17-.09-.2-.2a7.75,7.75,0,0,1,.7-1.94c.09-.13.18-.16.26-.13s.18.11.18.2" transform="translate(0 0)" style="fill: #171714"/>
</g>
</g>
</g>
</g>
<g id="EV3_logo" data-name="EV3 logo">
<path id="ev3_3" data-name="ev3 3" d="M36,155a.18.18,0,0,1,.2.16h0V156a.16.16,0,0,1-.17.14H34a.36.36,0,0,0-.38.33v1.14a.34.34,0,0,0,.38.3h2c.19,0,.17.14.17.14v.82c0,.08-.06.14-.17.14H32.57a.2.2,0,0,1-.2-.19h0v-.05a.38.38,0,0,0-.38-.38H30.87a.38.38,0,0,0-.38.38v1.64a.37.37,0,0,0,.38.33h6.75a.36.36,0,0,0,.39-.3v-6.89a.36.36,0,0,0-.39-.3H30.9a.34.34,0,0,0-.38.3v1.67a.38.38,0,0,0,.35.38H32a.36.36,0,0,0,.35-.36v-.05a.17.17,0,0,1,.16-.19H36Z" transform="translate(0 0)" style="fill: #f1f1f1"/>
<path id="ev3_v" data-name="ev3 v" d="M29.29,153.2H27.7c-.24,0-.33.17-.44.38L25.32,158a.27.27,0,0,1-.19.11.23.23,0,0,1-.19-.14L23,153.58c-.11-.21-.22-.38-.46-.38H20.92c-.35,0-.41.19-.3.41l3.2,6.73c.17.32.28.38.58.38h1.42a.55.55,0,0,0,.57-.38l3.22-6.73c.11-.22,0-.41-.32-.41" transform="translate(0 0)" style="fill: #f1f1f1"/>
<path id="ev3_e" data-name="ev3 e" d="M19.39,156.1a.34.34,0,0,1,.38.3v1.17a.33.33,0,0,1-.38.3H14.34c-.11,0-.2.06-.2.14h0v.82h0c0,.08.09.14.2.14h5.05a.33.33,0,0,1,.38.3v1.15a.33.33,0,0,1-.35.3H12.67a.35.35,0,0,1-.38-.3v-6.89a.34.34,0,0,1,.38-.3h6.75a.31.31,0,0,1,.35.28v1.17a.31.31,0,0,1-.33.3H14.36c-.11,0-.22,0-.22.13V156c0,.08.09.14.19.14Z" transform="translate(0 0)" style="fill: #f1f1f1"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,108 @@
namespace pxsim.visuals {
export const EV3_SVG = `<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 110.73 170.04">
<defs>
<linearGradient id="linear-gradient" x1="-374.89" y1="432.9" x2="-374.89" y2="432.82" gradientTransform="matrix(110.73, 0, 0, -106.94, 41567.45, 46425.3)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f1f1f1"/>
<stop offset="1" stop-color="#7a7a7a"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="-376" y1="450.74" x2="-376" y2="450.72" gradientTransform="matrix(100.11, 0, 0, -79.18, 37697.19, 35762.28)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="gray"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="-376.21" y1="614.94" x2="-376.21" y2="614.75" gradientTransform="matrix(98.29, 0, 0, -23.36, 37033.43, 14529.9)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
<linearGradient id="linear-gradient-black" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#6a6a6a"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-green" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#8CE300"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-red" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#D02E26"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-orange" x1="-382.07" y1="493.36" x2="-382.07" y2="494.25" gradientTransform="matrix(65.53, 0, 0, -48.84, 25091.11, 24228.69)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a6a6a"/>
<stop offset="0.52" stop-color="#F8D039"/>
<stop offset="1" stop-color="#6a6a6a"/>
</linearGradient>
<linearGradient id="linear-gradient-5" x1="-743.87" y1="1256.85" x2="-743.87" y2="1257.21" gradientTransform="matrix(3.03, 0, 0, -6.22, 2312.41, 7891.56)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#757575"/>
<stop offset="1" stop-color="#393939"/>
</linearGradient>
<clipPath id="clip-path" transform="translate(0 0)">
<rect x="86.48" y="149.58" width="12.38" height="12.38" style="fill: none"/>
</clipPath>
</defs>
<title>EV3</title>
<g id="EV3">
<g id="brick">
<path id="ev3_body_2" data-name="ev3 body 2" d="M2,31.7H108.76a2,2,0,0,1,2,2h0v103a2,2,0,0,1-2,2H2a2,2,0,0,1-2-2H0v-103a2,2,0,0,1,2-2Z" transform="translate(0 0)" style="fill: url(#linear-gradient)"/>
<path id="ev3_body_1" data-name="ev3 body 1" d="M8.19,127.57h94.35a2,2,0,0,1,2,2h0v38.53a2,2,0,0,1-2,2H8.19a2,2,0,0,1-2-2h0V129.54a2,2,0,0,1,2-2Z" transform="translate(0 0)" style="fill: #f1f1f1"/>
<path id="ev3_screen_grey" data-name="ev3 screen grey" d="M7.28,0h96.17a2,2,0,0,1,2,2h0V77.21a2,2,0,0,1-2,2H7.28a2,2,0,0,1-2-2h0V2a2,2,0,0,1,2-2Z" transform="translate(0 0)" style="fill: url(#linear-gradient-2)"/>
<path id="ev3_screenborder" data-name="ev3 screenborder" d="M18.2,10.47H92.68a3.79,3.79,0,0,1,3.79,3.79V56.12a3.8,3.8,0,0,1-3.79,3.8H18.2a3.8,3.8,0,0,1-3.79-3.8V14.26A3.79,3.79,0,0,1,18.2,10.47Z" transform="translate(0 0)" style="fill: #393939"/>
<g id="ev3_screen">
<path id="ev3_screen_path" data-name="ev3 screen" d="M24,12.44H87.22A2.73,2.73,0,0,1,90,15.17h0V55.52a2.73,2.73,0,0,1-2.73,2.73H24a2.73,2.73,0,0,1-2.73-2.73V15.17A2.73,2.73,0,0,1,24,12.44Z" transform="translate(0 0)" style="fill: #97b5a6"/>
</g>
<path id="ev3_grey_buttom" data-name="ev3 grey buttom" d="M6.22,146.68h98.29v21.39a2,2,0,0,1-2,2H8.19a2,2,0,0,1-2-2h0Z" transform="translate(0 0)" style="fill: url(#linear-gradient-3)"/>
</g>
<g id="buttons">
<path id="btn_grey" data-name="btn grey" d="M48.69,133.94c-5.58-5.39-14.26-14.26-14.26-14.26v-1.82H31a8.27,8.27,0,1,1,0-16.53h3.41V99.5L48.54,85.09H62.65C69,91.61,76.75,99.2,76.75,99.2v2.13H80a8.27,8.27,0,1,1,0,16.53H76.75v1.82c-7.51,7.73-14,14.26-14,14.26Z" transform="translate(0 0)" style="fill: #6a6a6a"/>
<path id="btn_color" data-name="btn color" d="M48.69,133.94c-5.58-5.39-14.26-14.26-14.26-14.26v-1.82H31a8.27,8.27,0,1,1,0-16.53h3.41V99.5L48.54,85.09H62.65C69,91.61,76.75,99.2,76.75,99.2v2.13H80a8.27,8.27,0,1,1,0,16.53H76.75v1.82c-7.51,7.73-14,14.26-14,14.26Z" transform="translate(0 0)" style="fill: url(#linear-gradient-black)"/>
<path id="btn_left" data-name="btn left" d="M30.87,103.3H41.26v12.28H30.87a6.14,6.14,0,0,1-6.14-6.14h0a6.14,6.14,0,0,1,6.14-6.14Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<path id="btn_right" data-name="btn right" d="M80,115.58H69.62V103.3H80a6.14,6.14,0,0,1,6.15,6.14h0A6.14,6.14,0,0,1,80,115.58Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<path id="btn_enter" data-name="btn enter" d="M49.45,103.3h12a.46.46,0,0,1,.46.45v11.38a.46.46,0,0,1-.46.45h-12a.46.46,0,0,1-.46-.45V103.75a.46.46,0,0,1,.46-.45Z" transform="translate(0 0)" style="fill: #393939"/>
<path id="btn_up" data-name="btn up" d="M49.15,87.37l12.74-.15,9.55,9.86L67.5,101v7.13H63.86v-4.1a3,3,0,0,0-3-3c-3,0-11.08,0-11.08,0a2.47,2.47,0,0,0-2.58,2.35v4.77H43.53V101l-3.94-4Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<path id="btn_down" data-name="btn down" d="M61.83,131.54l-12.66.1-9.58-9.85,4-3.91v-7.17h3.6v4.11a3.06,3.06,0,0,0,3,3.06c3,.05,11,0,11,0a2.6,2.6,0,0,0,2.65-2.55v-4.62h3.67v7.17l3.91,3.91Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
<rect id="btn_part_4" data-name="btn part 4" x="54" y="59.76" width="3.03" height="13.8" style="fill: #393939"/>
<rect id="btn_part_3" data-name="btn part 3" x="54" y="72.96" width="3.03" height="6.07" style="fill: #9a9a9a"/>
<rect id="btn_part_2" data-name="btn part 2" x="54" y="72.96" width="3.03" height="6.22" style="fill: url(#linear-gradient-5)"/>
<rect id="btn_part_1" data-name="btn part 1" x="54" y="79.18" width="3.03" height="5.92" style="fill: gray"/>
<path id="btn_back" data-name="btn back" d="M13.2,79.18H36.86v5.71c-2.62,2.64-6,6-6,6H15.17a2,2,0,0,1-2-2Z" transform="translate(0 0)" style="fill: #a8aaa8"/>
</g>
<g id="LEGO_logo" data-name="LEGO logo">
<rect id="logo_white_bg" data-name="logo white bg" x="86.56" y="149.66" width="12.21" height="12.21" style="fill: #fff"/>
<g id="lego">
<g style="clip-path: url(#clip-path)">
<g id="logo_part_5" data-name="logo part 5">
<path id="Path_18" data-name="Path 18" d="M86.56,161.87H98.77V149.66H86.56ZM98,154.73a4.76,4.76,0,0,1-.24,1.18c-.43,1.27-.91,2.07-2.07,2.07a1.12,1.12,0,0,1-1.16-.7l0-.15-.09.11a1.86,1.86,0,0,1-1.46.72,1.29,1.29,0,0,1-1-.43l-.07-.09-.06.06a1.6,1.6,0,0,1-1.16.42,1.32,1.32,0,0,1-1-.37l-.09,0-.07.07a1.55,1.55,0,0,1-1.11.37.87.87,0,0,1-.95-.79.37.37,0,0,1,0-.11,8.15,8.15,0,0,1,1.09-3.1,1,1,0,0,1,.92-.52.78.78,0,0,1,.57.17c.11.11.11.2.13.42l0,.28.16-.24a1.6,1.6,0,0,1,1.52-.65,1,1,0,0,1,.9.37l0,.09.06-.07a1.8,1.8,0,0,1,1.2-.39,1.53,1.53,0,0,1,1.12.37.69.69,0,0,1,.13.2l.07.11.08-.11a1.59,1.59,0,0,1,1.31-.57,1.27,1.27,0,0,1,1,.35,1.37,1.37,0,0,1,.26,1" transform="translate(0 0)" style="fill: #ffed00"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_4" data-name="logo part 4">
<path id="Path_19" data-name="Path 19" d="M86.56,161.87H98.77V149.66H86.56Zm11.75-6.66a6.73,6.73,0,0,1-.52,1.59,2.28,2.28,0,0,1-2.1,1.55,1.67,1.67,0,0,1-1.36-.54,2.18,2.18,0,0,1-1.48.54,1.73,1.73,0,0,1-1.09-.35,2.12,2.12,0,0,1-1.22.35,1.8,1.8,0,0,1-1-.3,2.19,2.19,0,0,1-1.18.3A1.28,1.28,0,0,1,87,157.14v0a8.26,8.26,0,0,1,1.18-3.34A1.37,1.37,0,0,1,89.4,153c.59,0,.79.17.9.37a2.24,2.24,0,0,1,1.46-.4,1.51,1.51,0,0,1,1,.33A2.4,2.4,0,0,1,94,153a1.74,1.74,0,0,1,1.36.45,2,2,0,0,1,1.33-.43,1.53,1.53,0,0,1,1.37.61,1.79,1.79,0,0,1,.29,1.55" transform="translate(0 0)" style="fill: #d42715"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_3" data-name="logo part 3">
<path id="Path_20" data-name="Path 20" d="M86.48,162H98.86V149.58H86.48Zm12.2-.18h-12v-12h12Z" transform="translate(0 0)" style="fill: #171714"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_2" data-name="logo part 2">
<path id="Path_21" data-name="Path 21" d="M98.25,153.29v-.09h.06s.07,0,.07.05,0,0-.07,0Zm.2.17,0-.06c0-.05,0-.07-.06-.07h0a.09.09,0,0,0,.08-.09.07.07,0,0,0-.07-.08h-.17v.3h.07v-.13h0s0,0,0,0,0,.05,0,.07l0,0Zm-.16-.39a.24.24,0,0,1,0,.48.24.24,0,0,1-.24-.24.26.26,0,0,1,.24-.24m0-.07a.31.31,0,0,0,0,.62.31.31,0,0,0,.31-.31h0a.32.32,0,0,0-.31-.31" transform="translate(0 0)" style="fill: #171714"/>
</g>
</g>
<g style="clip-path: url(#clip-path)">
<g id="logo_part_1" data-name="logo part 1">
<path id="Path_22" data-name="Path 22" d="M96.7,153.33a1.73,1.73,0,0,0-1.38.61.66.66,0,0,0-.15-.22,1.47,1.47,0,0,0-1.16-.41,1.87,1.87,0,0,0-1.25.41,1.1,1.1,0,0,0-1-.41,1.7,1.7,0,0,0-1.6.7.52.52,0,0,0-.15-.46.87.87,0,0,0-.63-.2,1.1,1.1,0,0,0-1,.57,8.73,8.73,0,0,0-1.13,3.17,1,1,0,0,0,1,1h.08a1.53,1.53,0,0,0,1.18-.39,1.35,1.35,0,0,0,1,.39,1.61,1.61,0,0,0,1.23-.46,1.36,1.36,0,0,0,1.09.46,1.92,1.92,0,0,0,1.53-.77,1.2,1.2,0,0,0,1.24.75c1.2,0,1.73-.83,2.16-2.12a4.54,4.54,0,0,0,.27-1.2,1.21,1.21,0,0,0-1-1.37,1.29,1.29,0,0,0-.34,0m-8,3.28c.62-.11.79.11.77.33-.07.61-.63.76-1.12.74a.62.62,0,0,1-.69-.55v0a8.47,8.47,0,0,1,1.07-3,.72.72,0,0,1,.68-.39c.31,0,.37.15.37.33a14.2,14.2,0,0,1-1.07,2.55m2-.57c0,.11-.11.35-.17.59a3.1,3.1,0,0,1,.61-.09c.31,0,.48.14.48.38,0,.59-.66.76-1.11.76a.85.85,0,0,1-.94-.75v-.08a6,6,0,0,1,.67-2.27,1.36,1.36,0,0,1,1.53-.9c.31,0,.68.13.68.44s-.35.56-.68.59h-.5a3.58,3.58,0,0,0-.24.48c.64-.09.9,0,.79.41s-.59.52-1.12.44m3.13-1.55a.46.46,0,0,0-.37.21,6.33,6.33,0,0,0-.64,1.73c0,.28.09.35.22.35s.46-.24.55-.61c0,0-.42,0-.31-.37s.33-.44.68-.46c.7,0,.63.48.57.76a1.81,1.81,0,0,1-1.7,1.6.9.9,0,0,1-1-.79v-.18a5,5,0,0,1,.4-1.55c.37-.87.76-1.48,1.77-1.48.59,0,1.07.22,1,.76a.67.67,0,0,1-.64.68c-.11,0-.52,0-.39-.42,0-.13,0-.24-.15-.24m3.75.75a7.06,7.06,0,0,1-.59,1.61,1.41,1.41,0,0,1-1.37.86.82.82,0,0,1-.94-.86,5.11,5.11,0,0,1,.39-1.63c.31-.83.63-1.51,1.66-1.49s.94,1.05.85,1.51m-.85-.52a9.84,9.84,0,0,1-.63,1.85.33.33,0,0,1-.31.22c-.13,0-.17-.09-.2-.2a7.75,7.75,0,0,1,.7-1.94c.09-.13.18-.16.26-.13s.18.11.18.2" transform="translate(0 0)" style="fill: #171714"/>
</g>
</g>
</g>
</g>
<g id="EV3_logo" data-name="EV3 logo">
<path id="ev3_3" data-name="ev3 3" d="M36,155a.18.18,0,0,1,.2.16h0V156a.16.16,0,0,1-.17.14H34a.36.36,0,0,0-.38.33v1.14a.34.34,0,0,0,.38.3h2c.19,0,.17.14.17.14v.82c0,.08-.06.14-.17.14H32.57a.2.2,0,0,1-.2-.19h0v-.05a.38.38,0,0,0-.38-.38H30.87a.38.38,0,0,0-.38.38v1.64a.37.37,0,0,0,.38.33h6.75a.36.36,0,0,0,.39-.3v-6.89a.36.36,0,0,0-.39-.3H30.9a.34.34,0,0,0-.38.3v1.67a.38.38,0,0,0,.35.38H32a.36.36,0,0,0,.35-.36v-.05a.17.17,0,0,1,.16-.19H36Z" transform="translate(0 0)" style="fill: #f1f1f1"/>
<path id="ev3_v" data-name="ev3 v" d="M29.29,153.2H27.7c-.24,0-.33.17-.44.38L25.32,158a.27.27,0,0,1-.19.11.23.23,0,0,1-.19-.14L23,153.58c-.11-.21-.22-.38-.46-.38H20.92c-.35,0-.41.19-.3.41l3.2,6.73c.17.32.28.38.58.38h1.42a.55.55,0,0,0,.57-.38l3.22-6.73c.11-.22,0-.41-.32-.41" transform="translate(0 0)" style="fill: #f1f1f1"/>
<path id="ev3_e" data-name="ev3 e" d="M19.39,156.1a.34.34,0,0,1,.38.3v1.17a.33.33,0,0,1-.38.3H14.34c-.11,0-.2.06-.2.14h0v.82h0c0,.08.09.14.2.14h5.05a.33.33,0,0,1,.38.3v1.15a.33.33,0,0,1-.35.3H12.67a.35.35,0,0,1-.38-.3v-6.89a.34.34,0,0,1,.38-.3h6.75a.31.31,0,0,1,.35.28v1.17a.31.31,0,0,1-.33.3H14.36c-.11,0-.22,0-.22.13V156c0,.08.09.14.19.14Z" transform="translate(0 0)" style="fill: #f1f1f1"/>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,92 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82 156">
<defs>
<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="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="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="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 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>
<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>
<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>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,94 @@
namespace pxsim {
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="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="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="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="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 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>
<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>
<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>
</g>
</g>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48">
<defs>
<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 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>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,35 @@
namespace pxsim.visuals {
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="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 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>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,13 @@
namespace pxsim.visuals {
export const PORT_SVG = `<svg id="svg6348" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 49.96 58.93">
<title>port</title>
<g id="icn_port" data-name="icn port">
<path id="port_2" data-name="port 2" d="M4.48,0h50V58.93h-50Z" transform="translate(-4.48)" style="fill: #eaeaea"/>
<path id="port_1" data-name="port 1" d="M9.74,46.49V18.66H26.85V11.75h4.72V2.59H44.49v8.82h5.06V46.49h-8v7.43H38.9V46.41h-2v7.5H34.71v-7.5H32.55v7.5h-2.1v-7.5H28.6v7.5H26.5v-7.5H24.68v7.5H22.22v-7.5H20.54v7.5H18V46.46Z" transform="translate(-4.48)" style="fill: #a8aaa8"/>
<g id="text10060" style="isolation: isolate">
<text id="port_text" transform="translate(22.21 40.2)" style="isolation: isolate;font-size: 16px;fill: white;font-family: ArialMT, Arial">B</text>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,61 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 37 37">
<defs>
<clipPath id="clip-path" transform="translate(-23.5 -19.95)">
<path d="M27.94,55.37c-1.06,0-2-.6-2-1.36v-4.4H25a1,1,0,0,1-1-1V29.37a1,1,0,0,1,1-1h1v-5c0-.77.91-1.37,2-1.37H38.71v1.37h6.37V22H56.16c1.07,0,2,.6,2,1.37v5h1a1,1,0,0,1,1,1V48.61a1,1,0,0,1-1,1h-1V54c0,.76-.9,1.37-2,1.37Z" style="fill: none"/>
</clipPath>
<linearGradient id="linear-gradient" x1="-419.47" y1="499.03" x2="-419.47" y2="498.9" gradientTransform="matrix(32.16, 0, 0, -33.37, 13531, 16705.16)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="-523.02" y1="1202.1" x2="-523.91" y2="1202.1" gradientTransform="matrix(9.56, 0, 0, -6.22, 5012.11, 7495.73)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#d42715"/>
<stop offset="1" stop-color="#a20800"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="-937.08" y1="111.13" x2="-937.97" y2="111.13" gradientTransform="matrix(0, 9.56, 6.22, 0, -672.58, 8970)" xlink:href="#linear-gradient-2"/>
<linearGradient id="linear-gradient-4" x1="-227.08" y1="-525.1" x2="-227.97" y2="-525.1" gradientTransform="matrix(-9.56, 0, 0, 6.22, -2147, 3285.47)" xlink:href="#linear-gradient-2"/>
<linearGradient id="linear-gradient-5" x1="186.98" y1="565.87" x2="186.08" y2="565.87" gradientTransform="matrix(0, -9.56, -6.22, 0, 3537.68, 1810.89)" xlink:href="#linear-gradient-2"/>
<linearGradient id="linear-gradient-6" x1="-523.02" y1="1202.1" x2="-523.91" y2="1202.1" gradientTransform="matrix(9.56, 0, 0, -6.22, 5012.11, 7495.73)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#d42715"/>
<stop offset="1" stop-color="#000"/>
</linearGradient>
<linearGradient id="linear-gradient-7" x1="-937.08" y1="111.13" x2="-937.97" y2="111.13" gradientTransform="matrix(0, 9.56, 6.22, 0, -672.58, 8970)" xlink:href="#linear-gradient-6"/>
<linearGradient id="linear-gradient-8" x1="-227.08" y1="-525.1" x2="-227.97" y2="-525.1" gradientTransform="matrix(-9.56, 0, 0, 6.22, -2147, 3285.47)" xlink:href="#linear-gradient-6"/>
<linearGradient id="linear-gradient-9" x1="186.98" y1="565.87" x2="186.08" y2="565.87" gradientTransform="matrix(0, -9.56, -6.22, 0, 3537.68, 1810.89)" xlink:href="#linear-gradient-6"/>
</defs>
<title>Touch sensor</title>
<g style="isolation: isolate">
<g id="_457f40bb-bec4-4a4a-9249-bb4ee7f4f5d6" data-name="457f40bb-bec4-4a4a-9249-bb4ee7f4f5d6">
<g id="Touch_sensor" data-name="Touch sensor">
<g id="touch_box" data-name="touch box">
<g style="clip-path: url(#clip-path)">
<g id="touch_box_grey" data-name="touch box grey">
<g id="touch_box_total" data-name="touch box total">
<path id="touch_box_1" data-name="touch box 1" d="M24.76,28.37H59.34a.76.76,0,0,1,.76.76V48.85a.76.76,0,0,1-.76.76H24.76a.76.76,0,0,1-.76-.76V29.13A.76.76,0,0,1,24.76,28.37Z" transform="translate(-23.5 -19.95)" style="fill: #a8aaa8"/>
<g>
<image width="37" height="37" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/UlEQVRYR+2YvY3DMAxG3xekVGZw+gyQOW6Sm+VmuAEyTfr4VrB6ppBkGEb+6ATJFXyAIAigyEdbFWVm/EfW04MkXQu8hV3o7tlcajklJWALpOvXLpKBvu6NBHQsy3Uys7yGsbst8A3sAE+3PXCoe6MDvur+KAYcgR9Jx+mvTBSpPT6xXV3PfrH2HBLM3hhFqK1H2VDE5nhyNMY7c7GlLJG4yepewKcIMS8h5iXEvISYlxDzEmJeQsxLiHkJMS8h5iXEvISYlxDzMhf79HhxrD8Vy8AfMNSAd6+h1s9Qpz1mZpJ64JdCx/tp9Xszs3HUCSBpg3/g9ioyRWoAOAOiRFr4OQdRFAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="touch_box_2-2" data-name="touch box 2-2" d="M27.94,22H38.71v1.37h6.37V22H56.15c1.07,0,2,.6,2,1.37V54c0,.76-.9,1.36-2,1.36H27.94c-1.07,0-2-.6-2-1.36V23.37C26,22.6,26.87,22,27.94,22Z" transform="translate(-23.5 -19.95)" style="fill: url(#linear-gradient)"/>
</g>
<rect id="touch_boxside_1" data-name="touch boxside 1" x="35.08" y="11.45" width="0.76" height="6.07"/>
<rect id="touch_boxside_2" data-name="touch boxside 2" x="1.41" y="11.45" width="0.76" height="6.07"/>
<rect id="touch_boxside_3" data-name="touch boxside 3" x="35.08" y="20.1" width="0.76" height="6.07"/>
<rect id="touch_boxside_4" data-name="touch boxside 4" x="1.41" y="20.1" width="0.76" height="6.07"/>
</g>
</g>
</g>
</g>
<g id="touch_btn" data-name="touch btn">
<rect id="touch_gradient4" data-name="touch gradient4" x="5.05" y="16.76" width="9.56" height="6.22" style="fill: url(#linear-gradient-2)"/>
<rect id="touch_gradient3" data-name="touch gradient3" x="15.44" y="6.22" width="6.22" height="9.56" style="fill: url(#linear-gradient-3)"/>
<rect id="touch_gradient2" data-name="touch gradient2" x="22.49" y="16.76" width="9.56" height="6.22" style="fill: url(#linear-gradient-4)"/>
<rect id="touch_gradient1" data-name="touch gradient1" x="15.44" y="23.67" width="6.22" height="9.56" style="fill: url(#linear-gradient-5)"/>
<g id="touch_red" data-name="touch red">
<circle cx="18.55" cy="19.8" r="6.07" style="fill: #d42715"/>
<circle cx="18.55" cy="19.8" r="5.94" style="fill: none;stroke: #a20800;stroke-width: 0.25px"/>
</g>
<path id="touch_hole" data-name="touch hole" d="M40.52,37.33s-.05.91-.69.92H38.52a2.13,2.13,0,0,0-.41,1.52c0,1.14.32,1.44.32,1.44h1.46a.58.58,0,0,1,.63.53v1.6a3.46,3.46,0,0,0,1.59.26,5.9,5.9,0,0,0,1.5-.26v-1.5a.67.67,0,0,1,.66-.63,9.38,9.38,0,0,0,1.3-.12s.36-.34.39-1.47-.31-1.48-.31-1.48H44.12a.57.57,0,0,1-.67-.45.86.86,0,0,1,0-.16,9,9,0,0,1,0-1.47,4.31,4.31,0,0,0-2.92,0C40.5,36.68,40.52,37.33,40.52,37.33Z" transform="translate(-23.5 -19.95)"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,64 @@
namespace pxsim.visuals {
export const TOUCH_SENSOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 37 37">
<defs>
<clipPath id="clip-path" transform="translate(-23.5 -19.95)">
<path d="M27.94,55.37c-1.06,0-2-.6-2-1.36v-4.4H25a1,1,0,0,1-1-1V29.37a1,1,0,0,1,1-1h1v-5c0-.77.91-1.37,2-1.37H38.71v1.37h6.37V22H56.16c1.07,0,2,.6,2,1.37v5h1a1,1,0,0,1,1,1V48.61a1,1,0,0,1-1,1h-1V54c0,.76-.9,1.37-2,1.37Z" style="fill: none"/>
</clipPath>
<linearGradient id="linear-gradient" x1="-419.47" y1="499.03" x2="-419.47" y2="498.9" gradientTransform="matrix(32.16, 0, 0, -33.37, 13531, 16705.16)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#a8aaa8"/>
<stop offset="1" stop-color="#535453"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="-523.02" y1="1202.1" x2="-523.91" y2="1202.1" gradientTransform="matrix(9.56, 0, 0, -6.22, 5012.11, 7495.73)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#d42715"/>
<stop offset="1" stop-color="#a20800"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="-937.08" y1="111.13" x2="-937.97" y2="111.13" gradientTransform="matrix(0, 9.56, 6.22, 0, -672.58, 8970)" xlink:href="#linear-gradient-2"/>
<linearGradient id="linear-gradient-4" x1="-227.08" y1="-525.1" x2="-227.97" y2="-525.1" gradientTransform="matrix(-9.56, 0, 0, 6.22, -2147, 3285.47)" xlink:href="#linear-gradient-2"/>
<linearGradient id="linear-gradient-5" x1="186.98" y1="565.87" x2="186.08" y2="565.87" gradientTransform="matrix(0, -9.56, -6.22, 0, 3537.68, 1810.89)" xlink:href="#linear-gradient-2"/>
<linearGradient id="linear-gradient-6" x1="-523.02" y1="1202.1" x2="-523.91" y2="1202.1" gradientTransform="matrix(9.56, 0, 0, -6.22, 5012.11, 7495.73)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#d42715"/>
<stop offset="1" stop-color="#000"/>
</linearGradient>
<linearGradient id="linear-gradient-7" x1="-937.08" y1="111.13" x2="-937.97" y2="111.13" gradientTransform="matrix(0, 9.56, 6.22, 0, -672.58, 8970)" xlink:href="#linear-gradient-6"/>
<linearGradient id="linear-gradient-8" x1="-227.08" y1="-525.1" x2="-227.97" y2="-525.1" gradientTransform="matrix(-9.56, 0, 0, 6.22, -2147, 3285.47)" xlink:href="#linear-gradient-6"/>
<linearGradient id="linear-gradient-9" x1="186.98" y1="565.87" x2="186.08" y2="565.87" gradientTransform="matrix(0, -9.56, -6.22, 0, 3537.68, 1810.89)" xlink:href="#linear-gradient-6"/>
</defs>
<title>Touch sensor</title>
<g style="isolation: isolate">
<g id="_457f40bb-bec4-4a4a-9249-bb4ee7f4f5d6" data-name="457f40bb-bec4-4a4a-9249-bb4ee7f4f5d6">
<g id="Touch_sensor" data-name="Touch sensor">
<g id="touch_box" data-name="touch box">
<g style="clip-path: url(#clip-path)">
<g id="touch_box_grey" data-name="touch box grey">
<g id="touch_box_total" data-name="touch box total">
<path id="touch_box_1" data-name="touch box 1" d="M24.76,28.37H59.34a.76.76,0,0,1,.76.76V48.85a.76.76,0,0,1-.76.76H24.76a.76.76,0,0,1-.76-.76V29.13A.76.76,0,0,1,24.76,28.37Z" transform="translate(-23.5 -19.95)" style="fill: #a8aaa8"/>
<g>
<image width="37" height="37" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/UlEQVRYR+2YvY3DMAxG3xekVGZw+gyQOW6Sm+VmuAEyTfr4VrB6ppBkGEb+6ATJFXyAIAigyEdbFWVm/EfW04MkXQu8hV3o7tlcajklJWALpOvXLpKBvu6NBHQsy3Uys7yGsbst8A3sAE+3PXCoe6MDvur+KAYcgR9Jx+mvTBSpPT6xXV3PfrH2HBLM3hhFqK1H2VDE5nhyNMY7c7GlLJG4yepewKcIMS8h5iXEvISYlxDzEmJeQsxLiHkJMS8h5iXEvISYlxDzMhf79HhxrD8Vy8AfMNSAd6+h1s9Qpz1mZpJ64JdCx/tp9Xszs3HUCSBpg3/g9ioyRWoAOAOiRFr4OQdRFAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="touch_box_2-2" data-name="touch box 2-2" d="M27.94,22H38.71v1.37h6.37V22H56.15c1.07,0,2,.6,2,1.37V54c0,.76-.9,1.36-2,1.36H27.94c-1.07,0-2-.6-2-1.36V23.37C26,22.6,26.87,22,27.94,22Z" transform="translate(-23.5 -19.95)" style="fill: url(#linear-gradient)"/>
</g>
<rect id="touch_boxside_1" data-name="touch boxside 1" x="35.08" y="11.45" width="0.76" height="6.07"/>
<rect id="touch_boxside_2" data-name="touch boxside 2" x="1.41" y="11.45" width="0.76" height="6.07"/>
<rect id="touch_boxside_3" data-name="touch boxside 3" x="35.08" y="20.1" width="0.76" height="6.07"/>
<rect id="touch_boxside_4" data-name="touch boxside 4" x="1.41" y="20.1" width="0.76" height="6.07"/>
</g>
</g>
</g>
</g>
<g id="touch_btn" data-name="touch btn">
<rect id="touch_gradient4" data-name="touch gradient4" x="5.05" y="16.76" width="9.56" height="6.22" style="fill: url(#linear-gradient-2)"/>
<rect id="touch_gradient3" data-name="touch gradient3" x="15.44" y="6.22" width="6.22" height="9.56" style="fill: url(#linear-gradient-3)"/>
<rect id="touch_gradient2" data-name="touch gradient2" x="22.49" y="16.76" width="9.56" height="6.22" style="fill: url(#linear-gradient-4)"/>
<rect id="touch_gradient1" data-name="touch gradient1" x="15.44" y="23.67" width="6.22" height="9.56" style="fill: url(#linear-gradient-5)"/>
<g id="touch_red" data-name="touch red">
<circle cx="18.55" cy="19.8" r="6.07" style="fill: #d42715"/>
<circle cx="18.55" cy="19.8" r="5.94" style="fill: none;stroke: #a20800;stroke-width: 0.25px"/>
</g>
<path id="touch_hole" data-name="touch hole" d="M40.52,37.33s-.05.91-.69.92H38.52a2.13,2.13,0,0,0-.41,1.52c0,1.14.32,1.44.32,1.44h1.46a.58.58,0,0,1,.63.53v1.6a3.46,3.46,0,0,0,1.59.26,5.9,5.9,0,0,0,1.5-.26v-1.5a.67.67,0,0,1,.66-.63,9.38,9.38,0,0,0,1.3-.12s.36-.34.39-1.47-.31-1.48-.31-1.48H44.12a.57.57,0,0,1-.67-.45.86.86,0,0,1,0-.16,9,9,0,0,1,0-1.47,4.31,4.31,0,0,0-2.92,0C40.5,36.68,40.52,37.33,40.52,37.33Z" transform="translate(-23.5 -19.95)"/>
</g>
</g>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 37 62.5">
<defs>
<clipPath id="clip-path" transform="translate(-11.5 1.52)">
<path d="M21.71,61a8.91,8.91,0,0,1-2-.46H15.49a2,2,0,0,1-2-2h0V54h-.76a.76.76,0,0,1-.76-.76V7a.76.76,0,0,1,.76-.76h.76V2a2,2,0,0,1,2-2H28.14V.61h3.42V0H44.31a2,2,0,0,1,2,2h0V6.22h.91A.76.76,0,0,1,48,7h0V53.24a.76.76,0,0,1-.76.76h-.91v4.55a2,2,0,0,1-2,2H40.17c-.18.3-1.91.46-2.23.46Z" style="fill: none"/>
</clipPath>
<linearGradient id="linear-gradient" x1="-438.91" y1="1403.05" x2="-438.91" y2="1402.05" gradientTransform="matrix(32.76, 0, 0, -5.01, 14410.48, 7079.21)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff" stop-opacity="0"/>
<stop offset="1" stop-color="#3e3e3e"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="-468.07" y1="2985.3" x2="-468.07" y2="2984.3" gradientTransform="matrix(20.61, 0, 0, -2.03, 9674.72, 6104.84)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff"/>
<stop offset="1" stop-color="gray"/>
</linearGradient>
</defs>
<title>gyro</title>
<g style="isolation: isolate">
<g id="svg5788">
<g id="gyro">
<g id="gyro_total" data-name="gyro total">
<g style="clip-path: url(#clip-path)">
<g id="gyro_mask_total" data-name="gyro mask total">
<g id="gyro_box" data-name="gyro box">
<path id="gyro_sides" data-name="gyro sides" d="M12.76,6.22H47.19A.76.76,0,0,1,48,7V53.24a.76.76,0,0,1-.76.76H12.76a.76.76,0,0,1-.76-.76V7A.76.76,0,0,1,12.76,6.22Z" transform="translate(-11.5 1.52)" style="fill: #a8aaa8"/>
<rect id="gyro_grey_behind" data-name="gyro grey behind" x="15.52" y="2.13" width="5.76" height="15.77" style="fill: #9a9a9a"/>
<g>
<image width="37" height="51" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAA0CAYAAADmI0o+AAAACXBIWXMAAAsSAAALEgHS3X78AAABDklEQVRYR+2ZwU3DQBBF36DcSBHcU0DKohZqoKZwJmkh5jwcNg62iRHfHkEO/0mWbXn99TyytKudyEzukU1/ERFb4AnYzo6epwPeM7OrytkARETQwp6BHRDz734jgQPwEhFv1OQcrhWjfeEO2KMHwleFSnKGYtCC+kNhOn51zsNPo/4Ti6lYTMViKhZTsZhKpdjS+fEm00l8Kf2Kgst5yVpsRIXYcC33ATxe7ldVrkIMxhWDlVJQJwYFMkMqf/5SLKZiMRWLqVhMxWIqFlOxmIrFVCymYjEVi6lYTMViKvcmdm2eTjdVcvjwjzkDJ1rPciTW0fqFULxz80uOwCtwzMyMvvW8sjNbQUeTOgN8AqpBM7JNqq/cAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="gyro_grey_large-2" data-name="gyro grey large-2" d="M15.49,0H28.15V16l3.42-.26V0H44.31a2,2,0,0,1,2,2h0V45.81a2,2,0,0,1-2,2H15.49a2,2,0,0,1-2-2h0V2a2,2,0,0,1,2-2Z" transform="translate(-11.5 1.52)" style="fill: #a8aaa8"/>
</g>
<g id="gyro_white" data-name="gyro white">
<g id="gyro_white-2" data-name="gyro white-2">
<path id="gyro_white_2" data-name="gyro white 2" d="M14.73,29.58H45.07a1.21,1.21,0,0,1,1.21,1.21h0V58.55a2,2,0,0,1-2,2H15.49a2,2,0,0,1-2-2h0V30.79A1.21,1.21,0,0,1,14.73,29.58Z" transform="translate(-11.5 1.52)" style="fill: #f1f1f1"/>
<path id="gyro_white_1" data-name="gyro white 1" d="M13.52,55.52H46.28v3a2,2,0,0,1-2,2H15.49a2,2,0,0,1-2-2h0Z" transform="translate(-11.5 1.52)" style="opacity: 0.600000023841858;isolation: isolate;fill: url(#linear-gradient)"/>
</g>
<g id="gyro_white_small" data-name="gyro white small">
<path d="M37.94,60.85H21.71a7.84,7.84,0,0,1-1.26-.24,3.14,3.14,0,0,1-1-.38l1.25-1a1.8,1.8,0,0,1,1.05-.36H37.94a1,1,0,0,1,.6.33l.16.15L40,60.49a2.47,2.47,0,0,1-.93.25A11.33,11.33,0,0,1,37.94,60.85Z" transform="translate(-11.5 1.52)" style="fill: url(#linear-gradient-2)"/>
<path d="M37.94,60.73a9,9,0,0,0,1.85-.27L38.62,59.4l-.17-.14c-.18-.17-.34-.31-.51-.31H21.71a1.68,1.68,0,0,0-1,.34l-1.11.93a8.7,8.7,0,0,0,2.08.51H37.94m0,.25H21.71c-.34,0-2.46-.44-2.46-.78l1.33-1.1a1.88,1.88,0,0,1,1.13-.4H37.94c.33,0,.57.29.84.51l1.4,1.26C40.18,60.81,38.27,61,37.94,61Z" transform="translate(-11.5 1.52)" style="fill: #9a9a9a"/>
</g>
</g>
<g id="gyro_red_elements" data-name="gyro red elements">
<circle id="gyro_reddot" data-name="gyro reddot" cx="18.4" cy="18.97" r="3.03" style="fill: #d42715"/>
<path id="gyro_arrow_2" data-name="gyro arrow 2" d="M21.92,23.36s-3.9-5.13-.76-10.77a9.59,9.59,0,0,0,.91.45c.65.37.74-.3.61-.75l-1.21-3.8-3.8,1.07a1.2,1.2,0,0,0-.45.15c-.2.12-.37.4,0,.61l1.06.6s-4.53,6.65,1.06,14.26C20.94,24,21.92,23.36,21.92,23.36Z" transform="translate(-11.5 1.52)" style="fill: #d42715"/>
<path id="gyro_arrow1" data-name="gyro arrow1" d="M37.84,23.36s3.9-5.13.76-10.77a9.59,9.59,0,0,1-.91.45c-.64.37-.74-.3-.6-.75l1.21-3.8,3.79,1.07a1.31,1.31,0,0,1,.46.15c.2.12.36.4,0,.61l-1.07.6S46,17.57,40.42,25.18C38.83,24,37.84,23.36,37.84,23.36Z" transform="translate(-11.5 1.52)" style="fill: #d42715"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,54 @@
namespace pxsim {
export const GYRO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 37 62.5">
<defs>
<clipPath id="clip-path" transform="translate(-11.5 1.52)">
<path d="M21.71,61a8.91,8.91,0,0,1-2-.46H15.49a2,2,0,0,1-2-2h0V54h-.76a.76.76,0,0,1-.76-.76V7a.76.76,0,0,1,.76-.76h.76V2a2,2,0,0,1,2-2H28.14V.61h3.42V0H44.31a2,2,0,0,1,2,2h0V6.22h.91A.76.76,0,0,1,48,7h0V53.24a.76.76,0,0,1-.76.76h-.91v4.55a2,2,0,0,1-2,2H40.17c-.18.3-1.91.46-2.23.46Z" style="fill: none"/>
</clipPath>
<linearGradient id="linear-gradient" x1="-438.91" y1="1403.05" x2="-438.91" y2="1402.05" gradientTransform="matrix(32.76, 0, 0, -5.01, 14410.48, 7079.21)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff" stop-opacity="0"/>
<stop offset="1" stop-color="#3e3e3e"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="-468.07" y1="2985.3" x2="-468.07" y2="2984.3" gradientTransform="matrix(20.61, 0, 0, -2.03, 9674.72, 6104.84)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff"/>
<stop offset="1" stop-color="gray"/>
</linearGradient>
</defs>
<title>gyro</title>
<g style="isolation: isolate">
<g id="svg5788">
<g id="gyro">
<g id="gyro_total" data-name="gyro total">
<g style="clip-path: url(#clip-path)">
<g id="gyro_mask_total" data-name="gyro mask total">
<g id="gyro_box" data-name="gyro box">
<path id="gyro_sides" data-name="gyro sides" d="M12.76,6.22H47.19A.76.76,0,0,1,48,7V53.24a.76.76,0,0,1-.76.76H12.76a.76.76,0,0,1-.76-.76V7A.76.76,0,0,1,12.76,6.22Z" transform="translate(-11.5 1.52)" style="fill: #a8aaa8"/>
<rect id="gyro_grey_behind" data-name="gyro grey behind" x="15.52" y="2.13" width="5.76" height="15.77" style="fill: #9a9a9a"/>
<g>
<image width="37" height="51" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAA0CAYAAADmI0o+AAAACXBIWXMAAAsSAAALEgHS3X78AAABDklEQVRYR+2ZwU3DQBBF36DcSBHcU0DKohZqoKZwJmkh5jwcNg62iRHfHkEO/0mWbXn99TyytKudyEzukU1/ERFb4AnYzo6epwPeM7OrytkARETQwp6BHRDz734jgQPwEhFv1OQcrhWjfeEO2KMHwleFSnKGYtCC+kNhOn51zsNPo/4Ti6lYTMViKhZTsZhKpdjS+fEm00l8Kf2Kgst5yVpsRIXYcC33ATxe7ldVrkIMxhWDlVJQJwYFMkMqf/5SLKZiMRWLqVhMxWIqFlOxmIrFVCymYjEVi6lYTMViKvcmdm2eTjdVcvjwjzkDJ1rPciTW0fqFULxz80uOwCtwzMyMvvW8sjNbQUeTOgN8AqpBM7JNqq/cAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
<path id="gyro_grey_large-2" data-name="gyro grey large-2" d="M15.49,0H28.15V16l3.42-.26V0H44.31a2,2,0,0,1,2,2h0V45.81a2,2,0,0,1-2,2H15.49a2,2,0,0,1-2-2h0V2a2,2,0,0,1,2-2Z" transform="translate(-11.5 1.52)" style="fill: #a8aaa8"/>
</g>
<g id="gyro_white" data-name="gyro white">
<g id="gyro_white-2" data-name="gyro white-2">
<path id="gyro_white_2" data-name="gyro white 2" d="M14.73,29.58H45.07a1.21,1.21,0,0,1,1.21,1.21h0V58.55a2,2,0,0,1-2,2H15.49a2,2,0,0,1-2-2h0V30.79A1.21,1.21,0,0,1,14.73,29.58Z" transform="translate(-11.5 1.52)" style="fill: #f1f1f1"/>
<path id="gyro_white_1" data-name="gyro white 1" d="M13.52,55.52H46.28v3a2,2,0,0,1-2,2H15.49a2,2,0,0,1-2-2h0Z" transform="translate(-11.5 1.52)" style="opacity: 0.600000023841858;isolation: isolate;fill: url(#linear-gradient)"/>
</g>
<g id="gyro_white_small" data-name="gyro white small">
<path d="M37.94,60.85H21.71a7.84,7.84,0,0,1-1.26-.24,3.14,3.14,0,0,1-1-.38l1.25-1a1.8,1.8,0,0,1,1.05-.36H37.94a1,1,0,0,1,.6.33l.16.15L40,60.49a2.47,2.47,0,0,1-.93.25A11.33,11.33,0,0,1,37.94,60.85Z" transform="translate(-11.5 1.52)" style="fill: url(#linear-gradient-2)"/>
<path d="M37.94,60.73a9,9,0,0,0,1.85-.27L38.62,59.4l-.17-.14c-.18-.17-.34-.31-.51-.31H21.71a1.68,1.68,0,0,0-1,.34l-1.11.93a8.7,8.7,0,0,0,2.08.51H37.94m0,.25H21.71c-.34,0-2.46-.44-2.46-.78l1.33-1.1a1.88,1.88,0,0,1,1.13-.4H37.94c.33,0,.57.29.84.51l1.4,1.26C40.18,60.81,38.27,61,37.94,61Z" transform="translate(-11.5 1.52)" style="fill: #9a9a9a"/>
</g>
</g>
<g id="gyro_red_elements" data-name="gyro red elements">
<circle id="gyro_reddot" data-name="gyro reddot" cx="18.4" cy="18.97" r="3.03" style="fill: #d42715"/>
<path id="gyro_arrow_2" data-name="gyro arrow 2" d="M21.92,23.36s-3.9-5.13-.76-10.77a9.59,9.59,0,0,0,.91.45c.65.37.74-.3.61-.75l-1.21-3.8-3.8,1.07a1.2,1.2,0,0,0-.45.15c-.2.12-.37.4,0,.61l1.06.6s-4.53,6.65,1.06,14.26C20.94,24,21.92,23.36,21.92,23.36Z" transform="translate(-11.5 1.52)" style="fill: #d42715"/>
<path id="gyro_arrow1" data-name="gyro arrow1" d="M37.84,23.36s3.9-5.13.76-10.77a9.59,9.59,0,0,1-.91.45c-.64.37-.74-.3-.6-.75l1.21-3.8,3.79,1.07a1.31,1.31,0,0,1,.46.15c.2.12.36.4,0,.61l-1.07.6S46,17.57,40.42,25.18C38.83,24,37.84,23.36,37.84,23.36Z" transform="translate(-11.5 1.52)" style="fill: #d42715"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,10 @@
<svg id="svg6348" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 49.96 58.93">
<title>port</title>
<g id="icn_port" data-name="icn port">
<path id="port_2" data-name="port 2" d="M4.48,0h50V58.93h-50Z" transform="translate(-4.48)" style="fill: #eaeaea"/>
<path id="port_1" data-name="port 1" d="M9.74,46.49V18.66H26.85V11.75h4.72V2.59H44.49v8.82h5.06V46.49h-8v7.43H38.9V46.41h-2v7.5H34.71v-7.5H32.55v7.5h-2.1v-7.5H28.6v7.5H26.5v-7.5H24.68v7.5H22.22v-7.5H20.54v7.5H18V46.46Z" transform="translate(-4.48)" style="fill: #a8aaa8"/>
<g id="text10060" style="isolation: isolate">
<text id="port_text" transform="translate(22.21 40.2)" style="isolation: isolate;font-size: 16px;fill: white;font-family: ArialMT, Arial">B</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 765 B

View File

@ -0,0 +1,77 @@
<svg id="svg5190" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 83.53 35.14">
<defs>
<clipPath id="clip-path" transform="translate(0.04 -22.43)">
<circle cx="17.53" cy="40" r="6.98" style="fill: none"/>
</clipPath>
<clipPath id="clip-path-2" transform="translate(0.04 -22.43)">
<circle cx="65.92" cy="40" r="6.98" style="fill: none"/>
</clipPath>
</defs>
<title>ultra sonic</title>
<g id="ultra_sonic" data-name="ultra sonic">
<rect id="US_main_black2" data-name="US main black2" x="22.57" y="1.49" width="43.38" height="31.25" style="fill: #242424"/>
<rect id="US_main_black1" data-name="US main black1" x="30.16" y="8.47" width="25.33" height="17.59"/>
<g id="US_eye1" data-name="US eye1">
<g id="US_eye1_black" data-name="US eye1 black">
<circle cx="17.57" cy="17.57" r="17.44" style="stroke: #b3b3b3;stroke-miterlimit: 10;stroke-width: 0.25px"/>
<circle cx="17.57" cy="17.57" r="17.32" style="fill: none"/>
</g>
<circle id="US_eye1_red" data-name="US eye1 red" cx="17.57" cy="17.57" r="10.77" style="fill: #ab1919"/>
<circle id="US_eye1_gold_circle" data-name="US eye1 gold circle" cx="17.57" cy="17.57" r="8.04" style="fill: #aa7707"/>
<circle id="US_eye1_wh_in_gold" data-name="US eye1 wh in gold" cx="17.57" cy="17.57" r="6.98" style="fill: #f1f1f1"/>
<g id="US_eye1_net" data-name="US eye1 net">
<g style="clip-path: url(#clip-path)">
<g id="US_eye1_net_mask" data-name="US eye1 net mask">
<g id="US_eye1_net_total" data-name="US eye1 net total">
<rect id="US_eye1_net14" data-name="US eye1 net14" x="10.84" y="33.53" width="0.61" height="22.79" transform="translate(-20.93 -10.84) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net13" data-name="US eye1 net13" x="11.8" y="47.44" width="22.51" height="0.61" transform="translate(-20.75 -4.51) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net12" data-name="US eye1 net12" x="13.33" y="32.09" width="0.61" height="22.79" transform="translate(-19.88 -9.79) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net11" data-name="US eye1 net11" x="10.36" y="44.95" width="22.51" height="0.61" transform="translate(-19.7 -5.56) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net10" data-name="US eye1 net10" x="15.83" y="30.65" width="0.61" height="22.79" transform="translate(-18.82 -8.73) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net9" data-name="US eye1 net9" x="8.92" y="42.45" width="22.51" height="0.61" transform="translate(-18.64 -6.62) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net8" data-name="US eye1 net8" x="18.33" y="29.21" width="0.61" height="22.79" transform="translate(-17.77 -7.68) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net7" data-name="US eye1 net7" x="7.47" y="39.96" width="22.51" height="0.61" transform="translate(-17.59 -7.67) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net6" data-name="US eye1 net6" x="20.82" y="27.77" width="0.61" height="22.79" transform="translate(-16.72 -6.62) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net5" data-name="US eye1 net5" x="6.03" y="37.46" width="22.51" height="0.61" transform="translate(-16.53 -8.73) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net4" data-name="US eye1 net4" x="23.32" y="26.32" width="0.61" height="22.79" transform="translate(-15.66 -5.57) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net3" data-name="US eye1 net3" x="4.59" y="34.96" width="22.51" height="0.61" transform="translate(-15.48 -9.78) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net2" data-name="US eye1 net2" x="25.81" y="24.88" width="0.61" height="22.79" transform="translate(-14.61 -4.51) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net1" data-name="US eye1 net1" x="3.15" y="32.47" width="22.51" height="0.61" transform="translate(-14.42 -10.84) rotate(-30)" style="fill: #aa7707"/>
</g>
</g>
</g>
</g>
</g>
<g id="US_eye2" data-name="US eye2">
<g id="US_eye2_black" data-name="US eye2 black">
<circle cx="65.96" cy="17.57" r="17.44" style="stroke: #b3b3b3;stroke-miterlimit: 10;stroke-width: 0.25px"/>
<circle cx="65.96" cy="17.57" r="17.32" style="fill: none"/>
</g>
<circle id="US_eye2_red" data-name="US eye2 red" cx="65.96" cy="17.57" r="10.77" style="fill: #ab1919"/>
<circle id="US_eye2_gold_circle" data-name="US eye2 gold circle" cx="65.96" cy="17.57" r="8.04" style="fill: #aa7707"/>
<circle id="US_eye2_wh_in_gold" data-name="US eye2 wh in gold" cx="65.96" cy="17.57" r="6.98" style="fill: #f1f1f1"/>
<g id="US_eye2_net" data-name="US eye2 net">
<g style="clip-path: url(#clip-path-2)">
<g id="US_eye2_net_mask" data-name="US eye2 net mask">
<g id="US_eye2_net_total" data-name="US eye2 net total">
<rect id="US_eye2_net14" data-name="US eye2 net14" x="59.23" y="33.53" width="0.61" height="22.79" transform="translate(-14.45 13.35) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net13" data-name="US eye2 net13" x="60.18" y="47.44" width="22.51" height="0.61" transform="translate(-14.27 19.69) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net12" data-name="US eye2 net12" x="61.72" y="32.09" width="0.61" height="22.79" transform="translate(-13.4 14.41) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net11" data-name="US eye2 net11" x="58.74" y="44.95" width="22.51" height="0.61" transform="translate(-13.21 18.63) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net10" data-name="US eye2 net10" x="64.22" y="30.65" width="0.61" height="22.79" transform="translate(-12.34 15.46) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net9" data-name="US eye2 net9" x="57.3" y="42.45" width="22.51" height="0.61" transform="translate(-12.16 17.58) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net8" data-name="US eye2 net8" x="66.71" y="29.21" width="0.61" height="22.79" transform="translate(-11.29 16.52) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net7" data-name="US eye2 net7" x="55.86" y="39.96" width="22.51" height="0.61" transform="translate(-11.1 16.52) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net6" data-name="US eye2 net6" x="69.21" y="27.77" width="0.61" height="22.79" transform="translate(-10.23 17.57) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net5" data-name="US eye2 net5" x="54.42" y="37.46" width="22.51" height="0.61" transform="translate(-10.05 15.47) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net4" data-name="US eye2 net4" x="71.71" y="26.32" width="0.61" height="22.79" transform="translate(-9.18 18.63) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net3" data-name="US eye2 net3" x="52.98" y="34.96" width="22.51" height="0.61" transform="translate(-8.99 14.41) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net2" data-name="US eye2 net2" x="74.2" y="24.88" width="0.61" height="22.79" transform="translate(-8.12 19.68) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net1" data-name="US eye2 net1" x="51.54" y="32.47" width="22.51" height="0.61" transform="translate(-7.94 13.36) rotate(-30)" style="fill: #aa7707"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,79 @@
namespace pxsim {
export const ULTRASONIC_SVG = `<svg id="svg5190" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 83.53 35.14">
<defs>
<clipPath id="clip-path" transform="translate(0.04 -22.43)">
<circle cx="17.53" cy="40" r="6.98" style="fill: none"/>
</clipPath>
<clipPath id="clip-path-2" transform="translate(0.04 -22.43)">
<circle cx="65.92" cy="40" r="6.98" style="fill: none"/>
</clipPath>
</defs>
<title>ultra sonic</title>
<g id="ultra_sonic" data-name="ultra sonic">
<rect id="US_main_black2" data-name="US main black2" x="22.57" y="1.49" width="43.38" height="31.25" style="fill: #242424"/>
<rect id="US_main_black1" data-name="US main black1" x="30.16" y="8.47" width="25.33" height="17.59"/>
<g id="US_eye1" data-name="US eye1">
<g id="US_eye1_black" data-name="US eye1 black">
<circle cx="17.57" cy="17.57" r="17.44" style="stroke: #b3b3b3;stroke-miterlimit: 10;stroke-width: 0.25px"/>
<circle cx="17.57" cy="17.57" r="17.32" style="fill: none"/>
</g>
<circle id="US_eye1_red" data-name="US eye1 red" cx="17.57" cy="17.57" r="10.77" style="fill: #ab1919"/>
<circle id="US_eye1_gold_circle" data-name="US eye1 gold circle" cx="17.57" cy="17.57" r="8.04" style="fill: #aa7707"/>
<circle id="US_eye1_wh_in_gold" data-name="US eye1 wh in gold" cx="17.57" cy="17.57" r="6.98" style="fill: #f1f1f1"/>
<g id="US_eye1_net" data-name="US eye1 net">
<g style="clip-path: url(#clip-path)">
<g id="US_eye1_net_mask" data-name="US eye1 net mask">
<g id="US_eye1_net_total" data-name="US eye1 net total">
<rect id="US_eye1_net14" data-name="US eye1 net14" x="10.84" y="33.53" width="0.61" height="22.79" transform="translate(-20.93 -10.84) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net13" data-name="US eye1 net13" x="11.8" y="47.44" width="22.51" height="0.61" transform="translate(-20.75 -4.51) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net12" data-name="US eye1 net12" x="13.33" y="32.09" width="0.61" height="22.79" transform="translate(-19.88 -9.79) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net11" data-name="US eye1 net11" x="10.36" y="44.95" width="22.51" height="0.61" transform="translate(-19.7 -5.56) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net10" data-name="US eye1 net10" x="15.83" y="30.65" width="0.61" height="22.79" transform="translate(-18.82 -8.73) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net9" data-name="US eye1 net9" x="8.92" y="42.45" width="22.51" height="0.61" transform="translate(-18.64 -6.62) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net8" data-name="US eye1 net8" x="18.33" y="29.21" width="0.61" height="22.79" transform="translate(-17.77 -7.68) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net7" data-name="US eye1 net7" x="7.47" y="39.96" width="22.51" height="0.61" transform="translate(-17.59 -7.67) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net6" data-name="US eye1 net6" x="20.82" y="27.77" width="0.61" height="22.79" transform="translate(-16.72 -6.62) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net5" data-name="US eye1 net5" x="6.03" y="37.46" width="22.51" height="0.61" transform="translate(-16.53 -8.73) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net4" data-name="US eye1 net4" x="23.32" y="26.32" width="0.61" height="22.79" transform="translate(-15.66 -5.57) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net3" data-name="US eye1 net3" x="4.59" y="34.96" width="22.51" height="0.61" transform="translate(-15.48 -9.78) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net2" data-name="US eye1 net2" x="25.81" y="24.88" width="0.61" height="22.79" transform="translate(-14.61 -4.51) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye1_net1" data-name="US eye1 net1" x="3.15" y="32.47" width="22.51" height="0.61" transform="translate(-14.42 -10.84) rotate(-30)" style="fill: #aa7707"/>
</g>
</g>
</g>
</g>
</g>
<g id="US_eye2" data-name="US eye2">
<g id="US_eye2_black" data-name="US eye2 black">
<circle cx="65.96" cy="17.57" r="17.44" style="stroke: #b3b3b3;stroke-miterlimit: 10;stroke-width: 0.25px"/>
<circle cx="65.96" cy="17.57" r="17.32" style="fill: none"/>
</g>
<circle id="US_eye2_red" data-name="US eye2 red" cx="65.96" cy="17.57" r="10.77" style="fill: #ab1919"/>
<circle id="US_eye2_gold_circle" data-name="US eye2 gold circle" cx="65.96" cy="17.57" r="8.04" style="fill: #aa7707"/>
<circle id="US_eye2_wh_in_gold" data-name="US eye2 wh in gold" cx="65.96" cy="17.57" r="6.98" style="fill: #f1f1f1"/>
<g id="US_eye2_net" data-name="US eye2 net">
<g style="clip-path: url(#clip-path-2)">
<g id="US_eye2_net_mask" data-name="US eye2 net mask">
<g id="US_eye2_net_total" data-name="US eye2 net total">
<rect id="US_eye2_net14" data-name="US eye2 net14" x="59.23" y="33.53" width="0.61" height="22.79" transform="translate(-14.45 13.35) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net13" data-name="US eye2 net13" x="60.18" y="47.44" width="22.51" height="0.61" transform="translate(-14.27 19.69) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net12" data-name="US eye2 net12" x="61.72" y="32.09" width="0.61" height="22.79" transform="translate(-13.4 14.41) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net11" data-name="US eye2 net11" x="58.74" y="44.95" width="22.51" height="0.61" transform="translate(-13.21 18.63) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net10" data-name="US eye2 net10" x="64.22" y="30.65" width="0.61" height="22.79" transform="translate(-12.34 15.46) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net9" data-name="US eye2 net9" x="57.3" y="42.45" width="22.51" height="0.61" transform="translate(-12.16 17.58) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net8" data-name="US eye2 net8" x="66.71" y="29.21" width="0.61" height="22.79" transform="translate(-11.29 16.52) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net7" data-name="US eye2 net7" x="55.86" y="39.96" width="22.51" height="0.61" transform="translate(-11.1 16.52) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net6" data-name="US eye2 net6" x="69.21" y="27.77" width="0.61" height="22.79" transform="translate(-10.23 17.57) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net5" data-name="US eye2 net5" x="54.42" y="37.46" width="22.51" height="0.61" transform="translate(-10.05 15.47) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net4" data-name="US eye2 net4" x="71.71" y="26.32" width="0.61" height="22.79" transform="translate(-9.18 18.63) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net3" data-name="US eye2 net3" x="52.98" y="34.96" width="22.51" height="0.61" transform="translate(-8.99 14.41) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net2" data-name="US eye2 net2" x="74.2" y="24.88" width="0.61" height="22.79" transform="translate(-8.12 19.68) rotate(-30)" style="fill: #aa7707"/>
<rect id="US_eye2_net1" data-name="US eye2 net1" x="51.54" y="32.47" width="22.51" height="0.61" transform="translate(-7.94 13.36) rotate(-30)" style="fill: #aa7707"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>`;
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 75 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
namespace pxsim.visuals {
mkBoardView = (opts: BoardViewOptions): BoardView => {
return new visuals.EV3BoardSvg({
return new visuals.EV3View({
runtime: runtime,
theme: visuals.randomTheme(),
disableTilt: false,

View File

@ -0,0 +1,75 @@
/// <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 {
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 {
return CONTROL_WIDTH;
}
getInnerHeight(): number {
return CONTROL_HEIGHT;
}
getPaddingRatio() {
return 0;
}
getWiringRatio() {
return 0.5;
}
public hasClick() {
return false;
}
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() {
}
}
}

View File

@ -0,0 +1,40 @@
namespace pxsim.visuals {
export class CloseIconControl extends ControlView<PortNode> {
private closeGroup: SVGGElement;
getInnerView() {
this.closeGroup = svg.elt("g") as SVGGElement;
this.closeGroup.style.cursor = 'pointer';
const circleCloseWrapper = pxsim.svg.child(this.closeGroup, "g");
pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "16", 'style': "fill: #fff" });
pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "15", 'style': "fill: none;stroke: #a8aaa8;stroke-width: 2px" });
pxsim.svg.child(this.closeGroup, "rect", { 'x': "10", 'y': "16", 'width': "18", 'height': "2", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
pxsim.svg.child(this.closeGroup, "rect", { 'x': "18", 'y': "8", 'width': "2", 'height': "18", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
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;
}
public getInnerWidth() {
return 32;
}
}
}

View File

@ -0,0 +1,50 @@
namespace pxsim.visuals {
export class ColorGridControl extends ControlView<ColorSensorNode> {
private group: SVGGElement;
getInnerView() {
this.group = svg.elt("g") as SVGGElement;
this.group.setAttribute("transform", `translate(1.02, 1.5) scale(0.8)`)
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;
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': '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

@ -0,0 +1,82 @@
namespace pxsim.visuals {
export class ColorWheelControl extends ControlView<ColorSensorNode> {
private group: SVGGElement;
private colorGradient: SVGLinearGradientElement;
private defs: SVGDefsElement;
getInnerView(parent: SVGSVGElement) {
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
this.group = svg.elt("g") as SVGGElement;
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 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;
}
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((1-t)*100);
}
}
}

View File

@ -0,0 +1,134 @@
namespace pxsim.visuals {
export class DistanceSliderControl extends ControlView<UltrasonicSensorNode> {
private group: SVGGElement;
private gradient: SVGLinearGradientElement;
private slider: SVGGElement;
private reporter: SVGTextElement;
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', '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 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 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.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,
y: 0,
width: this.getInnerWidth(),
height: this.getInnerHeight(),
opacity: 0,
cursor: '-webkit-grab'
})
let pt = parent.createSVGPoint();
let captured = false;
touchEvents(dragSurface, ev => {
if (captured && (ev as MouseEvent).clientY != undefined) {
ev.preventDefault();
this.updateSliderValue(pt, parent, ev as MouseEvent);
}
}, ev => {
captured = true;
if ((ev as MouseEvent).clientY != undefined) {
this.updateSliderValue(pt, parent, ev as MouseEvent);
}
}, () => {
captured = false;
}, () => {
captured = false;
})
return this.group;
}
getInnerHeight() {
return 192;
}
getInnerWidth() {
return 111;
}
private getReporterHeight() {
return 50;
}
private getSliderHeight() {
return 110;
}
private getSliderWidth() {
return 62;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue() / 10; /* convert back to cm */
const y = this.getSliderHeight() * percentage / this.getMax();
this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
// Update reporter text
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
}
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
const height = this.getSliderHeight();
const bBox = this.content.getBoundingClientRect();
let t = Math.max(0, Math.min(1, (DistanceSliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
const state = this.state;
state.setDistance((1 - t) * (this.getMax()));
}
private getMin() {
return 0;
}
private getMax() {
return 250; //cm
}
private getGradientDefinition(): LinearGradientDefinition {
return {
stops: [
{ offset: 0, color: '#626262' },
{ offset: 100, color: "#ddd" }
]
};
}
}
}

View File

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

View File

@ -0,0 +1,83 @@
namespace pxsim.visuals {
export class RotationSliderControl extends ControlView<GyroSensorNode> {
private group: SVGGElement;
private slider: SVGGElement;
private static SLIDER_WIDTH = 70;
private static SLIDER_HEIGHT = 78;
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
this.group = svg.elt("g") as SVGGElement;
const sliderGroup = pxsim.svg.child(this.group, "g");
sliderGroup.setAttribute("transform", `translate(5, ${10 + this.getTopPadding()})`)
const rotationLine = pxsim.svg.child(sliderGroup, "g");
pxsim.svg.child(rotationLine, "path", { 'transform': 'translate(5.11 -31.1)', 'd': 'M68.71,99.5l6.1-8S61.3,79.91,42.69,78.35,12,83.14,6.49,85.63a48.69,48.69,0,0,0-9.6,5.89L3.16,99.3S19.27,87.7,37.51,87.94,68.71,99.5,68.71,99.5Z', 'style': 'fill: #626262' });
this.slider = pxsim.svg.child(sliderGroup, "g") as SVGGElement;
const handleInner = pxsim.svg.child(sliderGroup, "g");
pxsim.svg.child(this.slider, "circle", { 'cx': 9, 'cy': 50, 'r': 13, 'style': 'fill: #f12a21' });
pxsim.svg.child(this.slider, "circle", { 'cx': 9, 'cy': 50, 'r': 12.5, 'style': 'fill: none;stroke: #b32e29' });
const dragSurface = svg.child(this.group, "rect", {
x: 0,
y: 0,
width: this.getInnerWidth(),
height: this.getInnerHeight(),
opacity: 0,
cursor: '-webkit-grab'
})
let pt = parent.createSVGPoint();
let captured = false;
touchEvents(dragSurface, ev => {
if (captured && (ev as MouseEvent).clientX != undefined) {
ev.preventDefault();
this.updateSliderValue(pt, parent, ev as MouseEvent);
}
}, ev => {
captured = true;
if ((ev as MouseEvent).clientX != undefined) {
this.updateSliderValue(pt, parent, ev as MouseEvent);
}
}, () => {
captured = false;
}, () => {
captured = false;
})
return this.group;
}
private getTopPadding() {
return this.getInnerHeight() / 4;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue();
const x = RotationSliderControl.SLIDER_WIDTH * percentage / 100;
const y = Math.abs((percentage - 50) / 50) * 10;
this.slider.setAttribute("transform", `translate(${x}, ${y})`);
}
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
const width = CONTROL_WIDTH; //DistanceSliderControl.SLIDER_HEIGHT;
const bBox = this.content.getBoundingClientRect();
let t = Math.max(0, Math.min(1, (width + bBox.left / this.scaleFactor - cur.x / this.scaleFactor) / width))
const state = this.state;
state.setAngle((1 - t) * (100));
}
}
}

379
sim/visuals/layoutView.ts Normal file
View File

@ -0,0 +1,379 @@
/// <reference path="./view.ts" />
/// <reference path="./nodes/moduleView.ts" />
/// <reference path="./nodes/portView.ts" />
namespace pxsim.visuals {
export const BRICK_HEIGHT_RATIO = 1 / 3;
export const MODULE_AND_WIRING_HEIGHT_RATIO = 1 / 3; // For inputs and outputs
export const MODULE_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO * 4 / 5;
export const WIRING_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO / 5;
export const MODULE_INNER_PADDING_RATIO = 1 / 35;
export const MAX_MODULE_WIDTH = 100;
export interface LayoutElement extends View {
getId(): number;
getPort(): number;
getPaddingRatio(): number;
getWiringRatio(): number;
}
export class LayoutView extends ViewContainer {
private inputs: LayoutElement[] = [];
private outputs: LayoutElement[] = [];
private inputContainers: ViewContainer[] = [];
private outputContainers: ViewContainer[] = [];
private inputControls: View[] = [];
private outputControls: View[] = [];
private inputCloseIcons: View[] = [];
private outputCloseIcons: View[] = [];
private inputWires: WireView[] = [];
private outputWires: WireView[] = [];
private brick: BrickView;
private offsets: number[];
private contentGroup: SVGGElement;
private scrollGroup: SVGGElement;
private renderedViews: Map<boolean> = {};
private hasDimensions = false;
constructor() {
super();
this.outputContainers = [new ViewContainer(), new ViewContainer, new ViewContainer(), new ViewContainer()];
this.inputContainers = [new ViewContainer(), new ViewContainer, new ViewContainer(), new ViewContainer()];
this.brick = new BrickView(0);
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
this.outputWires[port] = new WireView(port);
}
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
this.inputWires[port] = new WireView(port);
}
}
public layout(width: number, height: number) {
this.hasDimensions = true;
this.resize(width, height);
this.scrollGroup.setAttribute("width", width.toString());
this.scrollGroup.setAttribute("height", height.toString());
this.position();
}
public setBrick(brick: BrickView) {
this.brick = brick;
this.brick.inject(this.scrollGroup);
this.position();
}
public getBrick() {
return this.brick;
}
public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
if (this.inputs[port] != view || this.inputControls[port] != control) {
if (this.inputs[port]) {
// Remove current input
this.inputs[port].dispose();
}
this.inputs[port] = view;
if (this.inputControls[port]) {
this.inputControls[port].dispose();
}
this.inputControls[port] = control;
this.inputCloseIcons[port] = closeIcon;
this.inputContainers[port].clear();
this.inputContainers[port].addView(view);
if (control) this.inputContainers[port].addView(control);
if (view.hasClick()) view.registerClick((ev: any) => {
view.setSelected(true);
runtime.queueDisplayUpdate();
}, true);
if (control && closeIcon) {
this.inputContainers[port].addView(closeIcon);
closeIcon.registerClick(() => {
// Clear selection
view.setSelected(false);
runtime.queueDisplayUpdate();
})
}
}
this.position();
}
public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View) {
if (this.outputs[port] != view || this.outputControls[port] != control) {
if (this.outputs[port]) {
// Remove current output
this.outputs[port].dispose();
}
this.outputs[port] = view;
if (this.outputControls[port]) {
this.outputControls[port].dispose();
}
this.outputControls[port] = control;
this.outputCloseIcons[port] = closeIcon;
this.outputContainers[port].clear();
this.outputContainers[port].addView(view);
if (control) this.outputContainers[port].addView(control);
if (view.hasClick()) view.registerClick((ev: any) => {
view.setSelected(true);
runtime.queueDisplayUpdate();
}, true)
if (control && closeIcon) {
this.outputContainers[port].addView(closeIcon);
closeIcon.registerClick(() => {
// Clear selection
view.setSelected(false);
runtime.queueDisplayUpdate();
})
}
}
this.position();
}
protected buildDom() {
this.contentGroup = svg.elt("g") as SVGGElement;
this.scrollGroup = svg.child(this.contentGroup, "g") as SVGGElement;
this.inputs = [];
this.outputs = [];
this.inputControls = [];
this.outputControls = [];
// Inject all wires
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
this.outputWires[port].inject(this.scrollGroup);
}
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
this.inputWires[port].inject(this.scrollGroup);
}
// Inject all view containers
for (let i = 0; i < 4; i++) {
this.inputContainers[i].inject(this.scrollGroup);
this.outputContainers[i].inject(this.scrollGroup);
}
// Inject all ports
this.setInput(0, new PortView(0, 'A'));
this.setInput(1, new PortView(1, 'B'));
this.setInput(2, new PortView(2, 'C'));
this.setInput(3, new PortView(3, 'D'));
this.setOutput(0, new PortView(0, '1'));
this.setOutput(1, new PortView(1, '2'));
this.setOutput(2, new PortView(2, '3'));
this.setOutput(3, new PortView(3, '4'));
return this.contentGroup;
}
public getInnerWidth() {
if (!this.hasDimensions) {
return 0;
}
return this.width;
}
public getInnerHeight() {
if (!this.hasDimensions) {
return 0;
}
return this.height;
}
public updateTheme(theme: IBoardTheme) {
this.inputs.forEach(n => {
n.updateTheme(theme);
})
this.brick.updateTheme(theme);
this.outputs.forEach(n => {
n.updateTheme(theme);
})
}
private position() {
if (!this.hasDimensions) {
return;
}
this.offsets = [];
const contentWidth = this.width;
if (!contentWidth) return;
const contentHeight = this.height;
if (!contentHeight) return;
const noConnections = this.outputs.concat(this.inputs).filter(m => m.getId() != NodeType.Port).length == 0;
if (noConnections) {
// No connections render the entire board
this.brick.resize(contentWidth, contentHeight);
this.brick.translate(0, 0);
// Hide all other connections
this.outputs.concat(this.inputs).forEach(m => m.setVisible(false));
return;
} else {
this.outputs.concat(this.inputs).forEach(m => m.setVisible(true));
}
const moduleHeight = this.getModuleHeight();
const brickHeight = this.getBrickHeight();
const brickWidth = this.brick.getInnerWidth() / this.brick.getInnerHeight() * brickHeight;
const brickPadding = (contentWidth - brickWidth) / 2;
const modulePadding = this.getModulePadding();
const moduleSpacing = contentWidth / 4;
const moduleWidth = this.getInnerModuleWidth();
let currentX = this.getModulePadding();
let currentY = 0;
this.outputs.forEach((n, i) => {
this.outputContainers[i].translate(currentX, currentY);
if (this.outputs[i]) {
const view = this.outputs[i];
const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
const desiredOutputWidth = this.getInnerModuleWidth() - outputPadding * 2;
const outputWidth = Math.min(desiredOutputWidth, MAX_MODULE_WIDTH);
const outputHeight = this.getModuleHeight();
// Translate and resize view
view.resize(outputWidth, outputHeight);
const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
// Resize control
const control = this.outputControls[i];
if (control) {
control.resize(this.getInnerModuleWidth(), outputHeight);
// Translate close icon
const closeIcon = this.outputCloseIcons[i];
if (closeIcon) {
const closeIconWidth = closeIcon.getWidth();
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, 0);
}
}
}
currentX += moduleSpacing;
})
currentX = 0;
currentY = moduleHeight;
const wireBrickSpacing = brickWidth / 5;
const wiringYPadding = 5;
let wireStartX = 0;
let wireEndX = brickPadding + wireBrickSpacing;
let wireEndY = currentY + this.getWiringHeight() + wiringYPadding;
let wireStartY = currentY - wiringYPadding;
// Draw output lines
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
this.outputWires[port].updateDimensions(wireStartX + moduleSpacing * this.outputs[port].getWiringRatio(), wireStartY, wireEndX, wireEndY);
this.outputWires[port].setSelected(this.outputs[port].getId() == NodeType.Port);
wireStartX += moduleSpacing;
wireEndX += wireBrickSpacing;
}
currentX = brickPadding;
currentY += this.getWiringHeight();
// Render the brick in the middle
this.brick.resize(brickWidth, brickHeight);
this.brick.translate(currentX, currentY);
currentX = modulePadding;
currentY += brickHeight + this.getWiringHeight();
this.inputs.forEach((n, i) => {
this.inputContainers[i].translate(currentX, currentY);
if (this.inputs[i]) {
const view = this.inputs[i];
const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
const desiredInputWidth = this.getInnerModuleWidth() - inputPadding * 2;
const inputWidth = Math.min(desiredInputWidth, MAX_MODULE_WIDTH);
const inputHeight = this.getModuleHeight();
// Translate and resize view
view.resize(inputWidth, inputHeight);
view.translate(inputPadding + ((desiredInputWidth - inputWidth) / 2), 0, true);
// Resize control
const control = this.inputControls[i];
if (control) {
control.resize(this.getInnerModuleWidth(), inputHeight);
// Translate and resize close icon
const closeIcon = this.inputCloseIcons[i];
if (closeIcon) {
const closeIconWidth = closeIcon.getWidth();
const closeIconHeight = closeIcon.getHeight();
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, this.getModuleHeight() - closeIconHeight);
}
}
}
currentX += moduleSpacing;
})
wireStartX = moduleSpacing / 2;
wireEndX = brickPadding + wireBrickSpacing;
wireEndY = currentY - this.getWiringHeight() - wiringYPadding;
wireStartY = currentY + wiringYPadding;
// Draw input lines
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
this.inputWires[port].updateDimensions(wireStartX, wireStartY, wireEndX, wireEndY);
this.inputWires[port].setSelected(this.inputs[port].getId() == NodeType.Port);
wireStartX += moduleSpacing;
wireEndX += wireBrickSpacing;
}
}
public getBrickHeight() {
return this.height * BRICK_HEIGHT_RATIO;
}
public getWiringHeight() {
return this.height * WIRING_HEIGHT_RATIO;
}
public getModuleBounds() {
return {
width: this.width / 4,
height: this.getModuleHeight()
}
}
public getModulePadding() {
return this.getModuleBounds().width / 35;
}
public getInnerModuleWidth() {
return this.getModuleBounds().width - (this.getModulePadding() * 2);
}
public getModuleHeight() {
return this.height * MODULE_HEIGHT_RATIO;
}
}
}

View File

@ -0,0 +1,192 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export class BrickView extends ModuleView implements LayoutElement {
private static EV3_SCREEN_ID = "ev3_screen";
private static EV3_LIGHT_ID = "btn_color";
private buttons: SVGElement[];
private light: SVGElement;
private currentCanvasX = 178;
private currentCanvasY = 128;
constructor(port: number) {
super(EV3_SVG, "board", NodeType.Brick, port);
}
protected buildDomCore() {
// Setup buttons
const btnids = ["btn_up", "btn_enter", "btn_down", "btn_right", "btn_left", "btn_back"];
this.buttons = btnids.map(n => this.content.getElementById(this.normalizeId(n)) as SVGElement);
this.buttons.forEach(b => svg.addClass(b, "sim-button"));
this.light = this.content.getElementById(this.normalizeId(BrickView.EV3_LIGHT_ID)) as SVGElement;
}
private setStyleFill(svgId: string, fillUrl: string) {
const el = (this.content.getElementById(svgId) as SVGRectElement);
if (el) el.style.fill = `url("#${fillUrl}")`;
}
public hasClick() {
return false;
}
public updateState() {
this.updateLight();
}
public updateThemeCore() {
let theme = this.theme;
// svg.fill(this.buttons[0], theme.buttonUps[0]);
// svg.fill(this.buttons[1], theme.buttonUps[1]);
// svg.fill(this.buttons[2], theme.buttonUps[2]);
}
private lastLightPattern: number = -1;
private lastLightAnimationId: any;
private updateLight() {
let state = ev3board().getBrickNode().lightState;
const lightPattern = state.lightPattern;
if (lightPattern == this.lastLightPattern) return;
this.lastLightPattern = lightPattern;
if (this.lastLightAnimationId) cancelAnimationFrame(this.lastLightAnimationId);
switch (lightPattern) {
case 0: // LED_BLACK
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`));
//svg.fill(this.light, "#FFF");
break;
case 1: // LED_GREEN
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-green`));
//svg.fill(this.light, "#00ff00");
break;
case 2: // LED_RED
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-red`));
//svg.fill(this.light, "#ff0000");
break;
case 3: // LED_ORANGE
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-orange`));
//svg.fill(this.light, "#FFA500");
break;
case 4: // LED_GREEN_FLASH
this.flashLightAnimation('green');
break;
case 5: // LED_RED_FLASH
this.flashLightAnimation('red');
break;
case 6: // LED_ORANGE_FLASH
this.flashLightAnimation('orange');
break;
case 7: // LED_GREEN_PULSE
this.pulseLightAnimation('green');
break;
case 8: // LED_RED_PULSE
this.pulseLightAnimation('red');
break;
case 9: // LED_ORANGE_PULSE
this.pulseLightAnimation('orange');
break;
}
}
private flashLightAnimation(id: string) {
let fps = 3;
let now;
let then = Date.now();
let interval = 1000 / fps;
let delta;
let that = this;
function draw() {
that.lastLightAnimationId = requestAnimationFrame(draw);
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - (delta % interval);
that.flashLightAnimationStep(id);
}
}
draw();
}
private flash: boolean;
private flashLightAnimationStep(id: string) {
if (this.flash) {
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`));
} else {
this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`));
}
this.flash = !this.flash;
}
private pulseLightAnimation(id: string) {
let fps = 8;
let now;
let then = Date.now();
let interval = 1000 / fps;
let delta;
let that = this;
function draw() {
that.lastLightAnimationId = requestAnimationFrame(draw);
now = Date.now();
delta = now - then;
if (delta > interval) {
// update time stuffs
then = now - (delta % interval);
that.pulseLightAnimationStep(id);
}
}
draw();
}
private pulse: number = 0;
private pulseLightAnimationStep(id: string) {
switch (this.pulse) {
case 0: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
case 1: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
case 2: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
case 3: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
case 4: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
case 5: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`)); break;
case 6: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`)); break;
case 7: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-black`)); break;
case 8: this.setStyleFill(this.normalizeId(BrickView.EV3_LIGHT_ID), this.normalizeId(`linear-gradient-${id}`)); break;
}
this.pulse++;
if (this.pulse == 9) this.pulse = 0;
}
public attachEvents() {
let bpState = ev3board().getBrickNode().buttonState;
let stateButtons = bpState.buttons;
this.buttons.forEach((btn, index) => {
let button = stateButtons[index];
btn.addEventListener(pointerEvents.down, ev => {
button.setPressed(true);
svg.fill(this.buttons[index], this.theme.buttonDown);
})
btn.addEventListener(pointerEvents.leave, ev => {
button.setPressed(false);
svg.fill(this.buttons[index], this.theme.buttonUps[index]);
})
btn.addEventListener(pointerEvents.up, ev => {
button.setPressed(false);
svg.fill(this.buttons[index], this.theme.buttonUps[index]);
})
})
}
public getScreenBBox() {
if (!this.content) return undefined;
const screen = this.content.getElementById(this.normalizeId(BrickView.EV3_SCREEN_ID));
if (!screen) return undefined;
return screen.getBoundingClientRect();
}
}
}

View File

@ -0,0 +1,20 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export class ColorSensorView extends ModuleView implements LayoutElement {
private control: ColorGridControl;
constructor(port: number) {
super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port);
}
public getPaddingRatio() {
return 1 / 4;
}
public updateState() {
// TODO: show different color modes
}
}
}

View File

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

View File

@ -0,0 +1,33 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export class LargeMotorView extends ModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "hole";
constructor(port: number) {
super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port);
}
updateState() {
const motorState = ev3board().getMotors()[this.port];
if (!motorState) return;
const speed = motorState.getSpeed();
if (!speed) return;
this.setMotorAngle(motorState.getAngle());
}
private setMotorAngle(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(LargeMotorView.ROTATING_ECLIPSE_ID))
const width = 125.92;
const height = 37.9;
const transform = `rotate(${angle} ${width / 2} ${height / 2})`;
holeEl.setAttribute("transform", transform);
}
getWiringRatio() {
return 0.37;
}
}
}

View File

@ -0,0 +1,39 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export const MOTOR_ROTATION_FPS = 32;
export class MediumMotorView extends ModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "medmotor_Hole";
private hasPreviousAngle: boolean;
private previousAngle: number;
constructor(port: number) {
super(MEDIUM_MOTOR_SVG, "medium-motor", NodeType.MediumMotor, port);
}
public getPaddingRatio() {
return 1 / 5;
}
updateState() {
const motorState = ev3board().getMotors()[this.port];
if (!motorState) return;
const speed = motorState.getSpeed();
if (!speed) return;
this.setMotorAngle(motorState.getAngle());
}
private setMotorAngle(angle: number) {
const holeEl = this.content.getElementById(this.normalizeId(MediumMotorView.ROTATING_ECLIPSE_ID))
const width = 44.45;
const height = 44.45;
const transform = `translate(2 1.84) rotate(${angle} ${width / 2} ${height / 2})`;
holeEl.setAttribute("transform", transform);
}
}
}

View File

@ -0,0 +1,124 @@
namespace pxsim.visuals {
export class ModuleView extends View implements LayoutElement {
protected content: SVGSVGElement;
protected controlShown: boolean;
protected selected: boolean;
constructor(protected xml: string, protected prefix: string, protected id: NodeType, protected port: NodeType) {
super();
this.xml = this.normalizeXml(xml);
}
private normalizeXml(xml: string) {
const prefix = this.prefix;
xml = xml.replace(/id=\"(.*?)\"/g, (m: string, id: string) => {
return `id="${this.normalizeId(id)}"`;
});
xml = xml.replace(/url\(#(.*?)\)/g, (m: string, id: string) => {
return `url(#${this.normalizeId(id)}`;
});
xml = xml.replace(/xlink:href=\"#(.*?)\"/g, (m: string, id: string) => {
return `xlink:href="#${this.normalizeId(id)}"`;
});
return xml;
}
protected normalizeId(svgId: string) {
return `${this.prefix}-${svgId}`;
}
public getId() {
return this.id;
}
public getPort() {
return this.port;
}
public getPaddingRatio() {
return 0;
}
public getWiringRatio() {
return 0.5;
}
protected buildDom(): SVGElement {
this.content = svg.parseString(this.xml);
this.buildDomCore();
this.attachEvents();
if (this.hasClick())
this.content.style.cursor = "pointer";
return this.content;
}
protected buildDomCore() {
}
public getInnerHeight() {
if (!this.content) {
return 0;
}
if (!this.content.hasAttribute("viewBox")) {
return parseFloat(this.content.getAttribute("height"));
}
return parseFloat(this.content.getAttribute("viewBox").split(" ")[3]);
}
public getInnerWidth() {
if (!this.content) {
return 0;
}
if (!this.content.hasAttribute("viewBox")) {
return parseFloat(this.content.getAttribute("width"));
}
return parseFloat(this.content.getAttribute("viewBox").split(" ")[2]);
}
public attachEvents() {
}
public resize(width: number, height: number) {
super.resize(width, height);
this.updateDimensions(width, height);
}
private updateDimensions(width: number, height: number) {
if (this.content) {
const currentWidth = this.getInnerWidth();
const currentHeight = this.getInnerHeight();
const newHeight = currentHeight / currentWidth * width;
const newWidth = currentWidth / currentHeight * height;
this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`);
}
}
public hasClick() {
return true;
}
public setSelected(selected: boolean) {
super.setSelected(selected);
this.updateOpacity();
}
protected updateOpacity() {
if (this.rendered) {
const opacity = this.selected ? "0.2" : "1";
if (this.hasClick()) {
this.setOpacity(opacity);
if (this.selected) this.content.style.cursor = "";
else this.content.style.cursor = "pointer";
}
}
}
protected setOpacity(opacity: string) {
this.element.setAttribute("opacity", opacity);
}
}
}

View File

@ -0,0 +1,25 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export class PortView extends ModuleView implements LayoutElement {
constructor(port: NodeType, private label: string) {
super(PORT_SVG, "port", NodeType.Port, port);
}
protected buildDomCore() {
const textLabel = this.content.getElementById(this.normalizeId("port_text")) as SVGTextElement;
textLabel.textContent = this.label;
textLabel.style.userSelect = 'none';
}
public getPaddingRatio() {
return 1 / 6;
}
public hasClick() {
return false;
}
}
}

View File

@ -0,0 +1,67 @@
/// <reference path="./moduleView.ts" />
namespace pxsim.visuals {
export class TouchSensorView extends ModuleView implements LayoutElement {
private static RECT_ID = ["touch_gradient4", "touch_gradient3", "touch_gradient2", "touch_gradient1"];
private static TOUCH_GRADIENT_UNPRESSED = ["linear-gradient-2", "linear-gradient-3", "linear-gradient-4", "linear-gradient-5"];
private static TOUCH_GRADIENT_PRESSED = ["linear-gradient-6", "linear-gradient-7", "linear-gradient-8", "linear-gradient-9"];
private unpressedGradient: string;
private pressedGradient: string;
private xLinkGradients: string[];
constructor(port: number) {
super(TOUCH_SENSOR_SVG, "touch", NodeType.TouchSensor, port);
}
public getPaddingRatio() {
return 1 / 4;
}
public hasClick() {
return false;
}
private setAttribute(svgId: string, attribute: string, value: string) {
const el = this.content.getElementById(svgId);
if (el) el.setAttribute(attribute, value);
}
private setStyleFill(svgId: string, fillUrl: string) {
const el = (this.content.getElementById(svgId) as SVGRectElement);
if (el) el.style.fill = `url("#${fillUrl}")`;
}
public attachEvents() {
this.content.style.cursor = "pointer";
const btn = this.content;
const state = ev3board().getSensor(this.port, DAL.DEVICE_TYPE_TOUCH) as TouchSensorNode;
btn.addEventListener(pointerEvents.down, ev => {
this.setPressed(true);
state.setPressed(true);
})
btn.addEventListener(pointerEvents.leave, ev => {
this.setPressed(false);
state.setPressed(false);
})
btn.addEventListener(pointerEvents.up, ev => {
this.setPressed(false);
state.setPressed(false);
})
}
private setPressed(pressed: boolean) {
if (pressed) {
for (let i = 0; i < 4; i ++) {
this.setStyleFill(`${this.normalizeId(TouchSensorView.RECT_ID[i])}`, `${this.normalizeId(TouchSensorView.TOUCH_GRADIENT_PRESSED[i])}`);
}
} else {
for (let i = 0; i < 4; i ++) {
this.setStyleFill(`${this.normalizeId(TouchSensorView.RECT_ID[i])}`, `${this.normalizeId(TouchSensorView.TOUCH_GRADIENT_UNPRESSED[i])}`);
}
}
}
}
}

View File

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

View File

@ -1,92 +0,0 @@
namespace pxsim.visuals {
export class AnalogPinControl {
private outerElement: SVGElement;
private innerCircle: SVGCircleElement;
private gradient: SVGLinearGradientElement;
private currentValue: number;
private pin: Pin;
constructor(private parent: EV3BoardSvg, private defs: SVGDefsElement, private id: CPlayPinName, name: string) {
this.pin = board().edgeConnectorState.getPin(this.id);
// Init the button events
this.outerElement = parent.element.getElementById(name) as SVGElement;
svg.addClass(this.outerElement, "sim-pin-touch");
this.addButtonEvents();
// Init the gradient controls
// const gid = `gradient-${CPlayPinName[id]}-level`;
// this.innerCircle = parent.element.getElementById("PIN_CONNECTOR_" + CPlayPinName[id]) as SVGCircleElement;
// this.gradient = svg.linearGradient(this.defs, gid);
// this.innerCircle.setAttribute("fill", `url(#${gid})`);
// this.innerCircle.setAttribute("class", "sim-light-level-button")
// this.addLevelControlEvents()
this.updateTheme();
}
public updateTheme() {
const theme = this.parent.props.theme;
svg.setGradientColors(this.gradient, theme.lightLevelOff, 'darkorange');
}
public updateValue() {
const value = this.pin.value;
if (value === this.currentValue) {
return;
}
this.currentValue = value;
// svg.setGradientValue(this.gradient, 100 - Math.min(100, Math.max(0, Math.floor(value * 100 / 1023))) + '%')
// if (this.innerCircle.childNodes.length) {
// this.innerCircle.removeChild(this.innerCircle.childNodes[0])
// }
svg.title(this.outerElement, value.toString());
}
private addButtonEvents() {
this.outerElement.addEventListener(pointerEvents.down, ev => {
this.pin.touched = true;
svg.addClass(this.outerElement, "touched");
(pxtcore.getTouchButton(this.id - 1) as CommonButton).setPressed(true);
})
this.outerElement.addEventListener(pointerEvents.leave, ev => {
this.pin.touched = false;
svg.removeClass(this.outerElement, "touched");
(pxtcore.getTouchButton(this.id - 1) as CommonButton).setPressed(false);
})
this.outerElement.addEventListener(pointerEvents.up, ev => {
this.pin.touched = false;
svg.removeClass(this.outerElement, "touched");
(pxtcore.getTouchButton(this.id - 1) as CommonButton).setPressed(false);
})
}
private addLevelControlEvents() {
const cy = parseFloat(this.innerCircle.getAttribute("cy"));
const r = parseFloat(this.innerCircle.getAttribute("r"));
const pt = this.parent.element.createSVGPoint();
svg.buttonEvents(this.innerCircle,
(ev) => {
const pos = svg.cursorPoint(pt, this.parent.element, ev);
const rs = r / 2;
const level = Math.max(0, Math.min(1023, Math.floor((1 - (pos.y - (cy - rs)) / (2 * rs)) * 1023)));
if (level != this.pin.value) {
this.pin.value = level;
this.updateValue();
}
}, ev => { },
ev => { });
}
}
}

99
sim/visuals/util.ts Normal file
View File

@ -0,0 +1,99 @@
namespace pxsim.visuals {
export interface LinearGradientDefinition {
stops: LinearGradientStop[];
}
export interface LinearGradientStop {
offset: string | number;
color: string;
}
export type TouchCallback = (event: MouseEvent | TouchEvent | PointerEvent) => void;
export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) {
if (Array.isArray(e)) {
e.forEach(el => bindEvents(el, move, down, up, leave));
}
else {
bindEvents(e, move, down, up, leave);
}
}
function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) {
if ((window as any).PointerEvent) {
if (down) e.addEventListener("pointerdown", down);
if (up) e.addEventListener("pointerup", up);
if (leave) e.addEventListener("pointerleave", leave);
if (move) e.addEventListener("pointermove", move);
}
else {
if (down) e.addEventListener("mousedown", down);
if (up) e.addEventListener("mouseup", up);
if (leave) e.addEventListener("mouseleave", leave);
if (move) e.addEventListener("mousemove", move);
if (pxsim.svg.isTouchEnabled()) {
if (down) e.addEventListener("touchstart", down);
if (up) e.addEventListener("touchend", up);
if (leave) e.addEventListener("touchcancel", leave);
if (move) e.addEventListener("touchmove", move);
}
}
}
export function createGradient(id: string, opts: LinearGradientDefinition) {
const g = svg.elt("linearGradient") as SVGLinearGradientElement;
g.setAttribute("id", id);
opts.stops.forEach(stop => {
let offset: string;
if (typeof stop.offset === "number") {
offset = stop.offset + "%"
}
else {
offset = stop.offset as string;
}
svg.child(g, "stop", { offset, "stop-color": stop.color });
});
return g;
}
export function updateGradient(gradient: SVGLinearGradientElement, opts: LinearGradientDefinition) {
let j = 0;
forEachElement(gradient.childNodes, (e, i) => {
if (i < opts.stops.length) {
const stop = opts.stops[i];
e.setAttribute("offset", offsetString(stop.offset));
e.setAttribute("stop-color", stop.color);
}
else {
gradient.removeChild(e);
}
j = i + 1;
});
for (; j < opts.stops.length; j++) {
const stop = opts.stops[j];
svg.child(gradient, "stop", { offset: offsetString(stop.offset), "stop-color": stop.color });
}
}
export function forEachElement(nodes: NodeList, cb: (e: Element, i: number) => void) {
let index = 0;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.nodeType === Node.ELEMENT_NODE) {
cb(node as Element, index);
++index;
}
}
}
function offsetString(offset: string | number) {
return (typeof offset === "number") ? offset + "%" : offset;
}
}

309
sim/visuals/view.ts Normal file
View File

@ -0,0 +1,309 @@
namespace pxsim.visuals {
export abstract class View {
protected element: SVGGElement;
protected rendered = false;
protected visible = false;
protected selected: boolean;
protected width: number = 0;
protected height: number = 0;
protected left: number = 0;
protected top: number = 0;
protected scaleFactor: number = 1;
private changed: boolean;
private hover: boolean = false;
protected theme: IBoardTheme;
protected abstract buildDom(): SVGElement;
public abstract getInnerWidth(): number;
public abstract getInnerHeight(): number;
public inject(parent: SVGElement, width?: number, visible = true) {
this.width = width;
parent.appendChild(this.getView());
if (visible) {
this.visible = true;
this.onComponentInjected();
}
}
public getWidth() {
return this.scaleFactor == undefined ? this.getInnerWidth() : this.getInnerWidth() * this.scaleFactor;
}
public getHeight() {
return this.scaleFactor == undefined ? this.getInnerHeight() : this.getInnerHeight() * this.scaleFactor;
}
public onComponentInjected() {
// To be overridden by sub class
}
public onComponentVisible() {
// To be overridden by sub class
}
public onComponentHidden() {
// To be overridden by sub class
}
public translate(x: number, y: number, applyImmediately = true) {
this.left = x;
this.top = y;
if (applyImmediately) {
this.updateTransform();
}
}
public scale(scaleFactor: number, applyImmediately = true) {
this.scaleFactor = scaleFactor;
if (applyImmediately) {
this.updateTransform();
}
}
public updateState() {
}
public updateTheme(theme: IBoardTheme) {
this.theme = theme;
this.updateThemeCore();
}
public updateThemeCore() {
}
public setVisible(visible: boolean) {
if (this.rendered) {
this.getView().style.display = visible ? 'block' : 'none';
}
}
public hasClick() {
return true;
}
private onClickHandler: (ev: any) => void;
public registerClick(handler: (ev: any) => void, zoom?: boolean) {
this.onClickHandler = handler;
if (zoom) {
this.getView().addEventListener(pointerEvents.up, (ev: any) => {
if (!this.getSelected()) {
this.onClickHandler(ev);
this.setHover(false);
}
});
this.getView().addEventListener(pointerEvents.move, () => {
if (!this.getSelected()) {
this.setHover(true);
}
});
this.getView().addEventListener(pointerEvents.leave, () => {
this.setHover(false);
});
} else {
this.getView().addEventListener(pointerEvents.up, this.onClickHandler);
}
}
public dispose() {
if (this.onClickHandler) this.getView().removeEventListener(pointerEvents.up, this.onClickHandler)
View.dispose(this);
}
protected getView() {
if (!this.rendered) {
this.element = svg.elt("g") as SVGGElement;
View.track(this);
const content = this.buildDom();
if (content) {
this.element.appendChild(content);
}
this.updateTransform();
this.rendered = true;
}
return this.element;
}
public resize(width: number, height: number) {
this.width = width;
this.height = height;
}
private updateTransform() {
if (this.rendered) {
let left = this.left;
let top = this.top;
let scaleFactor = this.scaleFactor;
if (this.hover) {
const hoverScaleFactor = scaleFactor + 0.05;
// Scale around center of module
const centerX = this.getWidth() / 2;
const centerY = this.getHeight() / 2;
left = left - centerX * (hoverScaleFactor - 1);
top = top - centerY * (hoverScaleFactor - 1);
scaleFactor = hoverScaleFactor;
}
let transform = `translate(${left} ${top})`;
if (scaleFactor !== 1) {
transform += ` scale(${scaleFactor})`;
}
this.element.setAttribute("transform", transform);
}
}
private static currentId = 0;
private static allViews: Map<View> = {};
protected static getInstance(element: Element) {
if (element.hasAttribute("ref-id")) {
return View.allViews[element.getAttribute("ref-id")];
}
return undefined;
}
private static track(view: View) {
const myId = "id-" + (View.currentId++);
view.element.setAttribute("ref-id", myId);
View.allViews[myId] = view;
}
private static dispose(view: View) {
if (view.element) {
const id = view.element.getAttribute("ref-id");
// TODO: Remove from DOM
view.element.parentNode.removeChild(view.element);
delete View.allViews[id];
}
}
///////// HOVERED STATE /////////////
public getHover() {
return this.hover;
}
public setHover(hover: boolean) {
if (this.hover != hover) {
this.hover = hover;
this.updateTransform();
}
}
///////// SELECTED STATE /////////////
public getSelected() {
return this.selected;
}
public setSelected(selected: boolean) {
this.selected = selected;
this.setChangedState();
}
protected setChangedState() {
this.changed = true;
}
public didChange() {
const res = this.changed;
this.changed = false;
return res;
}
}
export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
constructor(protected state: T) {
super();
}
public getId() {
return this.state.id;
}
public getPort() {
return this.state.port;
}
public getPaddingRatio() {
return 0;
}
public getWiringRatio() {
return 0.5;
}
public setSelected(selected: boolean) { }
protected getView() {
return super.getView();
}
}
export class ViewContainer extends View {
public getInnerWidth() {
return 0;
}
public getInnerHeight() {
return 0;
}
public addView(view: View) {
view.inject(this.element);
}
public clear() {
forEachElement(this.element.childNodes, e => {
this.element.removeChild(e);
});
}
public onComponentInjected() {
const observer = new MutationObserver(records => {
records.forEach(r => {
forEachElement(r.addedNodes, node => {
const instance = View.getInstance(node);
if (instance) {
instance.onComponentVisible();
}
});
forEachElement(r.removedNodes, node => {
const instance = View.getInstance(node);
if (instance) {
instance.onComponentHidden();
}
});
})
});
observer.observe(this.element, {
childList: true,
subtree: true
});
}
protected buildDom(): SVGElement {
return undefined;
}
}
function forEachElement(nodes: NodeList, cb: (e: Element) => void) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.nodeType === Node.ELEMENT_NODE) {
cb(node as Element);
}
}
}
}

98
sim/visuals/wireView.ts Normal file
View File

@ -0,0 +1,98 @@
/// <reference path="./nodes/moduleView.ts" />
namespace pxsim.visuals {
export class WireView extends View implements LayoutElement {
private wire: SVGSVGElement;
private path: SVGPathElement;
private hasDimensions: boolean;
protected startX: number;
protected startY: number;
protected endX: number;
protected endY: number;
constructor(private port: number) {
super();
}
isRendered() {
return !!this.wire;
}
updateDimensions(startX: number, startY: number, endX: number, endY: number) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
this.hasDimensions = true;
this.updatePath();
}
buildDom(): SVGElement {
this.wire = svg.elt("svg", { height: "100%", width: "100%" }) as SVGSVGElement;
this.path = pxsim.svg.child(this.wire, "path", {
'd': '',
'fill': 'transparent',
'stroke': '#5A5A5A',
'stroke-width': '3px'
}) as SVGPathElement;
this.setSelected(true);
return this.wire;
}
updatePath() {
if (!this.hasDimensions) return;
const height = this.endY - this.startY;
const thirdHeight = height / 3;
const middleHeight = this.port == 1 || this.port == 2 ? thirdHeight : thirdHeight * 2;
let d = `M${this.startX} ${this.startY}`;
d += ` L${this.startX} ${this.startY + middleHeight}`;
d += ` L${this.endX} ${this.startY + middleHeight}`;
d += ` L${this.endX} ${this.endY}`;
this.path.setAttribute('d', d);
}
getId() {
return -2;
}
getPort() {
return this.port;
}
getPaddingRatio() {
return 0;
}
getWiringRatio() {
return 0.5;
}
getInnerWidth(): number {
return CONTROL_WIDTH;
}
getInnerHeight(): number {
return CONTROL_HEIGHT;
}
public setSelected(selected: boolean) {
super.setSelected(selected);
this.updateOpacity();
}
protected updateOpacity() {
const opacity = this.selected ? "0.2" : "1";
this.setOpacity(opacity);
}
protected setOpacity(opacity: string) {
this.element.setAttribute("opacity", opacity);
}
public hasClick() {
return false;
}
}
}

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