pxt-ev3/sim/visuals/controls/motorSlider.ts
Peli de Halleux 7e58b9b699
Motor angle (#559)
* control angle with slider

* called changed state

* remove logging
2018-05-02 22:04:29 -07:00

157 lines
5.3 KiB
TypeScript

namespace pxsim.visuals {
export class MotorSliderControl extends ControlView<MotorNode> {
private group: SVGGElement;
private gradient: SVGLinearGradientElement;
private slider: SVGGElement;
private reporter: SVGTextElement;
private dial: SVGGElement;
private static SLIDER_RADIUS = 100;
private internalAngle: number = 0;
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
this.group = svg.elt("g") as SVGGElement;
const slider = pxsim.svg.child(this.group, 'g', { 'transform': 'translate(25,25)' })
const outerCircle = pxsim.svg.child(slider, "circle", {
'stroke-dasharray': '565.48', 'stroke-dashoffset': '0',
'cx': 100, 'cy': 100, 'r': '90', 'style': `fill:transparent;`,
'stroke': '#a8aaa8', 'stroke-width': '1rem'
}) as SVGCircleElement;
this.reporter = pxsim.svg.child(this.group, "text", {
'x': this.getInnerWidth() / 2, 'y': this.getInnerHeight() / 2,
'text-anchor': 'middle', 'dominant-baseline': 'middle',
'style': 'font-size: 50px',
'class': 'sim-text inverted number'
}) as SVGTextElement;
this.dial = pxsim.svg.child(slider, "g", { 'cursor': '-webkit-grab' }) as SVGGElement;
const handleInner = pxsim.svg.child(this.dial, "g");
pxsim.svg.child(handleInner, "circle", { 'cx': 0, 'cy': 0, 'r': 30, 'style': 'fill: #f12a21;' });
pxsim.svg.child(handleInner, "circle", { 'cx': 0, 'cy': 0, 'r': 29.5, 'style': 'fill: none;stroke: #b32e29' });
this.updateDial();
let pt = parent.createSVGPoint();
let captured = false;
const dragSurface = svg.child(this.group, "rect", {
x: 0,
y: 0,
width: this.getInnerWidth(),
height: this.getInnerHeight(),
opacity: 0,
cursor: '-webkit-grab'
})
touchEvents(dragSurface, ev => {
if (captured && (ev as MouseEvent).clientY != undefined) {
ev.preventDefault();
this.updateSliderValue(pt, parent, ev as MouseEvent);
this.handleSliderMove();
}
}, ev => {
captured = true;
if ((ev as MouseEvent).clientY != undefined) {
this.updateSliderValue(pt, parent, ev as MouseEvent);
this.handleSliderDown();
}
}, () => {
captured = false;
this.handleSliderUp();
})
return this.group;
}
getInnerWidth() {
return 250;
}
getInnerHeight() {
return 250;
}
private lastPosition: number;
private prevVal: number;
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
let bBox = this.content.getBoundingClientRect();
const coords = {
x: cur.x / this.scaleFactor - bBox.left / this.scaleFactor,
y: cur.y / this.scaleFactor - bBox.top / this.scaleFactor
};
const radius = MotorSliderControl.SLIDER_RADIUS / 2;
const dx = coords.x - radius;
const dy = coords.y - radius;
const atan = Math.atan(-dy / dx);
let deg = Math.ceil(atan * (180 / Math.PI));
if (dx < 0) {
deg -= 270;
} else if (dy > 0) {
deg -= 450;
} else if (dx >= 0 && dy <= 0) {
deg = 90 - deg;
}
const value = Math.abs(Math.ceil((deg % 360)));
this.internalAngle = value;
this.updateDial();
this.prevVal = deg;
this.lastPosition = cur.x;
}
private handleSliderDown() {
const state = this.state;
state.manualMotorDown();
}
private handleSliderMove() {
this.dial.setAttribute('cursor', '-webkit-grabbing');
const state = this.state;
state.manualMotorAngle(this.internalAngle);
}
private handleSliderUp() {
this.dial.setAttribute('cursor', '-webkit-grab');
const state = this.state;
state.manualMotorUp();
this.internalAngle = 0;
this.updateDial();
}
private updateDial() {
let angle = this.internalAngle;
// Update dial position
const radius = MotorSliderControl.SLIDER_RADIUS;
const dialRadius = 5;
const x = Math.ceil((radius - dialRadius) * Math.sin(angle * Math.PI / 180)) + radius;
const y = Math.ceil((radius - dialRadius) * -Math.cos(angle * Math.PI / 180)) + radius;
this.dial.setAttribute('transform', `translate(${x}, ${y})`);
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const angle = node.getAngle() % 360;
// Update reporter
this.reporter.textContent = `${angle}°`;
}
}
}