Initial sim implementation
140
sim/dalboard.ts
@ -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
@ -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
@ -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
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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
@ -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();
|
||||
}
|
||||
}
|
@ -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
@ -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
@ -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
@ -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
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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;
|
||||
}
|
||||
}
|
32
sim/visuals/assets/Color Sensor.svg
Normal 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 |
34
sim/visuals/assets/ColorSensorsvg.ts
Normal 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
@ -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 |
108
sim/visuals/assets/EV3svg.ts
Normal 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>`;
|
||||
}
|
74
sim/visuals/assets/Large Motor.svg
Normal 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 |
76
sim/visuals/assets/LargeMotorsvg.ts
Normal 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>`;
|
||||
}
|
28
sim/visuals/assets/MediumMotor.svg
Normal 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 |
30
sim/visuals/assets/MediumMotorsvg.ts
Normal 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>`;
|
||||
}
|
13
sim/visuals/assets/Portsvg.ts
Normal 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>`;
|
||||
}
|
61
sim/visuals/assets/Touch sensor.svg
Normal 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 |
64
sim/visuals/assets/TouchSensorsvg.ts
Normal 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>`;
|
||||
}
|
51
sim/visuals/assets/gyro.svg
Normal 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 |
54
sim/visuals/assets/gyrosvg.ts
Normal 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>`;
|
||||
}
|
10
sim/visuals/assets/port.svg
Normal 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 |
77
sim/visuals/assets/ultra sonic.svg
Normal 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 |
79
sim/visuals/assets/ultrasonicsvg.ts
Normal 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>`;
|
||||
}
|
Before Width: | Height: | Size: 75 KiB |
@ -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,
|
||||
|
51
sim/visuals/controlView.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
29
sim/visuals/controls/closeIcon.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
41
sim/visuals/controls/colorGrid.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
34
sim/visuals/controls/colorWheel.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
120
sim/visuals/controls/distanceSlider.ts
Normal 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" }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
94
sim/visuals/controls/rotationSlider.ts
Normal 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
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
196
sim/visuals/nodes/brickView.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
16
sim/visuals/nodes/colorSensorView.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
14
sim/visuals/nodes/gyroSensorView.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
62
sim/visuals/nodes/largeMotorView.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
68
sim/visuals/nodes/mediumMotorView.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
25
sim/visuals/nodes/portView.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
123
sim/visuals/nodes/staticView.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
67
sim/visuals/nodes/touchSensorView.ts
Normal 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])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
sim/visuals/nodes/ultrasonicView.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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
@ -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
@ -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;
|
||||
}
|
||||
}
|
||||
}
|