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 { } } }