You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

389 lines
11 KiB
JavaScript

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 = `<title>${label}</title>`;
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 = `<title>Ego car</title>`;
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 };