namespace pxsim.visuals {
function createMicroServoElement() {
return svg.parseString(`
`).firstElementChild as SVGGElement;
}
export function mkMicroServoPart(xy: Coord = [0, 0]): SVGElAndSize {
return { el: createMicroServoElement(), x: xy[0], y: xy[1], w: 112.188, h: 299.674 };
}
const SPEED = 300; // 0.1s/60 degree
export class MicroServoView implements IBoardPart {
public style: string = "";
public overElement: SVGElement = undefined;
public element: SVGElement;
public defs: SVGElement[] = [];
public state: EdgeConnectorState;
public bus: EventBus;
private currentAngle = 0;
private targetAngle = 0;
private lastAngleTime = 0;
private pin: number;
private crankEl: SVGGElement;
private crankTransform: string;
public init(bus: EventBus, state: EdgeConnectorState, svgEl: SVGSVGElement, otherParams: Map) {
this.state = state;
this.pin = this.state.props.servos[
pxsim.readPin(otherParams["name"] || otherParams["pin"])
];
this.bus = bus;
this.defs = [];
this.initDom();
this.updateState();
}
initDom() {
this.element = createMicroServoElement();
this.crankEl = this.element.querySelector("#crank") as SVGGElement;
this.crankTransform = this.crankEl.getAttribute("transform");
}
moveToCoord(xy: visuals.Coord): void {
let [x, y] = xy;
translateEl(this.element, [x, y])
}
updateState(): void {
const p = this.state.getPin(this.pin);
const continuous = !!p.servoContinuous;
const servoAngle = p.servoAngle;
if (continuous) {
// for a continuous servo, the angle is interpreted as a rotation speed
// 0 -> -100%, 90 - 0%, 180 - 100%
const now = U.now();
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
this.currentAngle = this.targetAngle;
this.targetAngle += ((servoAngle - 90) / 90) * SPEED * dt;
} else {
this.targetAngle = 180.0 - servoAngle;
}
if (this.targetAngle != this.currentAngle)
this.renderAngle();
}
private renderAngle() {
const now = U.now();
const cx = 56.661;
const cy = 899.475;
const dt = Math.min(now - this.lastAngleTime, 50) / 1000;
const delta = this.targetAngle - this.currentAngle;
this.currentAngle += Math.min(Math.abs(delta), SPEED * dt) * (delta > 0 ? 1 : -1);
this.crankEl.setAttribute("transform", this.crankTransform
+ ` rotate(${this.currentAngle}, ${cx}, ${cy})`)
this.lastAngleTime = now;
setTimeout(() => runtime.updateDisplay(), 20);
}
updateTheme(): void {
}
}
}