Compare commits
64 Commits
Author | SHA1 | Date | |
---|---|---|---|
1bc93013e6 | |||
58148eb1c3 | |||
bd765d49ee | |||
5d470fdcef | |||
f30eac41e9 | |||
d7f46c0fb5 | |||
58384017f2 | |||
01f7fe633c | |||
a9a9a89811 | |||
7e2251d8ac | |||
1903a6e347 | |||
2fb75a2d83 | |||
0da175a8cd | |||
f01370e4fd | |||
751df2fe8c | |||
8be4bb11d8 | |||
342e714ae2 | |||
fb31b81f7e | |||
8204995749 | |||
85c14bb05a | |||
c398a5a133 | |||
afaedaa0b2 | |||
0aa41e9a64 | |||
88df2e14cb | |||
180f32f25c | |||
300a2c1476 | |||
703bd01931 | |||
75a65eeab2 | |||
38a9f153f7 | |||
3c5dae8c7b | |||
85345969d3 | |||
ac1e5d2846 | |||
11b4bbc07e | |||
7f5b8aed99 | |||
c989e2fdab | |||
d3dcb5de85 | |||
9cca35d49f | |||
7123bfecd3 | |||
c8ac770983 | |||
aa031036ee | |||
a7d002d949 | |||
93fd8c8c78 | |||
1765ca2d35 | |||
1ab7ae6cfa | |||
3acf4e9ac5 | |||
ef5fa9ae82 | |||
e1f7a5b8cf | |||
2c73bfc813 | |||
d78d9c8686 | |||
2157af3e63 | |||
eac3e183c3 | |||
785ddff706 | |||
e07d6e3a31 | |||
763ad3f763 | |||
919a03951c | |||
9e427898ae | |||
60bf3a17d3 | |||
0529759a80 | |||
b07f157181 | |||
2f5f7d4133 | |||
e6e1dce59f | |||
43a9d03231 | |||
c0f6cd3651 | |||
f1445c6e89 |
@ -1,66 +0,0 @@
|
||||
let errors: string[] = [];
|
||||
let tachoB = 0;
|
||||
let tachoC = 0;
|
||||
|
||||
function assert(name: string, condition: boolean) {
|
||||
if (!condition) {
|
||||
errors.push(name)
|
||||
}
|
||||
}
|
||||
|
||||
function assertClose(name: string, expected: number, actual: number, tolerance = 10) {
|
||||
assert(name + ` ${expected}/${actual}`, Math.abs(expected - actual) < tolerance);
|
||||
}
|
||||
|
||||
function test(name: string, f: () => void, check?: () => void) {
|
||||
motors.stopAllMotors();
|
||||
loops.pause(500);
|
||||
tachoB = motors.largeB.tachoCount()
|
||||
tachoC = motors.largeB.tachoCount()
|
||||
brick.clearScreen()
|
||||
brick.print(name, 0, 0)
|
||||
f();
|
||||
loops.pause(3000);
|
||||
motors.stopAllMotors();
|
||||
motors.largeB.setReversed(false);
|
||||
motors.largeC.setReversed(false);
|
||||
motors.mediumA.setReversed(false);
|
||||
loops.pause(1000);
|
||||
if (check)
|
||||
check()
|
||||
}
|
||||
|
||||
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
|
||||
test("lgB set speed 100", () => {
|
||||
motors.largeB.setSpeed(100)
|
||||
});
|
||||
test("lgB set speed (reversed)", () => {
|
||||
motors.largeB.setReversed(true)
|
||||
motors.largeB.setSpeed(100)
|
||||
})
|
||||
test("lgBC set speed 100", () => {
|
||||
motors.largeBC.setSpeed(100)
|
||||
})
|
||||
test("lgBC steer 50% 2x", () => {
|
||||
motors.largeBC.steer(50, 50, 2, MoveUnit.Rotations)
|
||||
}, () => {
|
||||
assertClose("largeB", 720, motors.largeB.tachoCount() - tachoB)
|
||||
});
|
||||
test("lgBC steer 50% 500deg", () => {
|
||||
motors.largeBC.steer(50, 50, 500, MoveUnit.Degrees)
|
||||
}, () => {
|
||||
assertClose("largeB", 500, motors.largeB.tachoCount() - tachoB)
|
||||
});
|
||||
test("lgBC steer 50% 2s", () => {
|
||||
motors.largeBC.steer(50, 50, 2, MoveUnit.Seconds)
|
||||
})
|
||||
test("lgBC tank 50% 2s", () => {
|
||||
motors.largeBC.tank(50, 50, 720, MoveUnit.Degrees)
|
||||
})
|
||||
|
||||
brick.clearScreen()
|
||||
brick.print(`${errors.length} errors`, 0, 0)
|
||||
let l = 1;
|
||||
for(const error of errors)
|
||||
brick.print(`error: ${error}`, 0, l++ * 12)
|
||||
})
|
@ -139,8 +139,98 @@ namespace pxt.editor {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the shape of Blockly blocks with square corners
|
||||
*/
|
||||
function updateBlocklyShape() {
|
||||
|
||||
/**
|
||||
* Rounded corner radius.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
|
||||
|
||||
/**
|
||||
* Inner space between edge of statement input and notch.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from left to right.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
|
||||
'l 8,8 ' +
|
||||
'h 16 ' +
|
||||
'l 8,-8 '
|
||||
);
|
||||
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from right to left.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
|
||||
'l -8,8 ' +
|
||||
'h -16 ' +
|
||||
'l -8,-8 '
|
||||
);
|
||||
|
||||
/**
|
||||
* SVG start point for drawing the top-left corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
|
||||
'm 0,' + 0;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded top-left corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
|
||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded top-right corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
|
||||
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded bottom-right corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
|
||||
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded bottom-left corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
|
||||
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
|
||||
|
||||
/**
|
||||
* SVG path for drawing the top-left corner of a statement input.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
|
||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the bottom-left corner of a statement input.
|
||||
* Includes the rounded inside corner.
|
||||
* @const
|
||||
*/
|
||||
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
|
||||
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
|
||||
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
|
||||
|
||||
}
|
||||
|
||||
initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
|
||||
pxt.debug('loading pxt-ev3 target extensions...')
|
||||
updateBlocklyShape();
|
||||
const res: pxt.editor.ExtensionResult = {
|
||||
deployCoreAsync,
|
||||
};
|
||||
|
5914
legoresources/MakeCode_overview.ai
Normal 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"
|
||||
}
|
@ -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"
|
||||
|
@ -68,13 +68,20 @@ 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;
|
||||
}
|
||||
|
||||
@ -99,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"
|
||||
@ -115,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"
|
||||
@ -139,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)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,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)
|
||||
@ -181,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)
|
||||
}
|
||||
|
@ -21,8 +21,6 @@
|
||||
"brick.Button.pauseUntil": "Waits until the event is raised",
|
||||
"brick.Button.pauseUntil|param|ev": "the event to wait for",
|
||||
"brick.Button.wasPressed": "See if the button was pressed again since the last time you checked.",
|
||||
"brick._imagePicker": "An image",
|
||||
"brick._imagePicker|param|image": "the image",
|
||||
"brick.buttonDown": "Down button on the EV3 Brick.",
|
||||
"brick.buttonEnter": "Enter button on the EV3 Brick.",
|
||||
"brick.buttonLeft": "Left button on the EV3 Brick.",
|
||||
@ -32,17 +30,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,21 +57,24 @@
|
||||
"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.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.reset": "Resets the motor(s).",
|
||||
"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.stop": "Stops the motor(s).",
|
||||
"motors.SingleMotor.clearCount": "Clears the motor count",
|
||||
"motors.SingleMotor.count": "Gets motor step count.",
|
||||
"motors.SingleMotor.speed": "Gets motor actual speed.",
|
||||
"motors.SingleMotor.tachoCount": "Gets motor tacho count.",
|
||||
"motors.Motor.angle": "Gets motor ration angle.",
|
||||
"motors.Motor.clearCount": "Clears the motor count",
|
||||
"motors.Motor.speed": "Gets motor actual speed.",
|
||||
"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``",
|
||||
@ -75,7 +84,17 @@
|
||||
"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.",
|
||||
|
@ -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,32 +24,36 @@
|
||||
"Output.CD|block": "C+D",
|
||||
"Output.C|block": "C",
|
||||
"Output.D|block": "D",
|
||||
"brick.Button.isPressed|block": "`icons.brickButtons` %button|is pressed",
|
||||
"brick.Button.onEvent|block": "on `icons.brickButtons` %button|%event",
|
||||
"brick.Button.pauseUntil|block": "pause until `icons.brickButtons` %button|%event",
|
||||
"brick.Button.wasPressed|block": "`icons.brickButtons` %button|was pressed",
|
||||
"brick._imagePicker|block": "%image",
|
||||
"brick.Button.isPressed|block": "%button|is pressed",
|
||||
"brick.Button.onEvent|block": "on %button|%event",
|
||||
"brick.Button.pauseUntil|block": "pause until %button|%event",
|
||||
"brick.Button.wasPressed|block": "%button|was pressed",
|
||||
"brick.buttonDown|block": "down",
|
||||
"brick.buttonEnter|block": "enter",
|
||||
"brick.buttonLeft|block": "left",
|
||||
"brick.buttonRight|block": "right",
|
||||
"brick.buttonUp|block": "up",
|
||||
"brick.clearScreen|block": "`icons.brickDisplay` clear screen",
|
||||
"brick.clearScreen|block": "clear screen",
|
||||
"brick.lightPattern|block": "%pattern",
|
||||
"brick.print|block": "`icons.brickDisplay` print %text| at x: %x| y: %y",
|
||||
"brick.setPixel|block": "`icons.brickDisplay` set pixel %on| at x: %x| y: %y",
|
||||
"brick.setStatusLight|block": "set `icons.brickButtons` to %pattern=led_pattern",
|
||||
"brick.showImage|block": "`icons.brickDisplay` show image %image=screen_image_picker",
|
||||
"brick.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.move|block": "move `icons.motorLarge` %motor|for %value|%unit|at %speed|%",
|
||||
"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.SingleMotor.count|block": "`icons.motorLarge` %motor|count",
|
||||
"motors.SingleMotor.speed|block": "`icons.motorLarge` %motor|speed",
|
||||
"motors.SingleMotor.tachoCount|block": "`icons.motorLarge` %motor|tacho count",
|
||||
"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",
|
||||
@ -71,6 +75,7 @@
|
||||
"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",
|
||||
|
@ -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
@ -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();
|
||||
}
|
||||
}
|
@ -1,260 +1,260 @@
|
||||
namespace images {
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsBigSmile = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsHeartLarge = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsHeartSmall = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsMouth1open = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsMouth1shut = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsMouth2open = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsMouth2shut = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsSad = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsSick = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsSmile = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsSwearing = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsTalking = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsWink = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const expressionsZzz = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesAngry = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesAwake = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesBlackEye = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesBottomLeft = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesBottomRight = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesCrazy1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesCrazy2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesDisappointed = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesDizzy = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesDown = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesEvil = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesHurt = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesKnockedOut = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesLove = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesMiddleLeft = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesMiddleRight = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesNeutral = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesNuclear = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesPinchLeft = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesPinchMiddle = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesPinchRight = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesSleeping = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesTear = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesTiredLeft = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesTiredMiddle = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesTiredRight = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesToxic = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesUp = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const eyesWinking = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationAccept = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationBackward = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationDecline = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationForward = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationLeft = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationNoGo = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationQuestionMark = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationRight = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationStop1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationStop2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationThumbsDown = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationThumbsUp = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const informationWarning = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoColorSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoEv3icon = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoEv3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoGyroSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoIrBeacon = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoIrSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoLego = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoLargeMotor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoMindstorms = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoMediumMotor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoSoundSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoTempSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoTouchSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const legoUsSensor = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsBomb = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsBoom = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsFire = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsFlowers = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsForest = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsLightOff = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsLightOn = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsLightning = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsNight = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsPirate = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsSnow = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const objectsTarget = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressBar0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressBar1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressBar2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressBar3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressBar4 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDial0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDial1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDial2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDial3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDial4 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDots0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDots1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDots2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressDots3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressHourglass0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressHourglass1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressHourglass2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressTimer0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressTimer1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressTimer2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressTimer3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressTimer4 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressWaterLevel0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressWaterLevel1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressWaterLevel2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const progressWaterLevel3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemAccept1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemAccept2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemAlert = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemBox = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemBusy0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemBusy1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemDecline1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemDecline2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemDotEmpty = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemDotFull = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemEv3small = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemPlay = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider0 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider1 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider2 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider3 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider4 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider5 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider6 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider7 = screen.unpackPNG(hex``);
|
||||
//% fixedInstance jres blockIdentity=brick._imagePicker
|
||||
//% fixedInstance jres blockIdentity=brick.__imagePicker
|
||||
export const systemSlider8 = screen.unpackPNG(hex``);
|
||||
}
|
||||
|
@ -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
|
||||
@ -196,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;
|
||||
|
@ -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,9 +116,18 @@ namespace motors {
|
||||
writePWM(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all motors
|
||||
*/
|
||||
//% group="Motion"
|
||||
export function resetAllMotors() {
|
||||
reset(Output.ALL)
|
||||
}
|
||||
|
||||
//% fixedInstances
|
||||
export class Motor extends control.Component {
|
||||
export class MotorBase extends control.Component {
|
||||
protected _port: Output;
|
||||
protected _portName: string;
|
||||
protected _brake: boolean;
|
||||
private _initialized: boolean;
|
||||
private _init: () => void;
|
||||
@ -104,6 +137,7 @@ namespace motors {
|
||||
constructor(port: Output, init: () => void, setSpeed: (speed: number) => void, move: (steps: boolean, stepsOrTime: number, speed: number) => void) {
|
||||
super();
|
||||
this._port = port;
|
||||
this._portName = outputToName(this._port);
|
||||
this._brake = false;
|
||||
this._initialized = false;
|
||||
this._init = init;
|
||||
@ -125,7 +159,7 @@ namespace motors {
|
||||
* 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"
|
||||
//% blockId=outputMotorSetBrakeMode block="set %motor|brake %brake"
|
||||
//% brake.fieldEditor=toggleonoff
|
||||
//% weight=60 blockGap=8
|
||||
//% group="Motion"
|
||||
@ -137,7 +171,7 @@ namespace motors {
|
||||
/**
|
||||
* Reverses the motor polarity
|
||||
*/
|
||||
//% blockId=motorSetReversed block="set `icons.motorLarge` %motor|reversed %reversed"
|
||||
//% blockId=motorSetReversed block="set %motor|reversed %reversed"
|
||||
//% reversed.fieldEditor=toggleonoff
|
||||
//% weight=59
|
||||
//% group="Motion"
|
||||
@ -170,7 +204,7 @@ namespace motors {
|
||||
* 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
|
||||
@ -190,7 +224,7 @@ 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"
|
||||
@ -220,10 +254,38 @@ namespace motors {
|
||||
|
||||
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 SingleMotor extends Motor {
|
||||
export class Motor extends MotorBase {
|
||||
private _large: boolean;
|
||||
|
||||
constructor(port: Output, large: boolean) {
|
||||
@ -248,7 +310,7 @@ namespace motors {
|
||||
b.setNumber(NumberFormat.Int8LE, 2, speed)
|
||||
writePWM(b)
|
||||
if (speed) {
|
||||
writePWM(mkCmd(this._port, DAL.opOutputStart, 0))
|
||||
writePWM(mkCmd(this._port, DAL.opOutputStart, 0))
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,7 +329,7 @@ namespace motors {
|
||||
* Gets motor actual speed.
|
||||
* @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 {
|
||||
@ -276,27 +338,15 @@ namespace motors {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
angle(): number {
|
||||
this.init();
|
||||
return getMotorData(this._port).tachoCount;
|
||||
return getMotorData(this._port).count;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,34 +363,42 @@ namespace motors {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the motor
|
||||
*/
|
||||
//%
|
||||
toString(): string {
|
||||
return `${this._large ? "" : "M"}${this._portName} ${this.speed()}% ${this.angle()}>`;
|
||||
}
|
||||
}
|
||||
|
||||
//% whenUsed fixedInstance block="large A"
|
||||
export const largeA = new SingleMotor(Output.A, true);
|
||||
export const largeA = new Motor(Output.A, true);
|
||||
|
||||
//% whenUsed fixedInstance block="large B"
|
||||
export const largeB = new SingleMotor(Output.B, true);
|
||||
export const largeB = new Motor(Output.B, true);
|
||||
|
||||
//% whenUsed fixedInstance block="large C"
|
||||
export const largeC = new SingleMotor(Output.C, true);
|
||||
export const largeC = new Motor(Output.C, true);
|
||||
|
||||
//% whenUsed fixedInstance block="large D"
|
||||
export const largeD = new SingleMotor(Output.D, true);
|
||||
export const largeD = new Motor(Output.D, true);
|
||||
|
||||
//% whenUsed fixedInstance block="medium A"
|
||||
export const mediumA = new SingleMotor(Output.A, false);
|
||||
export const mediumA = new Motor(Output.A, false);
|
||||
|
||||
//% whenUsed fixedInstance block="medium B"
|
||||
export const mediumB = new SingleMotor(Output.B, false);
|
||||
export const mediumB = new Motor(Output.B, false);
|
||||
|
||||
//% whenUsed fixedInstance block="medium C"
|
||||
export const mediumC = new SingleMotor(Output.C, false);
|
||||
export const mediumC = new Motor(Output.C, false);
|
||||
|
||||
//% whenUsed fixedInstance block="medium D"
|
||||
export const mediumD = new SingleMotor(Output.D, false);
|
||||
export const mediumD = new Motor(Output.D, false);
|
||||
|
||||
//% fixedInstances
|
||||
export class SynchedMotorPair extends Motor {
|
||||
export class SynchedMotorPair extends MotorBase {
|
||||
|
||||
constructor(ports: Output) {
|
||||
super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
|
||||
@ -449,6 +507,20 @@ namespace motors {
|
||||
const steering = (speedRight * 100 / speedLeft) >> 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//% whenUsed fixedInstance block="large B+C"
|
||||
@ -464,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) {
|
||||
@ -476,7 +548,7 @@ namespace motors {
|
||||
return 0
|
||||
}
|
||||
|
||||
interface MotorData {
|
||||
export interface MotorData {
|
||||
actualSpeed: number; // -100..+100
|
||||
tachoCount: number;
|
||||
count: number;
|
||||
@ -485,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),
|
||||
@ -493,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;
|
||||
|
@ -10,6 +10,7 @@
|
||||
"linux.cpp",
|
||||
"mmap.cpp",
|
||||
"control.cpp",
|
||||
"console.ts",
|
||||
"serialnumber.cpp",
|
||||
"buttons.ts",
|
||||
"png.cpp",
|
||||
|
@ -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;
|
||||
@ -156,14 +158,14 @@ namespace brick {
|
||||
//% image.fieldOptions.columns=6
|
||||
//% image.fieldOptions.hasSearchBar=true
|
||||
//% group="Screen" weight=0 blockHidden=1
|
||||
export function _imagePicker(image: Image): Image {
|
||||
export function __imagePicker(image: Image): Image {
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the screen
|
||||
*/
|
||||
//% blockId=screen_clear_screen block="`icons.brickDisplay` clear screen"
|
||||
//% 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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())
|
||||
})
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
//% color="#68C3E2" weight=100
|
||||
//% groups='["Light", "Buttons", "Screen"]'
|
||||
//% groups='["Buttons", "Screen"]'
|
||||
//% labelLineWidth=0
|
||||
namespace brick {
|
||||
}
|
||||
@ -24,7 +24,7 @@ namespace music {
|
||||
|
||||
}
|
||||
|
||||
//% color="#48150C"
|
||||
//% color="#5F3109"
|
||||
namespace control {
|
||||
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
@ -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
|
||||
```
|
9
libs/tests/_locales/tests-jsdoc-strings.json
Normal 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"
|
||||
}
|
6
libs/tests/_locales/tests-strings.json
Normal 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
@ -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
@ -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);
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pxt-ev3",
|
||||
"version": "0.0.41",
|
||||
"version": "0.0.47",
|
||||
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
|
@ -15,7 +15,8 @@
|
||||
"libs/ultrasonic-sensor",
|
||||
"libs/infrared-sensor",
|
||||
"libs/gyro-sensor",
|
||||
"libs/ev3"
|
||||
"libs/ev3",
|
||||
"libs/tests"
|
||||
],
|
||||
"simulator": {
|
||||
"autoRun": true,
|
||||
@ -52,8 +53,10 @@
|
||||
"serial": {
|
||||
"vendorId": "0x0694",
|
||||
"productId": "0x0005",
|
||||
"rawHID": true
|
||||
},
|
||||
"rawHID": true,
|
||||
"useEditor": true,
|
||||
"log": true
|
||||
},
|
||||
"runtime": {
|
||||
"mathBlocks": true,
|
||||
"loopsBlocks": true,
|
||||
@ -129,16 +132,14 @@
|
||||
"logic": "#1E5AA8",
|
||||
"math": "#9DC3F7",
|
||||
"variables": "#B40000",
|
||||
"text": "#F0890A",
|
||||
"text": "#FCAC00",
|
||||
"advanced": "#969696",
|
||||
"functions": "#064597",
|
||||
"arrays": "#890058"
|
||||
"functions": "#19325A",
|
||||
"arrays": "#901F76"
|
||||
},
|
||||
"monacoColors": {
|
||||
"editor.background": "#ecf6ff"
|
||||
},
|
||||
"simAnimationEnter": "horizontal flip in",
|
||||
"simAnimationExit": "horizontal flip out"
|
||||
}
|
||||
},
|
||||
"ignoreDocsErrors": true
|
||||
}
|
||||
|
@ -23,20 +23,6 @@ namespace pxsim {
|
||||
}
|
||||
|
||||
export class EV3Board extends CoreBoard {
|
||||
// state & update logic for component services
|
||||
// neopixelState: CommonNeoPixelState;
|
||||
buttonState: EV3ButtonState;
|
||||
slideSwitchState: SlideSwitchState;
|
||||
lightSensorState: AnalogSensorState;
|
||||
thermometerState: AnalogSensorState;
|
||||
thermometerUnitState: number;
|
||||
microphoneState: AnalogSensorState;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
capacitiveSensorState: CapacitiveSensorState;
|
||||
accelerometerState: AccelerometerState;
|
||||
touchButtonState: TouchButtonState;
|
||||
irState: InfraredState;
|
||||
|
||||
view: SVGSVGElement;
|
||||
|
||||
outputState: EV3OutputState;
|
||||
@ -86,11 +72,6 @@ namespace pxsim {
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
case "irpacket": {
|
||||
let ev = <SimulatorInfraredPacketMessage>msg;
|
||||
this.irState.receive(new RefBuffer(ev.packet));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,6 +101,9 @@ namespace pxsim {
|
||||
document.body.innerHTML = ""; // clear children
|
||||
document.body.appendChild(this.view = viewHost.getView() as SVGSVGElement);
|
||||
|
||||
this.inputNodes = [];
|
||||
this.outputNodes = [];
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@ -127,26 +111,33 @@ namespace pxsim {
|
||||
return svg.toDataUri(new XMLSerializer().serializeToString(this.view));
|
||||
}
|
||||
|
||||
//defaultNeopixelPin() {
|
||||
// return this.edgeConnectorState.getPin(CPlayPinName.D8);
|
||||
//}
|
||||
|
||||
getDefaultPitchPin() {
|
||||
return this.edgeConnectorState.getPin(CPlayPinName.D6);
|
||||
}
|
||||
|
||||
getBrickNode() {
|
||||
return this.brickNode;
|
||||
}
|
||||
|
||||
motorUsed(port:number, large: boolean) {
|
||||
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
||||
const p = 1 << i;
|
||||
if (port & p) {
|
||||
const motorPort = this.motorMap[p];
|
||||
if (!this.outputNodes[motorPort])
|
||||
this.outputNodes[motorPort] = new MotorNode(motorPort, large);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getMotor(port: number, large?: boolean): MotorNode[] {
|
||||
if (port == 0xFF) return this.getMotors(); // Return all motors
|
||||
const motorPort = this.motorMap[port];
|
||||
if (this.outputNodes[motorPort] == undefined) {
|
||||
this.outputNodes[motorPort] = large ?
|
||||
new LargeMotorNode(motorPort) : new MediumMotorNode(motorPort);
|
||||
const r = [];
|
||||
for(let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
|
||||
const p = 1 << i;
|
||||
if (port & p) {
|
||||
const motorPort = this.motorMap[p];
|
||||
const outputNode = this.outputNodes[motorPort];
|
||||
if (outputNode)
|
||||
r.push(outputNode);
|
||||
}
|
||||
}
|
||||
return [this.outputNodes[motorPort]];
|
||||
return r;
|
||||
}
|
||||
|
||||
getMotors() {
|
||||
@ -154,7 +145,7 @@ namespace pxsim {
|
||||
}
|
||||
|
||||
getSensor(port: number, type: number): SensorNode {
|
||||
if (this.inputNodes[port] == undefined) {
|
||||
if (!this.inputNodes[port]) {
|
||||
switch (type) {
|
||||
case DAL.DEVICE_TYPE_GYRO: this.inputNodes[port] = new GyroSensorNode(port); break;
|
||||
case DAL.DEVICE_TYPE_COLOR: this.inputNodes[port] = new ColorSensorNode(port); break;
|
||||
|
@ -38,7 +38,7 @@ namespace pxsim {
|
||||
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, node.getValue())
|
||||
util.map16Bit(data, AnalogOff.InPin6 + 2 * port, Math.floor(node.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace pxsim {
|
||||
export class ColorSensorNode extends UartSensorNode {
|
||||
id = NodeType.ColorSensor;
|
||||
|
||||
private color: number;
|
||||
private color: number = 50;
|
||||
|
||||
constructor(port: number) {
|
||||
super(port);
|
||||
@ -29,14 +29,10 @@ namespace pxsim {
|
||||
getDeviceType() {
|
||||
return DAL.DEVICE_TYPE_COLOR;
|
||||
}
|
||||
|
||||
|
||||
setColor(color: number) {
|
||||
this.color = color;
|
||||
this.changed = true;
|
||||
this.valueChanged = true;
|
||||
|
||||
runtime.queueDisplayUpdate();
|
||||
|
||||
this.setChangedState();
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
@ -81,7 +81,6 @@ namespace pxsim.control {
|
||||
export function dmesg(s: string) {
|
||||
console.log("DMESG: " + s)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace pxsim.output {
|
||||
|
@ -22,20 +22,14 @@ namespace pxsim {
|
||||
setAngle(angle: number) {
|
||||
if (this.angle != angle) {
|
||||
this.angle = angle;
|
||||
this.changed = true;
|
||||
this.valueChanged = true;
|
||||
|
||||
runtime.queueDisplayUpdate();
|
||||
this.setChangedState();
|
||||
}
|
||||
}
|
||||
|
||||
setRate(rate: number) {
|
||||
if (this.rate != rate) {
|
||||
this.rate = rate;
|
||||
this.changed = true;
|
||||
this.valueChanged = true;
|
||||
|
||||
runtime.queueDisplayUpdate();
|
||||
this.setChangedState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
namespace pxsim.motors {
|
||||
|
||||
export function __motorUsed(port: number, large: boolean) {
|
||||
console.log("MOTOR INIT " + port);
|
||||
const motors = ev3board().getMotor(port, large);
|
||||
//console.log("MOTOR INIT " + port);
|
||||
ev3board().motorUsed(port, large);
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ namespace pxsim.motors {
|
||||
namespace pxsim.sensors {
|
||||
|
||||
export function __sensorUsed(port: number, type: number) {
|
||||
console.log("SENSOR INIT " + port + ", type: " + type);
|
||||
//console.log("SENSOR INIT " + port + ", type: " + type);
|
||||
const sensor = ev3board().getSensor(port, type);
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
|
@ -12,8 +12,11 @@ namespace pxsim {
|
||||
namespace pxsim.output {
|
||||
|
||||
export function setLights(pattern: number) {
|
||||
const lightState = ev3board().getBrickNode().lightState;
|
||||
lightState.lightPattern = pattern;
|
||||
runtime.queueDisplayUpdate();
|
||||
const brickState = ev3board().getBrickNode();
|
||||
const lightState = brickState.lightState;
|
||||
if (lightState.lightPattern != pattern) {
|
||||
lightState.lightPattern = pattern;
|
||||
brickState.setChangedState();
|
||||
}
|
||||
}
|
||||
}
|
@ -15,11 +15,20 @@ namespace pxsim {
|
||||
MMapMethods.register("/dev/lms_motor", {
|
||||
data,
|
||||
beforeMemRead: () => {
|
||||
console.log("motor before read");
|
||||
const outputs = ev3board().outputNodes;
|
||||
// console.log("motor before read");
|
||||
for (let port = 0; port < DAL.NUM_OUTPUTS; ++port) {
|
||||
data[MotorDataOff.TachoCounts * port] = 0; // Tacho count
|
||||
data[MotorDataOff.Speed * port] = 50; // Speed
|
||||
data[MotorDataOff.TachoSensor * port] = 0; // Count
|
||||
const output = outputs[port];
|
||||
const speed = output ? outputs[port].getSpeed() : 0;
|
||||
const angle = output ? outputs[port].getAngle() : 0;
|
||||
const tci = MotorDataOff.TachoCounts + port * MotorDataOff.Size;
|
||||
const tsi = MotorDataOff.TachoSensor + port * MotorDataOff.Size;
|
||||
data[tci] = data[tci + 1] = data[tci + 2] = data[tci + 3] = 0; // Tacho count
|
||||
data[MotorDataOff.Speed + port * MotorDataOff.Size] = speed; // Speed
|
||||
data[tsi] = angle & 0xff; // Count
|
||||
data[tsi + 1] = (angle >> 8) & 0xff; // Count
|
||||
data[tsi + 2] = (angle >> 16) & 0xff; // Count
|
||||
data[tsi + 3] = (angle >> 24); // Count
|
||||
}
|
||||
},
|
||||
read: buf => {
|
||||
|
141
sim/state/motornode.ts
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
namespace pxsim {
|
||||
|
||||
export class MotorNode extends BaseNode {
|
||||
isOutput = true;
|
||||
|
||||
public angle: number = 0;
|
||||
|
||||
private speed: number;
|
||||
private large: boolean;
|
||||
private rotation: number;
|
||||
private polarity: boolean;
|
||||
|
||||
constructor(port: number) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
setSpeed(speed: number) {
|
||||
if (this.speed != speed) {
|
||||
this.speed = speed;
|
||||
this.changed = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
setLarge(large: boolean) {
|
||||
this.large = large;
|
||||
}
|
||||
|
||||
getSpeed() {
|
||||
return this.speed;
|
||||
}
|
||||
|
||||
stepSpeed(speed: number, angle: number, brake: boolean) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
setPolarity(polarity: number) {
|
||||
// Either 1 or 255 (reverse)
|
||||
this.polarity = polarity === 255;
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
reset() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
stop() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
start() {
|
||||
// TODO: implement
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
export class MediumMotorNode extends MotorNode {
|
||||
id = NodeType.MediumMotor;
|
||||
|
||||
constructor(port: number) {
|
||||
super(port);
|
||||
}
|
||||
}
|
||||
|
||||
export class LargeMotorNode extends MotorNode {
|
||||
id = NodeType.LargeMotor;
|
||||
|
||||
constructor(port: number) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -32,5 +32,17 @@ namespace pxsim {
|
||||
this.changed = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
setChangedState() {
|
||||
this.changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates any internal state according to the elapsed time since the last call to `updateState`
|
||||
* @param elapsed
|
||||
*/
|
||||
updateState(elapsed: number) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -34,24 +34,32 @@ namespace pxsim {
|
||||
motors.forEach(motor => motor.reset());
|
||||
return 2;
|
||||
}
|
||||
case DAL.opOutputStepSpeed: {
|
||||
case DAL.opOutputClearCount:
|
||||
const port = buf.data[1];
|
||||
const motors = ev3board().getMotor(port);
|
||||
motors.forEach(motor => motor.clearCount());
|
||||
break;
|
||||
case DAL.opOutputStepPower:
|
||||
case DAL.opOutputStepSpeed:
|
||||
case DAL.opOutputTimePower:
|
||||
case DAL.opOutputTimeSpeed: {
|
||||
// step speed
|
||||
const port = buf.data[1];
|
||||
const speed = buf.data[2];
|
||||
// note that b[3] is padding
|
||||
const step1 = buf.data[4];
|
||||
const step2 = buf.data[5]; // angle
|
||||
const step3 = buf.data[6];
|
||||
const brake = buf.data[7];
|
||||
const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2); // signed byte
|
||||
// note that b[3] is padding
|
||||
const step1 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 4);
|
||||
const step2 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 8);
|
||||
const step3 = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int32LE, 12);
|
||||
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 16);
|
||||
//console.log(buf);
|
||||
const motors = ev3board().getMotor(port);
|
||||
motors.forEach(motor => motor.stepSpeed(speed, step2, brake === 1));
|
||||
motors.forEach(motor => motor.setSpeedCmd(cmd, [speed, step1, step2, step3, brake]));
|
||||
return 2;
|
||||
}
|
||||
case DAL.opOutputStop: {
|
||||
// stop
|
||||
const port = buf.data[1];
|
||||
const brake = buf.data[2];
|
||||
const brake = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
|
||||
const motors = ev3board().getMotor(port);
|
||||
motors.forEach(motor => motor.stop());
|
||||
return 2;
|
||||
@ -59,9 +67,9 @@ namespace pxsim {
|
||||
case DAL.opOutputSpeed: {
|
||||
// setSpeed
|
||||
const port = buf.data[1];
|
||||
const speed = buf.data[2];
|
||||
const speed = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
|
||||
const motors = ev3board().getMotor(port);
|
||||
motors.forEach(motor => motor.setSpeed(speed));
|
||||
motors.forEach(motor => motor.setSpeedCmd(cmd, [speed]));
|
||||
return 2;
|
||||
}
|
||||
case DAL.opOutputStart: {
|
||||
@ -74,7 +82,7 @@ namespace pxsim {
|
||||
case DAL.opOutputPolarity: {
|
||||
// reverse
|
||||
const port = buf.data[1];
|
||||
const polarity = buf.data[2];
|
||||
const polarity = pxsim.BufferMethods.getNumber(buf, BufferMethods.NumberFormat.Int8LE, 2);
|
||||
const motors = ev3board().getMotor(port);
|
||||
motors.forEach(motor => motor.setPolarity(polarity));
|
||||
return 2;
|
||||
@ -86,6 +94,9 @@ namespace pxsim {
|
||||
motors.forEach(motor => motor.setLarge(large));
|
||||
return 2;
|
||||
}
|
||||
default:
|
||||
console.warn('unknown cmd: ' + cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("pwm write");
|
||||
|
@ -7,6 +7,7 @@ namespace pxsim {
|
||||
|
||||
|
||||
export class EV3ScreenState {
|
||||
changed: boolean = true;
|
||||
points: Uint8Array;
|
||||
constructor() {
|
||||
this.points = new Uint8Array(visuals.SCREEN_WIDTH * visuals.SCREEN_HEIGHT)
|
||||
@ -23,13 +24,13 @@ namespace pxsim {
|
||||
|
||||
setPixel(x: number, y: number, v: number) {
|
||||
this.applyMode(OFF(x, y), v)
|
||||
runtime.queueDisplayUpdate();
|
||||
this.changed = true;
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (let i = 0; i < this.points.length; ++i)
|
||||
this.points[i] = 0;
|
||||
runtime.queueDisplayUpdate();
|
||||
this.changed = true;
|
||||
}
|
||||
|
||||
blitLineCore(x: number, y: number, w: number, buf: RefBuffer, mode: Draw, offset = 0) {
|
||||
@ -58,7 +59,7 @@ namespace pxsim {
|
||||
}
|
||||
}
|
||||
|
||||
runtime.queueDisplayUpdate();
|
||||
this.changed = true;
|
||||
}
|
||||
|
||||
clearLine(x: number, y: number, w: number) {
|
||||
@ -72,6 +73,12 @@ namespace pxsim {
|
||||
off++
|
||||
}
|
||||
}
|
||||
|
||||
didChange() {
|
||||
const res = this.changed;
|
||||
this.changed = false;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,11 @@ namespace pxsim {
|
||||
this.valueChanged = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
setChangedState() {
|
||||
this.changed = true;
|
||||
this.valueChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
export class AnalogSensorNode extends SensorNode {
|
||||
|
@ -14,8 +14,7 @@ namespace pxsim {
|
||||
|
||||
public setPressed(pressed: boolean) {
|
||||
this.pressed.push(pressed);
|
||||
this.changed = true;
|
||||
this.valueChanged = true;
|
||||
this.setChangedState();
|
||||
}
|
||||
|
||||
public isPressed() {
|
||||
|
@ -88,10 +88,10 @@ namespace pxsim {
|
||||
const inputNodes = ev3board().getInputNodes();
|
||||
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
||||
const node = inputNodes[port];
|
||||
if (node) {
|
||||
if (node && node.isUart()) {
|
||||
// Actual
|
||||
const index = 0; //UartOff.Actual + port * 2;
|
||||
data[UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index] = node.getValue();
|
||||
util.map16Bit(data, UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index, Math.floor(node.getValue()))
|
||||
// Status
|
||||
data[UartOff.Status + port] = node.valueChange() ? UartStatus.UART_PORT_CHANGED : UartStatus.UART_DATA_READY;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ namespace pxsim {
|
||||
export class UltrasonicSensorNode extends UartSensorNode {
|
||||
id = NodeType.UltrasonicSensor;
|
||||
|
||||
private distance: number = 50;
|
||||
private distance: number = 127; // in cm
|
||||
|
||||
constructor(port: number) {
|
||||
super(port);
|
||||
@ -17,15 +17,12 @@ namespace pxsim {
|
||||
setDistance(distance: number) {
|
||||
if (this.distance != distance) {
|
||||
this.distance = distance;
|
||||
this.changed = true;
|
||||
this.valueChanged = true;
|
||||
|
||||
runtime.queueDisplayUpdate();
|
||||
this.setChangedState();
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.distance;
|
||||
return this.distance * 10; // convert to 0.1 cm
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
namespace pxsim.util {
|
||||
|
||||
export function map16Bit(buffer: Uint8Array, index: number, value: number) {
|
||||
buffer[index] = (value >> 8) & 0xff;
|
||||
buffer[index+1] = value & 0xff;
|
||||
buffer[index] = value & 0xFF;
|
||||
buffer[index + 1] = (value >> 8) & 0xFF;
|
||||
}
|
||||
}
|
@ -1,69 +1,87 @@
|
||||
<svg id="e13b39e7-895d-4931-9be9-7aecaf709784" data-name="46d2506d-be16-4175-90b0-aabf357ea333" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 79.56 153.61">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82 156">
|
||||
<defs>
|
||||
<clipPath id="7dd75c25-af24-4f64-9ea1-f270fbd17872">
|
||||
<path d="M12.82.54a17.07,17.07,0,0,1,20.77,12.3,1.53,1.53,0,0,1,1.6-.24l.21.19,10.41,10.1c.31.3.47.3.47,1.14V35.81h6.79a4,4,0,0,1,4,4V65.63a4,4,0,0,1-4,4H46.15v11.8l13.68-.1h.61s7.76,9.81,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78H69.17v7.57A4,4,0,0,1,67,151.75V153h0a.63.63,0,0,1-.61.61H40.08a.6.6,0,0,1-.64-.56V152a4,4,0,0,1-2.89-3.85v-7.57H30.07a5.78,5.78,0,0,1-5.78-5.78v-8.18h-6.8a4,4,0,0,1-4-4V96.84a4,4,0,0,1,4-4h6.8V92l.75-.78v-10L14.13,70.71c-.25-.27-.39-.25-.39-.67V34.27l.37-.39A17.06,17.06,0,0,1,12.82.54Z" style="fill: none"/>
|
||||
<clipPath id="clip-path" transform="translate(0.98 1.9)">
|
||||
<path d="M79,17.05A17.07,17.07,0,0,1,64.92,33.86l.36.39V70c0,.42-.14.4-.39.68L54,81.22v10l.75.78v.79h6.79a4,4,0,0,1,4,4v25.82a4,4,0,0,1-4,4H54.75v8.18A5.79,5.79,0,0,1,49,140.61h-6.5v7.57a4,4,0,0,1-2.9,3.84v1a.6.6,0,0,1-.6.6H12.69a.6.6,0,0,1-.6-.6h0v-1.29a4,4,0,0,1-2.17-3.55v-7.57H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59l13.68.1,0-11.8H26a4,4,0,0,1-4-4V39.79a4,4,0,0,1,4-4h6.78l0-11.77c0-.84.15-.84.46-1.14l10.41-10.1.22-.19a1.53,1.53,0,0,1,1.59.23A17.07,17.07,0,0,1,79,17.05Z" style="fill: none"/>
|
||||
</clipPath>
|
||||
<clipPath id="9d9a0722-eca6-4c98-bf03-49097e654f0e">
|
||||
<path d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78V92l9.26-9.58a2.29,2.29,0,0,1,2.38-1c4.4,0,23.86-.15,23.86-.15h.65s7.76,9.8,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/>
|
||||
<clipPath id="clip-path-2" transform="translate(0.98 1.9)">
|
||||
<path d="M49,140.61H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59s19.46.14,23.87.14a2.27,2.27,0,0,1,2.37,1L54.75,92v42.79A5.79,5.79,0,0,1,49,140.61Z" style="fill: none"/>
|
||||
</clipPath>
|
||||
<clipPath id="0f29df77-bfe3-4749-8432-7a5b3681f927">
|
||||
<path d="M35.85,135.26a5.78,5.78,0,0,1-5.78-5.78h0V92.6h0a5.78,5.78,0,0,1,5.77-5.79H57.17S64.08,96,72.73,107.22v22.25h0A5.78,5.78,0,0,1,67,135.26H35.85Z" style="fill: none"/>
|
||||
<clipPath id="clip-path-3" transform="translate(0.98 1.9)">
|
||||
<path d="M43.18,135.24H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.21C15,95.94,21.86,86.8,21.86,86.8H43.18A5.78,5.78,0,0,1,49,92.58h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<title>Large Motor</title>
|
||||
<g style="clip-path: url(#7dd75c25-af24-4f64-9ea1-f270fbd17872)">
|
||||
<g>
|
||||
<path id="1614620b-40ec-4523-b90a-428aad6bd045" data-name="eadcf40d-5b1f-4f82-8d01-d628e9e4bcbb" d="M40.49,64.16V41.26A5.48,5.48,0,0,1,46,35.8h5.62A5.48,5.48,0,0,1,57,41.26v22.9h0a5.45,5.45,0,0,1-5.46,5.46H46A5.45,5.45,0,0,1,40.49,64.16Z" style="fill: #a8a9a8"/>
|
||||
<circle id="1a43435f-56ec-4b4e-a3ae-b40d13b2f218" data-name="93612c19-30fe-4c1d-bb05-67002522431d" cx="51.72" cy="64.32" r="3.79" style="fill: #fff"/>
|
||||
<circle id="43fd443a-13b5-48ef-af02-32d5de3e95b1" data-name="aee14a95-6af8-4dce-8543-2eda3545de29" cx="51.72" cy="41.11" r="3.79" style="fill: #fff"/>
|
||||
<path id="7bfb4e09-fb97-433a-8104-a0426c319fe6" data-name="1d2e9494-7cc7-48f0-9562-12d1d83b7bb0" d="M39.44,153v-5.29a.61.61,0,0,1,.61-.61H66.34a.61.61,0,0,1,.61.61h0V153h0a.63.63,0,0,1-.61.61H40.08a.61.61,0,0,1-.64-.58Z" style="fill: #a8a9a8"/>
|
||||
<path id="ded8d34f-3eb8-4d18-bbd7-80e03d734fef" data-name="ccd9d8b0-00ff-4f0b-bfdc-94153d199419" d="M36.58,151.58V136.89a.61.61,0,0,1,.61-.61H68.51a.61.61,0,0,1,.61.61h0v14.69a.61.61,0,0,1-.61.61H37.19a.61.61,0,0,1-.61-.61Z" style="fill: #a8a9a8"/>
|
||||
<path id="a30c6f9d-adb5-4251-ad04-1bd8bfaab7af" data-name="e8b462c5-81f0-4111-9486-bb5fc1221c77" d="M55.44,59.6V57.8c0-.87-.81-.46-.81-.46l-1.19.59c-.38.18-.33-.34-.33-.34V55c0-.72.82-.77.82-.77h1.25s.26-.2.26-1.38-.29-1.32-.29-1.32H53.9a.68.68,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1-.05-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H48.56s-.3.18-.3,1.32.3,1.38.3,1.38h1.38a.73.73,0,0,1,.73.77v2.62c0,.52-.36.34-.36.34l-1-.59c-.68-.38-1.23,0-1.23.46v1.8s0,1,1.23.1a5.08,5.08,0,0,1,3-.7,4.18,4.18,0,0,1,2.35.7C55.37,60.14,55.44,59.6,55.44,59.6Z" style="fill: #fff"/>
|
||||
<path id="c7805530-90df-4e25-868f-c9475cacc294" data-name="51a46c0a-a8a3-4420-b297-1d82512a0b32" d="M47.79,46.14c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.84,5.72,5.72,0,0,0,2.76-.84.85.85,0,0,1,.31-.09.71.71,0,0,1,.76.65V47.9s-.24,1-1.07.6a5,5,0,0,0-2.76-.77A6.46,6.46,0,0,0,49,48.5a.68.68,0,0,1-.24.08.83.83,0,0,1-1-.61s0,0,0-.07S47.76,46.94,47.79,46.14Z" style="fill: #fff"/>
|
||||
<path id="f8fcbff1-7e8c-4c85-8f0e-222f81e36654" data-name="be4fbc2c-74e0-495a-bfad-cf557a83a287" d="M13.75,70V34.27L33.21,13.56c0-.84,1.4-1.36,2-1l.21.19,10.42,10.1c.31.3.46.3.46,1.14l-.19,59-18.57.67-13.4-13C13.89,70.44,13.75,70.46,13.75,70Z" style="fill: #a8a9a8"/>
|
||||
<path id="1dc809f3-4ab0-4e83-b8a0-ab178c9c843d" data-name="4d02192d-b81d-48fe-8732-1ddf5bded46b" d="M35.44,82.8l10.67-.57v-8l-4.43-4.7H30.05a1.6,1.6,0,0,0-1.16.4L25.34,73.6c-.28.27-.3,1.12-.3,1.12V92Z" style="fill: #a8a9a8"/>
|
||||
<g id="e0794b99-de5a-4337-b43b-7cdb158e4272" data-name="233cb9b7-9706-41a9-a0a4-7b51b61b2ff5">
|
||||
<path id="c86708aa-fcc1-4e15-8d85-6e5415398b0c" data-name="6ed465b7-465a-4389-8ebc-d8f92a4226dd" d="M13.49,121.2V98.29A5.46,5.46,0,0,1,19,92.84h5.62A5.45,5.45,0,0,1,30,98.3v22.9h0a5.48,5.48,0,0,1-5.46,5.46H19A5.48,5.48,0,0,1,13.49,121.2Z" style="fill: #a8a9a8"/>
|
||||
<circle id="0355a228-ed20-4633-ba10-c36cf551e228" data-name="955ad5d6-af09-42ad-8d56-9ff62c40912d" cx="18.35" cy="121.35" r="3.79" style="fill: #fff"/>
|
||||
<circle id="73922eab-afce-48dd-9bae-80da92a8b392" data-name="75a0ff09-d40c-46e3-aaf0-c5878f2efdac" cx="18.35" cy="98.14" r="3.79" style="fill: #fff"/>
|
||||
<path id="e9f7b6a6-dcdd-4d55-a7bb-ad3951bb1e39" data-name="eeb5a6b6-ef4a-4f05-95cd-8b1c3eb419c4" d="M22.07,116.64v-1.8c0-.86-.81-.46-.81-.46L20,115c-.38.18-.33-.34-.33-.34V112c0-.72.82-.76.82-.76h1.25s.32-.26.29-1.39-.29-1.32-.29-1.32H20.53a.67.67,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1,0-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H15.19s-.3.18-.3,1.32.3,1.39.3,1.39h1.38a.71.71,0,0,1,.73.69v2.69c0,.52-.36.34-.36.34l-1-.58c-.68-.39-1.23,0-1.23.46v1.8s0,1,1.23.1a5.09,5.09,0,0,1,3-.71,4.16,4.16,0,0,1,2.35.71C22,117.17,22.07,116.64,22.07,116.64Z" style="fill: #fff"/>
|
||||
<path id="82138da6-9b2a-4b9a-952e-568841e5ed52" data-name="80481c02-bb01-43aa-bd64-15ab30fbbb5e" d="M14.44,103.18c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.83,5.78,5.78,0,0,0,2.77-.83.7.7,0,0,1,1,.25.67.67,0,0,1,.1.31v1.76s-.24,1-1.07.59a5,5,0,0,0-2.77-.77,6.59,6.59,0,0,0-2.85.77.68.68,0,0,1-.24.08.83.83,0,0,1-1-.63v0S14.44,104,14.44,103.18Z" style="fill: #fff"/>
|
||||
</g>
|
||||
<path id="aa62b88a-4cd5-4c76-acaf-e85a7fd45341" data-name="6b63d025-68d6-47eb-ba62-60017f4a1dd4" d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78h0V92l9.26-9.57a2.29,2.29,0,0,1,2.38-1c4.4,0,23.87-.14,23.87-.14h.64s7.76,9.8,18.13,23.86v29.64h0a5.79,5.79,0,0,1-5.79,5.79Z" style="fill: #a8a9a8"/>
|
||||
<g>
|
||||
<g id="1172473e-5b0c-4dd6-85c5-28f5593b9312" data-name="a0498222-7e69-48db-90b7-05f2ce124a03">
|
||||
<g style="clip-path: url(#9d9a0722-eca6-4c98-bf03-49097e654f0e)">
|
||||
<g id="da9d77a3-c9b0-4118-a8e3-c65ec5cbe7d4" data-name="8270fb93-0862-4480-a5b8-675cc77ac153">
|
||||
<path id="77253f44-139e-4b90-82df-465a787cab9c" data-name="bb0a4ec3-45a5-4c91-b064-db5494e447e6" d="M78.56,142.76l1-7.69V103.85L66.49,87.72v14.86L42.7,133.66,35.55,141Z" style="fill: #f2f2f2"/>
|
||||
</g>
|
||||
<g style="isolation: isolate">
|
||||
<g id="e13b39e7-895d-4931-9be9-7aecaf709784">
|
||||
<g style="clip-path: url(#clip-path)">
|
||||
<g id="Large_Motor" data-name="Large Motor">
|
||||
<path id="LM_back2" data-name="LM back2" d="M39,153.62H12.69a.6.6,0,0,1-.6-.6h0v-5.3a.6.6,0,0,1,.6-.6H39a.6.6,0,0,1,.6.6V153A.6.6,0,0,1,39,153.62Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
<g>
|
||||
<image width="36" height="20" transform="translate(9 136)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAVCAYAAADB5CeuAAAACXBIWXMAAAsSAAALEgHS3X78AAAAp0lEQVRIS+3WsW3DMBBG4Y+GSmcGp/cAHsuzeIasFPdWVpD6c0FKcBWwOxV8wIEgSIAPvOL+EhFKKcUBiIiAgjO+25rJildErJMqdMdVlcwg8MQDv5P6Q1fc5ErRujW1TfmoLPa3T//dymJI9TKkehlSvQypXoZUL5vUNhAz2R1Oao75w9IOMmpRHVZqSpjxo3KRw+YwQ2lx+EsVykqfK+aIWGhSR+MNDy9ID5XUCOsAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_back1-2" data-name="LM back1-2" d="M41.86,152.17H10.53a.6.6,0,0,1-.6-.6v-14.7a.6.6,0,0,1,.6-.6H41.86a.6.6,0,0,1,.6.6h0v14.7A.6.6,0,0,1,41.86,152.17Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="9d05c10c-72eb-4f9f-8517-e403ec636357" data-name="74e07bdd-431b-40c1-b0f3-71128f2a0c08" d="M35.85,135.56a5.78,5.78,0,0,1-5.78-5.78h0V92.9h0a5.78,5.78,0,0,1,5.77-5.79H57.17s6.91,9.15,15.56,20.41v22.25h0A5.78,5.78,0,0,1,67,135.56H35.85Z" style="fill: #a8a9a8"/>
|
||||
<g id="6df8eb1f-3187-427f-8d99-bc7a37e6cc25" data-name="9bbfb3ec-86c2-4d27-8275-76f2f9737a7f">
|
||||
<g id="e4d7143c-1fb6-45a4-a205-1c920b8a823c" data-name="7e017c33-f09b-4d39-803f-1bedf1ebf566">
|
||||
<g id="3a4e493d-cd17-48c4-97b6-da7ee3c4898e" data-name="69e3df75-72a6-4499-baab-f65ec194e193">
|
||||
<g style="clip-path: url(#0f29df77-bfe3-4749-8432-7a5b3681f927)">
|
||||
<g id="7cada08f-1e90-4ef2-8ae4-3f2adfd3e953" data-name="b7007e32-15c9-40b3-a885-a7231fd104d1">
|
||||
<path id="02e64b51-bd56-4079-9736-8a269cb37029" data-name="c599a4f4-ae5d-411c-902d-dbcde04a2d6e" d="M60.44,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112Zm-3.61,0a1.09,1.09,0,1,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112ZM46,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6A1.08,1.08,0,0,1,47,140.65h0a1.07,1.07,0,0,1-1-1.11V112Z" style="fill: #6a6a6a"/>
|
||||
<g id="LM_rightside" data-name="LM rightside">
|
||||
<path id="LM_rightside5" data-name="LM rightside5" d="M33.08,69.61H27.47A5.45,5.45,0,0,1,22,64.15h0V41.25a5.45,5.45,0,0,1,5.46-5.46h5.61a5.46,5.46,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,33.08,69.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
<circle id="LM_rightside4" data-name="LM rightside4" cx="28.3" cy="66.21" r="3.79" style="fill: #fff"/>
|
||||
<circle id="LM_rightside3" data-name="LM rightside3" cx="28.3" cy="43" r="3.79" style="fill: #fff"/>
|
||||
<path id="LM_rightside2" data-name="LM rightside2" d="M24.4,59.69A4.16,4.16,0,0,1,26.74,59a5.23,5.23,0,0,1,3,.71c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.85-1.23-.46l-1,.58s-.36.19-.36-.33V55s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.39-.29-1.32-.29-1.32H29.1s-.73,0-.73-.65v-1.3s-.23-.36-1.17-.3a3.56,3.56,0,0,0-1.4.3v1.3a.66.66,0,0,1-.66.65c-.63,0-1.26,0-1.26,0s-.25.19-.29,1.32.29,1.39.29,1.39h1.26s.8.05.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.41-.82.46,0,1.8,0,1.8S23.67,60.12,24.4,59.69Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
<path id="LM_rightside1" data-name="LM rightside1" d="M31.25,47.89a.84.84,0,0,1-1,.68.85.85,0,0,1-.24-.09,6.63,6.63,0,0,0-2.85-.77,5.11,5.11,0,0,0-2.77.77c-.83.4-1.07-.59-1.07-.59V46.13a.72.72,0,0,1,.76-.66.67.67,0,0,1,.31.1,5.79,5.79,0,0,0,2.77.84,7.12,7.12,0,0,0,2.85-.84s1.18-.24,1.2.56S31.25,47.89,31.25,47.89Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
</g>
|
||||
<g>
|
||||
<image width="36" height="76" transform="translate(32 12)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAABNCAYAAAAhOa00AAAACXBIWXMAAAsSAAALEgHS3X78AAACAElEQVRoQ+3avVHDMBiA4VdcypgREmo4RggLpKKHAZiIAWgoKZggrEAGCBsQpxeFpNgJ/vkkK5YLvXe6g2Cb53zE9oVPaa3xSSmlALTvjh4pn2MrpQpgab/dAYdL4MQoC3oAnuxLH8AW2Gmty9YdAxKhaqAXYAUozJnaAm/AJiZs1rfBGegBKOyP7oAFMLfbRYN1ojpAYM7WtX3dbR8F1orqAdVz27n9BsMaUR4gV1yY1vpkYX7BGvgE9oD2WL/AO3CPfROFrKs6MOAMnVcAt3b57lsV6QzV194eYw0UfWelacUGRYFdAjQYNsPcy54xV+rwv4P/Bb8jZ5gr8oK4IFcQzF2nVOdWw/KG9d77IuUFGwsFHrAxUSCEjY0CASwFCnpgqVDQAUuJghZYahQY2Ao4YJ77v08eXRJWUHvenwoKaneVKaGOZZS0jJKWUdIySlpGScsoaRklLaOkZZS0jJKWUdIySlpGScsoaRklLaOkTQml3RdTQZXAD+bD2EmgSuALMwmyg/SoEtgAr9RGU1KiGkGQDtUKgjSoThCMj+oFwbgoEQjGQ4lBMA7KCwSXR3mDoPonpBsFccWYUwgCgUEdMAN/YDBzzKjJvG0nQe7W4Q2CCnFDhVgCj1TznCHtGDDVqLTWxylXW4wzdWDA/GfjnOcZMijddGBhouHTsfsDst/cEY24RzwAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_level4-2" data-name="LM level4-2" d="M64.91,70.7,51.5,83.63,32.93,83,32.76,24c0-.84.16-.84.46-1.13l10.42-10.1.21-.19c.59-.4,2,.11,2,.95L65.29,34.25V70C65.3,70.44,65.16,70.43,64.91,70.7Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g>
|
||||
<image width="25" height="27" transform="translate(32 69)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAAACXBIWXMAAAsSAAALEgHS3X78AAABYUlEQVRIS+2WvVXDMBRG7/NxGVf0CRVFchghWSAVA2QAxmEGGgZgguzgHlPTELv/KCSD/BMs2wkV9xwf2/LPlZ70JJkk2piZdQojUd8PAWuXm1kGrIBF3wcDVEAhqWw/aIi8ZAcccLKxFMAzcGzL0voikDwCWyBjPGt8JMysKfMtMuAeeAE+Ac04TsArsAcySUj6blGGq82aaS0JyXARqYDCzHJJSnzItrh+ucW1bi4ZsCQYUAmu0w9M75dzNCqc4KxLLivpkPjzJcL1Kwl/xL9oMnXC1lkdw6SBk+IyOPf3Qz9Z8DOzD73bIAXegCfiloUV8ABs/HV07qWSKjPLiSP3xwY3m+yIlKVwflXsofSVeseFHCJlKSPxlTqZ2TEoHpSNFtVIKsfIJotgnGyWCHplW3pG8GwRdGQVbqXu35zMJZAVONEdgayzr5uL33wugBvgQ1IFVxDVmJmF+Xk1UZsvIeCPQjWq5BoAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_level3-2" data-name="LM level3-2" d="M54,92V74.7a2.19,2.19,0,0,0-.3-1.12c-.28-.27-3.52-3.62-3.52-3.62a1.55,1.55,0,0,0-1.16-.4c-.75,0-11.62,0-11.62,0L33,74.26v8l10.66.57Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g id="LM_leftside" data-name="LM leftside">
|
||||
<path id="LM_leftside5" data-name="LM leftside5" d="M60.08,126.64H54.47A5.45,5.45,0,0,1,49,121.18h0V98.28a5.46,5.46,0,0,1,5.46-5.46h5.61a5.47,5.47,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,60.08,126.64Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
<circle id="LM_leftside4" data-name="LM leftside4" cx="61.67" cy="123.24" r="3.79" style="fill: #fff"/>
|
||||
<circle id="LM_leftside3" data-name="LM leftside3" cx="61.67" cy="100.03" r="3.79" style="fill: #fff"/>
|
||||
<path id="LM_leftside2" data-name="LM leftside2" d="M57.77,116.72a4.15,4.15,0,0,1,2.34-.7,5.14,5.14,0,0,1,3,.7c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.84-1.23-.46l-1,.58s-.36.19-.36-.33V112s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.38-.29-1.33-.29-1.33H62.47s-.73,0-.73-.64v-1.3s-.23-.36-1.17-.31a3.57,3.57,0,0,0-1.4.31v1.3a.66.66,0,0,1-.66.64H57.25s-.25.2-.29,1.33.29,1.38.29,1.38h1.26s.8,0,.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.4-.82.46,0,1.8,0,1.8S57,117.16,57.77,116.72Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
<path id="LM_leftside1" data-name="LM leftside1" d="M64.62,104.92a.83.83,0,0,1-1,.68.68.68,0,0,1-.24-.08,6.46,6.46,0,0,0-2.85-.77,5,5,0,0,0-2.77.77c-.83.4-1.07-.6-1.07-.6v-1.76a.71.71,0,0,1,.76-.65.8.8,0,0,1,.31.09,5.79,5.79,0,0,0,2.77.84,7,7,0,0,0,2.85-.84s1.18-.24,1.2.56S64.62,104.92,64.62,104.92Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
</g>
|
||||
<g>
|
||||
<image width="58" height="64" transform="translate(0 81)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAABBCAYAAABvnNwUAAAACXBIWXMAAAsSAAALEgHS3X78AAACFElEQVRoQ+2bQY7TMBSGvxcqsaFbJDbDrEEcYeYCcwYOwIlYsWLDjg0nGG6A1ANkbgCNhGb1WNgBpxPbaUjT+smf5E3lxPlq/25S5YmqshQiIrk+S6EzLlxmHDOKiGyB18CLXN8F6IAW6I6RXkTWi94C73HCp6YFvgI7oFXVfaY/sIBsIPoBuAG26SMWoZ/ZHfAZuJ8ivMl1SHEgess6ouDGeQtc4WMjInlhVZ3VcAPeAd+AX4Ceqf0EvgDv8Cs11hpm4Hfda1xG11q6MbbAG9+S1zFLliMGWIHBF++jNcrRmfUnu8Gd/Bo32Lnp9w4gnt+jZM+4IU0hKzxZ9sJyGiMpPFmWy8ppiqjwJNkLzWmK/no7oBWRnapqdje+8Jym2BLcdEDmp6eQnKYYrMDczJaS00lEZQvMaZZR2YJzmuSJrIGcRhmbWVM5DRnIWsxpyF9ZqzkNacB2TkP6mTWb05DGek5DGtxfn6aXb0+Du1G+wrgo/Mus2aUbknsQMEWVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUovq8le5TLwanBvbj4A2VKRwtjjvLr+g2fAb1wFxUvgFfB89NCy2APfgU/AD1V9BNio6l5E7oOOpb9u0It+5KC4aQNwINyxTonZqWiJVHENytNk3eLBU9ERqc97UovnXwArGj2U8vx34WFJ/AG0vvY6LSeZMAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_level2_grey-2" data-name="LM level2 grey-2" d="M49,140.61H6.31a5.78,5.78,0,0,1-5.78-5.78h0V105.19C10.89,91.13,18.65,81.32,18.65,81.32h.6s19.46.15,23.86.15a2.29,2.29,0,0,1,2.38,1L54.75,92v42.8A5.78,5.78,0,0,1,49,140.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g id="LM_total" data-name="LM total">
|
||||
<g id="LM_whitepart" data-name="LM whitepart">
|
||||
<g style="clip-path: url(#clip-path-2)">
|
||||
<g id="LM_whitepart_combined" data-name="LM whitepart combined">
|
||||
<path id="LM_whitepart_top" data-name="LM whitepart top" d="M43.48,141l-7.15-7.37L12.54,102.56V87.7L-.52,103.84v31.22l1,7.69Z" transform="translate(0.98 1.9)" style="fill: #f1f1f1"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<circle id="a5a29926-0401-43af-b098-0c7439485e7c" data-name="bcdc8e69-90f7-4365-8f7a-7c1567704161" cx="29.69" cy="135.22" r="2.24" style="fill: #9a9a9a"/>
|
||||
<circle id="313e9fdc-7bbb-4d49-9d19-2415450fb691" data-name="b7785d8e-a2e4-41a7-9c34-4d9f6aebd906" cx="58.36" cy="84.71" r="2.24" style="fill: #9a9a9a"/>
|
||||
<g id="1eb2ae58-2419-47d4-86bf-4f26a7f0cf61" data-name="KNOB">
|
||||
<g id="26377a78-5df7-4bea-8daa-669ea612118c" data-name="5301e639-3e28-489d-96ec-91bda6d459b7">
|
||||
<circle id="13e05316-9dcc-4be0-9e0e-4b1df48fad92" data-name="f1bc8b30-bdb2-4084-8c02-e322d1c0b9bf" cx="17.06" cy="17.06" r="17.06" style="fill: #b72b1c"/>
|
||||
<g>
|
||||
<image width="47" height="52" transform="translate(5 87)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAA1CAYAAAAHz2g0AAAACXBIWXMAAAsSAAALEgHS3X78AAABnElEQVRoQ+2azXHCMBBG3zIccQuBM5MWoIHUkAJSUQrIJQWkAlIJ6SDYd+UgGWTjHxQHr5TRm9HFIzz75G8Z8KwYY5iKiMjYnr/AdBQrUwVEpADWwGps70Qq4GiMKf2LkwRc8XvgGStxT47AG3DwJZb9+4fxin8BdkAx/InJbHFPWUQuEsaY4IUt9gn4AE6AmWl9A+/AIy49CwJxDbvBxmaOk/cpgAe8fgsWwN5k69acxdc0vvGCBFzud9jT39C6mQY3C7Sado/O6V9xk4By7ge5SQD93PcyKhBj7n0GBWLNvU+vQArFQ49AzE3bpu8JRNu0ba4EYm/aNg2BVHLvcxZIKfc+/hNIJvc+C0gv9z4LF501iUWnpo7QCvtHIaniodkDycTGZ/THXOxkAW2ygDZZQJssoE0W0CYLaJMFtMkC2mQBbbKANv9K4PdDE/PSqLMWqIAvoDEJEiElts7qfEV59iFknbD1PQFFPbexBDDGlCJy4EJs70hL4BN4pW/cpiVRcf/5nxA6Z4WgY+BpxgmsEDqntWDixFYM/ACNRtJx4q2GRAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_top_grey-2" data-name="LM top grey-2" d="M43.18,135.54H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.51C15,96.24,21.86,87.1,21.86,87.1H43.18A5.78,5.78,0,0,1,49,92.88h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g id="LM_top" data-name="LM top">
|
||||
<g style="clip-path: url(#clip-path-3)">
|
||||
<g id="LM_top_total" data-name="LM top total">
|
||||
<path id="LM_top_lines" data-name="LM top lines" d="M18.6,112.05v27.47a1.08,1.08,0,0,1-1.12,1,1.06,1.06,0,0,1-1.05-1V112.05A1.09,1.09,0,0,1,18.6,112Zm3.61,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.62,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.61,0v27.47a1.09,1.09,0,0,1-2.17.07V112.05a1.09,1.09,0,0,1,2.17-.07Zm3.62,0v27.47A1.1,1.1,0,0,1,32,140.64a1.08,1.08,0,0,1-1.11-1V112.05a1.09,1.09,0,0,1,2.17-.07Z" transform="translate(0.98 1.9)" style="fill: #6a6a6a"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<circle id="LM_detail_2" data-name="LM detail 2" cx="50.32" cy="137.11" r="2.24" style="fill: #9a9a9a"/>
|
||||
<circle id="LM_detail_1" data-name="LM detail 1" cx="21.66" cy="86.59" r="2.24" style="fill: #9a9a9a"/>
|
||||
<g id="hole">
|
||||
<g>
|
||||
<circle id="58277aee-b73f-4f42-94ef-3b959dc26807" data-name="ce4b2c57-9b3b-4aed-902e-31d9f366dc61" cx="20.12" cy="28.12" r="3.79" style="fill: #3c3c3c"/>
|
||||
<circle id="980a54d2-804e-408d-8655-debf2e5856d0" data-name="6a44a65a-f04b-4564-81a2-4eefa438c5f1" cx="28.27" cy="14" r="3.79" style="fill: #3c3c3c"/>
|
||||
<circle id="9b2e3cd0-e4bd-42ce-b558-4f4bc46eaf23" data-name="d5fea8d1-47c1-448e-adb6-8519e2516216" cx="14.15" cy="5.85" r="3.79" style="fill: #3c3c3c"/>
|
||||
<circle id="06781f30-70ec-4914-81c3-4083325e7c18" data-name="1faf9727-3903-4be3-a3d3-4ab2d5d92b1f" cx="6" cy="19.97" r="3.79" style="fill: #3c3c3c"/>
|
||||
<path id="f1de636f-1fd2-4419-ae5c-d6a853c5834d" data-name="9e52c672-4ec8-4d92-9380-e44b4f69f848" d="M13.56,16.89a2.91,2.91,0,0,0,.7,2.26,12,12,0,0,1,1.2-.39c.69-.18.95.59.95.59l.33,1.21s.33.24,1.41-.08,1.2-.62,1.2-.62-.17-.6-.32-1.22a.66.66,0,0,1,.45-.8l1.26-.34a3.63,3.63,0,0,0-.07-1.43c-.19-.93-.6-1.05-.6-1.05l-1.25.34c-.65.17-.82-.54-.82-.54l-.36-1.33s-.25-.24-1.35,0-1.26.64-1.26.64.14.61.35,1.34a.7.7,0,0,1-.48.88l-.07,0Z" style="fill: #242424"/>
|
||||
<path id="e8012624-d689-4ee7-a77a-99e9713ea199" data-name="3179af54-0c25-42bc-9804-f3575e0b1b0b" d="M17.44,24.25a.47.47,0,0,1-.56-.32l-.55-2.06h0a.45.45,0,0,1,.32-.55h0l1.63-.23,1.44-.6a.47.47,0,0,1,.56.32l.55,2.05h0a.45.45,0,0,1-.32.55h0L18.94,24Z" style="fill: #3c3c3c"/>
|
||||
<path id="31a64bab-6b92-41d0-afe9-392c1fae8ddb" data-name="841a583e-aa25-4a7e-8907-fbe64ce57066" d="M9.88,17.31a.46.46,0,0,1,.33-.56l2-.55h0a.47.47,0,0,1,.56.33L13,18.16l.6,1.45a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.57-1.53L9.84,17.3Z" style="fill: #3c3c3c"/>
|
||||
<path id="2b1c6b9e-6fda-40be-8bb8-318c30b7dca4" data-name="42515acc-688f-41c7-884c-80b8a90e09c0" d="M16.82,9.8a.47.47,0,0,1,.56.32l.55,2.05h0a.46.46,0,0,1-.33.55L16,13l-1.45.61a.45.45,0,0,1-.55-.32h0l-.53-2.1h0a.45.45,0,0,1,.31-.56h0l1.53-.57,1.55-.25Z" style="fill: #3c3c3c"/>
|
||||
<path id="be04cab5-eaac-4d03-a1ee-dbe70dd5bf90" data-name="8690082b-7091-4631-a199-b79823698dd6" d="M24.33,16.74a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.22-1.64-.6-1.44a.45.45,0,0,1,.31-.56h0l2-.55h0a.46.46,0,0,1,.56.33l.57,1.53.25,1.55Z" style="fill: #3c3c3c"/>
|
||||
<image width="38" height="38" transform="translate(44)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAnCAYAAACMo1E1AAAACXBIWXMAAAsSAAALEgHS3X78AAACRklEQVRYR9WYPXLbMBBGHzgpxRwhTJ3ER0gukMauUvl0btIlhU9gdSkzQ9dWbmBTtTYFdiUA/BGh0ILyzWBAUiTx+C0JYdeJCDlyzrlgdwU02pu2wEb7vSR3IMDNvUahUpgGuNHetAF+aG/aA+dAzoJzztV4gI/EMHOdM+AW2IhIxwxNwqlbNfAZuMXDpTBhmE3pTQ24Be6ANdAdc/HN2A+JW7d4wJphmFTpOTX+Pu/wD7YCWufcpIuDzinYF2K36t6Jp6kjdvFhFFBEooaH+ArcA8/ADh+mJdtO732vY9Uph4iQgjngCviuFx8b5F/bs451hUYxbBWxauCDtqXCOKXp8UbC+cLxp16qvTAS3lLhTJuF9xNBeC2sK84bzlQ2bYXzJ5VOtA1+5n/PvHnsNdT7twmdswmyhEKDGltchF9rKcdMPYMMrjSYyRGwVJQPaaiIpSJek5V0MHrv4D9w7pK0Bf5oT5UeKCghWeJXyQEZvfQ8uljnTHuD7J0r7Zgp4gg/iNKAvQgaXOnQRh+DZWWVbtgPT5RzsJfvhs49apuV8C4sy8jiyF38Mj0AfAt8A37zOinhbLCh7KvjfOHd4d/xO2AtA4l1BKcfx/4CDuFdWh0+4/+l/aARvVqJiHTOuQfd3bJsOULwIGu8AS3wZFNHqtEq00QhB/LXfcIJlabRKpM62HKYnLccsiPrQxlwOlgIlVWjyy0eGlhazbTjcKRwSEZ1cxYcRLXgIeca4Fq3f7JAyRUy4EIlRWuYdo5cKNNJcOfSX2jmA7RYwAeRAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<circle id="LM_red-2" data-name="LM red-2" cx="62.96" cy="18.95" r="17.06" style="fill: #d42715"/>
|
||||
</g>
|
||||
<g id="LM_details_in_red" data-name="LM details in red">
|
||||
<circle id="LM_detail_red_hole4" data-name="LM detail red hole4" cx="59.9" cy="30.01" r="3.79" style="fill: #393939"/>
|
||||
<circle id="LM_detail_red_hole3" data-name="LM detail red hole3" cx="51.75" cy="15.89" r="3.79" style="fill: #393939"/>
|
||||
<circle id="LM_detail_red_hole2" data-name="LM detail red hole2" cx="65.87" cy="7.74" r="3.79" style="fill: #393939"/>
|
||||
<circle id="LM_detail_red_hole1" data-name="LM detail red hole1" cx="74.02" cy="21.86" r="3.79" style="fill: #393939"/>
|
||||
<path id="LM_detail_hole" data-name="LM detail hole" d="M64.2,16.41a.72.72,0,0,1-.55-.91c.21-.72.36-1.33.36-1.33s-.17-.35-1.27-.64-1.35-.06-1.35-.06L61,14.8s-.16.72-.81.54L59,15s-.4.13-.6,1.06a3.44,3.44,0,0,0-.06,1.43l1.25.33a.68.68,0,0,1,.46.81c-.16.61-.33,1.21-.33,1.21s.12.3,1.2.62,1.42.09,1.42.09l.32-1.22s.26-.76,1-.59q.6.17,1.2.39a2.92,2.92,0,0,0,.7-2.25C64.91,16.65,64.2,16.41,64.2,16.41Z" transform="translate(0.98 1.9)" style="fill: #1f1f1f"/>
|
||||
<path id="LM_detail_red4" data-name="LM detail red4" d="M61.64,24.23,60.09,24l-1.53-.57a.46.46,0,0,1-.33-.56h0l.55-2.05a.46.46,0,0,1,.56-.32l1.45.6,1.63.22a.46.46,0,0,1,.33.56h0l-.55,2.05a.44.44,0,0,1-.55.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
<path id="LM_detail_red3" data-name="LM detail red3" d="M69.15,17.3l-.26,1.55-.57,1.52a.45.45,0,0,1-.55.33h0l-2.06-.55a.46.46,0,0,1-.32-.56L66,18.15l.22-1.63a.45.45,0,0,1,.55-.33h0l2,.55a.46.46,0,0,1,.32.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
<path id="LM_detail_red2" data-name="LM detail red2" d="M62.21,9.79l1.55.25,1.53.57a.46.46,0,0,1,.33.55h0l-.55,2.05a.46.46,0,0,1-.56.32l-1.45-.6-1.63-.22a.47.47,0,0,1-.33-.56h0l.55-2.05a.44.44,0,0,1,.55-.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
<path id="LM_detail_red1" data-name="LM detail red1" d="M54.7,16.72,55,15.17l.57-1.52a.45.45,0,0,1,.55-.33h0l2.06.55a.46.46,0,0,1,.32.56l-.61,1.44-.22,1.63a.45.45,0,0,1-.55.33h0l-2-.55a.46.46,0,0,1-.32-.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
@ -1,70 +1,88 @@
|
||||
namespace pxsim {
|
||||
export const LARGE_MOTOR_SVG = `<svg id="e13b39e7-895d-4931-9be9-7aecaf709784" data-name="46d2506d-be16-4175-90b0-aabf357ea333" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 79.56 153.61">
|
||||
export const LARGE_MOTOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82 156">
|
||||
<defs>
|
||||
<clipPath id="7dd75c25-af24-4f64-9ea1-f270fbd17872">
|
||||
<path d="M12.82.54a17.07,17.07,0,0,1,20.77,12.3,1.53,1.53,0,0,1,1.6-.24l.21.19,10.41,10.1c.31.3.47.3.47,1.14V35.81h6.79a4,4,0,0,1,4,4V65.63a4,4,0,0,1-4,4H46.15v11.8l13.68-.1h.61s7.76,9.81,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78H69.17v7.57A4,4,0,0,1,67,151.75V153h0a.63.63,0,0,1-.61.61H40.08a.6.6,0,0,1-.64-.56V152a4,4,0,0,1-2.89-3.85v-7.57H30.07a5.78,5.78,0,0,1-5.78-5.78v-8.18h-6.8a4,4,0,0,1-4-4V96.84a4,4,0,0,1,4-4h6.8V92l.75-.78v-10L14.13,70.71c-.25-.27-.39-.25-.39-.67V34.27l.37-.39A17.06,17.06,0,0,1,12.82.54Z" style="fill: none"/>
|
||||
<clipPath id="clip-path" transform="translate(0.98 1.9)">
|
||||
<path d="M79,17.05A17.07,17.07,0,0,1,64.92,33.86l.36.39V70c0,.42-.14.4-.39.68L54,81.22v10l.75.78v.79h6.79a4,4,0,0,1,4,4v25.82a4,4,0,0,1-4,4H54.75v8.18A5.79,5.79,0,0,1,49,140.61h-6.5v7.57a4,4,0,0,1-2.9,3.84v1a.6.6,0,0,1-.6.6H12.69a.6.6,0,0,1-.6-.6h0v-1.29a4,4,0,0,1-2.17-3.55v-7.57H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59l13.68.1,0-11.8H26a4,4,0,0,1-4-4V39.79a4,4,0,0,1,4-4h6.78l0-11.77c0-.84.15-.84.46-1.14l10.41-10.1.22-.19a1.53,1.53,0,0,1,1.59.23A17.07,17.07,0,0,1,79,17.05Z" style="fill: none"/>
|
||||
</clipPath>
|
||||
<clipPath id="9d9a0722-eca6-4c98-bf03-49097e654f0e">
|
||||
<path d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78V92l9.26-9.58a2.29,2.29,0,0,1,2.38-1c4.4,0,23.86-.15,23.86-.15h.65s7.76,9.8,18.12,23.87v29.64h0a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/>
|
||||
<clipPath id="clip-path-2" transform="translate(0.98 1.9)">
|
||||
<path d="M49,140.61H6.3a5.78,5.78,0,0,1-5.78-5.79h0V105.18C10.89,91.12,18.65,81.32,18.65,81.32h.59s19.46.14,23.87.14a2.27,2.27,0,0,1,2.37,1L54.75,92v42.79A5.79,5.79,0,0,1,49,140.61Z" style="fill: none"/>
|
||||
</clipPath>
|
||||
<clipPath id="0f29df77-bfe3-4749-8432-7a5b3681f927">
|
||||
<path d="M35.85,135.26a5.78,5.78,0,0,1-5.78-5.78h0V92.6h0a5.78,5.78,0,0,1,5.77-5.79H57.17S64.08,96,72.73,107.22v22.25h0A5.78,5.78,0,0,1,67,135.26H35.85Z" style="fill: none"/>
|
||||
<clipPath id="clip-path-3" transform="translate(0.98 1.9)">
|
||||
<path d="M43.18,135.24H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.21C15,95.94,21.86,86.8,21.86,86.8H43.18A5.78,5.78,0,0,1,49,92.58h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" style="fill: none"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<title>Large Motor</title>
|
||||
<g style="clip-path: url(#7dd75c25-af24-4f64-9ea1-f270fbd17872)">
|
||||
<g>
|
||||
<path id="1614620b-40ec-4523-b90a-428aad6bd045" data-name="eadcf40d-5b1f-4f82-8d01-d628e9e4bcbb" d="M40.49,64.16V41.26A5.48,5.48,0,0,1,46,35.8h5.62A5.48,5.48,0,0,1,57,41.26v22.9h0a5.45,5.45,0,0,1-5.46,5.46H46A5.45,5.45,0,0,1,40.49,64.16Z" style="fill: #a8a9a8"/>
|
||||
<circle id="1a43435f-56ec-4b4e-a3ae-b40d13b2f218" data-name="93612c19-30fe-4c1d-bb05-67002522431d" cx="51.72" cy="64.32" r="3.79" style="fill: #fff"/>
|
||||
<circle id="43fd443a-13b5-48ef-af02-32d5de3e95b1" data-name="aee14a95-6af8-4dce-8543-2eda3545de29" cx="51.72" cy="41.11" r="3.79" style="fill: #fff"/>
|
||||
<path id="7bfb4e09-fb97-433a-8104-a0426c319fe6" data-name="1d2e9494-7cc7-48f0-9562-12d1d83b7bb0" d="M39.44,153v-5.29a.61.61,0,0,1,.61-.61H66.34a.61.61,0,0,1,.61.61h0V153h0a.63.63,0,0,1-.61.61H40.08a.61.61,0,0,1-.64-.58Z" style="fill: #a8a9a8"/>
|
||||
<path id="ded8d34f-3eb8-4d18-bbd7-80e03d734fef" data-name="ccd9d8b0-00ff-4f0b-bfdc-94153d199419" d="M36.58,151.58V136.89a.61.61,0,0,1,.61-.61H68.51a.61.61,0,0,1,.61.61h0v14.69a.61.61,0,0,1-.61.61H37.19a.61.61,0,0,1-.61-.61Z" style="fill: #a8a9a8"/>
|
||||
<path id="a30c6f9d-adb5-4251-ad04-1bd8bfaab7af" data-name="e8b462c5-81f0-4111-9486-bb5fc1221c77" d="M55.44,59.6V57.8c0-.87-.81-.46-.81-.46l-1.19.59c-.38.18-.33-.34-.33-.34V55c0-.72.82-.77.82-.77h1.25s.26-.2.26-1.38-.29-1.32-.29-1.32H53.9a.68.68,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1-.05-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H48.56s-.3.18-.3,1.32.3,1.38.3,1.38h1.38a.73.73,0,0,1,.73.77v2.62c0,.52-.36.34-.36.34l-1-.59c-.68-.38-1.23,0-1.23.46v1.8s0,1,1.23.1a5.08,5.08,0,0,1,3-.7,4.18,4.18,0,0,1,2.35.7C55.37,60.14,55.44,59.6,55.44,59.6Z" style="fill: #fff"/>
|
||||
<path id="c7805530-90df-4e25-868f-c9475cacc294" data-name="51a46c0a-a8a3-4420-b297-1d82512a0b32" d="M47.79,46.14c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.84,5.72,5.72,0,0,0,2.76-.84.85.85,0,0,1,.31-.09.71.71,0,0,1,.76.65V47.9s-.24,1-1.07.6a5,5,0,0,0-2.76-.77A6.46,6.46,0,0,0,49,48.5a.68.68,0,0,1-.24.08.83.83,0,0,1-1-.61s0,0,0-.07S47.76,46.94,47.79,46.14Z" style="fill: #fff"/>
|
||||
<path id="f8fcbff1-7e8c-4c85-8f0e-222f81e36654" data-name="be4fbc2c-74e0-495a-bfad-cf557a83a287" d="M13.75,70V34.27L33.21,13.56c0-.84,1.4-1.36,2-1l.21.19,10.42,10.1c.31.3.46.3.46,1.14l-.19,59-18.57.67-13.4-13C13.89,70.44,13.75,70.46,13.75,70Z" style="fill: #a8a9a8"/>
|
||||
<path id="1dc809f3-4ab0-4e83-b8a0-ab178c9c843d" data-name="4d02192d-b81d-48fe-8732-1ddf5bded46b" d="M35.44,82.8l10.67-.57v-8l-4.43-4.7H30.05a1.6,1.6,0,0,0-1.16.4L25.34,73.6c-.28.27-.3,1.12-.3,1.12V92Z" style="fill: #a8a9a8"/>
|
||||
<g id="e0794b99-de5a-4337-b43b-7cdb158e4272" data-name="233cb9b7-9706-41a9-a0a4-7b51b61b2ff5">
|
||||
<path id="c86708aa-fcc1-4e15-8d85-6e5415398b0c" data-name="6ed465b7-465a-4389-8ebc-d8f92a4226dd" d="M13.49,121.2V98.29A5.46,5.46,0,0,1,19,92.84h5.62A5.45,5.45,0,0,1,30,98.3v22.9h0a5.48,5.48,0,0,1-5.46,5.46H19A5.48,5.48,0,0,1,13.49,121.2Z" style="fill: #a8a9a8"/>
|
||||
<circle id="0355a228-ed20-4633-ba10-c36cf551e228" data-name="955ad5d6-af09-42ad-8d56-9ff62c40912d" cx="18.35" cy="121.35" r="3.79" style="fill: #fff"/>
|
||||
<circle id="73922eab-afce-48dd-9bae-80da92a8b392" data-name="75a0ff09-d40c-46e3-aaf0-c5878f2efdac" cx="18.35" cy="98.14" r="3.79" style="fill: #fff"/>
|
||||
<path id="e9f7b6a6-dcdd-4d55-a7bb-ad3951bb1e39" data-name="eeb5a6b6-ef4a-4f05-95cd-8b1c3eb419c4" d="M22.07,116.64v-1.8c0-.86-.81-.46-.81-.46L20,115c-.38.18-.33-.34-.33-.34V112c0-.72.82-.76.82-.76h1.25s.32-.26.29-1.39-.29-1.32-.29-1.32H20.53a.67.67,0,0,1-.66-.65v-1.3a3.57,3.57,0,0,0-1.4-.31c-1,0-1.17.31-1.17.31v1.3c0,.67-.73.65-.73.65H15.19s-.3.18-.3,1.32.3,1.39.3,1.39h1.38a.71.71,0,0,1,.73.69v2.69c0,.52-.36.34-.36.34l-1-.58c-.68-.39-1.23,0-1.23.46v1.8s0,1,1.23.1a5.09,5.09,0,0,1,3-.71,4.16,4.16,0,0,1,2.35.71C22,117.17,22.07,116.64,22.07,116.64Z" style="fill: #fff"/>
|
||||
<path id="82138da6-9b2a-4b9a-952e-568841e5ed52" data-name="80481c02-bb01-43aa-bd64-15ab30fbbb5e" d="M14.44,103.18c0-.8,1.2-.56,1.2-.56a7,7,0,0,0,2.85.83,5.78,5.78,0,0,0,2.77-.83.7.7,0,0,1,1,.25.67.67,0,0,1,.1.31v1.76s-.24,1-1.07.59a5,5,0,0,0-2.77-.77,6.59,6.59,0,0,0-2.85.77.68.68,0,0,1-.24.08.83.83,0,0,1-1-.63v0S14.44,104,14.44,103.18Z" style="fill: #fff"/>
|
||||
</g>
|
||||
<path id="aa62b88a-4cd5-4c76-acaf-e85a7fd45341" data-name="6b63d025-68d6-47eb-ba62-60017f4a1dd4" d="M30.07,140.62a5.78,5.78,0,0,1-5.78-5.78h0V92l9.26-9.57a2.29,2.29,0,0,1,2.38-1c4.4,0,23.87-.14,23.87-.14h.64s7.76,9.8,18.13,23.86v29.64h0a5.79,5.79,0,0,1-5.79,5.79Z" style="fill: #a8a9a8"/>
|
||||
<g>
|
||||
<g id="1172473e-5b0c-4dd6-85c5-28f5593b9312" data-name="a0498222-7e69-48db-90b7-05f2ce124a03">
|
||||
<g style="clip-path: url(#9d9a0722-eca6-4c98-bf03-49097e654f0e)">
|
||||
<g id="da9d77a3-c9b0-4118-a8e3-c65ec5cbe7d4" data-name="8270fb93-0862-4480-a5b8-675cc77ac153">
|
||||
<path id="77253f44-139e-4b90-82df-465a787cab9c" data-name="bb0a4ec3-45a5-4c91-b064-db5494e447e6" d="M78.56,142.76l1-7.69V103.85L66.49,87.72v14.86L42.7,133.66,35.55,141Z" style="fill: #f2f2f2"/>
|
||||
</g>
|
||||
<g style="isolation: isolate">
|
||||
<g id="e13b39e7-895d-4931-9be9-7aecaf709784">
|
||||
<g style="clip-path: url(#clip-path)">
|
||||
<g id="Large_Motor" data-name="Large Motor">
|
||||
<path id="LM_back2" data-name="LM back2" d="M39,153.62H12.69a.6.6,0,0,1-.6-.6h0v-5.3a.6.6,0,0,1,.6-.6H39a.6.6,0,0,1,.6.6V153A.6.6,0,0,1,39,153.62Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
<g>
|
||||
<image width="36" height="20" transform="translate(9 136)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAVCAYAAADB5CeuAAAACXBIWXMAAAsSAAALEgHS3X78AAAAp0lEQVRIS+3WsW3DMBBG4Y+GSmcGp/cAHsuzeIasFPdWVpD6c0FKcBWwOxV8wIEgSIAPvOL+EhFKKcUBiIiAgjO+25rJildErJMqdMdVlcwg8MQDv5P6Q1fc5ErRujW1TfmoLPa3T//dymJI9TKkehlSvQypXoZUL5vUNhAz2R1Oao75w9IOMmpRHVZqSpjxo3KRw+YwQ2lx+EsVykqfK+aIWGhSR+MNDy9ID5XUCOsAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_back1-2" data-name="LM back1-2" d="M41.86,152.17H10.53a.6.6,0,0,1-.6-.6v-14.7a.6.6,0,0,1,.6-.6H41.86a.6.6,0,0,1,.6.6h0v14.7A.6.6,0,0,1,41.86,152.17Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="9d05c10c-72eb-4f9f-8517-e403ec636357" data-name="74e07bdd-431b-40c1-b0f3-71128f2a0c08" d="M35.85,135.56a5.78,5.78,0,0,1-5.78-5.78h0V92.9h0a5.78,5.78,0,0,1,5.77-5.79H57.17s6.91,9.15,15.56,20.41v22.25h0A5.78,5.78,0,0,1,67,135.56H35.85Z" style="fill: #a8a9a8"/>
|
||||
<g id="6df8eb1f-3187-427f-8d99-bc7a37e6cc25" data-name="9bbfb3ec-86c2-4d27-8275-76f2f9737a7f">
|
||||
<g id="e4d7143c-1fb6-45a4-a205-1c920b8a823c" data-name="7e017c33-f09b-4d39-803f-1bedf1ebf566">
|
||||
<g id="3a4e493d-cd17-48c4-97b6-da7ee3c4898e" data-name="69e3df75-72a6-4499-baab-f65ec194e193">
|
||||
<g style="clip-path: url(#0f29df77-bfe3-4749-8432-7a5b3681f927)">
|
||||
<g id="7cada08f-1e90-4ef2-8ae4-3f2adfd3e953" data-name="b7007e32-15c9-40b3-a885-a7231fd104d1">
|
||||
<path id="02e64b51-bd56-4079-9736-8a269cb37029" data-name="c599a4f4-ae5d-411c-902d-dbcde04a2d6e" d="M60.44,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112Zm-3.61,0a1.09,1.09,0,1,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6a1.08,1.08,0,0,1-1.11,1.05h0a1.07,1.07,0,0,1-1-1.11V112Zm-3.62,0a1.1,1.1,0,0,1,1.12-1.06,1.08,1.08,0,0,1,1,1.12V139.6a1.09,1.09,0,0,1-2.17-.06V112ZM46,112a1.09,1.09,0,0,1,2.18-.14,1.34,1.34,0,0,1,0,.2V139.6A1.08,1.08,0,0,1,47,140.65h0a1.07,1.07,0,0,1-1-1.11V112Z" style="fill: #6a6a6a"/>
|
||||
<g id="LM_rightside" data-name="LM rightside">
|
||||
<path id="LM_rightside5" data-name="LM rightside5" d="M33.08,69.61H27.47A5.45,5.45,0,0,1,22,64.15h0V41.25a5.45,5.45,0,0,1,5.46-5.46h5.61a5.46,5.46,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,33.08,69.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
<circle id="LM_rightside4" data-name="LM rightside4" cx="28.3" cy="66.21" r="3.79" style="fill: #fff"/>
|
||||
<circle id="LM_rightside3" data-name="LM rightside3" cx="28.3" cy="43" r="3.79" style="fill: #fff"/>
|
||||
<path id="LM_rightside2" data-name="LM rightside2" d="M24.4,59.69A4.16,4.16,0,0,1,26.74,59a5.23,5.23,0,0,1,3,.71c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.85-1.23-.46l-1,.58s-.36.19-.36-.33V55s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.39-.29-1.32-.29-1.32H29.1s-.73,0-.73-.65v-1.3s-.23-.36-1.17-.3a3.56,3.56,0,0,0-1.4.3v1.3a.66.66,0,0,1-.66.65c-.63,0-1.26,0-1.26,0s-.25.19-.29,1.32.29,1.39.29,1.39h1.26s.8.05.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.41-.82.46,0,1.8,0,1.8S23.67,60.12,24.4,59.69Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
<path id="LM_rightside1" data-name="LM rightside1" d="M31.25,47.89a.84.84,0,0,1-1,.68.85.85,0,0,1-.24-.09,6.63,6.63,0,0,0-2.85-.77,5.11,5.11,0,0,0-2.77.77c-.83.4-1.07-.59-1.07-.59V46.13a.72.72,0,0,1,.76-.66.67.67,0,0,1,.31.1,5.79,5.79,0,0,0,2.77.84,7.12,7.12,0,0,0,2.85-.84s1.18-.24,1.2.56S31.25,47.89,31.25,47.89Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
</g>
|
||||
<g>
|
||||
<image width="36" height="76" transform="translate(32 12)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAABNCAYAAAAhOa00AAAACXBIWXMAAAsSAAALEgHS3X78AAACAElEQVRoQ+3avVHDMBiA4VdcypgREmo4RggLpKKHAZiIAWgoKZggrEAGCBsQpxeFpNgJ/vkkK5YLvXe6g2Cb53zE9oVPaa3xSSmlALTvjh4pn2MrpQpgab/dAYdL4MQoC3oAnuxLH8AW2Gmty9YdAxKhaqAXYAUozJnaAm/AJiZs1rfBGegBKOyP7oAFMLfbRYN1ojpAYM7WtX3dbR8F1orqAdVz27n9BsMaUR4gV1yY1vpkYX7BGvgE9oD2WL/AO3CPfROFrKs6MOAMnVcAt3b57lsV6QzV194eYw0UfWelacUGRYFdAjQYNsPcy54xV+rwv4P/Bb8jZ5gr8oK4IFcQzF2nVOdWw/KG9d77IuUFGwsFHrAxUSCEjY0CASwFCnpgqVDQAUuJghZYahQY2Ao4YJ77v08eXRJWUHvenwoKaneVKaGOZZS0jJKWUdIySlpGScsoaRklLaOkZZS0jJKWUdIySlpGScsoaRklLaOkTQml3RdTQZXAD+bD2EmgSuALMwmyg/SoEtgAr9RGU1KiGkGQDtUKgjSoThCMj+oFwbgoEQjGQ4lBMA7KCwSXR3mDoPonpBsFccWYUwgCgUEdMAN/YDBzzKjJvG0nQe7W4Q2CCnFDhVgCj1TznCHtGDDVqLTWxylXW4wzdWDA/GfjnOcZMijddGBhouHTsfsDst/cEY24RzwAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_level4-2" data-name="LM level4-2" d="M64.91,70.7,51.5,83.63,32.93,83,32.76,24c0-.84.16-.84.46-1.13l10.42-10.1.21-.19c.59-.4,2,.11,2,.95L65.29,34.25V70C65.3,70.44,65.16,70.43,64.91,70.7Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g>
|
||||
<image width="25" height="27" transform="translate(32 69)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAcCAYAAAB/E6/TAAAACXBIWXMAAAsSAAALEgHS3X78AAABYUlEQVRIS+2WvVXDMBRG7/NxGVf0CRVFchghWSAVA2QAxmEGGgZgguzgHlPTELv/KCSD/BMs2wkV9xwf2/LPlZ70JJkk2piZdQojUd8PAWuXm1kGrIBF3wcDVEAhqWw/aIi8ZAcccLKxFMAzcGzL0voikDwCWyBjPGt8JMysKfMtMuAeeAE+Ac04TsArsAcySUj6blGGq82aaS0JyXARqYDCzHJJSnzItrh+ucW1bi4ZsCQYUAmu0w9M75dzNCqc4KxLLivpkPjzJcL1Kwl/xL9oMnXC1lkdw6SBk+IyOPf3Qz9Z8DOzD73bIAXegCfiloUV8ABs/HV07qWSKjPLiSP3xwY3m+yIlKVwflXsofSVeseFHCJlKSPxlTqZ2TEoHpSNFtVIKsfIJotgnGyWCHplW3pG8GwRdGQVbqXu35zMJZAVONEdgayzr5uL33wugBvgQ1IFVxDVmJmF+Xk1UZsvIeCPQjWq5BoAAAAASUVORK5CYII=" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_level3-2" data-name="LM level3-2" d="M54,92V74.7a2.19,2.19,0,0,0-.3-1.12c-.28-.27-3.52-3.62-3.52-3.62a1.55,1.55,0,0,0-1.16-.4c-.75,0-11.62,0-11.62,0L33,74.26v8l10.66.57Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g id="LM_leftside" data-name="LM leftside">
|
||||
<path id="LM_leftside5" data-name="LM leftside5" d="M60.08,126.64H54.47A5.45,5.45,0,0,1,49,121.18h0V98.28a5.46,5.46,0,0,1,5.46-5.46h5.61a5.47,5.47,0,0,1,5.46,5.46v22.9A5.45,5.45,0,0,1,60.08,126.64Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
<circle id="LM_leftside4" data-name="LM leftside4" cx="61.67" cy="123.24" r="3.79" style="fill: #fff"/>
|
||||
<circle id="LM_leftside3" data-name="LM leftside3" cx="61.67" cy="100.03" r="3.79" style="fill: #fff"/>
|
||||
<path id="LM_leftside2" data-name="LM leftside2" d="M57.77,116.72a4.15,4.15,0,0,1,2.34-.7,5.14,5.14,0,0,1,3,.7c1.25.93,1.23-.1,1.23-.1v-1.8c0-.45-.56-.84-1.23-.46l-1,.58s-.36.19-.36-.33V112s0-.79.73-.77,1.38,0,1.38,0,.29-.25.29-1.38-.29-1.33-.29-1.33H62.47s-.73,0-.73-.64v-1.3s-.23-.36-1.17-.31a3.57,3.57,0,0,0-1.4.31v1.3a.66.66,0,0,1-.66.64H57.25s-.25.2-.29,1.33.29,1.38.29,1.38h1.26s.8,0,.82.77,0,2.62,0,2.62,0,.51-.34.33l-1.21-.58s-.83-.4-.82.46,0,1.8,0,1.8S57,117.16,57.77,116.72Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
<path id="LM_leftside1" data-name="LM leftside1" d="M64.62,104.92a.83.83,0,0,1-1,.68.68.68,0,0,1-.24-.08,6.46,6.46,0,0,0-2.85-.77,5,5,0,0,0-2.77.77c-.83.4-1.07-.6-1.07-.6v-1.76a.71.71,0,0,1,.76-.65.8.8,0,0,1,.31.09,5.79,5.79,0,0,0,2.77.84,7,7,0,0,0,2.85-.84s1.18-.24,1.2.56S64.62,104.92,64.62,104.92Z" transform="translate(0.98 1.9)" style="fill: #fff"/>
|
||||
</g>
|
||||
<g>
|
||||
<image width="58" height="64" transform="translate(0 81)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADsAAABBCAYAAABvnNwUAAAACXBIWXMAAAsSAAALEgHS3X78AAACFElEQVRoQ+2bQY7TMBSGvxcqsaFbJDbDrEEcYeYCcwYOwIlYsWLDjg0nGG6A1ANkbgCNhGb1WNgBpxPbaUjT+smf5E3lxPlq/25S5YmqshQiIrk+S6EzLlxmHDOKiGyB18CLXN8F6IAW6I6RXkTWi94C73HCp6YFvgI7oFXVfaY/sIBsIPoBuAG26SMWoZ/ZHfAZuJ8ivMl1SHEgess6ouDGeQtc4WMjInlhVZ3VcAPeAd+AX4Ceqf0EvgDv8Cs11hpm4Hfda1xG11q6MbbAG9+S1zFLliMGWIHBF++jNcrRmfUnu8Gd/Bo32Lnp9w4gnt+jZM+4IU0hKzxZ9sJyGiMpPFmWy8ppiqjwJNkLzWmK/no7oBWRnapqdje+8Jym2BLcdEDmp6eQnKYYrMDczJaS00lEZQvMaZZR2YJzmuSJrIGcRhmbWVM5DRnIWsxpyF9ZqzkNacB2TkP6mTWb05DGek5DGtxfn6aXb0+Du1G+wrgo/Mus2aUbknsQMEWVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUqVtUovq8le5TLwanBvbj4A2VKRwtjjvLr+g2fAb1wFxUvgFfB89NCy2APfgU/AD1V9BNio6l5E7oOOpb9u0It+5KC4aQNwINyxTonZqWiJVHENytNk3eLBU9ERqc97UovnXwArGj2U8vx34WFJ/AG0vvY6LSeZMAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_level2_grey-2" data-name="LM level2 grey-2" d="M49,140.61H6.31a5.78,5.78,0,0,1-5.78-5.78h0V105.19C10.89,91.13,18.65,81.32,18.65,81.32h.6s19.46.15,23.86.15a2.29,2.29,0,0,1,2.38,1L54.75,92v42.8A5.78,5.78,0,0,1,49,140.61Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g id="LM_total" data-name="LM total">
|
||||
<g id="LM_whitepart" data-name="LM whitepart">
|
||||
<g style="clip-path: url(#clip-path-2)">
|
||||
<g id="LM_whitepart_combined" data-name="LM whitepart combined">
|
||||
<path id="LM_whitepart_top" data-name="LM whitepart top" d="M43.48,141l-7.15-7.37L12.54,102.56V87.7L-.52,103.84v31.22l1,7.69Z" transform="translate(0.98 1.9)" style="fill: #f1f1f1"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<circle id="a5a29926-0401-43af-b098-0c7439485e7c" data-name="bcdc8e69-90f7-4365-8f7a-7c1567704161" cx="29.69" cy="135.22" r="2.24" style="fill: #9a9a9a"/>
|
||||
<circle id="313e9fdc-7bbb-4d49-9d19-2415450fb691" data-name="b7785d8e-a2e4-41a7-9c34-4d9f6aebd906" cx="58.36" cy="84.71" r="2.24" style="fill: #9a9a9a"/>
|
||||
<g id="1eb2ae58-2419-47d4-86bf-4f26a7f0cf61" data-name="KNOB">
|
||||
<g id="26377a78-5df7-4bea-8daa-669ea612118c" data-name="5301e639-3e28-489d-96ec-91bda6d459b7">
|
||||
<circle id="13e05316-9dcc-4be0-9e0e-4b1df48fad92" data-name="f1bc8b30-bdb2-4084-8c02-e322d1c0b9bf" cx="17.06" cy="17.06" r="17.06" style="fill: #b72b1c"/>
|
||||
<g>
|
||||
<image width="47" height="52" transform="translate(5 87)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAA1CAYAAAAHz2g0AAAACXBIWXMAAAsSAAALEgHS3X78AAABnElEQVRoQ+2azXHCMBBG3zIccQuBM5MWoIHUkAJSUQrIJQWkAlIJ6SDYd+UgGWTjHxQHr5TRm9HFIzz75G8Z8KwYY5iKiMjYnr/AdBQrUwVEpADWwGps70Qq4GiMKf2LkwRc8XvgGStxT47AG3DwJZb9+4fxin8BdkAx/InJbHFPWUQuEsaY4IUt9gn4AE6AmWl9A+/AIy49CwJxDbvBxmaOk/cpgAe8fgsWwN5k69acxdc0vvGCBFzud9jT39C6mQY3C7Sado/O6V9xk4By7ge5SQD93PcyKhBj7n0GBWLNvU+vQArFQ49AzE3bpu8JRNu0ba4EYm/aNg2BVHLvcxZIKfc+/hNIJvc+C0gv9z4LF501iUWnpo7QCvtHIaniodkDycTGZ/THXOxkAW2ygDZZQJssoE0W0CYLaJMFtMkC2mQBbbKANv9K4PdDE/PSqLMWqIAvoDEJEiElts7qfEV59iFknbD1PQFFPbexBDDGlCJy4EJs70hL4BN4pW/cpiVRcf/5nxA6Z4WgY+BpxgmsEDqntWDixFYM/ACNRtJx4q2GRAAAAABJRU5ErkJggg==" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="LM_top_grey-2" data-name="LM top grey-2" d="M43.18,135.54H12.09a5.78,5.78,0,0,1-5.79-5.78h0V107.51C15,96.24,21.86,87.1,21.86,87.1H43.18A5.78,5.78,0,0,1,49,92.88h0v36.88a5.78,5.78,0,0,1-5.78,5.78Z" transform="translate(0.98 1.9)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<g id="LM_top" data-name="LM top">
|
||||
<g style="clip-path: url(#clip-path-3)">
|
||||
<g id="LM_top_total" data-name="LM top total">
|
||||
<path id="LM_top_lines" data-name="LM top lines" d="M18.6,112.05v27.47a1.08,1.08,0,0,1-1.12,1,1.06,1.06,0,0,1-1.05-1V112.05A1.09,1.09,0,0,1,18.6,112Zm3.61,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.62,0v27.47a1.09,1.09,0,1,1-2.17,0V112.05a1.09,1.09,0,1,1,2.17,0Zm3.61,0v27.47a1.09,1.09,0,0,1-2.17.07V112.05a1.09,1.09,0,0,1,2.17-.07Zm3.62,0v27.47A1.1,1.1,0,0,1,32,140.64a1.08,1.08,0,0,1-1.11-1V112.05a1.09,1.09,0,0,1,2.17-.07Z" transform="translate(0.98 1.9)" style="fill: #6a6a6a"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<circle id="LM_detail_2" data-name="LM detail 2" cx="50.32" cy="137.11" r="2.24" style="fill: #9a9a9a"/>
|
||||
<circle id="LM_detail_1" data-name="LM detail 1" cx="21.66" cy="86.59" r="2.24" style="fill: #9a9a9a"/>
|
||||
<g id="hole">
|
||||
<g>
|
||||
<circle id="58277aee-b73f-4f42-94ef-3b959dc26807" data-name="ce4b2c57-9b3b-4aed-902e-31d9f366dc61" cx="20.12" cy="28.12" r="3.79" style="fill: #3c3c3c"/>
|
||||
<circle id="980a54d2-804e-408d-8655-debf2e5856d0" data-name="6a44a65a-f04b-4564-81a2-4eefa438c5f1" cx="28.27" cy="14" r="3.79" style="fill: #3c3c3c"/>
|
||||
<circle id="9b2e3cd0-e4bd-42ce-b558-4f4bc46eaf23" data-name="d5fea8d1-47c1-448e-adb6-8519e2516216" cx="14.15" cy="5.85" r="3.79" style="fill: #3c3c3c"/>
|
||||
<circle id="06781f30-70ec-4914-81c3-4083325e7c18" data-name="1faf9727-3903-4be3-a3d3-4ab2d5d92b1f" cx="6" cy="19.97" r="3.79" style="fill: #3c3c3c"/>
|
||||
<path id="f1de636f-1fd2-4419-ae5c-d6a853c5834d" data-name="9e52c672-4ec8-4d92-9380-e44b4f69f848" d="M13.56,16.89a2.91,2.91,0,0,0,.7,2.26,12,12,0,0,1,1.2-.39c.69-.18.95.59.95.59l.33,1.21s.33.24,1.41-.08,1.2-.62,1.2-.62-.17-.6-.32-1.22a.66.66,0,0,1,.45-.8l1.26-.34a3.63,3.63,0,0,0-.07-1.43c-.19-.93-.6-1.05-.6-1.05l-1.25.34c-.65.17-.82-.54-.82-.54l-.36-1.33s-.25-.24-1.35,0-1.26.64-1.26.64.14.61.35,1.34a.7.7,0,0,1-.48.88l-.07,0Z" style="fill: #242424"/>
|
||||
<path id="e8012624-d689-4ee7-a77a-99e9713ea199" data-name="3179af54-0c25-42bc-9804-f3575e0b1b0b" d="M17.44,24.25a.47.47,0,0,1-.56-.32l-.55-2.06h0a.45.45,0,0,1,.32-.55h0l1.63-.23,1.44-.6a.47.47,0,0,1,.56.32l.55,2.05h0a.45.45,0,0,1-.32.55h0L18.94,24Z" style="fill: #3c3c3c"/>
|
||||
<path id="31a64bab-6b92-41d0-afe9-392c1fae8ddb" data-name="841a583e-aa25-4a7e-8907-fbe64ce57066" d="M9.88,17.31a.46.46,0,0,1,.33-.56l2-.55h0a.47.47,0,0,1,.56.33L13,18.16l.6,1.45a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.57-1.53L9.84,17.3Z" style="fill: #3c3c3c"/>
|
||||
<path id="2b1c6b9e-6fda-40be-8bb8-318c30b7dca4" data-name="42515acc-688f-41c7-884c-80b8a90e09c0" d="M16.82,9.8a.47.47,0,0,1,.56.32l.55,2.05h0a.46.46,0,0,1-.33.55L16,13l-1.45.61a.45.45,0,0,1-.55-.32h0l-.53-2.1h0a.45.45,0,0,1,.31-.56h0l1.53-.57,1.55-.25Z" style="fill: #3c3c3c"/>
|
||||
<path id="be04cab5-eaac-4d03-a1ee-dbe70dd5bf90" data-name="8690082b-7091-4631-a199-b79823698dd6" d="M24.33,16.74a.43.43,0,0,1-.3.54h0l-2,.55h0a.46.46,0,0,1-.55-.33l-.22-1.64-.6-1.44a.45.45,0,0,1,.31-.56h0l2-.55h0a.46.46,0,0,1,.56.33l.57,1.53.25,1.55Z" style="fill: #3c3c3c"/>
|
||||
<image width="38" height="38" transform="translate(44)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAnCAYAAACMo1E1AAAACXBIWXMAAAsSAAALEgHS3X78AAACRklEQVRYR9WYPXLbMBBGHzgpxRwhTJ3ER0gukMauUvl0btIlhU9gdSkzQ9dWbmBTtTYFdiUA/BGh0ILyzWBAUiTx+C0JYdeJCDlyzrlgdwU02pu2wEb7vSR3IMDNvUahUpgGuNHetAF+aG/aA+dAzoJzztV4gI/EMHOdM+AW2IhIxwxNwqlbNfAZuMXDpTBhmE3pTQ24Be6ANdAdc/HN2A+JW7d4wJphmFTpOTX+Pu/wD7YCWufcpIuDzinYF2K36t6Jp6kjdvFhFFBEooaH+ArcA8/ADh+mJdtO732vY9Uph4iQgjngCviuFx8b5F/bs451hUYxbBWxauCDtqXCOKXp8UbC+cLxp16qvTAS3lLhTJuF9xNBeC2sK84bzlQ2bYXzJ5VOtA1+5n/PvHnsNdT7twmdswmyhEKDGltchF9rKcdMPYMMrjSYyRGwVJQPaaiIpSJek5V0MHrv4D9w7pK0Bf5oT5UeKCghWeJXyQEZvfQ8uljnTHuD7J0r7Zgp4gg/iNKAvQgaXOnQRh+DZWWVbtgPT5RzsJfvhs49apuV8C4sy8jiyF38Mj0AfAt8A37zOinhbLCh7KvjfOHd4d/xO2AtA4l1BKcfx/4CDuFdWh0+4/+l/aARvVqJiHTOuQfd3bJsOULwIGu8AS3wZFNHqtEq00QhB/LXfcIJlabRKpM62HKYnLccsiPrQxlwOlgIlVWjyy0eGlhazbTjcKRwSEZ1cxYcRLXgIeca4Fq3f7JAyRUy4EIlRWuYdo5cKNNJcOfSX2jmA7RYwAeRAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<circle id="LM_red-2" data-name="LM red-2" cx="62.96" cy="18.95" r="17.06" style="fill: #d42715"/>
|
||||
</g>
|
||||
<g id="LM_details_in_red" data-name="LM details in red">
|
||||
<circle id="LM_detail_red_hole4" data-name="LM detail red hole4" cx="59.9" cy="30.01" r="3.79" style="fill: #393939"/>
|
||||
<circle id="LM_detail_red_hole3" data-name="LM detail red hole3" cx="51.75" cy="15.89" r="3.79" style="fill: #393939"/>
|
||||
<circle id="LM_detail_red_hole2" data-name="LM detail red hole2" cx="65.87" cy="7.74" r="3.79" style="fill: #393939"/>
|
||||
<circle id="LM_detail_red_hole1" data-name="LM detail red hole1" cx="74.02" cy="21.86" r="3.79" style="fill: #393939"/>
|
||||
<path id="LM_detail_hole" data-name="LM detail hole" d="M64.2,16.41a.72.72,0,0,1-.55-.91c.21-.72.36-1.33.36-1.33s-.17-.35-1.27-.64-1.35-.06-1.35-.06L61,14.8s-.16.72-.81.54L59,15s-.4.13-.6,1.06a3.44,3.44,0,0,0-.06,1.43l1.25.33a.68.68,0,0,1,.46.81c-.16.61-.33,1.21-.33,1.21s.12.3,1.2.62,1.42.09,1.42.09l.32-1.22s.26-.76,1-.59q.6.17,1.2.39a2.92,2.92,0,0,0,.7-2.25C64.91,16.65,64.2,16.41,64.2,16.41Z" transform="translate(0.98 1.9)" style="fill: #1f1f1f"/>
|
||||
<path id="LM_detail_red4" data-name="LM detail red4" d="M61.64,24.23,60.09,24l-1.53-.57a.46.46,0,0,1-.33-.56h0l.55-2.05a.46.46,0,0,1,.56-.32l1.45.6,1.63.22a.46.46,0,0,1,.33.56h0l-.55,2.05a.44.44,0,0,1-.55.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
<path id="LM_detail_red3" data-name="LM detail red3" d="M69.15,17.3l-.26,1.55-.57,1.52a.45.45,0,0,1-.55.33h0l-2.06-.55a.46.46,0,0,1-.32-.56L66,18.15l.22-1.63a.45.45,0,0,1,.55-.33h0l2,.55a.46.46,0,0,1,.32.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
<path id="LM_detail_red2" data-name="LM detail red2" d="M62.21,9.79l1.55.25,1.53.57a.46.46,0,0,1,.33.55h0l-.55,2.05a.46.46,0,0,1-.56.32l-1.45-.6-1.63-.22a.47.47,0,0,1-.33-.56h0l.55-2.05a.44.44,0,0,1,.55-.32Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
<path id="LM_detail_red1" data-name="LM detail red1" d="M54.7,16.72,55,15.17l.57-1.52a.45.45,0,0,1,.55-.33h0l2.06.55a.46.46,0,0,1,.32.56l-.61,1.44-.22,1.63a.45.45,0,0,1-.55.33h0l-2-.55a.46.46,0,0,1-.32-.56Z" transform="translate(0.98 1.9)" style="fill: #393939"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
@ -1,28 +1,33 @@
|
||||
<svg id="svg7610" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 44.14 44.15">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48">
|
||||
<defs>
|
||||
<linearGradient id="motor-linear-gradient" x1="-313.1" y1="551.59" x2="-313.1" y2="551.43" gradientTransform="matrix(44.14, 0, 0, -44.15, 13843.7, 24396.04)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a8a9a8"/>
|
||||
<stop offset="1" stop-color="#545554"/>
|
||||
<linearGradient id="linear-gradient" x1="-427.2" y1="440.79" x2="-427.2" y2="440.63" gradientTransform="matrix(44.14, 0, 0, -44.15, 18878.72, 19502.57)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a8aaa8"/>
|
||||
<stop offset="1" stop-color="#535453"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<title>MediumMotor</title>
|
||||
<g id="Medium_Motor" data-name="Medium Motor">
|
||||
<g id="Group_4" data-name="Group 4">
|
||||
<path id="Path_1" data-name="Path 1" d="M4.07,1.5h39a2.36,2.36,0,0,1,2.57,2V43.82c0,1-1.1,1.82-2.57,1.82h-39c-1.47,0-2.57-.81-2.57-1.82V3.47A2.36,2.36,0,0,1,4.07,1.5Z" transform="translate(-1.5 -1.49)" style="fill: url(#motor-linear-gradient)"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7">
|
||||
<g id="g7596">
|
||||
<path id="Union_1-2" data-name="Union 1-2" d="M1.5,22.74a6.21,6.21,0,0,1,6.22-6.22h8.8V7.72A6.22,6.22,0,0,1,22.74,1.5H24.4a6.21,6.21,0,0,1,6.22,6.22v8.8h8.8a6.21,6.21,0,0,1,6.22,6.22V24.4a6.22,6.22,0,0,1-6.22,6.22h-8.8v8.8a6.22,6.22,0,0,1-6.22,6.22H22.74a6.23,6.23,0,0,1-6.22-6.22h0v-8.8H7.72A6.22,6.22,0,0,1,1.5,24.4h0Z" transform="translate(-1.5 -1.49)" style="fill: #a8a9a8"/>
|
||||
<g style="isolation: isolate">
|
||||
<g id="svg7610">
|
||||
<g id="Medium_Motor" data-name="Medium Motor">
|
||||
<g id="medmotor_box" data-name="medmotor box">
|
||||
<path id="medmotor_box_wgradient" data-name="medmotor box wgradient" d="M2.57,0h39a2.36,2.36,0,0,1,2.57,2V42.33c0,1-1.1,1.82-2.57,1.82h-39C1.1,44.15,0,43.34,0,42.33V2A2.36,2.36,0,0,1,2.57,0Z" transform="translate(2 1.84)" style="fill: url(#linear-gradient)"/>
|
||||
</g>
|
||||
<g id="medmotor_star" data-name="medmotor star">
|
||||
<g>
|
||||
<image width="48" height="48" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAxCAYAAACcXioiAAAACXBIWXMAAAsSAAALEgHS3X78AAACfElEQVRoQ+2aPXLbMBBG32ZUijlCnDo/R1AukCoHyAFS5Sw5g5t0bnwCu0ofpTZ9A1usvSmwoClRJEEAI4gzejMcqliS+2FB4AMoUVVyISICrIErO3dpgBpoNONDJde9RKTCJf4R+Ga/u9TADbAFalXdkYEsAiz5L8B3nICxCmyBa+Auh4jVVMAUneR/ABugAuRIaIUT9w4TJyLJIpIEWJ+/wrX8Bng7fgViMRtcRR5F5G/KO/FmKiCANa5Vq6nADhXwwY7DrjaLHALgeJcZQ4D32MtulYwil4AYfOXOogKxRLe8p7SAZC4CSnMRUJrBmThwbBbSRxIh4HFDs3VPwIQl7oXj/M1U3BBr3PUAY3aiEZGjVnzPjQZY4mOscbNqjIgGeLDzGINWvBUQaImHSOlGIUZu2IqbgAr4CtwCT8AL7sbndLzgcrvF5Vq1jQ98An5bwNSNSh9PuFw/A+KH0RhLXIo9K96dB1L68SnZs+JLnchaK75UAWA9ZskCgGULUFiugAZ4BJquAB0IPjcUZz9ugNoL8IqSNplOxA74Z0ezUlU1p3eNG5787tocUuaQOZXfAfe4XB9UVVcAqroTkTsLaghzoZ5TuFGPb+jWzA3Z6dBk/Hrgp53nVEJx7vKXnUMq0XBgp/cWNFaJbe+yYXzCoS14SINL/g9hAnors96KbM5Gqy0Dg+MHUNxjo+6z1Hmg5SKgNBcBpSktIGrk6VJSQOsopwLHKCVAed2sqmPnAMgnICaB9sv9VOAYOQTEWPEdGZKHRAFWeu8Q74Fnpjemnnm1xEndBxI/dEOUFe9Z4hSy/FcCZlnxniVOIZsACPhKYaR2my5ZBZTgP7HrUIs43RAaAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="medmotor_cut-2" data-name="medmotor cut-2" d="M0,21.25A6.21,6.21,0,0,1,6.22,15H15V6.23A6.22,6.22,0,0,1,21.24,0H22.9a6.22,6.22,0,0,1,6.22,6.22V15h8.8a6.21,6.21,0,0,1,6.22,6.22v1.66a6.21,6.21,0,0,1-6.22,6.22h-8.8v8.8a6.21,6.21,0,0,1-6.22,6.22H21.24A6.22,6.22,0,0,1,15,37.93v-8.8H6.22A6.22,6.22,0,0,1,0,22.92H0Z" transform="translate(2 1.84)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<circle id="medmotor_hole_4" data-name="medmotor hole 4" cx="39.77" cy="24" r="4.85" style="fill: #393939"/>
|
||||
<circle id="medmotor_hole_3" data-name="medmotor hole 3" cx="8.37" cy="24" r="4.85" style="fill: #393939"/>
|
||||
<circle id="medmotor_hole_2" data-name="medmotor hole 2" cx="24.15" cy="8.22" r="4.85" style="fill: #393939"/>
|
||||
<circle id="medmotor_hole_1" data-name="medmotor hole 1" cx="24.15" cy="39.62" r="4.85" style="fill: #393939"/>
|
||||
</g>
|
||||
<g id="medmotor_red" data-name="medmotor red">
|
||||
<circle cx="24.3" cy="24" r="6.75" style="fill: #d42715"/>
|
||||
<circle cx="24.3" cy="24" r="6.63" style="fill: none;stroke: #a20800;stroke-width: 0.25px"/>
|
||||
</g>
|
||||
<path id="medmotor_Hole" data-name="medmotor Hole" d="M20.59,19.46s-.05,1-.77,1-1.46,0-1.46,0a2.38,2.38,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.21.21,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29A6.88,6.88,0,0,0,24,26.15V24.48a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H24.6a.65.65,0,0,1-.75-.51.49.49,0,0,1,0-.17,11.22,11.22,0,0,1,0-1.64,4.78,4.78,0,0,0-3.25,0C20.58,18.74,20.59,19.46,20.59,19.46Z" transform="translate(2 1.84)"/>
|
||||
</g>
|
||||
<circle id="Ellipse_1" data-name="Ellipse 1" cx="37.77" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
|
||||
<circle id="Ellipse_2" data-name="Ellipse 2" cx="6.37" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
|
||||
<circle id="Ellipse_1-2" data-name="Ellipse 1-2" cx="22.15" cy="6.38" r="4.85" style="fill: #3c3c3c"/>
|
||||
<circle id="Ellipse_2-2" data-name="Ellipse 2-2" cx="22.15" cy="37.78" r="4.85" style="fill: #3c3c3c"/>
|
||||
</g>
|
||||
<g id="Ellipse_4" data-name="Ellipse 4">
|
||||
<circle id="ellipse7603" cx="22.3" cy="22.16" r="6.75" style="fill: #b72b1c"/>
|
||||
<circle id="ellipse7605" cx="22.3" cy="22.16" r="6.63" style="fill: none;stroke: #8b1104;stroke-width: 0.25px"/>
|
||||
</g>
|
||||
<path id="Hole" d="M23.9,26.72s.56-.86,1.18-.51,1.27.73,1.27.73a2.37,2.37,0,0,0,1.24-1.23c.63-1.1.49-1.57.49-1.57l-1.41-.81a.64.64,0,0,1-.32-.86l.06-.1.84-1.44a3.9,3.9,0,0,0-1.39-1.13,6,6,0,0,0-1.6-.59l-.83,1.44a.73.73,0,0,1-1,.24,11.15,11.15,0,0,0-1.32-.6s-.54.13-1.2,1.2-.51,1.6-.51,1.6l1.46.84a.64.64,0,0,1,.4.82.55.55,0,0,1-.08.15,11.25,11.25,0,0,1-.82,1.43A4.83,4.83,0,0,0,23.19,28C23.56,27.35,23.9,26.72,23.9,26.72Z" transform="translate(-1.5 -1.49)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.6 KiB |
@ -1,30 +1,35 @@
|
||||
namespace pxsim.visuals {
|
||||
export const MEDIUM_MOTOR_SVG = `<svg id="svg7610" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 44.14 44.15">
|
||||
export const MEDIUM_MOTOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48">
|
||||
<defs>
|
||||
<linearGradient id="motor-linear-gradient" x1="-313.1" y1="551.59" x2="-313.1" y2="551.43" gradientTransform="matrix(44.14, 0, 0, -44.15, 13843.7, 24396.04)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a8a9a8"/>
|
||||
<stop offset="1" stop-color="#545554"/>
|
||||
<linearGradient id="linear-gradient" x1="-427.2" y1="440.79" x2="-427.2" y2="440.63" gradientTransform="matrix(44.14, 0, 0, -44.15, 18878.72, 19502.57)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#a8aaa8"/>
|
||||
<stop offset="1" stop-color="#535453"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<title>Medium Motor</title>
|
||||
<g id="Medium_Motor" data-name="Medium Motor">
|
||||
<g id="Group_4" data-name="Group 4">
|
||||
<path id="Path_1" data-name="Path 1" d="M4.07,1.5h39a2.36,2.36,0,0,1,2.57,2V43.82c0,1-1.1,1.82-2.57,1.82h-39c-1.47,0-2.57-.81-2.57-1.82V3.47A2.36,2.36,0,0,1,4.07,1.5Z" transform="translate(-1.5 -1.49)" style="fill: url(#motor-linear-gradient)"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7">
|
||||
<g id="g7596">
|
||||
<path id="Union_1-2" data-name="Union 1-2" d="M1.5,22.74a6.21,6.21,0,0,1,6.22-6.22h8.8V7.72A6.22,6.22,0,0,1,22.74,1.5H24.4a6.21,6.21,0,0,1,6.22,6.22v8.8h8.8a6.21,6.21,0,0,1,6.22,6.22V24.4a6.22,6.22,0,0,1-6.22,6.22h-8.8v8.8a6.22,6.22,0,0,1-6.22,6.22H22.74a6.23,6.23,0,0,1-6.22-6.22h0v-8.8H7.72A6.22,6.22,0,0,1,1.5,24.4h0Z" transform="translate(-1.5 -1.49)" style="fill: #a8a9a8"/>
|
||||
<title>MediumMotor</title>
|
||||
<g style="isolation: isolate">
|
||||
<g id="svg7610">
|
||||
<g id="Medium_Motor" data-name="Medium Motor">
|
||||
<g id="medmotor_box" data-name="medmotor box">
|
||||
<path id="medmotor_box_wgradient" data-name="medmotor box wgradient" d="M2.57,0h39a2.36,2.36,0,0,1,2.57,2V42.33c0,1-1.1,1.82-2.57,1.82h-39C1.1,44.15,0,43.34,0,42.33V2A2.36,2.36,0,0,1,2.57,0Z" transform="translate(2 1.84)" style="fill: url(#linear-gradient)"/>
|
||||
</g>
|
||||
<g id="medmotor_star" data-name="medmotor star">
|
||||
<g>
|
||||
<image width="48" height="48" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAxCAYAAACcXioiAAAACXBIWXMAAAsSAAALEgHS3X78AAACfElEQVRoQ+2aPXLbMBBG32ZUijlCnDo/R1AukCoHyAFS5Sw5g5t0bnwCu0ofpTZ9A1usvSmwoClRJEEAI4gzejMcqliS+2FB4AMoUVVyISICrIErO3dpgBpoNONDJde9RKTCJf4R+Ga/u9TADbAFalXdkYEsAiz5L8B3nICxCmyBa+Auh4jVVMAUneR/ABugAuRIaIUT9w4TJyLJIpIEWJ+/wrX8Bng7fgViMRtcRR5F5G/KO/FmKiCANa5Vq6nADhXwwY7DrjaLHALgeJcZQ4D32MtulYwil4AYfOXOogKxRLe8p7SAZC4CSnMRUJrBmThwbBbSRxIh4HFDs3VPwIQl7oXj/M1U3BBr3PUAY3aiEZGjVnzPjQZY4mOscbNqjIgGeLDzGINWvBUQaImHSOlGIUZu2IqbgAr4CtwCT8AL7sbndLzgcrvF5Vq1jQ98An5bwNSNSh9PuFw/A+KH0RhLXIo9K96dB1L68SnZs+JLnchaK75UAWA9ZskCgGULUFiugAZ4BJquAB0IPjcUZz9ugNoL8IqSNplOxA74Z0ezUlU1p3eNG5787tocUuaQOZXfAfe4XB9UVVcAqroTkTsLaghzoZ5TuFGPb+jWzA3Z6dBk/Hrgp53nVEJx7vKXnUMq0XBgp/cWNFaJbe+yYXzCoS14SINL/g9hAnors96KbM5Gqy0Dg+MHUNxjo+6z1Hmg5SKgNBcBpSktIGrk6VJSQOsopwLHKCVAed2sqmPnAMgnICaB9sv9VOAYOQTEWPEdGZKHRAFWeu8Q74Fnpjemnnm1xEndBxI/dEOUFe9Z4hSy/FcCZlnxniVOIZsACPhKYaR2my5ZBZTgP7HrUIs43RAaAAAAAElFTkSuQmCC" style="opacity: 0.30000000000000004;mix-blend-mode: multiply"/>
|
||||
<path id="medmotor_cut-2" data-name="medmotor cut-2" d="M0,21.25A6.21,6.21,0,0,1,6.22,15H15V6.23A6.22,6.22,0,0,1,21.24,0H22.9a6.22,6.22,0,0,1,6.22,6.22V15h8.8a6.21,6.21,0,0,1,6.22,6.22v1.66a6.21,6.21,0,0,1-6.22,6.22h-8.8v8.8a6.21,6.21,0,0,1-6.22,6.22H21.24A6.22,6.22,0,0,1,15,37.93v-8.8H6.22A6.22,6.22,0,0,1,0,22.92H0Z" transform="translate(2 1.84)" style="fill: #a8aaa8"/>
|
||||
</g>
|
||||
<circle id="medmotor_hole_4" data-name="medmotor hole 4" cx="39.77" cy="24" r="4.85" style="fill: #393939"/>
|
||||
<circle id="medmotor_hole_3" data-name="medmotor hole 3" cx="8.37" cy="24" r="4.85" style="fill: #393939"/>
|
||||
<circle id="medmotor_hole_2" data-name="medmotor hole 2" cx="24.15" cy="8.22" r="4.85" style="fill: #393939"/>
|
||||
<circle id="medmotor_hole_1" data-name="medmotor hole 1" cx="24.15" cy="39.62" r="4.85" style="fill: #393939"/>
|
||||
</g>
|
||||
<g id="medmotor_red" data-name="medmotor red">
|
||||
<circle cx="24.3" cy="24" r="6.75" style="fill: #d42715"/>
|
||||
<circle cx="24.3" cy="24" r="6.63" style="fill: none;stroke: #a20800;stroke-width: 0.25px"/>
|
||||
</g>
|
||||
<path id="medmotor_Hole" data-name="medmotor Hole" d="M20.59,19.46s-.05,1-.77,1-1.46,0-1.46,0a2.38,2.38,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.21.21,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29A6.88,6.88,0,0,0,24,26.15V24.48a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H24.6a.65.65,0,0,1-.75-.51.49.49,0,0,1,0-.17,11.22,11.22,0,0,1,0-1.64,4.78,4.78,0,0,0-3.25,0C20.58,18.74,20.59,19.46,20.59,19.46Z" transform="translate(2 1.84)"/>
|
||||
</g>
|
||||
<circle id="Ellipse_1" data-name="Ellipse 1" cx="37.77" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
|
||||
<circle id="Ellipse_2" data-name="Ellipse 2" cx="6.37" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
|
||||
<circle id="Ellipse_1-2" data-name="Ellipse 1-2" cx="22.15" cy="6.38" r="4.85" style="fill: #3c3c3c"/>
|
||||
<circle id="Ellipse_2-2" data-name="Ellipse 2-2" cx="22.15" cy="37.78" r="4.85" style="fill: #3c3c3c"/>
|
||||
</g>
|
||||
<g id="Ellipse_4" data-name="Ellipse 4">
|
||||
<circle id="ellipse7603" cx="22.3" cy="22.16" r="6.75" style="fill: #b72b1c"/>
|
||||
<circle id="ellipse7605" cx="22.3" cy="22.16" r="6.63" style="fill: none;stroke: #8b1104;stroke-width: 0.25px"/>
|
||||
</g>
|
||||
<path id="Hole" d="M22.09,21s-.05,1-.77,1H19.86a2.37,2.37,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.2.2,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29,6.88,6.88,0,0,0,1.68-.29V26a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H26.1a.65.65,0,0,1-.75-.51.5.5,0,0,1,0-.17,11.36,11.36,0,0,1,0-1.65,4.9,4.9,0,0,0-3.25,0C22.08,20.23,22.09,21,22.09,21Z" transform="translate(-1.5 -1.49)"/>
|
||||
</g>
|
||||
</svg>`;
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
/// <reference path="./layoutView.ts" />
|
||||
|
||||
namespace pxsim {
|
||||
export const GAME_LOOP_FPS = 32;
|
||||
}
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
const EV3_STYLE = `
|
||||
@ -29,13 +33,21 @@ namespace pxsim.visuals {
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:8px;
|
||||
fill:#fff;
|
||||
pointer-events: none; user-select: none;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
.sim-text.small {
|
||||
font-size:6px;
|
||||
}
|
||||
.sim-text.large {
|
||||
font-size:30px;
|
||||
}
|
||||
.sim-text.number {
|
||||
font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro;
|
||||
font-weight: bold;
|
||||
}
|
||||
.sim-text.inverted {
|
||||
fill:#000;
|
||||
fill:#5A5A5A;
|
||||
}
|
||||
|
||||
/* Color Grid */
|
||||
@ -95,16 +107,9 @@ namespace pxsim.visuals {
|
||||
|
||||
private layoutView: LayoutView;
|
||||
|
||||
private controlGroup: ViewContainer;
|
||||
private selectedNode: NodeType;
|
||||
private selectedPort: number;
|
||||
private controlView: View;
|
||||
private cachedControlNodes: { [index: string]: View[] } = {};
|
||||
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
|
||||
|
||||
private closeGroup: ViewContainer;
|
||||
private closeIconView: View;
|
||||
|
||||
private screenCanvas: HTMLCanvasElement;
|
||||
private screenCanvasCtx: CanvasRenderingContext2D;
|
||||
private screenCanvasData: ImageData;
|
||||
@ -136,6 +141,17 @@ namespace pxsim.visuals {
|
||||
this.board.updateSubscribers.push(() => this.updateState());
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
Runtime.messagePosted = (msg) => {
|
||||
switch (msg.type || "") {
|
||||
case "status": {
|
||||
const state = (msg as pxsim.SimulatorStateMessage).state;
|
||||
if (state == "killed") this.kill();
|
||||
if (state == "running") this.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getView(): SVGAndSize<SVGSVGElement> {
|
||||
@ -167,84 +183,6 @@ namespace pxsim.visuals {
|
||||
this.layoutView.updateTheme(theme);
|
||||
}
|
||||
|
||||
public updateState() {
|
||||
this.updateVisibleNodes();
|
||||
this.updateScreen();
|
||||
}
|
||||
|
||||
private updateVisibleNodes() {
|
||||
const inputNodes = ev3board().getInputNodes();
|
||||
inputNodes.forEach((node, index) => {
|
||||
const view = this.getDisplayViewForNode(node.id, index);
|
||||
if (view) {
|
||||
this.layoutView.setInput(index, view);
|
||||
view.updateState();
|
||||
}
|
||||
});
|
||||
|
||||
this.getDisplayViewForNode(ev3board().getBrickNode().id, -1).updateState();
|
||||
|
||||
const outputNodes = ev3board().getMotors();
|
||||
outputNodes.forEach((node, index) => {
|
||||
const view = this.getDisplayViewForNode(node.id, index);
|
||||
if (view) {
|
||||
this.layoutView.setOutput(index, view);
|
||||
view.updateState();
|
||||
}
|
||||
});
|
||||
|
||||
const selected = this.layoutView.getSelected();
|
||||
if (selected && (selected.getId() !== this.selectedNode || selected.getPort() !== this.selectedPort)) {
|
||||
this.selectedNode = selected.getId();
|
||||
this.selectedPort = selected.getPort();
|
||||
this.controlGroup.clear();
|
||||
const control = this.getControlForNode(this.selectedNode, selected.getPort());
|
||||
if (control) {
|
||||
this.controlView = control;
|
||||
this.controlGroup.addView(control);
|
||||
}
|
||||
this.closeIconView.setVisible(true);
|
||||
} else if (!selected) {
|
||||
this.controlGroup.clear();
|
||||
this.controlView = undefined;
|
||||
this.selectedNode = undefined;
|
||||
this.selectedPort = undefined;
|
||||
this.closeIconView.setVisible(false);
|
||||
}
|
||||
|
||||
this.resize();
|
||||
}
|
||||
|
||||
public resize() {
|
||||
const bounds = this.element.getBoundingClientRect();
|
||||
this.width = bounds.width;
|
||||
this.height = bounds.height;
|
||||
this.layoutView.layout(bounds.width, bounds.height);
|
||||
|
||||
if (this.selectedNode) {
|
||||
const scale = this.width / this.closeIconView.getInnerWidth() / 10;
|
||||
// Translate close icon
|
||||
this.closeIconView.scale(Math.max(0, Math.min(1, scale)));
|
||||
const closeIconWidth = this.closeIconView.getWidth();
|
||||
const closeIconHeight = this.closeIconView.getHeight();
|
||||
const closeCoords = this.layoutView.getCloseIconCoords(closeIconWidth, closeIconHeight);
|
||||
this.closeIconView.translate(closeCoords.x, closeCoords.y);
|
||||
}
|
||||
|
||||
if (this.controlView) {
|
||||
const h = this.controlView.getInnerHeight();
|
||||
const w = this.controlView.getInnerWidth();
|
||||
const bh = this.layoutView.getModuleBounds().height - this.closeIconView.getHeight();
|
||||
const bw = this.layoutView.getModuleBounds().width - (this.width * MODULE_INNER_PADDING_RATIO * 2);
|
||||
this.controlView.scale(Math.min(bh / h, bw / w), false);
|
||||
|
||||
const controlCoords = this.layoutView.getSelectedCoords();
|
||||
this.controlView.translate(controlCoords.x, controlCoords.y);
|
||||
}
|
||||
|
||||
this.updateScreen();
|
||||
}
|
||||
|
||||
private getControlForNode(id: NodeType, port: number) {
|
||||
if (this.cachedControlNodes[id] && this.cachedControlNodes[id][port]) {
|
||||
return this.cachedControlNodes[id][port];
|
||||
@ -275,9 +213,10 @@ namespace pxsim.visuals {
|
||||
}
|
||||
case NodeType.MediumMotor:
|
||||
case NodeType.LargeMotor: {
|
||||
// const state = ev3board().getMotor(port)[0];
|
||||
// view = new MotorInputControl(this.element, this.defs, state, port);
|
||||
// break;
|
||||
// TODO: figure out if the motor is in "input" or "output" mode
|
||||
const state = ev3board().getMotors()[port];
|
||||
view = new MotorReporterControl(this.element, this.defs, state, port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,6 +262,10 @@ namespace pxsim.visuals {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getCloseIconView() {
|
||||
return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
|
||||
}
|
||||
|
||||
private buildDom() {
|
||||
this.wrapper = document.createElement('div');
|
||||
this.wrapper.style.display = 'inline';
|
||||
@ -337,25 +280,10 @@ namespace pxsim.visuals {
|
||||
this.layoutView = new LayoutView();
|
||||
this.layoutView.inject(this.element);
|
||||
|
||||
this.controlGroup = new ViewContainer();
|
||||
this.controlGroup.inject(this.element);
|
||||
|
||||
this.closeGroup = new ViewContainer();
|
||||
this.closeGroup.inject(this.element);
|
||||
|
||||
// Add EV3 module element
|
||||
this.layoutView.setBrick(new BrickView(-1));
|
||||
|
||||
this.closeIconView = new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
|
||||
this.closeIconView.registerClick(() => {
|
||||
this.layoutView.clearSelected();
|
||||
this.updateState();
|
||||
})
|
||||
this.closeGroup.addView(this.closeIconView);
|
||||
this.closeIconView.setVisible(false);
|
||||
|
||||
this.resize();
|
||||
this.updateState();
|
||||
|
||||
// Add Screen canvas to board
|
||||
this.buildScreenCanvas();
|
||||
@ -369,6 +297,17 @@ namespace pxsim.visuals {
|
||||
});
|
||||
}
|
||||
|
||||
public resize() {
|
||||
if (!this.element) return;
|
||||
this.width = document.body.offsetWidth;
|
||||
this.height = document.body.offsetHeight;
|
||||
this.layoutView.layout(this.width, this.height);
|
||||
|
||||
this.updateState();
|
||||
let state = ev3board().screenState;
|
||||
this.updateScreenStep(state);
|
||||
}
|
||||
|
||||
private buildScreenCanvas() {
|
||||
this.screenCanvas = document.createElement("canvas");
|
||||
this.screenCanvas.id = "board-screen-canvas";
|
||||
@ -394,18 +333,124 @@ namespace pxsim.visuals {
|
||||
this.screenCanvasTemp.style.display = 'none';
|
||||
}
|
||||
|
||||
private updateScreen() {
|
||||
let state = ev3board().screenState;
|
||||
private kill() {
|
||||
this.running = false;
|
||||
if (this.lastAnimationIds.length > 0) {
|
||||
this.lastAnimationIds.forEach(animationId => {
|
||||
cancelAnimationFrame(animationId);
|
||||
})
|
||||
}
|
||||
// Save previous inputs for the next cycle
|
||||
EV3View.previousSelectedInputs = ev3board().getInputNodes().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1)
|
||||
EV3View.previousSeletedOutputs = ev3board().getMotors().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1);
|
||||
}
|
||||
|
||||
private static previousSelectedInputs: number[];
|
||||
private static previousSeletedOutputs: number[];
|
||||
|
||||
private static isPreviousInputSelected(index: number, id: number) {
|
||||
if (EV3View.previousSelectedInputs && EV3View.previousSelectedInputs[index] == id) {
|
||||
EV3View.previousSelectedInputs[index] = undefined;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static isPreviousOutputSelected(index: number, id: number) {
|
||||
if (EV3View.previousSeletedOutputs && EV3View.previousSeletedOutputs[index] == id) {
|
||||
EV3View.previousSeletedOutputs[index] = undefined;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private begin() {
|
||||
this.running = true;
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
private running: boolean = false;
|
||||
private lastAnimationIds: number[] = [];
|
||||
public updateState() {
|
||||
if (this.lastAnimationIds.length > 0) {
|
||||
this.lastAnimationIds.forEach(animationId => {
|
||||
cancelAnimationFrame(animationId);
|
||||
})
|
||||
}
|
||||
if (!this.running) return;
|
||||
const fps = GAME_LOOP_FPS;
|
||||
let now;
|
||||
let then = pxsim.U.now();
|
||||
let interval = 1000 / fps;
|
||||
let delta;
|
||||
let that = this;
|
||||
function loop() {
|
||||
const animationId = requestAnimationFrame(loop);
|
||||
that.lastAnimationIds.push(animationId);
|
||||
now = pxsim.U.now();
|
||||
delta = now - then;
|
||||
if (delta > interval) {
|
||||
then = now;
|
||||
that.updateStateStep(delta);
|
||||
}
|
||||
}
|
||||
loop();
|
||||
}
|
||||
|
||||
private updateStateStep(elapsed: number) {
|
||||
const inputNodes = ev3board().getInputNodes();
|
||||
inputNodes.forEach((node, index) => {
|
||||
node.updateState(elapsed);
|
||||
const view = this.getDisplayViewForNode(node.id, index);
|
||||
if (!node.didChange() && !view.didChange()) return;
|
||||
if (view) {
|
||||
const isSelected = EV3View.isPreviousInputSelected(index, node.id) || view.getSelected();
|
||||
if (isSelected && !view.getSelected()) view.setSelected(true);
|
||||
const control = isSelected ? this.getControlForNode(node.id, index) : undefined;
|
||||
const closeIcon = control ? this.getCloseIconView() : undefined;
|
||||
this.layoutView.setInput(index, view, control, closeIcon);
|
||||
view.updateState();
|
||||
if (control) control.updateState();
|
||||
}
|
||||
});
|
||||
|
||||
const brickNode = ev3board().getBrickNode();
|
||||
if (brickNode.didChange()) {
|
||||
this.getDisplayViewForNode(brickNode.id, -1).updateState();
|
||||
}
|
||||
|
||||
const outputNodes = ev3board().getMotors();
|
||||
outputNodes.forEach((node, index) => {
|
||||
node.updateState(elapsed);
|
||||
const view = this.getDisplayViewForNode(node.id, index);
|
||||
if (!node.didChange() && !view.didChange()) return;
|
||||
if (view) {
|
||||
const isSelected = EV3View.isPreviousOutputSelected(index, node.id) || view.getSelected();
|
||||
if (isSelected && !view.getSelected()) view.setSelected(true);
|
||||
const control = isSelected ? this.getControlForNode(node.id, index) : undefined;
|
||||
const closeIcon = control ? this.getCloseIconView() : undefined;
|
||||
this.layoutView.setOutput(index, view, control, closeIcon);
|
||||
view.updateState();
|
||||
if (control) control.updateState();
|
||||
}
|
||||
});
|
||||
|
||||
let state = ev3board().screenState;
|
||||
if (state.didChange()) {
|
||||
this.updateScreenStep(state);
|
||||
}
|
||||
}
|
||||
|
||||
private updateScreenStep(state: EV3ScreenState) {
|
||||
const bBox = this.layoutView.getBrick().getScreenBBox();
|
||||
if (!bBox || bBox.width == 0) return;
|
||||
|
||||
const scale = bBox.width / SCREEN_WIDTH;
|
||||
this.screenScaledWidth = bBox.width;
|
||||
const scale = (bBox.width - 4) / SCREEN_WIDTH;
|
||||
this.screenScaledWidth = (bBox.width - 4);
|
||||
this.screenScaledHeight = this.screenScaledWidth / SCREEN_WIDTH * SCREEN_HEIGHT;
|
||||
|
||||
this.screenCanvas.style.top = `${bBox.top}px`;
|
||||
this.screenCanvas.style.left = `${bBox.left}px`;
|
||||
this.screenCanvas.style.top = `${bBox.top + 2}px`;
|
||||
this.screenCanvas.style.left = `${bBox.left + 2}px`;
|
||||
this.screenCanvas.width = this.screenScaledWidth;
|
||||
this.screenCanvas.height = this.screenScaledHeight;
|
||||
|
||||
|
@ -1,17 +1,20 @@
|
||||
/// <reference path="./nodes/staticView.ts" />
|
||||
/// <reference path="./nodes/moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export const CONTROL_WIDTH = 87.5;
|
||||
export const CONTROL_HEIGHT = 175;
|
||||
|
||||
export const CONTROL_TEXT_COLOR = '#000';
|
||||
|
||||
export abstract class ControlView<T extends BaseNode> extends SimView<T> implements LayoutElement {
|
||||
private background: SVGSVGElement;
|
||||
protected content: SVGSVGElement;
|
||||
|
||||
abstract getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement): SVGElement;
|
||||
|
||||
constructor(protected parent: SVGSVGElement, protected globalDefs: SVGDefsElement, protected state: T, protected port: number) {
|
||||
super(state);
|
||||
|
||||
}
|
||||
|
||||
getInnerWidth(): number {
|
||||
@ -34,18 +37,39 @@ namespace pxsim.visuals {
|
||||
return false;
|
||||
}
|
||||
|
||||
buildDom(width: number): SVGElement {
|
||||
this.background = svg.elt("svg", { height: "100%", width: "100%"}) as SVGSVGElement;
|
||||
this.background.appendChild(this.getInnerView(this.parent, this.globalDefs));
|
||||
return this.background;
|
||||
buildDom(): SVGElement {
|
||||
this.content = svg.elt("svg", { viewBox: `0 0 ${this.getInnerWidth()} ${this.getInnerHeight()}` }) as SVGSVGElement;
|
||||
this.content.appendChild(this.getInnerView(this.parent, this.globalDefs));
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public resize(width: number, height: number) {
|
||||
super.resize(width, height);
|
||||
this.updateDimensions(width, height);
|
||||
}
|
||||
|
||||
private updateDimensions(width: number, height: number) {
|
||||
if (this.content) {
|
||||
const currentWidth = this.getInnerWidth();
|
||||
const currentHeight = this.getInnerHeight();
|
||||
const newHeight = currentHeight / currentWidth * width;
|
||||
const newWidth = currentWidth / currentHeight * height;
|
||||
if (newHeight > height) {
|
||||
// scale width instead
|
||||
this.content.setAttribute('width', `${newWidth}`);
|
||||
this.content.setAttribute('height', `${height}`);
|
||||
// translate to the middle (width)
|
||||
this.translate(width / 2 - newWidth / 2, 0);
|
||||
} else {
|
||||
this.content.setAttribute('width', `${width}`);
|
||||
this.content.setAttribute('height', `${newHeight}`);
|
||||
// translate to the middle (height)
|
||||
this.translate(0, height / 2 - newHeight / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onComponentVisible() {
|
||||
|
||||
}
|
||||
|
||||
getWeight() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,17 @@ namespace pxsim.visuals {
|
||||
return this.closeGroup;
|
||||
}
|
||||
|
||||
|
||||
buildDom(): SVGElement {
|
||||
this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement;
|
||||
this.content.appendChild(this.getInnerView());
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public resize(width: number, height: number) {
|
||||
super.resize(width, height);
|
||||
}
|
||||
|
||||
public getInnerHeight() {
|
||||
return 32;
|
||||
}
|
||||
|
@ -7,35 +7,44 @@ namespace pxsim.visuals {
|
||||
|
||||
getInnerView() {
|
||||
this.group = svg.elt("g") as SVGGElement;
|
||||
this.group.setAttribute("transform", `translate(17, ${35 + this.getHeight() / 4}) scale(5)`)
|
||||
this.group.setAttribute("transform", `translate(1.02, 1.5) scale(0.8)`)
|
||||
|
||||
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
|
||||
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
|
||||
const colorIds = ['red', 'yellow', 'blue', 'green', undefined, 'grey'];
|
||||
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', undefined, '#6c2d00'];
|
||||
const colorValue = [5, 4, 2, 3, 1, 7];
|
||||
|
||||
let cy = -4;
|
||||
for (let c = 0; c < colorIds.length; c++) {
|
||||
const cx = c % 2 == 0 ? 2.2 : 8.2;
|
||||
if (c % 2 == 0) cy += 5;
|
||||
const circle = pxsim.svg.child(this.group, "circle", { 'class': 'sim-color-grid-circle', 'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` });
|
||||
circle.addEventListener(pointerEvents.down, ev => {
|
||||
this.setColor(colorValue[c]);
|
||||
})
|
||||
if (colorIds[c]) {
|
||||
const circle = pxsim.svg.child(this.group, "circle", { 'class': 'sim-color-grid-circle', 'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` });
|
||||
circle.addEventListener(pointerEvents.down, ev => {
|
||||
this.setColor(colorValue[c]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
|
||||
pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
|
||||
pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.5px` });
|
||||
pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle', 'cx': 2.2, 'cy': '11', 'r': '2', 'style': `fill: #fff` });
|
||||
pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '11', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` });
|
||||
whiteCircleWrapper.addEventListener(pointerEvents.down, ev => {
|
||||
this.setColor(6);
|
||||
})
|
||||
return this.group;
|
||||
}
|
||||
|
||||
getInnerWidth() {
|
||||
return 10.2;
|
||||
}
|
||||
|
||||
getInnerHeight() {
|
||||
return 15;
|
||||
}
|
||||
|
||||
private setColor(color: number) {
|
||||
const state = this.state;
|
||||
state.setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,30 +4,78 @@ namespace pxsim.visuals {
|
||||
|
||||
export class ColorWheelControl extends ControlView<ColorSensorNode> {
|
||||
private group: SVGGElement;
|
||||
private colorGradient: SVGLinearGradientElement;
|
||||
private defs: SVGDefsElement;
|
||||
|
||||
private static COLOR_DARK = 1;
|
||||
private static COLOR_LIGHT = 99;
|
||||
|
||||
getInnerView() {
|
||||
getInnerView(parent: SVGSVGElement) {
|
||||
this.defs = <SVGDefsElement>svg.child(this.element, "defs", {});
|
||||
this.group = svg.elt("g") as SVGGElement;
|
||||
this.group.setAttribute("transform", `translate(12, ${this.getHeight() / 2 - 15}) scale(2.5)`)
|
||||
this.group.setAttribute("transform", `translate(12, 0) scale(2)`)
|
||||
|
||||
let gc = "gradient-color";
|
||||
this.colorGradient = svg.linearGradient(this.defs, gc, true);
|
||||
svg.setGradientValue(this.colorGradient, "50%");
|
||||
svg.setGradientColors(this.colorGradient, "black", "white");
|
||||
|
||||
const circle = pxsim.svg.child(this.group, "g");
|
||||
const lightHalf = pxsim.svg.child(circle, "path", { 'class': 'sim-color-wheel-half', 'd': 'M19,28.76a11.71,11.71,0,1,1,4.58-.92A11.74,11.74,0,0,1,19,28.76Z', 'transform': 'translate(-6.5 -4.5)', 'style': `fill: #fff;stroke: #000;stroke-miterlimit: 10` });
|
||||
pxsim.svg.child(circle, "path", { 'd': 'M19,28.52a11.42,11.42,0,0,0,4.48-.9,11.75,11.75,0,0,0,3.67-2.47,11.55,11.55,0,0,0,2.46-3.67,11.48,11.48,0,0,0,0-9,11.41,11.41,0,0,0-6.13-6.13,11.48,11.48,0,0,0-9,0,11.41,11.41,0,0,0-6.13,6.13,11.48,11.48,0,0,0,0,9,11.55,11.55,0,0,0,2.46,3.67,11.75,11.75,0,0,0,3.67,2.47,11.42,11.42,0,0,0,4.48.9M19,29A12,12,0,1,1,31,17,12,12,0,0,1,19,29Z', 'transform': 'translate(-6.5 -4.5)', 'style': `fill: #fff;stroke: #000;stroke-miterlimit: 10` });
|
||||
lightHalf.addEventListener(pointerEvents.down, ev => {
|
||||
this.setColor(ColorWheelControl.COLOR_LIGHT);
|
||||
})
|
||||
const darkHalf = pxsim.svg.child(this.group, "path", { 'class': 'sim-color-wheel-half', 'd': 'M19,5c.16,8.54,0,14.73,0,24A12,12,0,0,1,19,5Z', 'transform': 'translate(-6.5 -4.5)' });
|
||||
darkHalf.addEventListener(pointerEvents.down, ev => {
|
||||
this.setColor(ColorWheelControl.COLOR_DARK);
|
||||
})
|
||||
const innerCircle = pxsim.svg.child(circle, "circle",
|
||||
{cursor: '-webkit-grab',
|
||||
fill: `url(#${gc})`,
|
||||
r: 17,
|
||||
cx: 13,
|
||||
cy: 20,
|
||||
stroke: 'black',
|
||||
'stroke-width': 2
|
||||
});
|
||||
|
||||
let pt = parent.createSVGPoint();
|
||||
let captured = false;
|
||||
touchEvents(circle,
|
||||
ev => {
|
||||
if (captured && (ev as MouseEvent).clientX) {
|
||||
ev.preventDefault();
|
||||
this.setColor(pt, parent, ev as MouseEvent);
|
||||
}
|
||||
},
|
||||
ev => {
|
||||
captured = true;
|
||||
if ((ev as MouseEvent).clientX) {
|
||||
this.setColor(pt, parent, ev as MouseEvent);
|
||||
}
|
||||
},
|
||||
ev => {
|
||||
captured = false;
|
||||
},
|
||||
ev => {
|
||||
captured = false;
|
||||
}
|
||||
)
|
||||
return this.group;
|
||||
}
|
||||
|
||||
private setColor(color: number) {
|
||||
getInnerWidth() {
|
||||
return CONTROL_WIDTH;
|
||||
}
|
||||
|
||||
getInnerHeight() {
|
||||
return CONTROL_WIDTH;
|
||||
}
|
||||
|
||||
updateState() {
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
const node = this.state;
|
||||
const percentage = node.getValue();
|
||||
svg.setGradientValue(this.colorGradient, percentage + "%");
|
||||
}
|
||||
|
||||
private setColor(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
||||
const width = CONTROL_WIDTH;
|
||||
let cur = svg.cursorPoint(pt, parent, ev);
|
||||
let t = Math.max(0, Math.min(1, (width + this.left / this.scaleFactor - cur.x / this.scaleFactor) / width));
|
||||
const state = this.state;
|
||||
state.setColor(color);
|
||||
state.setColor((1-t)*100);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,33 +7,38 @@ namespace pxsim.visuals {
|
||||
private gradient: SVGLinearGradientElement;
|
||||
private slider: SVGGElement;
|
||||
|
||||
private static SLIDER_HANDLE_HEIGHT = 31;
|
||||
private reporter: SVGTextElement;
|
||||
|
||||
private isVisible = false;
|
||||
private static SLIDER_HANDLE_HEIGHT = 26;
|
||||
private static SLIDER_SIDE_PADDING = 6;
|
||||
|
||||
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
|
||||
let gid = "gradient-slider-" + this.getId();
|
||||
this.group = svg.elt("g") as SVGGElement;
|
||||
this.gradient = createGradient(gid, this.getGradientDefinition());
|
||||
this.gradient.setAttribute('x1', '-438.37');
|
||||
this.gradient.setAttribute('y1', '419.43');
|
||||
this.gradient.setAttribute('x2', '-438.37');
|
||||
this.gradient.setAttribute('y2', '418.43');
|
||||
this.gradient.setAttribute('gradientTransform', 'matrix(50, 0, 0, -110, 21949.45, 46137.67)');
|
||||
this.gradient.setAttribute('gradientUnits', 'userSpaceOnUse');
|
||||
this.gradient.setAttribute('x1', '0%');
|
||||
this.gradient.setAttribute('y1', '0%');
|
||||
this.gradient.setAttribute('x2', '0%');
|
||||
this.gradient.setAttribute('y2', '100%');
|
||||
// this.gradient.setAttribute('gradientTransform', 'matrix(50, 0, 0, -110, 21949.45, 46137.67)');
|
||||
// this.gradient.setAttribute('gradientUnits', 'userSpaceOnUse');
|
||||
globalDefs.appendChild(this.gradient);
|
||||
|
||||
this.group = svg.elt("g") as SVGGElement;
|
||||
|
||||
const sliderGroup = pxsim.svg.child(this.group, "g");
|
||||
sliderGroup.setAttribute("transform", `translate(0, ${10 + this.getTopPadding()})`)
|
||||
const reporterGroup = pxsim.svg.child(this.group, "g");
|
||||
reporterGroup.setAttribute("transform", `translate(31, 42)`);
|
||||
this.reporter = pxsim.svg.child(reporterGroup, "text", { 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
|
||||
|
||||
const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': this.getLeftPadding(), 'y': 2, 'width': this.getWidth() - this.getLeftPadding() * 2, 'height': this.getContentHeight(), 'style': `fill: url(#${gid})` });
|
||||
const sliderGroup = pxsim.svg.child(this.group, "g");
|
||||
sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
|
||||
|
||||
const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': DistanceSliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - DistanceSliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
|
||||
|
||||
this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
|
||||
const sliderInner = pxsim.svg.child(this.slider, "g");
|
||||
pxsim.svg.child(sliderInner, "rect", { 'width': this.getWidth(), 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT, 'rx': '2', 'ry': '2', 'style': 'fill: #f12a21' });
|
||||
pxsim.svg.child(sliderInner, "rect", { 'x': '0.5', 'y': '0.5', 'width': this.getWidth() - 1, 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT - 1, 'rx': '1.5', 'ry': '1.5', 'style': 'fill: none;stroke: #b32e29' });
|
||||
pxsim.svg.child(sliderInner, "rect", { 'width': this.getSliderWidth(), 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT, 'rx': '2', 'ry': '2', 'style': 'fill: #f12a21' });
|
||||
pxsim.svg.child(sliderInner, "rect", { 'x': '0.5', 'y': '0.5', 'width': this.getSliderWidth() - 1, 'height': DistanceSliderControl.SLIDER_HANDLE_HEIGHT - 1, 'rx': '1.5', 'ry': '1.5', 'style': 'fill: none;stroke: #b32e29' });
|
||||
|
||||
const dragSurface = svg.child(this.group, "rect", {
|
||||
x: 0,
|
||||
@ -66,52 +71,61 @@ namespace pxsim.visuals {
|
||||
return this.group;
|
||||
}
|
||||
|
||||
private getLeftPadding() {
|
||||
return this.getInnerWidth() * 0.12;
|
||||
getInnerHeight() {
|
||||
return 192;
|
||||
}
|
||||
|
||||
private getTopPadding() {
|
||||
return this.getInnerHeight() / 4;
|
||||
getInnerWidth() {
|
||||
return 111;
|
||||
}
|
||||
|
||||
private getContentHeight() {
|
||||
return this.getInnerHeight() * 0.6;
|
||||
private getReporterHeight() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
onBoardStateChanged() {
|
||||
if (!this.isVisible) {
|
||||
private getSliderHeight() {
|
||||
return 110;
|
||||
}
|
||||
|
||||
private getSliderWidth() {
|
||||
return 62;
|
||||
}
|
||||
|
||||
updateState() {
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
const node = this.state;
|
||||
const percentage = node.getValue();
|
||||
const y = this.getContentHeight() * percentage / 100;
|
||||
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})`);
|
||||
}
|
||||
|
||||
onComponentVisible() {
|
||||
super.onComponentVisible();
|
||||
this.isVisible = true;
|
||||
this.onBoardStateChanged();
|
||||
}
|
||||
|
||||
onComponentHidden() {
|
||||
this.isVisible = false;
|
||||
// Update reporter text
|
||||
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
|
||||
}
|
||||
|
||||
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
||||
let cur = svg.cursorPoint(pt, parent, ev);
|
||||
const height = this.getContentHeight(); //DistanceSliderControl.SLIDER_HEIGHT;
|
||||
let t = Math.max(0, Math.min(1, (this.getTopPadding() + height + this.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
|
||||
const height = this.getSliderHeight();
|
||||
const bBox = this.content.getBoundingClientRect();
|
||||
let t = Math.max(0, Math.min(1, (DistanceSliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
|
||||
|
||||
const state = this.state;
|
||||
state.setDistance((1 - t) * (100));
|
||||
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: 1, color: "#ddd" }
|
||||
{ offset: 100, color: "#ddd" }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
60
sim/visuals/controls/motorReporter.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,6 @@ namespace pxsim.visuals {
|
||||
private group: SVGGElement;
|
||||
private slider: SVGGElement;
|
||||
|
||||
private isVisible = false;
|
||||
|
||||
private static SLIDER_WIDTH = 70;
|
||||
private static SLIDER_HEIGHT = 78;
|
||||
|
||||
@ -60,8 +58,8 @@ namespace pxsim.visuals {
|
||||
return this.getInnerHeight() / 4;
|
||||
}
|
||||
|
||||
onBoardStateChanged() {
|
||||
if (!this.isVisible) {
|
||||
updateState() {
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
const node = this.state;
|
||||
@ -71,20 +69,11 @@ namespace pxsim.visuals {
|
||||
this.slider.setAttribute("transform", `translate(${x}, ${y})`);
|
||||
}
|
||||
|
||||
onComponentVisible() {
|
||||
super.onComponentVisible();
|
||||
this.isVisible = true;
|
||||
this.onBoardStateChanged();
|
||||
}
|
||||
|
||||
onComponentHidden() {
|
||||
this.isVisible = false;
|
||||
}
|
||||
|
||||
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
|
||||
let cur = svg.cursorPoint(pt, parent, ev);
|
||||
const width = CONTROL_WIDTH; //DistanceSliderControl.SLIDER_HEIGHT;
|
||||
let t = Math.max(0, Math.min(1, (width + this.left / this.scaleFactor - cur.x / this.scaleFactor) / width))
|
||||
const bBox = this.content.getBoundingClientRect();
|
||||
let t = Math.max(0, Math.min(1, (width + bBox.left / this.scaleFactor - cur.x / this.scaleFactor) / width))
|
||||
|
||||
const state = this.state;
|
||||
state.setAngle((1 - t) * (100));
|
||||
|
@ -1,67 +1,56 @@
|
||||
/// <reference path="./view.ts" />
|
||||
/// <reference path="./nodes/staticView.ts" />
|
||||
/// <reference path="./nodes/moduleView.ts" />
|
||||
/// <reference path="./nodes/portView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
export const DEFAULT_WIDTH = 350;
|
||||
export const DEFAULT_HEIGHT = 700;
|
||||
|
||||
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 * 3 / 4;
|
||||
export const WIRING_HEIGHT_RATIO = MODULE_AND_WIRING_HEIGHT_RATIO / 4;
|
||||
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;
|
||||
setSelected(selected: boolean): void;
|
||||
}
|
||||
|
||||
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 selected: number;
|
||||
private selectedIsInput: boolean;
|
||||
private brick: BrickView;
|
||||
private offsets: number[];
|
||||
private contentGroup: SVGGElement;
|
||||
private scrollGroup: SVGGElement;
|
||||
private renderedViews: Map<boolean> = {};
|
||||
|
||||
private childScaleFactor: number;
|
||||
|
||||
private totalLength: number;
|
||||
private height: number;
|
||||
private hasDimensions = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.outputs = [
|
||||
new PortView(0, 'A'),
|
||||
new PortView(1, 'B'),
|
||||
new PortView(2, 'C'),
|
||||
new PortView(3, 'D')
|
||||
];
|
||||
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);
|
||||
|
||||
this.inputs = [
|
||||
new PortView(0, '1'),
|
||||
new PortView(1, '2'),
|
||||
new PortView(2, '3'),
|
||||
new PortView(3, '4')
|
||||
];
|
||||
|
||||
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
||||
this.outputWires[port] = new WireView(port);
|
||||
}
|
||||
@ -72,8 +61,7 @@ namespace pxsim.visuals {
|
||||
|
||||
public layout(width: number, height: number) {
|
||||
this.hasDimensions = true;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.resize(width, height);
|
||||
this.scrollGroup.setAttribute("width", width.toString());
|
||||
this.scrollGroup.setAttribute("height", height.toString());
|
||||
this.position();
|
||||
@ -81,6 +69,7 @@ namespace pxsim.visuals {
|
||||
|
||||
public setBrick(brick: BrickView) {
|
||||
this.brick = brick;
|
||||
this.brick.inject(this.scrollGroup);
|
||||
this.position();
|
||||
}
|
||||
|
||||
@ -88,55 +77,112 @@ namespace pxsim.visuals {
|
||||
return this.brick;
|
||||
}
|
||||
|
||||
public setInput(port: number, child: LayoutElement) {
|
||||
if (this.inputs[port]) {
|
||||
// Remove current input
|
||||
this.inputs[port].dispose();
|
||||
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.inputs[port] = child;
|
||||
|
||||
this.position();
|
||||
}
|
||||
|
||||
public setOutput(port: number, child: LayoutElement) {
|
||||
if (this.outputs[port]) {
|
||||
// Remove current input
|
||||
this.outputs[port].dispose();
|
||||
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.outputs[port] = child;
|
||||
|
||||
this.position();
|
||||
}
|
||||
|
||||
public onClick(index: number, input: boolean, ev: any) {
|
||||
this.setSelected(index, input);
|
||||
}
|
||||
|
||||
public clearSelected() {
|
||||
this.selected = undefined;
|
||||
this.selectedIsInput = undefined;
|
||||
}
|
||||
|
||||
public setSelected(index: number, input?: boolean) {
|
||||
if (index !== this.selected || input !== this.selectedIsInput) {
|
||||
this.selected = index;
|
||||
this.selectedIsInput = input;
|
||||
const node = this.getSelected();
|
||||
if (node) node.setSelected(true);
|
||||
|
||||
//this.redoPositioning();
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public getSelected() {
|
||||
if (this.selected !== undefined) {
|
||||
return this.selectedIsInput ? this.inputs[this.selected] : this.outputs[this.selected];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected buildDom(width: number) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -171,34 +217,63 @@ namespace pxsim.visuals {
|
||||
|
||||
this.offsets = [];
|
||||
|
||||
const selectedNode = this.getSelected();
|
||||
const contentWidth = this.width;
|
||||
if (!contentWidth) return;
|
||||
const contentHeight = this.height;
|
||||
if (!contentHeight) return;
|
||||
|
||||
const contentWidth = this.width || DEFAULT_WIDTH;
|
||||
const contentHeight = this.height || DEFAULT_HEIGHT;
|
||||
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();
|
||||
this.brick.inject(this.scrollGroup);
|
||||
const brickWidth = this.brick.getInnerWidth() / this.brick.getInnerHeight() * brickHeight;
|
||||
const brickPadding = (contentWidth - brickWidth) / 2;
|
||||
|
||||
const modulePadding = contentWidth / 35;
|
||||
const modulePadding = this.getModulePadding();
|
||||
const moduleSpacing = contentWidth / 4;
|
||||
const moduleWidth = moduleSpacing - (modulePadding * 2);
|
||||
let currentX = modulePadding;
|
||||
const moduleWidth = this.getInnerModuleWidth();
|
||||
let currentX = this.getModulePadding();
|
||||
let currentY = 0;
|
||||
this.outputs.forEach((n, i) => {
|
||||
const outputPadding = moduleWidth * n.getPaddingRatio();
|
||||
const outputWidth = moduleWidth - outputPadding * 2;
|
||||
n.inject(this.scrollGroup, outputWidth);
|
||||
n.resize(outputWidth);
|
||||
const nHeight = n.getHeight() / n.getWidth() * outputWidth;
|
||||
n.translate(currentX + outputPadding, currentY + moduleHeight - nHeight);
|
||||
n.setSelected(n == selectedNode);
|
||||
if (n.hasClick()) n.registerClick((ev: any) => {
|
||||
this.onClick(i, false, ev);
|
||||
})
|
||||
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;
|
||||
})
|
||||
|
||||
@ -206,7 +281,7 @@ namespace pxsim.visuals {
|
||||
currentY = moduleHeight;
|
||||
|
||||
const wireBrickSpacing = brickWidth / 5;
|
||||
const wiringYPadding = 10;
|
||||
const wiringYPadding = 5;
|
||||
let wireStartX = 0;
|
||||
let wireEndX = brickPadding + wireBrickSpacing;
|
||||
let wireEndY = currentY + this.getWiringHeight() + wiringYPadding;
|
||||
@ -214,7 +289,6 @@ namespace pxsim.visuals {
|
||||
|
||||
// Draw output lines
|
||||
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
|
||||
if (!this.outputWires[port].isRendered()) this.outputWires[port].inject(this.scrollGroup);
|
||||
this.outputWires[port].updateDimensions(wireStartX + moduleSpacing * this.outputs[port].getWiringRatio(), wireStartY, wireEndX, wireEndY);
|
||||
this.outputWires[port].setSelected(this.outputs[port].getId() == NodeType.Port);
|
||||
wireStartX += moduleSpacing;
|
||||
@ -225,22 +299,39 @@ namespace pxsim.visuals {
|
||||
currentY += this.getWiringHeight();
|
||||
|
||||
// Render the brick in the middle
|
||||
this.brick.resize(brickWidth);
|
||||
this.brick.resize(brickWidth, brickHeight);
|
||||
this.brick.translate(currentX, currentY);
|
||||
|
||||
currentX = modulePadding;
|
||||
currentY += brickHeight + this.getWiringHeight();
|
||||
|
||||
this.inputs.forEach((n, i) => {
|
||||
const inputPadding = moduleWidth * n.getPaddingRatio();
|
||||
const inputWidth = moduleWidth - inputPadding * 2;
|
||||
n.inject(this.scrollGroup, inputWidth);
|
||||
n.resize(inputWidth);
|
||||
n.translate(currentX + inputPadding, currentY);
|
||||
n.setSelected(n == selectedNode);
|
||||
if (n.hasClick()) n.registerClick((ev: any) => {
|
||||
this.onClick(i, true, ev);
|
||||
})
|
||||
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;
|
||||
})
|
||||
|
||||
@ -251,7 +342,6 @@ namespace pxsim.visuals {
|
||||
|
||||
// Draw input lines
|
||||
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
|
||||
if (!this.inputWires[port].isRendered()) this.inputWires[port].inject(this.scrollGroup);
|
||||
this.inputWires[port].updateDimensions(wireStartX, wireStartY, wireEndX, wireEndY);
|
||||
this.inputWires[port].setSelected(this.inputs[port].getId() == NodeType.Port);
|
||||
wireStartX += moduleSpacing;
|
||||
@ -259,33 +349,12 @@ namespace pxsim.visuals {
|
||||
}
|
||||
}
|
||||
|
||||
public getSelectedCoords() {
|
||||
const selected = this.getSelected();
|
||||
if (!selected) return undefined;
|
||||
const port = this.getSelected().getPort();
|
||||
return {
|
||||
x: this.getSelected().getPort() * this.width / 4 + this.width * MODULE_INNER_PADDING_RATIO,
|
||||
y: this.selectedIsInput ? this.getModuleHeight() + 2 * this.getWiringHeight() + this.getBrickHeight() : this.getModuleHeight() / 4
|
||||
}
|
||||
}
|
||||
|
||||
public getCloseIconCoords(closeIconWidth: number, closeIconHeight: number) {
|
||||
return {
|
||||
x: this.getSelected().getPort() * this.width / 4 + this.getModuleBounds().width / 2 - closeIconWidth / 2,
|
||||
y: this.selectedIsInput ? this.getModuleHeight() + 2 * this.getWiringHeight() + this.getBrickHeight() + this.getModuleHeight() - closeIconHeight : 0
|
||||
}
|
||||
}
|
||||
|
||||
public getModuleHeight() {
|
||||
return (this.height || DEFAULT_HEIGHT) * MODULE_HEIGHT_RATIO;
|
||||
}
|
||||
|
||||
public getBrickHeight() {
|
||||
return (this.height || DEFAULT_HEIGHT) * BRICK_HEIGHT_RATIO;
|
||||
return this.height * BRICK_HEIGHT_RATIO;
|
||||
}
|
||||
|
||||
public getWiringHeight() {
|
||||
return (this.height || DEFAULT_HEIGHT) * WIRING_HEIGHT_RATIO;
|
||||
return this.height * WIRING_HEIGHT_RATIO;
|
||||
}
|
||||
|
||||
public getModuleBounds() {
|
||||
@ -294,5 +363,17 @@ namespace pxsim.visuals {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export class BrickView extends StaticModuleView implements LayoutElement {
|
||||
export class BrickView extends ModuleView implements LayoutElement {
|
||||
|
||||
private static EV3_SCREEN_ID = "ev3_screen";
|
||||
private static EV3_LIGHT_ID = "btn_color";
|
||||
@ -35,19 +35,15 @@ namespace pxsim.visuals {
|
||||
return false;
|
||||
}
|
||||
|
||||
public shouldUpdateState() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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]);
|
||||
// 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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
export class ColorSensorView extends StaticModuleView implements LayoutElement {
|
||||
export class ColorSensorView extends ModuleView implements LayoutElement {
|
||||
|
||||
private control: ColorGridControl;
|
||||
|
||||
@ -10,7 +10,12 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
public getPaddingRatio() {
|
||||
return 1 / 8;
|
||||
return 1 / 4;
|
||||
}
|
||||
|
||||
public updateState() {
|
||||
super.updateState();
|
||||
// TODO: show different color modes
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
export class GyroSensorView extends StaticModuleView implements LayoutElement {
|
||||
export class GyroSensorView extends ModuleView implements LayoutElement {
|
||||
|
||||
constructor(port: number) {
|
||||
super(GYRO_SVG, "gyro", NodeType.GyroSensor, port);
|
||||
}
|
||||
|
||||
public getPaddingRatio() {
|
||||
return 1 / 4;
|
||||
return 0.3;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +1,34 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
export class LargeMotorView extends StaticModuleView implements LayoutElement {
|
||||
export class LargeMotorView extends ModuleView implements LayoutElement {
|
||||
|
||||
private static ROTATING_ECLIPSE_ID = "1eb2ae58-2419-47d4-86bf-4f26a7f0cf61";
|
||||
|
||||
private lastMotorAnimationId: any;
|
||||
private static ROTATING_ECLIPSE_ID = "hole";
|
||||
|
||||
constructor(port: number) {
|
||||
super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port);
|
||||
}
|
||||
|
||||
updateState() {
|
||||
super.updateState();
|
||||
const motorState = ev3board().getMotors()[this.port];
|
||||
if (!motorState) return;
|
||||
const speed = motorState.getSpeed();
|
||||
if (this.lastMotorAnimationId) cancelAnimationFrame(this.lastMotorAnimationId);
|
||||
|
||||
if (!speed) return;
|
||||
this.playMotorAnimation(motorState);
|
||||
this.setMotorAngle(motorState.getAngle());
|
||||
}
|
||||
|
||||
private playMotorAnimation(state: MotorNode) {
|
||||
// Max medium motor RPM is 170 according to http://www.cs.scranton.edu/~bi/2015s-html/cs358/EV3-Motor-Guide.docx
|
||||
const rotationsPerMinute = 170; // 170 rpm at speed 100
|
||||
const rotationsPerSecond = rotationsPerMinute / 60;
|
||||
const fps = MOTOR_ROTATION_FPS;
|
||||
const rotationsPerFrame = rotationsPerSecond / fps;
|
||||
let now;
|
||||
let then = Date.now();
|
||||
let interval = 1000 / fps;
|
||||
let delta;
|
||||
let that = this;
|
||||
function draw() {
|
||||
that.lastMotorAnimationId = requestAnimationFrame(draw);
|
||||
now = Date.now();
|
||||
delta = now - then;
|
||||
if (delta > interval) {
|
||||
then = now - (delta % interval);
|
||||
that.playMotorAnimationStep(state.angle);
|
||||
const rotations = state.getSpeed() / 100 * rotationsPerFrame;
|
||||
const angle = rotations * 360;
|
||||
state.angle += angle;
|
||||
}
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
||||
private playMotorAnimationStep(angle: number) {
|
||||
private setMotorAngle(angle: number) {
|
||||
const holeEl = this.content.getElementById(this.normalizeId(LargeMotorView.ROTATING_ECLIPSE_ID))
|
||||
const width = 34;
|
||||
const height = 34;
|
||||
const width = 125.92;
|
||||
const height = 37.9;
|
||||
const transform = `rotate(${angle} ${width / 2} ${height / 2})`;
|
||||
holeEl.setAttribute("transform", transform);
|
||||
}
|
||||
|
||||
getWiringRatio() {
|
||||
return 0.62;
|
||||
return 0.37;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +1,39 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export const MOTOR_ROTATION_FPS = 32;
|
||||
|
||||
export class MediumMotorView extends StaticModuleView implements LayoutElement {
|
||||
export class MediumMotorView extends ModuleView implements LayoutElement {
|
||||
|
||||
private static ROTATING_ECLIPSE_ID = "Hole";
|
||||
private static ROTATING_ECLIPSE_ID = "medmotor_Hole";
|
||||
|
||||
private hasPreviousAngle: boolean;
|
||||
private previousAngle: number;
|
||||
|
||||
private lastMotorAnimationId: any;
|
||||
|
||||
constructor(port: number) {
|
||||
super(MEDIUM_MOTOR_SVG, "medium-motor", NodeType.MediumMotor, port);
|
||||
}
|
||||
|
||||
public getPaddingRatio() {
|
||||
return 1 / 10;
|
||||
return 1 / 5;
|
||||
}
|
||||
|
||||
updateState() {
|
||||
super.updateState();
|
||||
const motorState = ev3board().getMotors()[this.port];
|
||||
if (!motorState) return;
|
||||
const speed = motorState.getSpeed();
|
||||
if (this.lastMotorAnimationId) cancelAnimationFrame(this.lastMotorAnimationId);
|
||||
|
||||
if (!speed) return;
|
||||
this.playMotorAnimation(motorState);
|
||||
this.setMotorAngle(motorState.getAngle());
|
||||
}
|
||||
|
||||
private playMotorAnimation(state: MotorNode) {
|
||||
// Max medium motor RPM is 250 according to http://www.cs.scranton.edu/~bi/2015s-html/cs358/EV3-Motor-Guide.docx
|
||||
const rotationsPerMinute = 250; // 250 rpm at speed 100
|
||||
const rotationsPerSecond = rotationsPerMinute / 60;
|
||||
const fps = MOTOR_ROTATION_FPS;
|
||||
const rotationsPerFrame = rotationsPerSecond / fps;
|
||||
let now;
|
||||
let then = Date.now();
|
||||
let interval = 1000 / fps;
|
||||
let delta;
|
||||
let that = this;
|
||||
function draw() {
|
||||
that.lastMotorAnimationId = requestAnimationFrame(draw);
|
||||
now = Date.now();
|
||||
delta = now - then;
|
||||
if (delta > interval) {
|
||||
then = now - (delta % interval);
|
||||
that.playMotorAnimationStep(state.angle);
|
||||
const rotations = state.getSpeed() / 100 * rotationsPerFrame;
|
||||
const angle = rotations * 360;
|
||||
state.angle += angle;
|
||||
}
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
||||
private playMotorAnimationStep(angle: number) {
|
||||
private setMotorAngle(angle: number) {
|
||||
const holeEl = this.content.getElementById(this.normalizeId(MediumMotorView.ROTATING_ECLIPSE_ID))
|
||||
const width = 47.9;
|
||||
const height = 47.2;
|
||||
const transform = `translate(-1.5 -1.49) rotate(${angle} ${width / 2} ${height / 2})`;
|
||||
const width = 44.45;
|
||||
const height = 44.45;
|
||||
const transform = `translate(2 1.84) rotate(${angle} ${width / 2} ${height / 2})`;
|
||||
holeEl.setAttribute("transform", transform);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export class StaticModuleView extends View implements LayoutElement {
|
||||
export class ModuleView extends View implements LayoutElement {
|
||||
protected content: SVGSVGElement;
|
||||
|
||||
protected controlShown: boolean;
|
||||
protected selected: boolean;
|
||||
|
||||
protected opacity: number;
|
||||
|
||||
constructor(protected xml: string, protected prefix: string, protected id: NodeType, protected port: NodeType) {
|
||||
super();
|
||||
@ -45,9 +46,8 @@ namespace pxsim.visuals {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
protected buildDom(width: number): SVGElement {
|
||||
protected buildDom(): SVGElement {
|
||||
this.content = svg.parseString(this.xml);
|
||||
this.updateDimensions(width);
|
||||
this.buildDomCore();
|
||||
this.attachEvents();
|
||||
if (this.hasClick())
|
||||
@ -82,15 +82,17 @@ namespace pxsim.visuals {
|
||||
public attachEvents() {
|
||||
}
|
||||
|
||||
public resize(width: number) {
|
||||
this.updateDimensions(width);
|
||||
public resize(width: number, height: number) {
|
||||
super.resize(width, height);
|
||||
this.updateDimensions(width, height);
|
||||
}
|
||||
|
||||
private updateDimensions(width: number) {
|
||||
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}`);
|
||||
}
|
||||
@ -101,23 +103,28 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
public setSelected(selected: boolean) {
|
||||
this.selected = selected;
|
||||
super.setSelected(selected);
|
||||
this.updateOpacity();
|
||||
}
|
||||
|
||||
public updateState() {
|
||||
this.updateOpacity();
|
||||
}
|
||||
|
||||
protected updateOpacity() {
|
||||
if (this.rendered) {
|
||||
const opacity = this.selected ? "0.5" : "1";
|
||||
if (this.hasClick()) {
|
||||
this.setOpacity(opacity);
|
||||
const opacity = this.selected ? 0.2 : 1;
|
||||
if (this.hasClick() && this.opacity != opacity) {
|
||||
this.opacity = opacity;
|
||||
this.setOpacity(this.opacity);
|
||||
if (this.selected) this.content.style.cursor = "";
|
||||
else this.content.style.cursor = "pointer";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected setOpacity(opacity: string) {
|
||||
this.element.setAttribute("opacity", opacity);
|
||||
protected setOpacity(opacity: number) {
|
||||
this.element.setAttribute("opacity", `${opacity}`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export class PortView extends StaticModuleView implements LayoutElement {
|
||||
export class PortView extends ModuleView implements LayoutElement {
|
||||
|
||||
constructor(port: NodeType, private label: string) {
|
||||
super(PORT_SVG, "port", NodeType.Port, port);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
export class TouchSensorView extends StaticModuleView implements LayoutElement {
|
||||
export class TouchSensorView extends ModuleView implements LayoutElement {
|
||||
|
||||
private static RECT_ID = ["touch_gradient4", "touch_gradient3", "touch_gradient2", "touch_gradient1"];
|
||||
private static TOUCH_GRADIENT_UNPRESSED = ["linear-gradient-2", "linear-gradient-3", "linear-gradient-4", "linear-gradient-5"];
|
||||
@ -17,7 +17,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
public getPaddingRatio() {
|
||||
return 1 / 10;
|
||||
return 1 / 4;
|
||||
}
|
||||
|
||||
public hasClick() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/// <reference path="./staticView.ts" />
|
||||
/// <reference path="./moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
export class UltrasonicSensorView extends StaticModuleView implements LayoutElement {
|
||||
export class UltrasonicSensorView extends ModuleView implements LayoutElement {
|
||||
|
||||
constructor(port: number) {
|
||||
super(ULTRASONIC_SVG, "ultrasonic", NodeType.UltrasonicSensor, port);
|
||||
|
@ -3,14 +3,19 @@ namespace pxsim.visuals {
|
||||
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(width: number): SVGElement;
|
||||
protected abstract buildDom(): SVGElement;
|
||||
public abstract getInnerWidth(): number;
|
||||
public abstract getInnerHeight(): number;
|
||||
|
||||
@ -61,10 +66,6 @@ namespace pxsim.visuals {
|
||||
}
|
||||
}
|
||||
|
||||
public shouldUpdateState() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public updateState() {
|
||||
}
|
||||
|
||||
@ -87,9 +88,26 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
private onClickHandler: (ev: any) => void;
|
||||
public registerClick(handler: (ev: any) => void) {
|
||||
public registerClick(handler: (ev: any) => void, zoom?: boolean) {
|
||||
this.onClickHandler = handler;
|
||||
this.getView().addEventListener(pointerEvents.up, this.onClickHandler);
|
||||
if (zoom) {
|
||||
this.getView().addEventListener(pointerEvents.up, (ev: any) => {
|
||||
if (!this.getSelected()) {
|
||||
this.onClickHandler(ev);
|
||||
this.setHover(false);
|
||||
}
|
||||
});
|
||||
this.getView().addEventListener(pointerEvents.move, () => {
|
||||
if (!this.getSelected()) {
|
||||
this.setHover(true);
|
||||
}
|
||||
});
|
||||
this.getView().addEventListener(pointerEvents.leave, () => {
|
||||
this.setHover(false);
|
||||
});
|
||||
} else {
|
||||
this.getView().addEventListener(pointerEvents.up, this.onClickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
@ -102,7 +120,7 @@ namespace pxsim.visuals {
|
||||
this.element = svg.elt("g") as SVGGElement;
|
||||
View.track(this);
|
||||
|
||||
const content = this.buildDom(this.width);
|
||||
const content = this.buildDom();
|
||||
if (content) {
|
||||
this.element.appendChild(content);
|
||||
}
|
||||
@ -112,16 +130,30 @@ namespace pxsim.visuals {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
public resize(width: number) {
|
||||
public resize(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
private updateTransform() {
|
||||
if (this.rendered) {
|
||||
let transform = `translate(${this.left} ${this.top})`;
|
||||
let left = this.left;
|
||||
let top = this.top;
|
||||
let scaleFactor = this.scaleFactor;
|
||||
if (this.hover) {
|
||||
const hoverScaleFactor = scaleFactor + 0.05;
|
||||
// Scale around center of module
|
||||
const centerX = this.getWidth() / 2;
|
||||
const centerY = this.getHeight() / 2;
|
||||
left = left - centerX * (hoverScaleFactor - 1);
|
||||
top = top - centerY * (hoverScaleFactor - 1);
|
||||
scaleFactor = hoverScaleFactor;
|
||||
}
|
||||
|
||||
if (this.scaleFactor !== 1) {
|
||||
transform += ` scale(${this.scaleFactor})`;
|
||||
let transform = `translate(${left} ${top})`;
|
||||
|
||||
if (scaleFactor !== 1) {
|
||||
transform += ` scale(${scaleFactor})`;
|
||||
}
|
||||
|
||||
this.element.setAttribute("transform", transform);
|
||||
@ -153,6 +185,40 @@ namespace pxsim.visuals {
|
||||
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 {
|
||||
@ -179,23 +245,8 @@ namespace pxsim.visuals {
|
||||
public setSelected(selected: boolean) { }
|
||||
|
||||
protected getView() {
|
||||
if (!this.rendered) {
|
||||
this.subscribe();
|
||||
}
|
||||
return super.getView();
|
||||
}
|
||||
|
||||
protected onBoardStateChanged() {
|
||||
// To be implemented by sub class
|
||||
}
|
||||
|
||||
protected subscribe() {
|
||||
board().updateSubscribers.push(() => {
|
||||
if (this.state.didChange()) {
|
||||
this.onBoardStateChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewContainer extends View {
|
||||
@ -241,7 +292,7 @@ namespace pxsim.visuals {
|
||||
});
|
||||
}
|
||||
|
||||
protected buildDom(width: number): SVGElement {
|
||||
protected buildDom(): SVGElement {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
/// <reference path="./nodes/staticView.ts" />
|
||||
/// <reference path="./nodes/moduleView.ts" />
|
||||
|
||||
namespace pxsim.visuals {
|
||||
|
||||
export class WireView extends View implements LayoutElement {
|
||||
private wire: SVGSVGElement;
|
||||
private path: SVGPathElement;
|
||||
private selected: boolean;
|
||||
private hasDimensions: boolean;
|
||||
|
||||
protected startX: number;
|
||||
@ -30,7 +29,7 @@ namespace pxsim.visuals {
|
||||
this.updatePath();
|
||||
}
|
||||
|
||||
buildDom(width: number): SVGElement {
|
||||
buildDom(): SVGElement {
|
||||
this.wire = svg.elt("svg", { height: "100%", width: "100%" }) as SVGSVGElement;
|
||||
this.path = pxsim.svg.child(this.wire, "path", {
|
||||
'd': '',
|
||||
@ -45,8 +44,8 @@ namespace pxsim.visuals {
|
||||
updatePath() {
|
||||
if (!this.hasDimensions) return;
|
||||
const height = this.endY - this.startY;
|
||||
const quarterHeight = height / 4;
|
||||
const middleHeight = this.port == 1 || this.port == 2 ? quarterHeight : quarterHeight * 2;
|
||||
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}`;
|
||||
@ -79,7 +78,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
public setSelected(selected: boolean) {
|
||||
this.selected = selected;
|
||||
super.setSelected(selected);
|
||||
this.updateOpacity();
|
||||
}
|
||||
|
||||
|
23
svgicons/categories/addpackage.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:none;stroke:#717171;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_expansions" transform="translate(-9596 11554)">
|
||||
<circle id="Ellipse_34" class="st0" cx="9607.5" cy="-11542" r="9"/>
|
||||
<g id="Rectangle_253_1_" transform="translate(527 574)">
|
||||
<rect x="9079.5" y="-12122" class="st0" width="2" height="12"/>
|
||||
<rect x="9080" y="-12121.5" class="st1" width="1" height="11"/>
|
||||
</g>
|
||||
<g id="Rectangle_254_1_" transform="translate(522 581) rotate(-90)">
|
||||
|
||||
<rect x="12117" y="9084.5" transform="matrix(-2.535182e-06 -1 1 -2.535182e-06 3037.5305 21208.5234)" class="st0" width="12" height="2"/>
|
||||
|
||||
<rect x="12117.5" y="9085" transform="matrix(-2.535182e-06 -1 1 -2.535182e-06 3037.5305 21208.5234)" class="st1" width="11" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
23
svgicons/categories/advancedcollapsed.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_advanced_closed" transform="translate(-9594 10075)">
|
||||
<g id="Line_9_1_">
|
||||
|
||||
<rect x="9600.7" y="-10067" transform="matrix(0.7071 -0.7071 0.7071 0.7071 9926.6836 3842.5476)" class="st0" width="2" height="11.3"/>
|
||||
</g>
|
||||
<g id="Line_10_1_">
|
||||
|
||||
<rect x="9603" y="-10062.3" transform="matrix(0.7071 -0.7071 0.7071 0.7071 9928.7334 3847.4973)" class="st0" width="11.3" height="2"/>
|
||||
</g>
|
||||
<g id="menu_icn_advanced_closed-2">
|
||||
|
||||
<rect x="9604.7" y="-10057.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 9924.7324 3846.2527)" class="st0" width="1" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1017 B |
23
svgicons/categories/advancedexpanded.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_advanced_open" transform="translate(-8544 12422.914) rotate(180)">
|
||||
<g id="Line_9">
|
||||
|
||||
<rect x="-8559.7" y="12406.3" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -11283.3965 -2416.5291)" class="st0" width="2" height="11.3"/>
|
||||
</g>
|
||||
<g id="Line_10">
|
||||
|
||||
<rect x="-8557.4" y="12411" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -11281.3467 -2411.5793)" class="st0" width="11.3" height="2"/>
|
||||
</g>
|
||||
<g id="Rectangle_251">
|
||||
|
||||
<rect x="-8555.7" y="12415.7" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -11285.3848 -2412.8174)" class="st0" width="1" height="1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1023 B |
10
svgicons/categories/brick.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<path id="menu_icn_brick" class="st0" d="M19.7,22.5H3.3v-22h16.5V22.5z M6,4.2v8.2h11V4.2H6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 520 B |
17
svgicons/categories/controls.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_controls" transform="translate(-9597 11323)">
|
||||
<rect id="Rectangle_253_2_" x="9600.5" y="-11319" class="st0" width="2" height="16"/>
|
||||
<rect id="Rectangle_254_2_" x="9607.5" y="-11319" class="st0" width="2" height="16"/>
|
||||
<rect id="Rectangle_255" x="9614.5" y="-11319" class="st0" width="2" height="16"/>
|
||||
<rect id="Rectangle_256" x="9598.5" y="-11309" class="st0" width="6" height="5"/>
|
||||
<rect id="Rectangle_257" x="9605.5" y="-11318" class="st0" width="6" height="5"/>
|
||||
<rect id="Rectangle_258" x="9612.5" y="-11313" class="st0" width="6" height="5"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 999 B |
31
svgicons/categories/functions.svg
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:0.3;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_FX" transform="translate(-9079.886 11883.081)">
|
||||
<path id="Path_166" class="st0" d="M9087.2-11874.5h-1.7c0-0.5,0-0.6,0.5-0.6c0.8,0,1.4-0.4,1.7-1.1c0.4-0.8,0.9-1.6,1.4-2.4
|
||||
c0.5-0.7,1.3-1.2,2.2-1.3c0.6-0.1,1.2,0.1,1.5,0.6c0.2,0.3,0.2,0.6,0.1,0.9c-0.2,0.3-0.5,0.4-0.8,0.3c-0.3,0-0.5-0.3-0.5-0.6
|
||||
c0-0.1,0-0.1,0-0.2c0-0.2,0.1-0.4,0.1-0.6c-0.2,0-0.5-0.1-0.7,0c-0.4,0.2-0.7,0.4-0.9,0.8c-0.4,0.6-0.7,1.3-0.9,2
|
||||
c-0.2,0.5-0.4,1.1-0.5,1.6h1.3c-0.1,0.2-0.1,0.4-0.2,0.6h-1.3c-0.3,1-0.5,1.9-0.8,2.8c-0.4,1.9-1.1,3.7-1.9,5.5
|
||||
c-0.4,0.8-0.9,1.5-1.5,2.1c-0.5,0.5-1.1,0.8-1.8,0.8c-0.4,0.1-0.8-0.1-1.2-0.3c-0.2-0.2-0.3-0.6-0.2-0.9c0.2-0.3,0.5-0.5,0.9-0.4
|
||||
c0.3,0,0.4,0.3,0.4,0.6c0,0.1-0.1,0.2-0.1,0.2c-0.1,0.1-0.1,0.2-0.1,0.3c0.1,0,0.2,0.1,0.4,0.1c0.5,0,1-0.3,1.3-0.8
|
||||
c0.3-0.5,0.6-1,0.7-1.5c0.8-2.8,1.6-5.6,2.4-8.4C9087.2-11874.4,9087.2-11874.4,9087.2-11874.5z"/>
|
||||
<path id="Path_167" class="st0" d="M9098.2-11863.3c0.1-0.1,0.1-0.2,0.2-0.3c1-0.6,1.7-1.7,1.9-2.8c0.4-1.9,0.4-3.8-0.1-5.6
|
||||
c-0.3-1-0.9-1.8-1.7-2.4c-0.1-0.1-0.2-0.2-0.2-0.3c1.8,0.7,3.5,2.9,3.5,5.7C9101.8-11866.6,9100.4-11864.3,9098.2-11863.3z"/>
|
||||
<path id="Path_168" class="st0" d="M9093.5-11874.7c0,0.1-0.1,0.2-0.1,0.2c-1,0.6-1.7,1.7-1.9,2.8c-0.4,1.9-0.4,3.8,0.1,5.7
|
||||
c0.3,1,0.9,1.8,1.7,2.4c0.1,0.1,0.2,0.1,0.2,0.3c-2.1-1-3.5-3.2-3.4-5.6C9090-11872,9091.9-11874.2,9093.5-11874.7z"/>
|
||||
<path id="Path_169" class="st0" d="M9097.9-11867.4c0,0,0.1,0,0.2,0.1c0,0,0,0,0,0.1c-0.3,0.5-0.6,0.9-1.1,1.2
|
||||
c-0.2,0.2-0.6,0.3-0.9,0c-0.1-0.1-0.2-0.2-0.2-0.4c-0.2-0.6-0.3-1.1-0.4-1.7c0-0.1,0-0.1-0.1-0.2c-0.1,0.2-0.2,0.4-0.4,0.6
|
||||
c-0.3,0.4-0.6,0.9-1,1.3c-0.2,0.2-0.4,0.4-0.7,0.5c-0.3,0.1-0.6,0-0.8-0.3c0,0,0-0.1,0-0.1c0-0.2,0-0.4,0.2-0.5
|
||||
c0.1-0.1,0.3-0.1,0.5,0c0,0,0.1,0.1,0.1,0.1c0.3,0.2,0.4,0.3,0.6-0.1c0.4-0.6,0.8-1.3,1.3-1.9c0-0.1,0-0.2,0-0.3
|
||||
c-0.1-0.4-0.2-0.8-0.3-1.2c-0.2-0.7-0.5-0.9-1.2-0.9h-0.1c-0.1-0.1,0-0.2,0.1-0.2c0.4-0.1,0.9-0.2,1.3-0.2c0.1,0,0.2,0,0.3,0.1
|
||||
c0.3,0.4,0.5,0.8,0.6,1.3c0,0.1,0.1,0.3,0.1,0.4c0.1-0.2,0.3-0.5,0.5-0.7c0.3-0.3,0.6-0.7,0.9-0.9c0.2-0.1,0.4-0.2,0.6-0.2
|
||||
c0.3,0,0.5,0.2,0.5,0.5c0,0,0,0.1,0,0.1c-0.1,0.3-0.3,0.4-0.6,0.3c0,0,0,0,0,0c-0.1,0-0.1,0-0.2,0c-0.3-0.2-0.7-0.1-0.9,0.2
|
||||
c0,0,0,0,0,0c-0.2,0.3-0.5,0.7-0.7,1c0,0.1,0,0.1,0,0.2c0.2,0.7,0.3,1.4,0.5,2.2c0,0.1,0,0.1,0.1,0.2c0.1,0.3,0.3,0.4,0.5,0.1
|
||||
C9097.5-11866.9,9097.6-11867.2,9097.9-11867.4z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
29
svgicons/categories/list.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{enable-background:new ;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_matrices" transform="translate(-9597 11374)">
|
||||
<rect id="Rectangle_253" x="9603.4" y="-11368.5" class="st0" width="14.8" height="1.8"/>
|
||||
<rect id="Rectangle_253-2" x="9603.4" y="-11363.9" class="st0" width="14.8" height="1.8"/>
|
||||
<rect id="Rectangle_254" x="9603.4" y="-11359.3" class="st0" width="14.8" height="1.8"/>
|
||||
<g class="st1">
|
||||
<path class="st0" d="M9600.2-11366.7h-1v-3.6h-0.8v-0.6c0.5,0,0.9-0.3,1-0.9h0.8V-11366.7z"/>
|
||||
</g>
|
||||
<g class="st1">
|
||||
<path class="st0" d="M9601.4-11361.1v0.9h-3.6v-0.4c0-1.2,0.8-1.7,1.5-2.1c1-0.5,1-0.7,1-1c0-0.3-0.2-0.6-0.7-0.6
|
||||
c-0.5,0-0.8,0.4-0.8,0.9h-1c0-0.9,0.6-1.8,1.8-1.8c1.2,0,1.7,0.8,1.7,1.5c0,1-0.7,1.3-1.5,1.8c-0.5,0.3-0.9,0.6-0.9,0.9H9601.4z"
|
||||
/>
|
||||
</g>
|
||||
<g class="st1">
|
||||
<path class="st0" d="M9599.6-11353.6c-1.2,0-1.8-0.8-1.9-1.7h1c0,0.5,0.3,0.8,0.9,0.8c0.6,0,0.9-0.3,0.9-0.7
|
||||
c0-0.4-0.3-0.7-0.8-0.7h-0.3v-0.8h0.2c0.6,0,0.8-0.3,0.8-0.6c0-0.4-0.3-0.6-0.7-0.6c-0.5,0-0.8,0.4-0.8,0.8h-1
|
||||
c0-0.9,0.6-1.7,1.7-1.7c1.1,0,1.7,0.7,1.7,1.4c0,0.4-0.2,0.8-0.7,1c0.6,0.2,0.8,0.7,0.8,1.2
|
||||
C9601.5-11354.5,9600.9-11353.6,9599.6-11353.6z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
17
svgicons/categories/logic.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_logic" transform="translate(1634.03 -5.889)">
|
||||
<path id="Path_161" class="st0" d="M-1632.4,20.8h2.6c1.5-0.1,2.8-1.1,3.3-2.6c1.1-2.6,3-7.3,6.1-7.3s3.5,0,3.5,0V8.3l4.1,4.3
|
||||
l-4.1,3.9v-2.8h-2.8c-1.5,0.4-2.7,1.4-3.2,2.8c-1.2,2.6-2.4,7-6.4,7.1c-1.8,0-3,0-3,0V20.8z"/>
|
||||
<path id="Path_162" class="st0" d="M-1632.4,11v2.8h3.1c0,0,1.8,0,2.8,2.4c0.4-1,0.9-1.9,1.5-2.8c0,0-2-2.3-4.2-2.3
|
||||
S-1632.4,11-1632.4,11z"/>
|
||||
<path id="Path_163" class="st0" d="M-1622.8,18.3c0,0,1.3,2.6,2.8,2.6h3.2v-2.6l4.2,4.1l-4.2,4.1v-2.8h-3.6c0,0-2,0-3.9-2.5
|
||||
C-1623.7,20.3-1623.2,19.3-1622.8,18.3z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1018 B |
16
svgicons/categories/loops.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:0.7;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_loops" transform="translate(-9080.903 10401.478)">
|
||||
<path id="Path_141" class="st0" d="M9085.3-10385.5c-0.9-1.3-1.9-2.5-2.8-3.8h1.9c0-2.2,0.9-4.4,2.5-5.9c1.1-1.1,2.4-1.8,3.9-2.1
|
||||
c2.7-0.6,5.6,0.2,7.6,2.2c-0.5,0.5-0.9,0.9-1.3,1.3c-2.4-2-4.9-2.4-7.7-0.9c-2,1.1-3.1,3.2-3.1,5.4h1.9
|
||||
C9087.2-10388,9086.2-10386.7,9085.3-10385.5z"/>
|
||||
<path id="Path_142" class="st0" d="M9098.9-10389.3h-1.9c1-1.3,1.9-2.5,2.8-3.8c1,1.3,1.9,2.5,2.8,3.8h-1.9c0,2.3-1,4.5-2.6,6
|
||||
c-1.1,1-2.4,1.7-3.8,2c-2.7,0.6-5.6-0.2-7.6-2.2l1.3-1.3c2.3,2,4.9,2.5,7.7,0.9C9097.7-10384.9,9098.9-10387,9098.9-10389.3z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
19
svgicons/categories/math.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;stroke:#FFFFFF;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_math" transform="translate(-9078.442 10583.172)">
|
||||
<path id="Path_153" class="st0" d="M9087.4-10564.7c-0.2,0.2-0.5,0.5-0.7,0.7l-2.2-2.2c-0.8,0.8-1.5,1.5-2.2,2.2
|
||||
c-0.2-0.2-0.5-0.5-0.7-0.7l2.2-2.2c-0.8-0.8-1.5-1.5-2.2-2.2c0.2-0.2,0.5-0.5,0.7-0.7l2.2,2.2l2.2-2.2c0.2,0.2,0.5,0.5,0.7,0.7
|
||||
l-2.2,2.2C9085.9-10566.1,9086.7-10565.4,9087.4-10564.7z"/>
|
||||
<path id="Path_154" class="st0" d="M9080.9-10575.6v-0.9h3.1v-3.1c0.3,0,0.6,0,0.9,0v3.1h3.1v0.9h-3.1v3.1h-1v-3.1H9080.9z"/>
|
||||
<path id="Path_155" class="st1" d="M9098.5-10567.3h-7.8v-1.7h7.8V-10567.3z"/>
|
||||
<path id="Path_156" class="st1" d="M9098.5-10565.6v1.7h-7.8v-1.7H9098.5z"/>
|
||||
<path id="Path_157" class="st1" d="M9098.5-10577v1.7h-7.8v-1.7H9098.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
17
svgicons/categories/motors.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_motor" transform="translate(-9078.587 10279.652)">
|
||||
<path id="Path_159" class="st0" d="M9095.2-10271.5c-0.8,0.8-1.7,1.7-2.5,2.5h6.4v-6.4l-2,2c-1.5-1.8-3.6-3-6-3.3
|
||||
c-2-0.3-4.1,0.2-5.8,1.3c-4.3,2.6-5.6,8.2-3,12.5s8.2,5.6,12.5,3c1.5-0.9,2.6-2.2,3.4-3.7c-0.8-0.3-1.6-0.7-2.4-1
|
||||
c-0.8,1.6-2.3,2.8-4,3.3c-1.3,0.4-2.7,0.3-4-0.2c-3.3-1.3-5-5-3.8-8.3c0.7-2,2.4-3.5,4.4-4
|
||||
C9091-10274.6,9093.7-10273.6,9095.2-10271.5z"/>
|
||||
<path id="Path_160" class="st0" d="M9088.2-10267.6c0,1.1,0.9,1.9,1.9,1.9s1.9-0.9,1.9-1.9c0-1.1-0.9-1.9-1.9-1.9
|
||||
C9089-10269.6,9088.2-10268.7,9088.2-10267.6C9088.2-10267.6,9088.2-10267.6,9088.2-10267.6z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
12
svgicons/categories/music.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<path id="menu_icn_music" class="st0" d="M13.9,12.8c1.7,0.3,3.3,1.2,4.3,2.6c0.5,0,1,0,1,0s1.5-4.4-1.8-7.5s-9.7-3.1-12.1,0
|
||||
c-1.7,2.1-2.3,5-1.4,7.5c0.3,0,0.5,0,0.8,0c0,0,1.7-2.5,4.4-2.6C9,17.2,9,21,9,21c-1.9-0.4-3.5-1.6-4.4-3.4C2.6,17.2,1,15.4,1,13.2
|
||||
C1,6.7,5.9,3,11.5,3s10.8,4.3,10.4,10.2c-0.2,4.1-3.6,4.4-3.6,4.4c-0.8,1.8-2.4,3.1-4.3,3.4C13.9,16.5,13.9,12.8,13.9,12.8z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 799 B |
18
svgicons/categories/sensors.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_sensor" transform="translate(-9778 10128)">
|
||||
<g id="Path_170" transform="translate(697 95)">
|
||||
<path class="st0" d="M9092.5-10221.5c5.5,0,10,4.5,10,10s-4.5,10-10,10s-10-4.5-10-10S9087-10221.5,9092.5-10221.5z"/>
|
||||
<path class="st1" d="M9092.5-10204.5c3.9,0,7-3.1,7-7s-3.1-7-7-7s-7,3.1-7,7S9088.6-10204.5,9092.5-10204.5 M9092.5-10201.5
|
||||
c-5.5,0-10-4.5-10-10s4.5-10,10-10s10,4.5,10,10S9098-10201.5,9092.5-10201.5z"/>
|
||||
</g>
|
||||
<path id="Path_171" class="st1" d="M9789.5-10120.5c2.2,0,4,1.8,4,4s-1.8,4-4,4s-4-1.8-4-4S9787.3-10120.5,9789.5-10120.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1010 B |
13
svgicons/categories/text.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g class="st0">
|
||||
<path class="st1" d="M13.3,5.4v16H9.5v-16H4.4V1.9h14.2v3.5H13.3z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 549 B |
29
svgicons/categories/variables.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{clip-path:url(#SVGID_2_);}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g>
|
||||
<defs>
|
||||
<rect id="SVGID_1_" x="3" y="5" width="16" height="13"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_2_">
|
||||
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
<g id="menu_icn_variables" transform="translate(-9077.432 10524.199)" class="st0">
|
||||
<g transform="translate(-518.568 -458.199)">
|
||||
<path id="Path_165" class="st1" d="M9615-10061v2h-16v-2H9615z"/>
|
||||
</g>
|
||||
<g transform="translate(-518.568 -453.199)">
|
||||
<path id="Path_165-2" class="st1" d="M9615-10061v2h-16v-2H9615z"/>
|
||||
</g>
|
||||
<g transform="translate(-518.568 -448.199)">
|
||||
<path id="Path_165-3" class="st1" d="M9615-10061v2h-16v-2H9615z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
15
svgicons/icons/cancel.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="icn_cancel_x" transform="translate(14923.365 9661)">
|
||||
|
||||
<rect id="Rectangle_277" x="-14920.9" y="-9651.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2455.6738 -13370.6289)" class="st0" width="18" height="3"/>
|
||||
|
||||
<rect id="Rectangle_278" x="-14913.4" y="-9658.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2455.6738 -13370.6299)" class="st0" width="3" height="18"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 812 B |
15
svgicons/icons/check.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="icn_cancel_ok" transform="translate(15066.061 9654)">
|
||||
|
||||
<rect id="Rectangle_275" x="-15061" y="-9644.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2409.7021 -13467.6172)" class="st0" width="18" height="3"/>
|
||||
|
||||
<rect id="Rectangle_276" x="-15061.3" y="-9643.9" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2405.1626 -13472.2129)" class="st0" width="3" height="9"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 810 B |
19
svgicons/icons/download.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_download" transform="translate(-9563 11322)">
|
||||
<rect id="Rectangle_167" x="9566" y="-11308" class="st0" width="18" height="8"/>
|
||||
<g id="arrow" transform="translate(6 -5)">
|
||||
<rect id="Rectangle_168" x="9568" y="-11315" class="st0" width="2" height="10"/>
|
||||
|
||||
<rect id="Rectangle_169" x="9567.6" y="-11307.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 10798.123 3455.804)" class="st0" width="6" height="2"/>
|
||||
|
||||
<rect id="Rectangle_170" x="9566.6" y="-11309.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 10797.2441 3453.6824)" class="st0" width="2" height="6"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
24
svgicons/icons/save.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:none;}
|
||||
.st2{fill:none;stroke:#FFFFFF;}
|
||||
</style>
|
||||
<title>color</title>
|
||||
<g id="menu_icn_savedisc" transform="translate(-8886 12201.056)">
|
||||
<path id="Path_116" class="st0" d="M8888.4-12189.1v-8.4c0-0.6,0.1-0.7,0.7-0.7h4c0.6,0,0.7,0.1,0.7,0.7v4.1c0,0.5,0.1,0.6,0.6,0.6
|
||||
h6c0.5,0,0.6-0.1,0.6-0.6v-4.2c0-0.5,0.1-0.6,0.6-0.6c1.2,0,1.2,0,2,0.8c0.9,0.9,1.7,1.7,2.6,2.6c0.2,0.2,0.3,0.4,0.3,0.7v13.5
|
||||
c0,0.5-0.1,0.7-0.6,0.7h-16.9c-0.5,0-0.6-0.1-0.6-0.7C8888.4-12183.5,8888.4-12186.3,8888.4-12189.1z M8897.5-12181.9h6.6
|
||||
c0.6,0,0.7-0.1,0.7-0.7v-7.7c0-0.5-0.1-0.7-0.7-0.7h-13.2c-0.5,0-0.7,0.1-0.7,0.7v7.7c0,0.6,0.1,0.7,0.7,0.7H8897.5z"/>
|
||||
<path id="Path_117" class="st0" d="M8899.8-12196.4c0,0.4,0,0.9,0,1.3c0,0.4-0.2,0.5-0.5,0.5c-0.3,0-0.6,0-0.8,0
|
||||
c-0.2,0-0.5-0.1-0.5-0.4c0,0,0-0.1,0-0.1v-2.6c0-0.2,0.1-0.4,0.4-0.5c0,0,0,0,0.1,0c0.3,0,0.6,0,0.9,0c0.3,0,0.5,0.2,0.5,0.4
|
||||
c0,0,0,0,0,0.1C8899.8-12197.3,8899.8-12196.8,8899.8-12196.4z"/>
|
||||
<g id="Rectangle_166" transform="translate(1.445 6.558)">
|
||||
<rect x="8888.4" y="-12198.2" class="st1" width="15.4" height="10.3"/>
|
||||
<rect x="8888.9" y="-12197.7" class="st2" width="14.4" height="9.3"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
35
tests/colors.ts
Normal file
@ -0,0 +1,35 @@
|
||||
tests.test("Detect color red", function () {
|
||||
brick.print("Point sensor to red", 0, 50)
|
||||
brick.print("and click enter", 0, 60)
|
||||
brick.buttonEnter.pauseUntil(ButtonEvent.Click)
|
||||
brick.clearScreen()
|
||||
let actualColor = sensors.color1.color()
|
||||
tests.assertClose("Color", actualColor, ColorSensorColor.Red, 0)
|
||||
})
|
||||
|
||||
tests.test("Bright ambient light", function () {
|
||||
brick.print("Point sensor to ceiling", 0, 50)
|
||||
brick.print("light and click enter", 0, 60)
|
||||
brick.buttonEnter.pauseUntil(ButtonEvent.Click)
|
||||
brick.clearScreen()
|
||||
let actualLight: number
|
||||
for (let i = 0; i < 4; i++) {
|
||||
actualLight = sensors.color1.ambientLight()
|
||||
loops.pause(500)
|
||||
}
|
||||
tests.assertClose("Light", actualLight, 20, 15)
|
||||
})
|
||||
|
||||
tests.test("Bright reflected light", function () {
|
||||
brick.print("Point sensor to white", 0, 50)
|
||||
brick.print("desk surface", 0, 60)
|
||||
brick.print("and click enter", 0, 70)
|
||||
brick.buttonEnter.pauseUntil(ButtonEvent.Click)
|
||||
brick.clearScreen()
|
||||
let actualLight: number
|
||||
for (let i = 0; i < 4; i++) {
|
||||
actualLight = sensors.color1.reflectedLight()
|
||||
loops.pause(500)
|
||||
}
|
||||
tests.assertClose("Light", actualLight, 17, 14)
|
||||
})
|
42
tests/motors.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// add tests package
|
||||
tests.test("lgB set speed 10", () => {
|
||||
motors.largeB.setSpeed(10);
|
||||
loops.pause(500)
|
||||
tests.assertClose("speedB", 10, motors.largeB.speed(), 2)
|
||||
});
|
||||
tests.test("lgB set speed 25 (reversed)", () => {
|
||||
motors.largeB.setReversed(true)
|
||||
motors.largeB.setSpeed(25)
|
||||
loops.pause(500)
|
||||
tests.assertClose("speedB", -25, motors.largeB.speed(), 2)
|
||||
});
|
||||
tests.test("lgBC set speed 5", () => {
|
||||
motors.largeBC.setSpeed(5)
|
||||
loops.pause(500)
|
||||
tests.assertClose("speedB", 5, motors.largeB.speed(), 1);
|
||||
tests.assertClose("speedC", 5, motors.largeC.speed(), 1);
|
||||
});
|
||||
tests.test("lgBC steer 50% 2x", () => {
|
||||
motors.largeBC.setBrake(true)
|
||||
motors.largeBC.steer(50, 50, 1, MoveUnit.Rotations)
|
||||
loops.pause(2000)
|
||||
tests.assertClose("largeB", 360, motors.largeB.angle(), 5)
|
||||
motors.largeBC.setBrake(false)
|
||||
})
|
||||
tests.test("lgBC steer 360deg", () => {
|
||||
motors.largeBC.setBrake(true)
|
||||
motors.largeBC.steer(50, 50, 360, MoveUnit.Degrees)
|
||||
loops.pause(2000)
|
||||
tests.assertClose("largeB", 360, motors.largeB.angle(), 5)
|
||||
});
|
||||
tests.test("lgBC steer 50% 1s", () => {
|
||||
motors.largeBC.setBrake(true)
|
||||
motors.largeBC.steer(10, 50, 1000, MoveUnit.MilliSeconds)
|
||||
loops.pause(2000)
|
||||
})
|
||||
tests.test("lgBC tank 50% 180deg", () => {
|
||||
motors.largeBC.setBrake(true)
|
||||
motors.largeBC.tank(50, 50, 180, MoveUnit.Degrees)
|
||||
loops.pause(1000)
|
||||
tests.assertClose("largeB", 180, motors.largeB.angle(), 5)
|
||||
});
|
7
tests/touch.ts
Normal file
@ -0,0 +1,7 @@
|
||||
tests.test("Touch sensor pressed", function () {
|
||||
brick.print("Press touch sensor", 0, 50)
|
||||
brick.print("and click enter", 0, 60)
|
||||
brick.buttonEnter.pauseUntil(ButtonEvent.Click)
|
||||
brick.clearScreen()
|
||||
tests.assert("Pressed", sensors.touchSensor1.isPressed())
|
||||
})
|
9
tests/ultrasonic.ts
Normal file
@ -0,0 +1,9 @@
|
||||
tests.test("Ultrasonic sensor", function () {
|
||||
brick.print("Place object ", 0, 50)
|
||||
brick.print("one finger's length", 0, 60)
|
||||
brick.print("in front of sensor", 0, 70)
|
||||
brick.print("and click enter", 0, 80)
|
||||
brick.buttonEnter.pauseUntil(ButtonEvent.Click)
|
||||
brick.clearScreen()
|
||||
tests.assertClose("Distance", sensors.ultrasonic1.distance(), 7, 6)
|
||||
})
|
@ -28,9 +28,14 @@
|
||||
|
||||
div.blocklyTreeRow {
|
||||
border-radius: 0px;
|
||||
-webkit-box-shadow: inset 0px 0px 0px 3px rgba(0,0,0,0.2);
|
||||
margin-bottom: 7px;
|
||||
/*-webkit-box-shadow: inset 0px 0px 0px 3px rgba(0,0,0,0.2);
|
||||
-moz-box-shadow: inset 0px 0px 0px 3px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0px 0px 0px 3px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0px 0px 0px 3px rgba(0,0,0,0.2);*/
|
||||
}
|
||||
|
||||
div.blocklyTreeSeparator {
|
||||
border-bottom: solid #CEA403 2px;
|
||||
}
|
||||
|
||||
/* Remove shadow around blockly blocks */
|
||||
@ -51,11 +56,18 @@ span.blocklyTreeLabel {
|
||||
margin: 0.5rem;
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
.blocklySearchInputField {
|
||||
border-radius: 1rem !important;
|
||||
}
|
||||
|
||||
|
||||
/* Square shaped corners */
|
||||
.blocklyDropDownDiv {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: @largestMobileScreen) {
|
||||
#blocklyTrashIcon {
|
||||
|
@ -10,7 +10,7 @@
|
||||
@emSize : 14px;
|
||||
@fontSize : 13px;
|
||||
|
||||
@primaryColor: @red;
|
||||
@primaryColor: @blue;
|
||||
@secondaryColor: @yellow;
|
||||
|
||||
@teal:#08415C;
|
||||
@ -88,6 +88,8 @@
|
||||
|
||||
@pageBackground: #fff;
|
||||
|
||||
@positiveColor: @blue;
|
||||
@defaultBorderRadius: 0px;
|
||||
|
||||
@inputPlaceholderColor: lighten(@inputColor, 80);
|
||||
|
||||
|