import { PopupDialog } from "./popup_dialog.js"; class Trajectory extends PopupDialog { mouseDown = false; constructor(ui) { super(ui); this.ui = ui; this.ui.addEventListener("keydown", (event) => { //anykey if (!event.ctrlKey && !event.shiftKey && !event.altKey) { this.hide(); event.preventDefault(); event.stopPropagation(); } }); this.tracksUi = this.ui.querySelector("#svg-arrows"); this.svgUi = this.ui.querySelector("#object-track-svg"); this.svgUi.addEventListener("wheel", (event) => { console.log("wheel", event.wheelDelta); let scaleRatio = event.wheelDelta / 2400; if (event.ctrlKey) { this.objScale *= 1 + scaleRatio; } else { let clientLength = Math.min( this.svgUi.clientWidth, this.svgUi.clientHeight ); let currentTargetRect = event.currentTarget.getBoundingClientRect(); let eventOffsetX = event.pageX - currentTargetRect.left; let eventOffsetY = event.pageY - currentTargetRect.top; let x = (eventOffsetX / clientLength) * 1000; let y = (eventOffsetY / clientLength) * 1000; this.posTrans.x = x - (x - this.posTrans.x) * (1 + scaleRatio); this.posTrans.y = y - (y - this.posTrans.y) * (1 + scaleRatio); this.posScale *= 1 + scaleRatio; } this.redrawAll(); event.preventDefault(); event.stopPropagation(); return false; }); this.inMovingCanvas = false; this.startPosForMovingCanvas = {}; this.svgUi.addEventListener("mousedown", (event) => { this.inMovingCanvas = true; this.startPosForMovingCanvas = { x: event.pageX, y: event.pageY }; }); this.svgUi.addEventListener("mouseup", (event) => { this.inMovingCanvas = false; }); this.svgUi.addEventListener("mousemove", (event) => { if (this.inMovingCanvas) { let delta = { x: event.pageX - this.startPosForMovingCanvas.x, y: event.pageY - this.startPosForMovingCanvas.y, }; let clientLength = Math.min( this.svgUi.clientWidth, this.svgUi.clientHeight ); this.posTrans.x += (delta.x / clientLength) * 1000; this.posTrans.y += (delta.y / clientLength) * 1000; this.startPosForMovingCanvas = { x: event.pageX, y: event.pageY }; this.redrawAll(); } }); this.resizeObserver = new ResizeObserver((elements) => { if (elements[0].contentRect.height == 0) return; this.redrawAll(); }); this.resizeObserver.observe(this.viewUi); } viewScale = 1; objScale = 1; updateObjectScale() { let v = this.viewUi; this.viewScale = Math.max(1000 / v.clientHeight, 1000 / v.clientWidth); } object = {}; setObject( objType, objId, tracks, funcOnExit //tracks is a list of [frameId, x, y, direction], in order ) { this.object = { type: objType, id: objId, tracks: tracks, }; this.funcOnExit = funcOnExit; //console.log(objType, objId, tracks); this.calculateCoordinateTransform(this.object.tracks); this.redrawAll(); this.ui.focus(); } redrawAll() { this.show(); this.clear(); this.updateObjectScale(); this.drawTracks(this.object); this.drawScaler(); } clear() { let arrows = this.ui.querySelector("#svg-arrows").children; if (arrows.length > 0) { for (var c = arrows.length - 1; c >= 0; c--) { arrows[c].remove(); } } let scaler = this.ui.querySelector("#svg-scaler").children; if (scaler.length > 0) { for (var c = scaler.length - 1; c >= 0; c--) { scaler[c].remove(); } } } /* the viewbox is 1000 by 1000 the drawing area is [100,900] by [100,900] viewbox coordinate system x goes right y goes down utm coordinate system x goes east (right) y goes north (up) */ calculateCoordinateTransform(tracks) { tracks = tracks.filter((x) => x[1]); let xs = tracks.map((x) => x[1].psr.position.x); let max_x = Math.max(...xs); //, 0); let min_x = Math.min(...xs); //, 0); let ys = tracks.map((x) => x[1].psr.position.y); let max_y = Math.max(...ys); //, 0); let min_y = Math.min(...ys); //, 0); let scale = Math.max(max_x - min_x, max_y - min_y); if (scale == 0) scale = 1; else scale = 800 / scale; // svg view is 1000*1000 this.posScale = scale; this.posTrans = { x: -min_x * this.posScale + 100, y: max_y * this.posScale + 100, }; } transform(x, y, theta, label, highlight) { return [ x * this.posScale + this.posTrans.x, -y * this.posScale + this.posTrans.y, x, y, theta, label, highlight, ]; } drawOneTrace(x, y, orgX, orgY, theta, label, highlight) { let svg = this.ui.querySelector("#svg-arrows"); let g = document.createElementNS("http://www.w3.org/2000/svg", "g"); g.innerHTML = `${label}`; g.setAttribute("class", "one-track"); g.ondblclick = (e) => { if (this.funcOnExit) { this.hide(); this.funcOnExit(label); } }; if (highlight) { g.setAttribute("class", "one-track object-track-current-frame"); } svg.appendChild(g); let r = 5 * this.objScale; let d = 25 * this.objScale; let a = 5 * this.objScale; //wrapper circle let p = document.createElementNS("http://www.w3.org/2000/svg", "circle"); p.setAttribute("cx", x + ((d - r) / 2) * this.viewScale * Math.cos(theta)); p.setAttribute("cy", y - ((d - r) / 2) * this.viewScale * Math.sin(theta)); p.setAttribute("r", ((d + 2 * r) / 2) * this.viewScale); p.setAttribute("class", "track-wrapper"); g.appendChild(p); //object p = document.createElementNS("http://www.w3.org/2000/svg", "circle"); p.setAttribute("cx", x); p.setAttribute("cy", y); p.setAttribute("r", r * this.viewScale); g.appendChild(p); // //arrow head // p = document.createElementNS("http://www.w3.org/2000/svg", 'line'); // p.setAttribute("x1", x + d * this.viewScale * Math.cos(theta)); // p.setAttribute("y1", y - d * this.viewScale* Math.sin(theta)); // p.setAttribute("x2", x + d * this.viewScale * Math.cos(theta) - a * this.viewScale * Math.cos(Math.PI/6+theta)); // p.setAttribute("y2", y - d * this.viewScale * Math.sin(theta) + a * this.viewScale * Math.sin(Math.PI/6+theta)); // g.appendChild(p); // p = document.createElementNS("http://www.w3.org/2000/svg", 'line'); // p.setAttribute("x1", x + d * this.viewScale * Math.cos(theta)); // p.setAttribute("y1", y - d * this.viewScale * Math.sin(theta)); // p.setAttribute("x2", x + d * this.viewScale * Math.cos(theta) - a * this.viewScale * Math.cos(-Math.PI/6+theta)); // p.setAttribute("y2", y - d * this.viewScale * Math.sin(theta) + a * this.viewScale * Math.sin(-Math.PI/6+theta)); // g.appendChild(p); // direction p = document.createElementNS("http://www.w3.org/2000/svg", "line"); p.setAttribute("x1", x + r * this.viewScale * Math.cos(theta)); p.setAttribute("y1", y - r * this.viewScale * Math.sin(theta)); p.setAttribute("x2", x + d * this.viewScale * Math.cos(theta)); p.setAttribute("y2", y - d * this.viewScale * Math.sin(theta)); g.appendChild(p); // frame // p = document.createElementNS("http://www.w3.org/2000/svg", 'text'); // p.setAttribute("x", x + 50 * this.scale); // p.setAttribute("y", y); // p.textContent = track[0]; // g.appendChild(p); p = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); p.setAttribute("x", x + 50 * this.viewScale); p.setAttribute("y", y); // p.setAttribute("width", 200 * this.scale); p.setAttribute("font-size", 10 * this.viewScale + "px"); p.setAttribute("class", "track-label"); let text = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); text.textContent = label; p.appendChild(text); g.appendChild(p); } calculateScalerUnit() { let x = 100 / this.posScale; let e = 0; while (x >= 10 || x < 1) { if (x >= 10) { e += 1; x /= 10; } else if (x < 1) { e -= 1; x *= 10; } } x = 10 * Math.pow(10, e); return x; } drawScaler() { let x = this.calculateScalerUnit(); let lineLen = x * this.posScale; let svg = this.ui.querySelector("#svg-scaler"); let g = document.createElementNS("http://www.w3.org/2000/svg", "g"); svg.appendChild(g); //direction let p = document.createElementNS("http://www.w3.org/2000/svg", "line"); p.setAttribute("x1", 100); p.setAttribute("y1", 900); p.setAttribute("x2", 100 + lineLen); p.setAttribute("y2", 900); g.appendChild(p); p = document.createElementNS("http://www.w3.org/2000/svg", "line"); p.setAttribute("x1", 100); p.setAttribute("y1", 900); p.setAttribute("x2", 100); p.setAttribute("y2", 900 - lineLen); g.appendChild(p); p = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); p.setAttribute("x", 105); p.setAttribute("y", 875); // p.setAttribute("width", 200 * this.scale); p.setAttribute("font-size", 10 * this.viewScale + "px"); p.setAttribute("class", "scaler-label"); let text = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); text.textContent = x.toString() + "m"; p.appendChild(text); g.appendChild(p); } drawTracks(object) { this.titleUi.innerText = object.type + " " + +object.id; let tracks = object.tracks; tracks .filter((x) => x[1]) .map((track) => [ track[1].psr.position.x, track[1].psr.position.y, track[1].psr.rotation.z, track[0], track[2], ]) .map((x) => this.transform(...x)) .forEach((x) => this.drawOneTrace(...x)); //ego car //this.draw_ego_car(...this.transform(0,0,0,"",false).slice(0,2)); } draw_ego_car(x, y) { let svg = this.ui.querySelector("#svg-arrows"); let g = document.createElementNS("http://www.w3.org/2000/svg", "g"); g.innerHTML = `Ego car`; g.setAttribute("id", "track-ego-car"); svg.appendChild(g); let p = document.createElementNS("http://www.w3.org/2000/svg", "line"); p.setAttribute("x1", x - 10 * this.viewScale); p.setAttribute("y1", y); p.setAttribute("x2", x + 10 * this.viewScale); p.setAttribute("y2", y); g.appendChild(p); p = document.createElementNS("http://www.w3.org/2000/svg", "line"); p.setAttribute("x1", x); p.setAttribute("y1", y - 10 * this.viewScale); p.setAttribute("x2", x); p.setAttribute("y2", y + 10 * this.viewScale); g.appendChild(p); } } export { Trajectory };