Initial sim implementation

This commit is contained in:
Sam El-Husseini
2017-12-18 13:04:17 -08:00
parent 6836852122
commit 6320379d02
88 changed files with 3949 additions and 3552 deletions

View File

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

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

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

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

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

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

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

View File

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

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

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

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

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

View File

@ -12,7 +12,7 @@ namespace pxsim {
namespace pxsim.output {
export function setLights(pattern: number) {
const lightState = (board() as DalBoard).lightState;
const lightState = ev3board().getBrickNode().lightState;
lightState.lightPattern = pattern;
runtime.queueDisplayUpdate();
}

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

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

73
sim/state/motors.ts Normal file
View File

@ -0,0 +1,73 @@
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);
}
}
}

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

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

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

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

View File

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

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

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

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

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

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

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

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

@ -0,0 +1,31 @@
/// <reference path="./sensor.ts"/>
namespace pxsim {
export class UltrasonicSensorNode extends UartSensorNode {
id = NodeType.UltrasonicSensor;
private distance: number = 50;
constructor(port: number) {
super(port);
}
getDeviceType() {
return DAL.DEVICE_TYPE_ULTRASONIC;
}
setDistance(distance: number) {
if (this.distance != distance) {
this.distance = distance;
this.changed = true;
this.valueChanged = true;
runtime.queueDisplayUpdate();
}
}
getValue() {
return this.distance;
}
}
}

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

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

View File

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

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

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

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

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

After

Width:  |  Height:  |  Size: 12 KiB

View File

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

View File

@ -0,0 +1,74 @@
<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">
<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>
<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>
<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>
</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>
</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>
</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>
<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"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,76 @@
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">
<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>
<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>
<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>
</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>
</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>
</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>
<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"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>`;
}

View File

@ -0,0 +1,28 @@
<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">
<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>
</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>
<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>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,30 @@
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">
<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>
</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"/>
</g>
<circle id="Ellipse_1" data-name="Ellipse 1" cx="37.77" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_2" data-name="Ellipse 2" cx="6.37" cy="22.16" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_1-2" data-name="Ellipse 1-2" cx="22.15" cy="6.38" r="4.85" style="fill: #3c3c3c"/>
<circle id="Ellipse_2-2" data-name="Ellipse 2-2" cx="22.15" cy="37.78" r="4.85" style="fill: #3c3c3c"/>
</g>
<g id="Ellipse_4" data-name="Ellipse 4">
<circle id="ellipse7603" cx="22.3" cy="22.16" r="6.75" style="fill: #b72b1c"/>
<circle id="ellipse7605" cx="22.3" cy="22.16" r="6.63" style="fill: none;stroke: #8b1104;stroke-width: 0.25px"/>
</g>
<path id="Hole" d="M22.09,21s-.05,1-.77,1H19.86a2.37,2.37,0,0,0-.45,1.69c0,1.27.36,1.6.36,1.6h1.62a.64.64,0,0,1,.7.59.2.2,0,0,1,0,.11v1.67a4,4,0,0,0,1.77.29,6.88,6.88,0,0,0,1.68-.29V26a.73.73,0,0,1,.73-.7,9.89,9.89,0,0,0,1.44-.14s.4-.37.44-1.63-.36-1.64-.36-1.64H26.1a.65.65,0,0,1-.75-.51.5.5,0,0,1,0-.17,11.36,11.36,0,0,1,0-1.65,4.9,4.9,0,0,0-3.25,0C22.08,20.23,22.09,21,22.09,21Z" transform="translate(-1.5 -1.49)"/>
</g>
</svg>`;
}

View File

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

View File

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

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

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

View File

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

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

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

View File

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

After

Width:  |  Height:  |  Size: 765 B

View File

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

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 75 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

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

View File

@ -0,0 +1,51 @@
/// <reference path="./nodes/staticView.ts" />
namespace pxsim.visuals {
export const CONTROL_WIDTH = 87.5;
export const CONTROL_HEIGHT = 175;
export abstract class ControlView<T extends BaseNode> extends SimView<T> implements LayoutElement {
private background: SVGSVGElement;
abstract getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement): SVGElement;
constructor(protected parent: SVGSVGElement, protected globalDefs: SVGDefsElement, protected state: T, protected port: number) {
super(state);
}
getInnerWidth(): number {
return CONTROL_WIDTH;
}
getInnerHeight(): number {
return CONTROL_HEIGHT;
}
getPaddingRatio() {
return 0;
}
getWiringRatio() {
return 0.5;
}
public hasClick() {
return false;
}
buildDom(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;
}
onComponentVisible() {
}
getWeight() {
return 0;
}
}
}

View File

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

View File

@ -0,0 +1,41 @@
namespace pxsim.visuals {
export class ColorGridControl extends ControlView<ColorSensorNode> {
private group: SVGGElement;
getInnerView() {
this.group = svg.elt("g") as SVGGElement;
this.group.setAttribute("transform", `translate(17, ${35 + this.getHeight() / 4}) scale(5)`)
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#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]);
})
}
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` });
whiteCircleWrapper.addEventListener(pointerEvents.down, ev => {
this.setColor(6);
})
return this.group;
}
private setColor(color: number) {
const state = this.state;
state.setColor(color);
}
}
}

View File

@ -0,0 +1,34 @@
namespace pxsim.visuals {
export class ColorWheelControl extends ControlView<ColorSensorNode> {
private group: SVGGElement;
private static COLOR_DARK = 1;
private static COLOR_LIGHT = 99;
getInnerView() {
this.group = svg.elt("g") as SVGGElement;
this.group.setAttribute("transform", `translate(12, ${this.getHeight() / 2 - 15}) scale(2.5)`)
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);
})
return this.group;
}
private setColor(color: number) {
const state = this.state;
state.setColor(color);
}
}
}

View File

@ -0,0 +1,120 @@
namespace pxsim.visuals {
export class DistanceSliderControl extends ControlView<UltrasonicSensorNode> {
private group: SVGGElement;
private gradient: SVGLinearGradientElement;
private slider: SVGGElement;
private static SLIDER_HANDLE_HEIGHT = 31;
private isVisible = false;
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');
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 rect = pxsim.svg.child(sliderGroup, "rect", { 'x': this.getLeftPadding(), 'y': 2, 'width': this.getWidth() - this.getLeftPadding() * 2, 'height': this.getContentHeight(), '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' });
const dragSurface = svg.child(this.group, "rect", {
x: 0,
y: 0,
width: this.getInnerWidth(),
height: this.getInnerHeight(),
opacity: 0,
cursor: '-webkit-grab'
})
let pt = parent.createSVGPoint();
let captured = false;
touchEvents(dragSurface, ev => {
if (captured && (ev as MouseEvent).clientY != undefined) {
ev.preventDefault();
this.updateSliderValue(pt, parent, ev as MouseEvent);
}
}, ev => {
captured = true;
if ((ev as MouseEvent).clientY != undefined) {
this.updateSliderValue(pt, parent, ev as MouseEvent);
}
}, () => {
captured = false;
}, () => {
captured = false;
})
return this.group;
}
private getLeftPadding() {
return this.getInnerWidth() * 0.12;
}
private getTopPadding() {
return this.getInnerHeight() / 4;
}
private getContentHeight() {
return this.getInnerHeight() * 0.6;
}
onBoardStateChanged() {
if (!this.isVisible) {
return;
}
const node = this.state;
const percentage = node.getValue();
const y = this.getContentHeight() * percentage / 100;
this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
}
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 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 state = this.state;
state.setDistance((1 - t) * (100));
}
private getGradientDefinition(): LinearGradientDefinition {
return {
stops: [
{ offset: 0, color: '#626262' },
{ offset: 1, color: "#ddd" }
]
};
}
}
}

View File

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

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

@ -0,0 +1,298 @@
/// <reference path="./view.ts" />
/// <reference path="./nodes/staticView.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_INNER_PADDING_RATIO = 1 / 35;
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 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.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);
}
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
this.inputWires[port] = new WireView(port);
}
}
public layout(width: number, height: number) {
this.hasDimensions = true;
this.width = width;
this.height = height;
this.scrollGroup.setAttribute("width", width.toString());
this.scrollGroup.setAttribute("height", height.toString());
this.position();
}
public setBrick(brick: BrickView) {
this.brick = brick;
this.position();
}
public getBrick() {
return this.brick;
}
public setInput(port: number, child: LayoutElement) {
if (this.inputs[port]) {
// Remove current input
this.inputs[port].dispose();
}
this.inputs[port] = child;
this.position();
}
public setOutput(port: number, child: LayoutElement) {
if (this.outputs[port]) {
// Remove current input
this.outputs[port].dispose();
}
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) {
this.contentGroup = svg.elt("g") as SVGGElement;
this.scrollGroup = svg.child(this.contentGroup, "g") as SVGGElement;
return this.contentGroup;
}
public getInnerWidth() {
if (!this.hasDimensions) {
return 0;
}
return this.width;
}
public getInnerHeight() {
if (!this.hasDimensions) {
return 0;
}
return this.height;
}
public updateTheme(theme: IBoardTheme) {
this.inputs.forEach(n => {
n.updateTheme(theme);
})
this.brick.updateTheme(theme);
this.outputs.forEach(n => {
n.updateTheme(theme);
})
}
private position() {
if (!this.hasDimensions) {
return;
}
this.offsets = [];
const selectedNode = this.getSelected();
const contentWidth = this.width || DEFAULT_WIDTH;
const contentHeight = this.height || DEFAULT_HEIGHT;
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 moduleSpacing = contentWidth / 4;
const moduleWidth = moduleSpacing - (modulePadding * 2);
let currentX = modulePadding;
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);
})
currentX += moduleSpacing;
})
currentX = 0;
currentY = moduleHeight;
const wireBrickSpacing = brickWidth / 5;
const wiringYPadding = 10;
let wireStartX = 0;
let wireEndX = brickPadding + wireBrickSpacing;
let wireEndY = currentY + this.getWiringHeight() + wiringYPadding;
let wireStartY = currentY - wiringYPadding;
// Draw output lines
for (let port = 0; port < DAL.NUM_OUTPUTS; port++) {
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;
wireEndX += wireBrickSpacing;
}
currentX = brickPadding;
currentY += this.getWiringHeight();
// Render the brick in the middle
this.brick.resize(brickWidth);
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);
})
currentX += moduleSpacing;
})
wireStartX = moduleSpacing / 2;
wireEndX = brickPadding + wireBrickSpacing;
wireEndY = currentY - this.getWiringHeight() - wiringYPadding;
wireStartY = currentY + wiringYPadding;
// Draw input lines
for (let port = 0; port < DAL.NUM_INPUTS; port++) {
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;
wireEndX += wireBrickSpacing;
}
}
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;
}
public getWiringHeight() {
return (this.height || DEFAULT_HEIGHT) * WIRING_HEIGHT_RATIO;
}
public getModuleBounds() {
return {
width: this.width / 4,
height: this.getModuleHeight()
}
}
}
}

View File

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

View File

@ -0,0 +1,16 @@
/// <reference path="./staticView.ts" />
namespace pxsim.visuals {
export class ColorSensorView extends StaticModuleView implements LayoutElement {
private control: ColorGridControl;
constructor(port: number) {
super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port);
}
public getPaddingRatio() {
return 1 / 8;
}
}
}

View File

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

View File

@ -0,0 +1,62 @@
/// <reference path="./staticView.ts" />
namespace pxsim.visuals {
export class LargeMotorView extends StaticModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "1eb2ae58-2419-47d4-86bf-4f26a7f0cf61";
private lastMotorAnimationId: any;
constructor(port: number) {
super(LARGE_MOTOR_SVG, "large-motor", NodeType.LargeMotor, port);
}
updateState() {
const motorState = ev3board().getMotors()[this.port];
if (!motorState) return;
const speed = motorState.getSpeed();
if (this.lastMotorAnimationId) cancelAnimationFrame(this.lastMotorAnimationId);
if (!speed) return;
this.playMotorAnimation(motorState);
}
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) {
const holeEl = this.content.getElementById(this.normalizeId(LargeMotorView.ROTATING_ECLIPSE_ID))
const width = 34;
const height = 34;
const transform = `rotate(${angle} ${width / 2} ${height / 2})`;
holeEl.setAttribute("transform", transform);
}
getWiringRatio() {
return 0.62;
}
}
}

View File

@ -0,0 +1,68 @@
/// <reference path="./staticView.ts" />
namespace pxsim.visuals {
export const MOTOR_ROTATION_FPS = 32;
export class MediumMotorView extends StaticModuleView implements LayoutElement {
private static ROTATING_ECLIPSE_ID = "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;
}
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);
}
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) {
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})`;
holeEl.setAttribute("transform", transform);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

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