From d4602681c77edf9a67c0e59a245899850c55c7de Mon Sep 17 00:00:00 2001 From: chenyanqun Date: Sun, 12 Oct 2025 15:50:24 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=88=9D=E5=A7=8B=E4=B8=80?= =?UTF-8?q?=E6=AC=A1js=E6=96=87=E4=BB=B6=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/annotation.js | 1204 ++++---- public/js/auto-adjust.js | 904 +++--- public/js/auto_annotate.js | 57 +- public/js/aux_lidar.js | 680 +++-- public/js/box_editor.js | 3151 ++++++++++----------- public/js/box_op.js | 1623 ++++++----- public/js/calib.js | 304 +- public/js/config.js | 215 +- public/js/config_ui.js | 648 +++-- public/js/context_menu.js | 325 +-- public/js/crop_scene.js | 108 +- public/js/data.js | 814 +++--- public/js/debug.js | 27 +- public/js/editor.js | 4982 +++++++++++++++++---------------- public/js/ego_pose.js | 133 +- public/js/error_check.js | 41 +- public/js/floatlabel.js | 1013 ++++--- public/js/header.js | 330 ++- public/js/image.js | 2048 +++++++------- public/js/info_box.js | 177 +- public/js/keydown_manager.js | 64 +- public/js/lidar.js | 2617 +++++++++-------- public/js/log.js | 369 +-- public/js/main.js | 53 +- public/js/ml.js | 931 +++--- public/js/mouse.js | 407 +-- public/js/obj_cfg.js | 324 ++- public/js/obj_id_list.js | 224 +- public/js/pcd_viewer.js | 284 +- public/js/play.js | 277 +- public/js/popup_dialog.js | 312 +-- public/js/radar.js | 737 ++--- public/js/reg_demo.js | 352 ++- public/js/save.js | 237 +- public/js/settings.js | 11 +- public/js/side_view_op.js | 2691 +++++++++--------- public/js/test.js | 138 +- public/js/text_file_loader.js | 85 +- public/js/trajectory.js | 749 +++-- public/js/util.js | 1105 ++++---- public/js/view.js | 1527 +++++----- public/js/world.js | 1094 ++++---- 42 files changed, 16814 insertions(+), 16558 deletions(-) diff --git a/public/js/annotation.js b/public/js/annotation.js index 3c8255d..6ea5405 100644 --- a/public/js/annotation.js +++ b/public/js/annotation.js @@ -1,591 +1,671 @@ - - -import * as THREE from './lib/three.module.js'; -import {globalObjectCategory} from './obj_cfg.js'; -import {saveWorldList} from "./save.js" -import { intersect } from './util.js'; - - -function Annotation(sceneMeta, world, frameInfo){ - this.world = world; - this.data = this.world.data; - //this.coordinatesOffset = this.world.coordinatesOffset; - this.boxes_load_time = 0; - this.frameInfo = frameInfo; - - +import * as THREE from "./lib/three.module.js"; +import { globalObjectCategory } from "./obj_cfg.js"; +import { saveWorldList } from "./save.js"; +import { intersect } from "./util.js"; + +function Annotation(sceneMeta, world, frameInfo) { + this.world = world; + this.data = this.world.data; + //this.coordinatesOffset = this.world.coordinatesOffset; + this.boxes_load_time = 0; + this.frameInfo = frameInfo; + + this.modified = false; + this.setModified = function () { + this.modified = true; + + if (pointsGlobalConfig.autoSave) { + saveWorldList([this.world]); + } + }; + this.resetModified = function () { this.modified = false; - this.setModified = function(){ - this.modified=true; - - if (pointsGlobalConfig.autoSave) - { - saveWorldList([this.world]); - } - }; - this.resetModified = function(){this.modified=false;}; - - - this.sort_boxes = function(){ - this.boxes = this.boxes.sort(function(x,y){ - return x.position.y - y.position.y; - }); - }; - this.findBoxByTrackId = function(id){ - if (this.boxes){ - let box = this.boxes.find(function(x){ - return x.obj_track_id == id; - }); - return box; - } - - return null; - }; - - this.findIntersectedBoxes = function(box){ - return this.boxes.filter(b=>b!=box).filter(b=>intersect(box, b)); - }; - - this.preload = function(on_preload_finished){ - this.on_preload_finished = on_preload_finished; - this.load_annotation((boxes)=>this.proc_annotation(boxes)); - }; - - - - this.go_cmd_received = false; - this.webglScene = null; - this.on_go_finished = null; - this.go = function(webglScene, on_go_finished){ - this.webglScene = webglScene; - - if (this.preloaded){ - - //this.boxes.forEach(b=>this.webglScene.add(b)); - if (this.data.cfg.color_obj != "no"){ - this.color_boxes(); - } - - if (on_go_finished) - on_go_finished(); - } else { - this.go_cmd_received = true; - this.on_go_finished = on_go_finished; - } - }; - - - // internal funcs below - this._afterPreload = function(){ - this.preloaded = true; - console.log("annotation preloaded"); - - if (this.on_preload_finished){ - this.on_preload_finished(); - } - if (this.go_cmd_received){ - this.go(this.webglScene, this.on_go_finished); - } - }; - - - this.unload = function(){ - if (this.boxes){ - this.boxes.forEach((b)=>{ - //this.webglGroup.remove(b); - - if (b.boxEditor) - b.boxEditor.detach(); - }); - } - }; - - this.deleteAll = function(){ - this.remove_all_boxes(); - }; - this.boxToAnn = function(box){ - let ann = { - psr: { - position:{ - x: box.position.x, - y: box.position.y, - z: box.position.z, - }, - scale:{ - x: box.scale.x, - y: box.scale.y, - z: box.scale.z, - }, - rotation:{ - x:box.rotation.x, - y:box.rotation.y, - z:box.rotation.z, - }, - }, - obj_type: box.obj_type, - obj_id: String(box.obj_track_id), - obj_attr: box.obj_attr, - //vertices: vertices, - }; - return ann; - }; - - this.toBoxAnnotations = function(){ - let anns = this.boxes.map((b)=>{ - //var vertices = psr_to_xyz(b.position, b.scale, b.rotation); - let ann = this.boxToAnn(b); - - if (b.annotator) - ann.annotator = b.annotator; - - if (b.follows) - ann.follows = b.follows; - return ann; - }); - - anns.sort((a,b)=>a.obj_id- b.obj_id); - - return anns; - }; - - - - // to real-world position (no offset) - this.ann_to_vector_global = function(box) { - let posG = this.world.lidarPosToScene(box.position); - let rotG = this.world.lidarRotToScene(box.rotation); - - return [ - posG.x - this.world.coordinatesOffset[0], posG.y-this.world.coordinatesOffset[1], posG.z-this.world.coordinatesOffset[2], - rotG.x, rotG.y, rotG.z, - box.scale.x, box.scale.y, box.scale.z, - ]; + }; + + this.sort_boxes = function () { + this.boxes = this.boxes.sort(function (x, y) { + return x.position.y - y.position.y; + }); + }; + this.findBoxByTrackId = function (id) { + if (this.boxes) { + let box = this.boxes.find(function (x) { + return x.obj_track_id == id; + }); + return box; + } - }; + return null; + }; + + this.findIntersectedBoxes = function (box) { + return this.boxes.filter((b) => b != box).filter((b) => intersect(box, b)); + }; + + this.preload = function (on_preload_finished) { + this.on_preload_finished = on_preload_finished; + this.load_annotation((boxes) => this.proc_annotation(boxes)); + }; + + this.go_cmd_received = false; + this.webglScene = null; + this.on_go_finished = null; + this.go = function (webglScene, on_go_finished) { + this.webglScene = webglScene; + + if (this.preloaded) { + //this.boxes.forEach(b=>this.webglScene.add(b)); + if (this.data.cfg.color_obj != "no") { + this.color_boxes(); + } + + if (on_go_finished) on_go_finished(); + } else { + this.go_cmd_received = true; + this.on_go_finished = on_go_finished; + } + }; - // real-world position to ann - this.vector_global_to_ann = function(v) - { - let posG = new THREE.Vector3(v[0]+this.world.coordinatesOffset[0], - v[1]+this.world.coordinatesOffset[1], - v[2]+this.world.coordinatesOffset[2]); - let rotG = new THREE.Euler(v[3],v[4],v[5]); + // internal funcs below + this._afterPreload = function () { + this.preloaded = true; + console.log("annotation preloaded"); - let rotL = this.world.sceneRotToLidar(rotG); - let posL = this.world.scenePosToLidar(posG); + if (this.on_preload_finished) { + this.on_preload_finished(); + } + if (this.go_cmd_received) { + this.go(this.webglScene, this.on_go_finished); + } + }; - return { - position: {x: posL.x, y: posL.y, z: posL.z}, - rotation: {x: rotL.x, y: rotL.y, z: rotL.z}, - scale: {x: v[6], y: v[7], z: v[8]} - }; + this.unload = function () { + if (this.boxes) { + this.boxes.forEach((b) => { + //this.webglGroup.remove(b); + if (b.boxEditor) b.boxEditor.detach(); + }); + } + }; + + this.deleteAll = function () { + this.remove_all_boxes(); + }; + this.boxToAnn = function (box) { + let ann = { + psr: { + position: { + x: box.position.x, + y: box.position.y, + z: box.position.z, + }, + scale: { + x: box.scale.x, + y: box.scale.y, + z: box.scale.z, + }, + rotation: { + x: box.rotation.x, + y: box.rotation.y, + z: box.rotation.z, + }, + }, + obj_type: box.obj_type, + obj_id: String(box.obj_track_id), + obj_attr: box.obj_attr, + //vertices: vertices, }; - - - // this.vector_to_ann = function(v){ - // return { - // position:{ - // x:v[0],// + this.coordinatesOffset[0], - // y:v[1],// + this.coordinatesOffset[1], - // z:v[2],// + this.coordinatesOffset[2], - // }, - - - // rotation:{ - // x:v[3], - // y:v[4], - // z:v[5], - // }, - - // scale:{ - // x:v[6], - // y:v[7], - // z:v[8], - // }, - - // }; - // }; - - this.remove_all_boxes = function(){ - if (this.boxes){ - this.boxes.forEach((b)=>{ - this.webglGroup.remove(b); - this.world.data.dbg.free(); - b.geometry.dispose(); - b.material.dispose(); - b.world = null; - b.boxEditor = null; - }); - - this.boxes = []; - } - else{ - console.error("destroy empty world!") - } + return ann; + }; + + this.toBoxAnnotations = function () { + let anns = this.boxes.map((b) => { + //var vertices = psr_to_xyz(b.position, b.scale, b.rotation); + let ann = this.boxToAnn(b); + + if (b.annotator) ann.annotator = b.annotator; + + if (b.follows) ann.follows = b.follows; + return ann; + }); + + anns.sort((a, b) => a.obj_id - b.obj_id); + + return anns; + }; + + // to real-world position (no offset) + this.ann_to_vector_global = function (box) { + let posG = this.world.lidarPosToScene(box.position); + let rotG = this.world.lidarRotToScene(box.rotation); + + return [ + posG.x - this.world.coordinatesOffset[0], + posG.y - this.world.coordinatesOffset[1], + posG.z - this.world.coordinatesOffset[2], + rotG.x, + rotG.y, + rotG.z, + box.scale.x, + box.scale.y, + box.scale.z, + ]; + }; + + // real-world position to ann + this.vector_global_to_ann = function (v) { + let posG = new THREE.Vector3( + v[0] + this.world.coordinatesOffset[0], + v[1] + this.world.coordinatesOffset[1], + v[2] + this.world.coordinatesOffset[2] + ); + let rotG = new THREE.Euler(v[3], v[4], v[5]); + + let rotL = this.world.sceneRotToLidar(rotG); + let posL = this.world.scenePosToLidar(posG); + + return { + position: { x: posL.x, y: posL.y, z: posL.z }, + rotation: { x: rotL.x, y: rotL.y, z: rotL.z }, + scale: { x: v[6], y: v[7], z: v[8] }, }; + }; + + // this.vector_to_ann = function(v){ + // return { + // position:{ + // x:v[0],// + this.coordinatesOffset[0], + // y:v[1],// + this.coordinatesOffset[1], + // z:v[2],// + this.coordinatesOffset[2], + // }, + + // rotation:{ + // x:v[3], + // y:v[4], + // z:v[5], + // }, + + // scale:{ + // x:v[6], + // y:v[7], + // z:v[8], + // }, + + // }; + // }; + + this.remove_all_boxes = function () { + if (this.boxes) { + this.boxes.forEach((b) => { + this.webglGroup.remove(b); + this.world.data.dbg.free(); + b.geometry.dispose(); + b.material.dispose(); + b.world = null; + b.boxEditor = null; + }); + + this.boxes = []; + } else { + console.error("destroy empty world!"); + } + }; + + this.new_bbox_cube = function (color) { + var h = 0.5; + + var body = [ + //top + -h, + h, + h, + h, + h, + h, + h, + h, + h, + h, + -h, + h, + h, + -h, + h, + -h, + -h, + h, + -h, + -h, + h, + -h, + h, + h, + + //botom + -h, + h, + -h, + h, + h, + -h, + h, + h, + -h, + h, + -h, + -h, + h, + -h, + -h, + -h, + -h, + -h, + -h, + -h, + -h, + -h, + h, + -h, + + // vertical lines + -h, + h, + h, + -h, + h, + -h, + h, + h, + h, + h, + h, + -h, + h, + -h, + h, + h, + -h, + -h, + -h, + -h, + h, + -h, + -h, + -h, + + //direction + h, + 0, + h, + 1.5 * h, + 0, + h, + //h/2, -h, h+0.1, h, 0, h+0.1, + //h/2, h, h+0.1, h, 0, h+0.1, + + //side direction + // h, h/2, h, h, h, 0, + // h, h/2, -h, h, h, 0, + // h, 0, 0, h, h, 0, + ]; + + this.world.data.dbg.alloc(); + + var bbox = new THREE.BufferGeometry(); + bbox.setAttribute("position", new THREE.Float32BufferAttribute(body, 3)); + + if (!color) { + color = 0x00ff00; + } - this.new_bbox_cube=function(color){ - - var h = 0.5; - - var body = [ - //top - -h,h,h, h,h,h, - h,h,h, h,-h,h, - h,-h,h, -h,-h,h, - -h,-h,h, -h, h, h, - - //botom - -h,h,-h, h,h,-h, - h,h,-h, h,-h,-h, - h,-h,-h, -h,-h,-h, - -h,-h,-h, -h, h, -h, - - // vertical lines - -h,h,h, -h,h,-h, - h,h,h, h,h,-h, - h,-h,h, h,-h,-h, - -h,-h,h, -h,-h,-h, - - //direction - h, 0, h, 1.5*h, 0, h, - //h/2, -h, h+0.1, h, 0, h+0.1, - //h/2, h, h+0.1, h, 0, h+0.1, - - //side direction - // h, h/2, h, h, h, 0, - // h, h/2, -h, h, h, 0, - // h, 0, 0, h, h, 0, - - ]; - - - this.world.data.dbg.alloc(); - - var bbox = new THREE.BufferGeometry(); - bbox.setAttribute( 'position', new THREE.Float32BufferAttribute(body, 3 ) ); - - if (!color){ - color = 0x00ff00; - } - - /* + /* https://threejs.org/docs/index.html#api/en/materials/LineBasicMaterial linewidth is 1, regardless of set value. */ - - var material = new THREE.LineBasicMaterial( { color: color, linewidth: 1, opacity: this.data.cfg.box_opacity, transparent: true } ); - var box = new THREE.LineSegments( bbox, material ); - - box.scale.x=1.8; - box.scale.y=4.5; - box.scale.z=1.5; - box.name="bbox"; - box.obj_type="car"; - - //box.computeLineDistances(); - - - - return box; - }; - - this.createCuboid = function(pos, scale, rotation, obj_type, track_id, obj_attr){ - let mesh = this.new_bbox_cube(parseInt("0x"+globalObjectCategory.get_obj_cfg_by_type(obj_type).color.slice(1))); - mesh.position.x = pos.x; - mesh.position.y = pos.y; - mesh.position.z = pos.z; - - mesh.scale.x = scale.x; - mesh.scale.y = scale.y; - mesh.scale.z = scale.z; - - mesh.rotation.x = rotation.x; - mesh.rotation.y = rotation.y; - mesh.rotation.z = rotation.z; - - mesh.obj_track_id = track_id; //tracking id - mesh.obj_type = obj_type; - mesh.obj_attr = obj_attr; - mesh.obj_local_id = this.get_new_box_local_id(); - - mesh.world = this.world; - - return mesh; - }; - /* + var material = new THREE.LineBasicMaterial({ + color: color, + linewidth: 1, + opacity: this.data.cfg.box_opacity, + transparent: true, + }); + var box = new THREE.LineSegments(bbox, material); + + box.scale.x = 1.8; + box.scale.y = 4.5; + box.scale.z = 1.5; + box.name = "bbox"; + box.obj_type = "car"; + + //box.computeLineDistances(); + + return box; + }; + + this.createCuboid = function ( + pos, + scale, + rotation, + obj_type, + track_id, + obj_attr + ) { + let mesh = this.new_bbox_cube( + parseInt( + "0x" + globalObjectCategory.get_obj_cfg_by_type(obj_type).color.slice(1) + ) + ); + mesh.position.x = pos.x; + mesh.position.y = pos.y; + mesh.position.z = pos.z; + + mesh.scale.x = scale.x; + mesh.scale.y = scale.y; + mesh.scale.z = scale.z; + + mesh.rotation.x = rotation.x; + mesh.rotation.y = rotation.y; + mesh.rotation.z = rotation.z; + + mesh.obj_track_id = track_id; //tracking id + mesh.obj_type = obj_type; + mesh.obj_attr = obj_attr; + mesh.obj_local_id = this.get_new_box_local_id(); + + mesh.world = this.world; + + return mesh; + }; + /* pos: offset position, after transformed */ - this.add_box=function(pos, scale, rotation, obj_type, track_id, obj_attr){ - - let mesh = this.createCuboid(pos, scale, rotation, obj_type, track_id, obj_attr) - - this.boxes.push(mesh); - this.sort_boxes(); - - this.webglGroup.add(mesh); - - return mesh; - }; - - this.load_box = function(box){ - - this.webglGroup.add(box); - }; - - this.unload_box = function(box){ - - this.webglGroup.remove(box); - }; - - this.remove_box=function(box){ - this.world.data.dbg.free(); - box.geometry.dispose(); - box.material.dispose(); - //selected_box.dispose(); - this.boxes = this.boxes.filter(function(x){return x !=box;}); - }; - - this.set_box_opacity=function(box_opacity){ - this.boxes.forEach(function(x){ - x.material.opacity = box_opacity; - }); - }; - - this.translate_box_position=function(pos, theta, axis, delta){ - switch (axis){ - case 'x': - pos.x += delta*Math.cos(theta); - pos.y += delta*Math.sin(theta); - break; - case 'y': - pos.x += delta*Math.cos(Math.PI/2 + theta); - pos.y += delta*Math.sin(Math.PI/2 + theta); - break; - case 'z': - pos.z += delta; - break; - - } - }; - - this.find_boxes_inside_rect = function(x,y,w,h, camera){ - - let selected_boxes_by_rect = []; - - if (!this.boxes) - return selected_boxes_by_rect; - - - var p = new THREE.Vector3(); - - for (var i=0; i< this.boxes.length; i++){ - let box_center = this.boxes[i].position; - - let pw = this.world.lidarPosToScene(box_center); - p.set(pw.x, pw.y, pw.z); - p.project(camera); - p.x = p.x/p.z; - p.y = p.y/p.z; - //console.log(p); - if ((p.x > x) && (p.x < x+w) && (p.y>y) && (p.ythis.webglGroup.add(b)); - - this.world.webglGroup.add(this.webglGroup); - - this.boxes_load_time = new Date().getTime(); - console.log(this.boxes_load_time, this.frameInfo.scene, this.frameInfo.frame, "loaded boxes ", this.boxes_load_time - this.create_time, "ms"); - - this.sort_boxes(); - - this._afterPreload(); - }; - - this.load_annotation=function(on_load){ - if (this.data.cfg.disableLabels){ - on_load([]); - }else { - var xhr = new XMLHttpRequest(); - // we defined the xhr - var _self = this; - xhr.onreadystatechange = function () { - if (this.readyState != 4) return; - - if (this.status == 200) { - let ann = _self.frameInfo.anno_to_boxes(this.responseText); - on_load(ann); - } - - // end of state change: it can be after some time (async) - }; - - xhr.open('GET', "/load_annotation"+"?scene="+this.frameInfo.scene+"&frame="+this.frameInfo.frame, true); - xhr.send(); - } - }; - - this.reloadAnnotation=function(done){ - this.load_annotation(ann=>{ - this.reapplyAnnotation(ann, done); - }); - }; + this.add_box = function (pos, scale, rotation, obj_type, track_id, obj_attr) { + let mesh = this.createCuboid( + pos, + scale, + rotation, + obj_type, + track_id, + obj_attr + ); + + this.boxes.push(mesh); + this.sort_boxes(); + + this.webglGroup.add(mesh); + + return mesh; + }; + + this.load_box = function (box) { + this.webglGroup.add(box); + }; + + this.unload_box = function (box) { + this.webglGroup.remove(box); + }; + + this.remove_box = function (box) { + this.world.data.dbg.free(); + box.geometry.dispose(); + box.material.dispose(); + //selected_box.dispose(); + this.boxes = this.boxes.filter(function (x) { + return x != box; + }); + }; + + this.set_box_opacity = function (box_opacity) { + this.boxes.forEach(function (x) { + x.material.opacity = box_opacity; + }); + }; + + this.translate_box_position = function (pos, theta, axis, delta) { + switch (axis) { + case "x": + pos.x += delta * Math.cos(theta); + pos.y += delta * Math.sin(theta); + break; + case "y": + pos.x += delta * Math.cos(Math.PI / 2 + theta); + pos.y += delta * Math.sin(Math.PI / 2 + theta); + break; + case "z": + pos.z += delta; + break; + } + }; - - this.reapplyAnnotation = function(boxes, done){ - // these boxes haven't attached a world - //boxes = this.transformBoxesByOffset(boxes); - - // mark all old boxes - this.boxes.forEach(b=>{b.delete=true;}); - - let pendingBoxList=[]; - - boxes.forEach(nb=>{ // nb is annotation format, not a true box - let old_box = this.boxes.find(function(x){ - return x.obj_track_id == nb.obj_id && x.obj_track_id != "" && nb.obj_id != "" && x.obj_type == nb.obj_type;; - }); - - if (old_box){ - // found - // update psr - delete old_box.delete; // unmark delete flag - old_box.position.set(nb.psr.position.x, nb.psr.position.y, nb.psr.position.z); - old_box.scale.set(nb.psr.scale.x, nb.psr.scale.y, nb.psr.scale.z); - old_box.rotation.set(nb.psr.rotation.x, nb.psr.rotation.y, nb.psr.rotation.z); - old_box.obj_attr = nb.obj_attr; - old_box.annotator = nb.annotator; - old_box.changed=false; // clear changed flag. - - }else{ - // not found - let box=this.createOneBoxByAnn(nb); - pendingBoxList.push(box); - } - }); - - // delete removed - let toBeDelBoxes = this.boxes.filter(b=>b.delete); - toBeDelBoxes.forEach(b=>{ - if (b.boxEditor){ - - b.boxEditor.detach("donthide"); - } - - this.webglGroup.remove(b); - - this.remove_box(b); - }) - - pendingBoxList.forEach(b=>{ - this.boxes.push(b); - }) - - - //todo, restore point color - //todo, update imagecontext, selected box, ... - //refer to normal delete operation - // re-color again - this.world.lidar.recolor_all_points(); - - this.color_boxes(); - - // add new boxes - pendingBoxList.forEach(b=>{ - this.webglGroup.add(b); - }) - - - this.resetModified(); - - if (done) - done(); - - } + (this.find_boxes_inside_rect = function (x, y, w, h, camera) { + let selected_boxes_by_rect = []; - this.createOneBoxByAnn = function(annotation){ - let b = annotation; - - let mesh = this.createCuboid(b.psr.position, - b.psr.scale, - b.psr.rotation, - b.obj_type, - b.obj_id, - b.obj_attr); - - if (b.annotator){ - mesh.annotator = b.annotator; - } + if (!this.boxes) return selected_boxes_by_rect; - if (b.follows) - mesh.follows = b.follows; - - return mesh; - }; + var p = new THREE.Vector3(); - this.createBoxes = function(annotations){ - return annotations.map((b)=>{ - return this.createOneBoxByAnn(b); - }); - }; - - - this.box_local_id = 0; - this.get_new_box_local_id=function(){ - var ret = this.box_local_id; - this.box_local_id+=1; - return ret; - }; + for (var i = 0; i < this.boxes.length; i++) { + let box_center = this.boxes[i].position; + let pw = this.world.lidarPosToScene(box_center); + p.set(pw.x, pw.y, pw.z); + p.project(camera); + p.x = p.x / p.z; + p.y = p.y / p.z; + //console.log(p); + if (p.x > x && p.x < x + w && p.y > y && p.y < y + h) { + selected_boxes_by_rect.push(this.boxes[i]); + } + } - this.color_box = function(box) - { - if (this.data.cfg.color_obj == "category" || this.data.cfg.color_obj == "no") - { - let color = globalObjectCategory.get_color_by_category(box.obj_type); - box.material.color.r=color.x; - box.material.color.g=color.y; - box.material.color.b=color.z; + console.log("select boxes", selected_boxes_by_rect.length); + return selected_boxes_by_rect; + }), + (this.proc_annotation = function (boxes) { + // boxes = this.transformBoxesByEgoPose(boxes); + // boxes = this.transformBoxesByOffset(boxes); + + // //var boxes = JSON.parse(this.responseText); + //console.log(ret); + this.boxes = this.createBoxes(boxes); //create in future world + + this.webglGroup = new THREE.Group(); + this.webglGroup.name = "annotations"; + this.boxes.forEach((b) => this.webglGroup.add(b)); + + this.world.webglGroup.add(this.webglGroup); + + this.boxes_load_time = new Date().getTime(); + console.log( + this.boxes_load_time, + this.frameInfo.scene, + this.frameInfo.frame, + "loaded boxes ", + this.boxes_load_time - this.create_time, + "ms" + ); + + this.sort_boxes(); + + this._afterPreload(); + }); + + this.load_annotation = function (on_load) { + if (this.data.cfg.disableLabels) { + on_load([]); + } else { + var xhr = new XMLHttpRequest(); + // we defined the xhr + var _self = this; + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + + if (this.status == 200) { + let ann = _self.frameInfo.anno_to_boxes(this.responseText); + on_load(ann); } - else - { - let color = globalObjectCategory.get_color_by_id(box.obj_track_id); - box.material.color.r=color.x; - box.material.color.g=color.y; - box.material.color.b=color.z; - } + // end of state change: it can be after some time (async) + }; + + xhr.open( + "GET", + "/load_annotation" + + "?scene=" + + this.frameInfo.scene + + "&frame=" + + this.frameInfo.frame, + true + ); + xhr.send(); + } + }; + + this.reloadAnnotation = function (done) { + this.load_annotation((ann) => { + this.reapplyAnnotation(ann, done); + }); + }; + + this.reapplyAnnotation = function (boxes, done) { + // these boxes haven't attached a world + //boxes = this.transformBoxesByOffset(boxes); + + // mark all old boxes + this.boxes.forEach((b) => { + b.delete = true; + }); + + let pendingBoxList = []; + + boxes.forEach((nb) => { + // nb is annotation format, not a true box + let old_box = this.boxes.find(function (x) { + return ( + x.obj_track_id == nb.obj_id && + x.obj_track_id != "" && + nb.obj_id != "" && + x.obj_type == nb.obj_type + ); + }); + + if (old_box) { + // found + // update psr + delete old_box.delete; // unmark delete flag + old_box.position.set( + nb.psr.position.x, + nb.psr.position.y, + nb.psr.position.z + ); + old_box.scale.set(nb.psr.scale.x, nb.psr.scale.y, nb.psr.scale.z); + old_box.rotation.set( + nb.psr.rotation.x, + nb.psr.rotation.y, + nb.psr.rotation.z + ); + old_box.obj_attr = nb.obj_attr; + old_box.annotator = nb.annotator; + old_box.changed = false; // clear changed flag. + } else { + // not found + let box = this.createOneBoxByAnn(nb); + pendingBoxList.push(box); + } + }); + + // delete removed + let toBeDelBoxes = this.boxes.filter((b) => b.delete); + toBeDelBoxes.forEach((b) => { + if (b.boxEditor) { + b.boxEditor.detach("donthide"); + } + + this.webglGroup.remove(b); + + this.remove_box(b); + }); + + pendingBoxList.forEach((b) => { + this.boxes.push(b); + }); + + //todo, restore point color + //todo, update imagecontext, selected box, ... + //refer to normal delete operation + // re-color again + this.world.lidar.recolor_all_points(); + + this.color_boxes(); + + // add new boxes + pendingBoxList.forEach((b) => { + this.webglGroup.add(b); + }); + + this.resetModified(); + + if (done) done(); + }; + + this.createOneBoxByAnn = function (annotation) { + let b = annotation; + + let mesh = this.createCuboid( + b.psr.position, + b.psr.scale, + b.psr.rotation, + b.obj_type, + b.obj_id, + b.obj_attr + ); + + if (b.annotator) { + mesh.annotator = b.annotator; } - this.color_boxes = function() - { - this.boxes.forEach(box=>{ - this.color_box(box); - }) + if (b.follows) mesh.follows = b.follows; + + return mesh; + }; + + this.createBoxes = function (annotations) { + return annotations.map((b) => { + return this.createOneBoxByAnn(b); + }); + }; + + this.box_local_id = 0; + this.get_new_box_local_id = function () { + var ret = this.box_local_id; + this.box_local_id += 1; + return ret; + }; + + this.color_box = function (box) { + if ( + this.data.cfg.color_obj == "category" || + this.data.cfg.color_obj == "no" + ) { + let color = globalObjectCategory.get_color_by_category(box.obj_type); + box.material.color.r = color.x; + box.material.color.g = color.y; + box.material.color.b = color.z; + } else { + let color = globalObjectCategory.get_color_by_id(box.obj_track_id); + box.material.color.r = color.x; + box.material.color.g = color.y; + box.material.color.b = color.z; } -} + }; + this.color_boxes = function () { + this.boxes.forEach((box) => { + this.color_box(box); + }); + }; +} -export{Annotation} \ No newline at end of file +export { Annotation }; diff --git a/public/js/auto-adjust.js b/public/js/auto-adjust.js index 62483c6..ee699ca 100644 --- a/public/js/auto-adjust.js +++ b/public/js/auto-adjust.js @@ -1,463 +1,469 @@ - -import {transpose, matmul2, euler_angle_to_rotate_matrix_3by3,normalizeAngle } from "./util.js"; +import { + transpose, + matmul2, + euler_angle_to_rotate_matrix_3by3, + normalizeAngle, +} from "./util.js"; import { logger } from "./log.js"; // todo: this module needs a proper name -function AutoAdjust(boxOp, mouse, header){ - this.boxOp = boxOp, - this.mouse = mouse; - this.header = header; - var marked_object = null; - - // mark bbox, which will be used as reference-bbox of an object. - this.mark_bbox=function(box){ - if (box){ - this.marked_object = { - frame: box.world.frameInfo.frame, - scene: box.world.frameInfo.scene, - ann: box.world.annotation.boxToAnn(box), - } - - logger.log(`selected reference objcet ${this.marked_object}`); - - this.header.set_ref_obj(this.marked_object); +function AutoAdjust(boxOp, mouse, header) { + (this.boxOp = boxOp), (this.mouse = mouse); + this.header = header; + var marked_object = null; + + // mark bbox, which will be used as reference-bbox of an object. + this.mark_bbox = function (box) { + if (box) { + this.marked_object = { + frame: box.world.frameInfo.frame, + scene: box.world.frameInfo.scene, + ann: box.world.annotation.boxToAnn(box), + }; + + logger.log(`selected reference objcet ${this.marked_object}`); + + this.header.set_ref_obj(this.marked_object); + } + }; + + this.followStaticObjects = function (box) { + let world = box.world; + let staticObjects = world.annotation.boxes + .filter((b) => b != box && b.obj_attr && b.obj_attr.search("static") >= 0) + .map((refObj) => { + let coord = euler_angle_to_rotate_matrix_3by3(refObj.rotation); + let trans = transpose(coord, 3); + let p = [ + box.position.x - refObj.position.x, + box.position.y - refObj.position.y, + box.position.z - refObj.position.z, + ]; + let relativePos = matmul2(trans, p, 3); + let relativeRot = { + x: normalizeAngle(box.rotation.x - refObj.rotation.x), + y: normalizeAngle(box.rotation.y - refObj.rotation.y), + z: normalizeAngle(box.rotation.z - refObj.rotation.z), + }; + + let distance = Math.sqrt( + relativePos[0] * relativePos[0] + + relativePos[1] * relativePos[1] + + relativePos[2] * relativePos[2] + ); + return { + obj_track_id: refObj.obj_track_id, + relativePos, + relativeRot, + distance, + }; + }); + + let worldList = box.world.data.worldList; + //let saveList = []; + worldList.forEach((w) => { + if (w === box.world) { + //current frame + return; + } + + let existedBox = w.annotation.boxes.find( + (b) => b.obj_track_id == box.obj_track_id + ); + if (existedBox && !existedBox.annotator) { + // have same objects annotated. + // if its generated by machine, lets overwrite it + return; + } + + let candPoseSets = staticObjects.map((refObj) => { + let refObjInW = w.annotation.boxes.find( + (b) => b.obj_track_id == refObj.obj_track_id + ); + if (!refObjInW) { + // not found refobj in this world, give up + return null; + } + + let relativePos = refObj.relativePos; + let relativeRot = refObj.relativeRot; + + let coord = euler_angle_to_rotate_matrix_3by3(refObjInW.rotation); + + let rp = matmul2(coord, relativePos, 3); + let newObjPos = { + x: refObjInW.position.x + rp[0], + y: refObjInW.position.y + rp[1], + z: refObjInW.position.z + rp[2], + }; + + let newObjRot = { + x: normalizeAngle(refObjInW.rotation.x + relativeRot.x), + y: normalizeAngle(refObjInW.rotation.y + relativeRot.y), + z: normalizeAngle(refObjInW.rotation.z + relativeRot.z), + }; + + return { + distance: refObj.distance, + weight: Math.exp(-refObj.distance * (refObjInW.annotator ? 1 : 0.1)), + position: newObjPos, + rotation: newObjRot, + }; + }); + + candPoseSets = candPoseSets.filter((p) => !!p); + + if (candPoseSets.length == 0) { + return; + } + + // calculate mean pos/rot + let denorm = candPoseSets.reduce((a, b) => a + b.weight, 0); + + let newObjPos = { x: 0, y: 0, z: 0 }; + let newObjRot = { x: 0, y: 0, z: 0, cosZ: 0, sinZ: 0 }; + candPoseSets.forEach((p) => { + newObjPos.x += p.position.x * p.weight; + newObjPos.y += p.position.y * p.weight; + newObjPos.z += p.position.z * p.weight; + + newObjRot.x += p.rotation.x * p.weight; + newObjRot.y += p.rotation.y * p.weight; + //newObjRot.z += p.rotation.z * p.weight; + newObjRot.cosZ += Math.cos(p.rotation.z) * p.weight; + newObjRot.sinZ += Math.sin(p.rotation.z) * p.weight; + }); + + newObjPos.x /= denorm; + newObjPos.y /= denorm; + newObjPos.z /= denorm; + newObjRot.x /= denorm; + newObjRot.y /= denorm; + newObjRot.cosZ /= denorm; + newObjRot.sinZ /= denorm; + newObjRot.z = Math.atan2(newObjRot.sinZ, newObjRot.cosZ); + + // ignor distant objects + + if (pointsGlobalConfig.ignoreDistantObject) { + let objDistance = Math.sqrt( + newObjPos.x * newObjPos.x + + newObjPos.y * newObjPos.y + + newObjPos.z * newObjPos.z + ); + + if ((box.scale.z < 2 && objDistance > 100) || objDistance > 150) { + return; } - }; - - this.followStaticObjects = function(box) { - let world = box.world; - let staticObjects = world.annotation.boxes. - filter(b=>b!=box && b.obj_attr && b.obj_attr.search('static')>=0). - map(refObj=>{ - let coord = euler_angle_to_rotate_matrix_3by3(refObj.rotation); - let trans = transpose(coord, 3); - let p = [box.position.x - refObj.position.x, - box.position.y - refObj.position.y, - box.position.z - refObj.position.z]; - let relativePos = matmul2(trans, p, 3); - let relativeRot = { - x: normalizeAngle(box.rotation.x - refObj.rotation.x), - y: normalizeAngle(box.rotation.y - refObj.rotation.y), - z: normalizeAngle(box.rotation.z - refObj.rotation.z), - }; - - - let distance = Math.sqrt(relativePos[0]*relativePos[0] + relativePos[1]*relativePos[1] + relativePos[2]*relativePos[2]); - return { - obj_track_id: refObj.obj_track_id, - relativePos, - relativeRot, - distance - - } - }); - - let worldList = box.world.data.worldList; - //let saveList = []; - worldList.forEach(w=>{ - if (w === box.world){ - //current frame - return; - } - - let existedBox = w.annotation.boxes.find(b=>b.obj_track_id == box.obj_track_id); - if (existedBox && !existedBox.annotator) - { - // have same objects annotated. - // if its generated by machine, lets overwrite it - return; - } - - let candPoseSets = staticObjects.map(refObj=>{ - - let refObjInW = w.annotation.boxes.find(b=>b.obj_track_id == refObj.obj_track_id); - if (!refObjInW){ - // not found refobj in this world, give up - return null; - } - - let relativePos = refObj.relativePos; - let relativeRot = refObj.relativeRot; - - let coord = euler_angle_to_rotate_matrix_3by3(refObjInW.rotation); - - let rp = matmul2(coord, relativePos, 3); - let newObjPos = { - x: refObjInW.position.x + rp[0], - y: refObjInW.position.y + rp[1], - z: refObjInW.position.z + rp[2], - }; - - let newObjRot = { - x: normalizeAngle(refObjInW.rotation.x + relativeRot.x), - y: normalizeAngle(refObjInW.rotation.y + relativeRot.y), - z: normalizeAngle(refObjInW.rotation.z + relativeRot.z) - }; - - - - return { - distance: refObj.distance, - weight: Math.exp(-refObj.distance * (refObjInW.annotator?1:0.1)), - position: newObjPos, - rotation: newObjRot, - }; - }); - - candPoseSets = candPoseSets.filter(p=>!!p); - - - if (candPoseSets.length == 0) { - return; - } - - // calculate mean pos/rot - let denorm = candPoseSets.reduce((a,b)=>a+b.weight, 0); - - let newObjPos = {x:0, y:0, z:0}; - let newObjRot = {x:0, y:0, z:0, cosZ: 0, sinZ:0}; - candPoseSets.forEach(p=>{ - newObjPos.x += p.position.x * p.weight; - newObjPos.y += p.position.y * p.weight; - newObjPos.z += p.position.z * p.weight; - - newObjRot.x += p.rotation.x * p.weight; - newObjRot.y += p.rotation.y * p.weight; - //newObjRot.z += p.rotation.z * p.weight; - newObjRot.cosZ += Math.cos(p.rotation.z) * p.weight; - newObjRot.sinZ += Math.sin(p.rotation.z) * p.weight; - }); - - newObjPos.x /= denorm; - newObjPos.y /= denorm; - newObjPos.z /= denorm; - newObjRot.x /= denorm; - newObjRot.y /= denorm; - newObjRot.cosZ /= denorm; - newObjRot.sinZ /= denorm; - newObjRot.z = Math.atan2(newObjRot.sinZ, newObjRot.cosZ); - - - // ignor distant objects - - if (pointsGlobalConfig.ignoreDistantObject){ - let objDistance = Math.sqrt(newObjPos.x * newObjPos.x + newObjPos.y * newObjPos.y + newObjPos.z * newObjPos.z); - - if ((box.scale.z < 2 && objDistance > 100) || objDistance > 150) - { - return; - } - } - - // apply - if (existedBox){ - existedBox.position.x = newObjPos.x; - existedBox.position.y = newObjPos.y; - existedBox.position.z = newObjPos.z; - - existedBox.rotation.x = newObjRot.x; - existedBox.rotation.y = newObjRot.y; - existedBox.rotation.z = newObjRot.z; - - existedBox.scale.x = box.scale.x; - existedBox.scale.y = box.scale.y; - existedBox.scale.z = box.scale.z; - - existedBox.annotator="S"; - - - logger.log(`modified box in ${w}`); - } else{ - let newBox = w.annotation.add_box(newObjPos, - box.scale, - newObjRot, - box.obj_type, - box.obj_track_id, - box.obj_attr); - newBox.annotator="S"; - - w.annotation.load_box(newBox); - logger.log(`inserted box in ${w}`); - } - - console.log("added box in ", w.frameInfo.frame); - //saveList.push(w); - w.annotation.setModified(); - - - }); - - }; - - this.followsRef = function(box){ - //find ref object in current frame - let world = box.world; - let refObj = world.annotation.boxes.find(b=>b.obj_track_id == this.marked_object.ann.obj_id); - if (refObj){ - console.log("found ref obj in current frame"); - world.annotation.setModified() - - //compute relative position - // represent obj in coordinate system of refobj - - let coord = euler_angle_to_rotate_matrix_3by3(refObj.rotation); - let trans = transpose(coord, 3); - let p = [box.position.x - refObj.position.x, - box.position.y - refObj.position.y, - box.position.z - refObj.position.z]; - const relativePos = matmul2(trans, p, 3); - const relativeRot = { - x: box.rotation.x - refObj.rotation.x, - y: box.rotation.y - refObj.rotation.y, - z: box.rotation.z - refObj.rotation.z, - }; - - let worldList = box.world.data.worldList; - //let saveList = []; - worldList.forEach(w=>{ - if (w === box.world){ - //current frame - return; - } - - let existedBox = w.annotation.boxes.find(b=>b.obj_track_id == box.obj_track_id); - - if (existedBox && !existedBox.annotator) - { - // have same objects annotated. - // if its generated by machine, lets overwrite it - return; - } - - let refObjInW = w.annotation.boxes.find(b=>b.obj_track_id == refObj.obj_track_id); - if (!refObjInW){ - // not found refobj in this world, give up - return; - } - - let coord = euler_angle_to_rotate_matrix_3by3(refObjInW.rotation); - - let rp = matmul2(coord, relativePos, 3); - let newObjPos = { - x: refObjInW.position.x + rp[0], - y: refObjInW.position.y + rp[1], - z: refObjInW.position.z + rp[2], - }; - - let newObjRot = { - x: refObjInW.rotation.x + relativeRot.x, - y: refObjInW.rotation.y + relativeRot.y, - z: refObjInW.rotation.z + relativeRot.z - }; - - if (existedBox){ - existedBox.position.x = newObjPos.x; - existedBox.position.y = newObjPos.y; - existedBox.position.z = newObjPos.z; - - existedBox.rotation.x = newObjRot.x; - existedBox.rotation.y = newObjRot.y; - existedBox.rotation.z = newObjRot.z; - - existedBox.scale.x = box.scale.x; - existedBox.scale.y = box.scale.y; - existedBox.scale.z = box.scale.z; - - existedBox.annotator="F"; - existedBox.follows = { - obj_track_id: refObj.obj_track_id, - relative_position: { - x: relativePos[0], - y: relativePos[1], - z: relativePos[2], - }, - relative_rotation: relativeRot, - }; - - logger.log(`modified box in ${w}`); - } else{ - let newBox = w.annotation.add_box(newObjPos, - box.scale, - newObjRot, - box.obj_type, - box.obj_track_id, - box.obj_attr); - newBox.annotator="F"; - newBox.follows = { - obj_track_id: refObj.obj_track_id, - relative_position: { - x: relativePos[0], - y: relativePos[1], - z: relativePos[2], - }, - relative_rotation: relativeRot, - }; - - w.annotation.load_box(newBox); - logger.log(`inserted box in ${w}`); - } - - console.log("added box in ", w.frameInfo.frame); - //saveList.push(w); - w.annotation.setModified(); - }); - - //saveWorldList(saveList); + } + + // apply + if (existedBox) { + existedBox.position.x = newObjPos.x; + existedBox.position.y = newObjPos.y; + existedBox.position.z = newObjPos.z; + + existedBox.rotation.x = newObjRot.x; + existedBox.rotation.y = newObjRot.y; + existedBox.rotation.z = newObjRot.z; + + existedBox.scale.x = box.scale.x; + existedBox.scale.y = box.scale.y; + existedBox.scale.z = box.scale.z; + + existedBox.annotator = "S"; + + logger.log(`modified box in ${w}`); + } else { + let newBox = w.annotation.add_box( + newObjPos, + box.scale, + newObjRot, + box.obj_type, + box.obj_track_id, + box.obj_attr + ); + newBox.annotator = "S"; + + w.annotation.load_box(newBox); + logger.log(`inserted box in ${w}`); + } + + console.log("added box in ", w.frameInfo.frame); + //saveList.push(w); + w.annotation.setModified(); + }); + }; + + this.followsRef = function (box) { + //find ref object in current frame + let world = box.world; + let refObj = world.annotation.boxes.find( + (b) => b.obj_track_id == this.marked_object.ann.obj_id + ); + if (refObj) { + console.log("found ref obj in current frame"); + world.annotation.setModified(); + + //compute relative position + // represent obj in coordinate system of refobj + + let coord = euler_angle_to_rotate_matrix_3by3(refObj.rotation); + let trans = transpose(coord, 3); + let p = [ + box.position.x - refObj.position.x, + box.position.y - refObj.position.y, + box.position.z - refObj.position.z, + ]; + const relativePos = matmul2(trans, p, 3); + const relativeRot = { + x: box.rotation.x - refObj.rotation.x, + y: box.rotation.y - refObj.rotation.y, + z: box.rotation.z - refObj.rotation.z, + }; + + let worldList = box.world.data.worldList; + //let saveList = []; + worldList.forEach((w) => { + if (w === box.world) { + //current frame + return; } - }; - this.syncFollowers = function(box){ - let world = box.world; - let allFollowers = world.annotation.boxes.filter(b=>b.follows && b.follows.obj_track_id === box.obj_track_id); + let existedBox = w.annotation.boxes.find( + (b) => b.obj_track_id == box.obj_track_id + ); - if (allFollowers.length == 0){ - console.log("no followers"); - return; + if (existedBox && !existedBox.annotator) { + // have same objects annotated. + // if its generated by machine, lets overwrite it + return; } - let refObj = box; - let coord = euler_angle_to_rotate_matrix_3by3(refObj.rotation); - - - allFollowers.forEach(fb=>{ - let relpos = [fb.follows.relative_position.x, - fb.follows.relative_position.y, - fb.follows.relative_position.z, - ]; - - let rp = matmul2(coord, relpos, 3); - - fb.position.x = refObj.position.x + rp[0]; - fb.position.y = refObj.position.y + rp[1]; - fb.position.z = refObj.position.z + rp[2]; - - fb.rotation.x = refObj.rotation.x + fb.follows.relative_rotation.x; - fb.rotation.y = refObj.rotation.y + fb.follows.relative_rotation.y; - fb.rotation.z = refObj.rotation.z + fb.follows.relative_rotation.z; - }); - }; - - this.paste_bbox=function(pos, add_box){ - - if (!pos) - pos = this.marked_object.ann.psr.position; - else - pos.z = this.marked_object.ann.psr.position.z; - - return add_box(pos, this.marked_object.ann.psr.scale, this.marked_object.ann.psr.rotation, - this.marked_object.ann.obj_type, this.marked_object.ann.obj_id, this.marked_object.ann.obj_attr); - }; - - - // this.auto_adjust_bbox=function(box, done, on_box_changed){ - - // saveWorld(function(){ - // do_adjust(box, on_box_changed); - // }); - // let _self =this; - // function do_adjust(box, on_box_changed){ - // console.log("auto adjust highlighted bbox"); - - // var xhr = new XMLHttpRequest(); - // // we defined the xhr - - // xhr.onreadystatechange = function () { - // if (this.readyState != 4) return; - - // if (this.status == 200) { - // console.log(this.responseText) - // console.log(box.position); - // console.log(box.rotation); - - - // var trans_mat = JSON.parse(this.responseText); - - // var rotation = Math.atan2(trans_mat[4], trans_mat[0]) + box.rotation.z; - // var transform = { - // x: -trans_mat[3], - // y: -trans_mat[7], - // z: -trans_mat[11], - // } - - - - // /* - // cos sin x - // -sin cos y - // */ - // var new_pos = { - // x: Math.cos(-rotation) * transform.x + Math.sin(-rotation) * transform.y, - // y: -Math.sin(-rotation) * transform.x + Math.cos(-rotation) * transform.y, - // z: transform.z, - // }; - - - // box.position.x += new_pos.x; - // box.position.y += new_pos.y; - // box.position.z += new_pos.z; - - - - // box.scale.x = marked_object.scale.x; - // box.scale.y = marked_object.scale.y; - // box.scale.z = marked_object.scale.z; - - // box.rotation.z -= Math.atan2(trans_mat[4], trans_mat[0]); - - // console.log(box.position); - // console.log(box.rotation); - - // on_box_changed(box); - - // _self.header.mark_changed_flag(); - - // if (done){ - // done(); - // } - // } - - // // end of state change: it can be after some time (async) - // }; - - // xhr.open('GET', - // "/auto_adjust"+"?scene="+marked_object.scene + "&"+ - // "ref_frame=" + marked_object.frame + "&" + - // "object_id=" + marked_object.obj_track_id + "&" + - // "adj_frame=" + data.world.frameInfo.frame, - // true); - // xhr.send(); - // } - // }; - - this.smart_paste=function(selected_box, add_box, on_box_changed){ - var box = selected_box; - if (!box){ - let sceneP = this.mouse.get_mouse_location_in_world() - // trans pos to world local pos - //let pos = this.data.world.scenePosToLidar(sceneP); - box = this.paste_bbox(pos, add_box); + let refObjInW = w.annotation.boxes.find( + (b) => b.obj_track_id == refObj.obj_track_id + ); + if (!refObjInW) { + // not found refobj in this world, give up + return; } - else if (this.marked_object){ - box.scale.x = this.marked_object.ann.psr.scale.x; - box.scale.y = this.marked_object.ann.psr.scale.y; - box.scale.z = this.marked_object.ann.psr.scale.z; + + let coord = euler_angle_to_rotate_matrix_3by3(refObjInW.rotation); + + let rp = matmul2(coord, relativePos, 3); + let newObjPos = { + x: refObjInW.position.x + rp[0], + y: refObjInW.position.y + rp[1], + z: refObjInW.position.z + rp[2], + }; + + let newObjRot = { + x: refObjInW.rotation.x + relativeRot.x, + y: refObjInW.rotation.y + relativeRot.y, + z: refObjInW.rotation.z + relativeRot.z, + }; + + if (existedBox) { + existedBox.position.x = newObjPos.x; + existedBox.position.y = newObjPos.y; + existedBox.position.z = newObjPos.z; + + existedBox.rotation.x = newObjRot.x; + existedBox.rotation.y = newObjRot.y; + existedBox.rotation.z = newObjRot.z; + + existedBox.scale.x = box.scale.x; + existedBox.scale.y = box.scale.y; + existedBox.scale.z = box.scale.z; + + existedBox.annotator = "F"; + existedBox.follows = { + obj_track_id: refObj.obj_track_id, + relative_position: { + x: relativePos[0], + y: relativePos[1], + z: relativePos[2], + }, + relative_rotation: relativeRot, + }; + + logger.log(`modified box in ${w}`); + } else { + let newBox = w.annotation.add_box( + newObjPos, + box.scale, + newObjRot, + box.obj_type, + box.obj_track_id, + box.obj_attr + ); + newBox.annotator = "F"; + newBox.follows = { + obj_track_id: refObj.obj_track_id, + relative_position: { + x: relativePos[0], + y: relativePos[1], + z: relativePos[2], + }, + relative_rotation: relativeRot, + }; + + w.annotation.load_box(newBox); + logger.log(`inserted box in ${w}`); } - - // this.auto_adjust_bbox(box, - // function(){saveWorld();}, - // on_box_changed); - - // this.header.mark_changed_flag(); - - - - this.boxOp.auto_rotate_xyz(box, null, null, - on_box_changed, - "noscaling"); - }; - -} + console.log("added box in ", w.frameInfo.frame); + //saveList.push(w); + w.annotation.setModified(); + }); + + //saveWorldList(saveList); + } + }; + + this.syncFollowers = function (box) { + let world = box.world; + let allFollowers = world.annotation.boxes.filter( + (b) => b.follows && b.follows.obj_track_id === box.obj_track_id + ); + + if (allFollowers.length == 0) { + console.log("no followers"); + return; + } + + let refObj = box; + let coord = euler_angle_to_rotate_matrix_3by3(refObj.rotation); + + allFollowers.forEach((fb) => { + let relpos = [ + fb.follows.relative_position.x, + fb.follows.relative_position.y, + fb.follows.relative_position.z, + ]; + + let rp = matmul2(coord, relpos, 3); + + fb.position.x = refObj.position.x + rp[0]; + fb.position.y = refObj.position.y + rp[1]; + fb.position.z = refObj.position.z + rp[2]; + + fb.rotation.x = refObj.rotation.x + fb.follows.relative_rotation.x; + fb.rotation.y = refObj.rotation.y + fb.follows.relative_rotation.y; + fb.rotation.z = refObj.rotation.z + fb.follows.relative_rotation.z; + }); + }; + + this.paste_bbox = function (pos, add_box) { + if (!pos) pos = this.marked_object.ann.psr.position; + else pos.z = this.marked_object.ann.psr.position.z; + + return add_box( + pos, + this.marked_object.ann.psr.scale, + this.marked_object.ann.psr.rotation, + this.marked_object.ann.obj_type, + this.marked_object.ann.obj_id, + this.marked_object.ann.obj_attr + ); + }; + + // this.auto_adjust_bbox=function(box, done, on_box_changed){ + + // saveWorld(function(){ + // do_adjust(box, on_box_changed); + // }); + // let _self =this; + // function do_adjust(box, on_box_changed){ + // console.log("auto adjust highlighted bbox"); + + // var xhr = new XMLHttpRequest(); + // // we defined the xhr + + // xhr.onreadystatechange = function () { + // if (this.readyState != 4) return; + + // if (this.status == 200) { + // console.log(this.responseText) + // console.log(box.position); + // console.log(box.rotation); + + // var trans_mat = JSON.parse(this.responseText); + + // var rotation = Math.atan2(trans_mat[4], trans_mat[0]) + box.rotation.z; + // var transform = { + // x: -trans_mat[3], + // y: -trans_mat[7], + // z: -trans_mat[11], + // } + + // /* + // cos sin x + // -sin cos y + // */ + // var new_pos = { + // x: Math.cos(-rotation) * transform.x + Math.sin(-rotation) * transform.y, + // y: -Math.sin(-rotation) * transform.x + Math.cos(-rotation) * transform.y, + // z: transform.z, + // }; + + // box.position.x += new_pos.x; + // box.position.y += new_pos.y; + // box.position.z += new_pos.z; + + // box.scale.x = marked_object.scale.x; + // box.scale.y = marked_object.scale.y; + // box.scale.z = marked_object.scale.z; + + // box.rotation.z -= Math.atan2(trans_mat[4], trans_mat[0]); + + // console.log(box.position); + // console.log(box.rotation); + + // on_box_changed(box); + + // _self.header.mark_changed_flag(); + + // if (done){ + // done(); + // } + // } + + // // end of state change: it can be after some time (async) + // }; + + // xhr.open('GET', + // "/auto_adjust"+"?scene="+marked_object.scene + "&"+ + // "ref_frame=" + marked_object.frame + "&" + + // "object_id=" + marked_object.obj_track_id + "&" + + // "adj_frame=" + data.world.frameInfo.frame, + // true); + // xhr.send(); + // } + // }; + + this.smart_paste = function (selected_box, add_box, on_box_changed) { + var box = selected_box; + if (!box) { + let sceneP = this.mouse.get_mouse_location_in_world(); + // trans pos to world local pos + //let pos = this.data.world.scenePosToLidar(sceneP); + box = this.paste_bbox(pos, add_box); + } else if (this.marked_object) { + box.scale.x = this.marked_object.ann.psr.scale.x; + box.scale.y = this.marked_object.ann.psr.scale.y; + box.scale.z = this.marked_object.ann.psr.scale.z; + } + + // this.auto_adjust_bbox(box, + // function(){saveWorld();}, + // on_box_changed); + + // this.header.mark_changed_flag(); + + this.boxOp.auto_rotate_xyz(box, null, null, on_box_changed, "noscaling"); + }; +} -export {AutoAdjust} \ No newline at end of file +export { AutoAdjust }; diff --git a/public/js/auto_annotate.js b/public/js/auto_annotate.js index 8f51f82..a8cc208 100644 --- a/public/js/auto_annotate.js +++ b/public/js/auto_annotate.js @@ -1,30 +1,39 @@ import { globalObjectCategory } from "./obj_cfg.js"; +function autoAnnotate(world, done, alg) { + var xhr = new XMLHttpRequest(); + // we defined the xhr + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + if (this.status == 200) { + let anns = JSON.parse(this.responseText); -function autoAnnotate(world, done, alg){ - var xhr = new XMLHttpRequest(); - // we defined the xhr - xhr.onreadystatechange = function () { - if (this.readyState != 4) return; - - if (this.status == 200) { - let anns = JSON.parse(this.responseText); - - anns.map(a=>a.obj_type = globalObjectCategory.guess_obj_type_by_dimension(a.psr.scale)); - - // load annotations - world.annotation.reapplyAnnotation(anns); - - if (done) - done(); - } - }; - - xhr.open('GET', "/auto_annotate?"+"scene="+world.frameInfo.scene+"&frame="+world.frameInfo.frame, true); - - xhr.send(); -} + anns.map( + (a) => + (a.obj_type = globalObjectCategory.guess_obj_type_by_dimension( + a.psr.scale + )) + ); + + // load annotations + world.annotation.reapplyAnnotation(anns); + + if (done) done(); + } + }; + xhr.open( + "GET", + "/auto_annotate?" + + "scene=" + + world.frameInfo.scene + + "&frame=" + + world.frameInfo.frame, + true + ); + + xhr.send(); +} -export {autoAnnotate} \ No newline at end of file +export { autoAnnotate }; diff --git a/public/js/aux_lidar.js b/public/js/aux_lidar.js index 570a362..e9f91b2 100644 --- a/public/js/aux_lidar.js +++ b/public/js/aux_lidar.js @@ -1,403 +1,387 @@ -import * as THREE from './lib/three.module.js'; -import { PCDLoader } from './lib/PCDLoader.js'; -import { matmul, euler_angle_to_rotate_matrix_3by3} from "./util.js" - +import * as THREE from "./lib/three.module.js"; +import { PCDLoader } from "./lib/PCDLoader.js"; +import { matmul, euler_angle_to_rotate_matrix_3by3 } from "./util.js"; //todo: clean arrows -function AuxLidar(sceneMeta, world, frameInfo, auxLidarName){ - this.world = world; - this.frameInfo = frameInfo; - this.name = auxLidarName; - this.sceneMeta = sceneMeta; - this.coordinatesOffset = world.coordinatesOffset; - - this.showPointsOnly = true; - this.showCalibBox = false; - //this.cssStyleSelector = this.sceneMeta.calib.aux_lidar[this.name].cssstyleselector; - this.color = this.sceneMeta.calib.aux_lidar[this.name].color; - +function AuxLidar(sceneMeta, world, frameInfo, auxLidarName) { + this.world = world; + this.frameInfo = frameInfo; + this.name = auxLidarName; + this.sceneMeta = sceneMeta; + this.coordinatesOffset = world.coordinatesOffset; + + this.showPointsOnly = true; + this.showCalibBox = false; + //this.cssStyleSelector = this.sceneMeta.calib.aux_lidar[this.name].cssstyleselector; + this.color = this.sceneMeta.calib.aux_lidar[this.name].color; + + if (!this.color) { + this.color = [ + this.world.data.cfg.point_brightness, + this.world.data.cfg.point_brightness, + this.world.data.cfg.point_brightness, + ]; + } + + this.lidar_points = null; // read from file, centered at 0 + this.elements = null; // geometry points + + this.preloaded = false; + this.loaded = false; + + this.go_cmd_received = false; + this.webglScene = null; + this.on_go_finished = null; + this.go = function (webglScene, on_go_finished) { + this.webglScene = webglScene; + + if (this.preloaded) { + if (this.elements) { + this.webglScene.add(this.elements.points); + + if (this.showCalibBox) this.webglScene.add(this.calib_box); + } + + this.loaded = true; + if (on_go_finished) on_go_finished(); + } - if (!this.color) + //anyway we save go cmd { - this.color = [ - this.world.data.cfg.point_brightness, - this.world.data.cfg.point_brightness, - this.world.data.cfg.point_brightness, - ]; + this.go_cmd_received = true; + this.on_go_finished = on_go_finished; } + }; - this.lidar_points = null; // read from file, centered at 0 - this.elements = null; // geometry points - - this.preloaded = false; - this.loaded = false; + this.showCalibBox = function () { + this.showCalibBox = true; + this.webglScene.add(this.calib_box); + }; + this.hideCalibBox = function () { + this.showCalibBox = false; + this.webglScene.remove(this.calib_box); + }; + + this.get_unoffset_lidar_points = function () { + if (this.elements) { + let pts = this.elements.points.geometry.getAttribute("position").array; + return pts.map((p, i) => p - this.world.coordinatesOffset[i % 3]); + } else { + return []; + } + }; - this.go_cmd_received = false; - this.webglScene = null; - this.on_go_finished = null; - this.go = function(webglScene, on_go_finished){ - this.webglScene = webglScene; - - if (this.preloaded){ - if (this.elements){ - this.webglScene.add(this.elements.points); - - - - if (this.showCalibBox) - this.webglScene.add(this.calib_box); - } - - this.loaded = true; - if (on_go_finished) - on_go_finished(); - } - - //anyway we save go cmd - { - this.go_cmd_received = true; - this.on_go_finished = on_go_finished; - } - }; + // todo: what if it's not preloaded yet + this.unload = function (keep_box) { + if (this.elements) { + this.webglScene.remove(this.elements.points); + if (!this.showPointsOnly) + this.elements.arrows.forEach((a) => this.webglScene.remove(a)); - this.showCalibBox = function(){ - this.showCalibBox = true; - this.webglScene.add(this.calib_box); - }; + if (!keep_box) this.webglScene.remove(this.calib_box); + } + this.loaded = false; + }; - this.hideCalibBox = function(){ - this.showCalibBox = false; - this.webglScene.remove(this.calib_box); - }; + // todo: its possible to remove points before preloading, + this.deleteAll = function (keep_box) { + if (this.loaded) { + this.unload(); + } - this.get_unoffset_lidar_points = function(){ - if (this.elements){ - let pts = this.elements.points.geometry.getAttribute("position").array; - return pts.map((p,i)=>p-this.world.coordinatesOffset[i %3]); - } - else{ - return []; - } - }; + if (this.elements) { + //this.scene.remove(this.points); + this.world.data.dbg.free(); - // todo: what if it's not preloaded yet - this.unload = function(keep_box){ - if (this.elements){ - this.webglScene.remove(this.elements.points); - if (!this.showPointsOnly) - this.elements.arrows.forEach(a=>this.webglScene.remove(a)); - - if (!keep_box) - this.webglScene.remove(this.calib_box); - } - this.loaded = false; - }; + if (this.elements.points) { + this.elements.points.geometry.dispose(); + this.elements.points.material.dispose(); + } - // todo: its possible to remove points before preloading, - this.deleteAll = function(keep_box){ - if (this.loaded){ - this.unload(); - } + if (this.elements.arrows) { + this.elements.arrows.forEach((a) => { + this.world.data.dbg.free(); + a.geometry.dispose(); + a.material.dispose(); + }); + } - if (this.elements){ - //this.scene.remove(this.points); - this.world.data.dbg.free(); - - if (this.elements.points) - { - this.elements.points.geometry.dispose(); - this.elements.points.material.dispose(); - } - - if (this.elements.arrows) - { - this.elements.arrows.forEach(a=>{ - this.world.data.dbg.free(); - a.geometry.dispose(); - a.material.dispose(); - }) - } - - this.elements = null; - } + this.elements = null; + } - if (!keep_box && this.calib_box){ - this.world.data.dbg.free(); - this.calib_box.geometry.dispose(); - this.calib_box.material.dispose(); - this.calib_box = null; - } - }; + if (!keep_box && this.calib_box) { + this.world.data.dbg.free(); + this.calib_box.geometry.dispose(); + this.calib_box.material.dispose(); + this.calib_box = null; + } + }; - this.filterPoints = function(position){ - let filtered_position = []; + this.filterPoints = function (position) { + let filtered_position = []; - if (pointsGlobalConfig.enableFilterPoints) - { - for(let i = 0; i <= position.length; i+=3) - { - if (position[i+2] <= pointsGlobalConfig.filterPointsZ) - { - filtered_position.push(position[i]); - filtered_position.push(position[i+1]); - filtered_position.push(position[i+2]); - - } - } + if (pointsGlobalConfig.enableFilterPoints) { + for (let i = 0; i <= position.length; i += 3) { + if (position[i + 2] <= pointsGlobalConfig.filterPointsZ) { + filtered_position.push(position[i]); + filtered_position.push(position[i + 1]); + filtered_position.push(position[i + 2]); } + } + } - return filtered_position; - }; + return filtered_position; + }; - this.preload = function(on_preload_finished){ - - this.on_preload_finished = on_preload_finished; - - var loader = new PCDLoader(); - - var _self = this; - loader.load( this.frameInfo.get_aux_lidar_path(this.name), - //ok - function ( pcd ) { - var position = pcd.position; - - - //_self.points_parse_time = new Date().getTime(); - //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "parse pionts ", _self.points_parse_time - _self.create_time, "ms"); - _self.lidar_points = position; - - // add one box to calibrate lidar with lidar - _self.calib_box = _self.createCalibBox(); - - // install callback for box changing - _self.calib_box.on_box_changed = ()=>{ - _self.move_lidar(_self.calib_box); - }; - - //position = _self.transformPointsByOffset(position); - position = _self.move_points(_self.calib_box); - - - - let elements = _self.buildGeometry(position); - - _self.elements = elements; - //_self.points_backup = mesh; - - _self._afterPreload(); - - }, - - // on progress, - function(){}, - - // on error - function(){ - //error - console.log("load lidar failed."); - _self._afterPreload(); - }, - - // on file loaded - function(){ - //_self.points_readfile_time = new Date().getTime(); - //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "read file ", _self.points_readfile_time - _self.create_time, "ms"); - } - ); - }; + this.preload = function (on_preload_finished) { + this.on_preload_finished = on_preload_finished; - // internal funcs below - this._afterPreload = function(){ - this.preloaded = true; - console.log(`lidar ${this.auxLidarName} preloaded`); - if (this.on_preload_finished){ - this.on_preload_finished(); - } - if (this.go_cmd_received){ - this.go(this.webglScene, this.on_go_finished); - } - }; + var loader = new PCDLoader(); - this.createCalibBox = function(){ - if (this.sceneMeta.calib.aux_lidar && this.sceneMeta.calib.aux_lidar[this.name]){ - return this.world.annotation.createCuboid( - { - x: this.sceneMeta.calib.aux_lidar[this.name].translation[0] + this.coordinatesOffset[0], - y: this.sceneMeta.calib.aux_lidar[this.name].translation[1] + this.coordinatesOffset[1], - z: this.sceneMeta.calib.aux_lidar[this.name].translation[2] + this.coordinatesOffset[2], - }, - {x:0.5, y:0.5, z:0.5}, - { - x: this.sceneMeta.calib.aux_lidar[this.name].rotation[0], - y: this.sceneMeta.calib.aux_lidar[this.name].rotation[1], - z: this.sceneMeta.calib.aux_lidar[this.name].rotation[2], - }, - "lidar", - this.name); - - }else { - return this.world.annotation.createCuboid( - {x: this.coordinatesOffset[0], - y: this.coordinatesOffset[1], - z: this.coordinatesOffset[2]}, - {x:0.5, y:0.5, z:0.5}, - {x:0,y:0,z:0}, - "lidar", - this.name); - } - }; + var _self = this; + loader.load( + this.frameInfo.get_aux_lidar_path(this.name), + //ok + function (pcd) { + var position = pcd.position; - this.buildPoints = function(position){ - // build geometry - this.world.data.dbg.alloc(); - let geometry = new THREE.BufferGeometry(); - if ( position.length > 0 ) - geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) ); - - - let pointColor = this.color; - let color=[]; - for (var i =0; i< position.length; i+=3){ - - color.push(pointColor[0]); - color.push(pointColor[1]); - color.push(pointColor[2]); - } + //_self.points_parse_time = new Date().getTime(); + //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "parse pionts ", _self.points_parse_time - _self.create_time, "ms"); + _self.lidar_points = position; - geometry.addAttribute( 'color', new THREE.Float32BufferAttribute(color, 3 ) ); + // add one box to calibrate lidar with lidar + _self.calib_box = _self.createCalibBox(); - geometry.computeBoundingSphere(); - - // build material - let pointSize = this.sceneMeta.calib.aux_lidar[this.name].point_size; - if (!pointSize) - pointSize = 1; - - let material = new THREE.PointsMaterial( { size: pointSize, vertexColors: THREE.VertexColors } ); - //material.size = 2; - material.sizeAttenuation = false; + // install callback for box changing + _self.calib_box.on_box_changed = () => { + _self.move_lidar(_self.calib_box); + }; - // build mesh - let mesh = new THREE.Points( geometry, material ); - mesh.name = "lidar"; + //position = _self.transformPointsByOffset(position); + position = _self.move_points(_self.calib_box); - return mesh; - }; + let elements = _self.buildGeometry(position); + _self.elements = elements; + //_self.points_backup = mesh; - this.buildGeometry = function(position){ - let points = this.buildPoints(position); - - return { - points: points, - }; - }; + _self._afterPreload(); + }, + + // on progress, + function () {}, + + // on error + function () { + //error + console.log("load lidar failed."); + _self._afterPreload(); + }, + + // on file loaded + function () { + //_self.points_readfile_time = new Date().getTime(); + //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "read file ", _self.points_readfile_time - _self.create_time, "ms"); + } + ); + }; + + // internal funcs below + this._afterPreload = function () { + this.preloaded = true; + console.log(`lidar ${this.auxLidarName} preloaded`); + if (this.on_preload_finished) { + this.on_preload_finished(); + } + if (this.go_cmd_received) { + this.go(this.webglScene, this.on_go_finished); + } + }; + + this.createCalibBox = function () { + if ( + this.sceneMeta.calib.aux_lidar && + this.sceneMeta.calib.aux_lidar[this.name] + ) { + return this.world.annotation.createCuboid( + { + x: + this.sceneMeta.calib.aux_lidar[this.name].translation[0] + + this.coordinatesOffset[0], + y: + this.sceneMeta.calib.aux_lidar[this.name].translation[1] + + this.coordinatesOffset[1], + z: + this.sceneMeta.calib.aux_lidar[this.name].translation[2] + + this.coordinatesOffset[2], + }, + { x: 0.5, y: 0.5, z: 0.5 }, + { + x: this.sceneMeta.calib.aux_lidar[this.name].rotation[0], + y: this.sceneMeta.calib.aux_lidar[this.name].rotation[1], + z: this.sceneMeta.calib.aux_lidar[this.name].rotation[2], + }, + "lidar", + this.name + ); + } else { + return this.world.annotation.createCuboid( + { + x: this.coordinatesOffset[0], + y: this.coordinatesOffset[1], + z: this.coordinatesOffset[2], + }, + { x: 0.5, y: 0.5, z: 0.5 }, + { x: 0, y: 0, z: 0 }, + "lidar", + this.name + ); + } + }; + + this.buildPoints = function (position) { + // build geometry + this.world.data.dbg.alloc(); + let geometry = new THREE.BufferGeometry(); + if (position.length > 0) + geometry.addAttribute( + "position", + new THREE.Float32BufferAttribute(position, 3) + ); + + let pointColor = this.color; + let color = []; + for (var i = 0; i < position.length; i += 3) { + color.push(pointColor[0]); + color.push(pointColor[1]); + color.push(pointColor[2]); + } - this.move_points = function(box){ - let points = this.lidar_points; - let trans = euler_angle_to_rotate_matrix_3by3(box.rotation); - let rotated_points = matmul(trans, points, 3); - let translation=[box.position.x, box.position.y, box.position.z]; - let translated_points = rotated_points.map((p,i)=>{ - return p + translation[i % 3]; - }); + geometry.addAttribute("color", new THREE.Float32BufferAttribute(color, 3)); - let filtered_position = this.filterPoints(translated_points); - return filtered_position; - }; + geometry.computeBoundingSphere(); + // build material + let pointSize = this.sceneMeta.calib.aux_lidar[this.name].point_size; + if (!pointSize) pointSize = 1; + let material = new THREE.PointsMaterial({ + size: pointSize, + vertexColors: THREE.VertexColors, + }); + //material.size = 2; + material.sizeAttenuation = false; - this.move_lidar= function(box){ + // build mesh + let mesh = new THREE.Points(geometry, material); + mesh.name = "lidar"; - let translated_points = this.move_points(box); + return mesh; + }; - let elements = this.buildGeometry(translated_points); - - // remove old points - this.unload(true); - this.deleteAll(true); + this.buildGeometry = function (position) { + let points = this.buildPoints(position); - this.elements = elements; - //_self.points_backup = mesh; - if (this.go_cmd_received) // this should be always true - { - this.webglScene.add(this.elements.points); - if (!this.showPointsOnly) - this.elements.arrows.forEach(a=>this.webglScene.add(a)); - } + return { + points: points, }; + }; + + this.move_points = function (box) { + let points = this.lidar_points; + let trans = euler_angle_to_rotate_matrix_3by3(box.rotation); + let rotated_points = matmul(trans, points, 3); + let translation = [box.position.x, box.position.y, box.position.z]; + let translated_points = rotated_points.map((p, i) => { + return p + translation[i % 3]; + }); + + let filtered_position = this.filterPoints(translated_points); + return filtered_position; + }; + + this.move_lidar = function (box) { + let translated_points = this.move_points(box); + + let elements = this.buildGeometry(translated_points); + + // remove old points + this.unload(true); + this.deleteAll(true); + + this.elements = elements; + //_self.points_backup = mesh; + if (this.go_cmd_received) { + // this should be always true + this.webglScene.add(this.elements.points); + if (!this.showPointsOnly) + this.elements.arrows.forEach((a) => this.webglScene.add(a)); + } + }; } -function AuxLidarManager(sceneMeta, world, frameInfo){ - this.lidarList = []; +function AuxLidarManager(sceneMeta, world, frameInfo) { + this.lidarList = []; - if (world.data.cfg.enableAuxLidar && sceneMeta.aux_lidar){ - let lidars = []; - - for (let r in sceneMeta.calib.aux_lidar){ - if (!sceneMeta.calib.aux_lidar[r].disable) - lidars.push(r); - } + if (world.data.cfg.enableAuxLidar && sceneMeta.aux_lidar) { + let lidars = []; - this.lidarList = lidars.map(name=>{ - return new AuxLidar(sceneMeta, world, frameInfo, name); - }); + for (let r in sceneMeta.calib.aux_lidar) { + if (!sceneMeta.calib.aux_lidar[r].disable) lidars.push(r); } - this.getAllBoxes = function() - { - if (this.showCalibBox) - { - return this.lidarList.map(r=>r.calib_box); - } - else - { - return []; - } - }; + this.lidarList = lidars.map((name) => { + return new AuxLidar(sceneMeta, world, frameInfo, name); + }); + } - this.preloaded = function(){ - for (let r in this.lidarList){ - if (!this.lidarList[r].preloaded) - return false; - } - return true; - }; + this.getAllBoxes = function () { + if (this.showCalibBox) { + return this.lidarList.map((r) => r.calib_box); + } else { + return []; + } + }; - this.go = function(webglScene, on_go_finished){ - this.lidarList.forEach(r=>r.go(webglScene, on_go_finished)); - }; + this.preloaded = function () { + for (let r in this.lidarList) { + if (!this.lidarList[r].preloaded) return false; + } + return true; + }; - this.preload = function(on_preload_finished){ - this.lidarList.forEach(r=>r.preload(on_preload_finished)); - }; + this.go = function (webglScene, on_go_finished) { + this.lidarList.forEach((r) => r.go(webglScene, on_go_finished)); + }; - this.unload = function(){ - this.lidarList.forEach(r=>r.unload()); - }; + this.preload = function (on_preload_finished) { + this.lidarList.forEach((r) => r.preload(on_preload_finished)); + }; - this.deleteAll = function(){ - this.lidarList.forEach(r=>r.deleteAll()); - }; + this.unload = function () { + this.lidarList.forEach((r) => r.unload()); + }; - this.getOperableObjects = function(){ - return this.lidarList.flatMap(r=>r.getOperableObjects()); - }; + this.deleteAll = function () { + this.lidarList.forEach((r) => r.deleteAll()); + }; - this.showCalibBox = false; - this.showCalibBox = function(){ - this.showCalibBox = true; - this.lidarList.forEach(r=>r.showCalibBox()); - }; + this.getOperableObjects = function () { + return this.lidarList.flatMap((r) => r.getOperableObjects()); + }; - this.hideCalibBox = function(){ - this.showCalibBox = false; - this.lidarList.forEach(r=>r.hideCalibBox()); - } -}; + this.showCalibBox = false; + this.showCalibBox = function () { + this.showCalibBox = true; + this.lidarList.forEach((r) => r.showCalibBox()); + }; + this.hideCalibBox = function () { + this.showCalibBox = false; + this.lidarList.forEach((r) => r.hideCalibBox()); + }; +} -export {AuxLidarManager} \ No newline at end of file +export { AuxLidarManager }; diff --git a/public/js/box_editor.js b/public/js/box_editor.js index 76cb159..dfbb965 100644 --- a/public/js/box_editor.js +++ b/public/js/box_editor.js @@ -1,1839 +1,1742 @@ - -import {ProjectiveViewOps} from "./side_view_op.js" -import {BoxImageContext} from "./image.js"; -import {saveWorldList, reloadWorldList} from "./save.js" -import {objIdManager} from "./obj_id_list.js" +import { ProjectiveViewOps } from "./side_view_op.js"; +import { BoxImageContext } from "./image.js"; +import { saveWorldList, reloadWorldList } from "./save.js"; +import { objIdManager } from "./obj_id_list.js"; import { globalKeyDownManager } from "./keydown_manager.js"; -import{ml} from "./ml.js"; +import { ml } from "./ml.js"; import { BooleanKeyframeTrack } from "./lib/three.module.js"; import { checkScene } from "./error_check.js"; import { logger } from "./log.js"; - /* 2 ways to attach and edit a box 1) attach/detach 2) setTarget, tryAttach, resetTarget, this is only for batch-editor-manager */ -function BoxEditor(parentUi, boxEditorManager, viewManager, cfg, boxOp, - func_on_box_changed, func_on_box_remove, name){ - - this.boxEditorManager = boxEditorManager; - this.parentUi = parentUi; - this.name=name; - let uiTmpl = document.getElementById("box-editor-ui-template"); - let tmpui = uiTmpl.content.cloneNode(true); //sub-views - - parentUi.appendChild(tmpui); - this.ui = parentUi.lastElementChild; - this.boxInfoUi = this.ui.querySelector("#box-info"); - - this.viewManager = viewManager; - this.boxOp = boxOp; - this.boxView = this.viewManager.addBoxView(this.ui); //this.editorUi.querySelector("#sub-views") - this.projectiveViewOps = new ProjectiveViewOps( - this.ui, //this.editorUi.querySelector("#sub-views"), - cfg, - this, - this.boxView.views, - this.boxOp, - func_on_box_changed, - func_on_box_remove); - - this.focusImageContext = new BoxImageContext(this.ui.querySelector("#focuscanvas")); - - this.pseudoBox = { - position: {x: 0, y: 0, z: 0}, - rotation: {x: 0, y: 0, z: 0}, - scale: {x: 1, y: 1, z: 1}, - }; - - this.copyPseudoBox = function(b) - { - this.pseudoBox.position.x = b.position.x; - this.pseudoBox.position.y = b.position.y; - this.pseudoBox.position.z = b.position.z; - - this.pseudoBox.rotation.x = b.rotation.x; - this.pseudoBox.rotation.y = b.rotation.y; - this.pseudoBox.rotation.z = b.rotation.z; - - this.pseudoBox.scale.x = b.scale.x; - this.pseudoBox.scale.y = b.scale.y; - this.pseudoBox.scale.z = b.scale.z; - }; - - this.isInBatchMode = function(){ - return !!this.boxEditorManager; +function BoxEditor( + parentUi, + boxEditorManager, + viewManager, + cfg, + boxOp, + func_on_box_changed, + func_on_box_remove, + name +) { + this.boxEditorManager = boxEditorManager; + this.parentUi = parentUi; + this.name = name; + let uiTmpl = document.getElementById("box-editor-ui-template"); + let tmpui = uiTmpl.content.cloneNode(true); //sub-views + + parentUi.appendChild(tmpui); + this.ui = parentUi.lastElementChild; + this.boxInfoUi = this.ui.querySelector("#box-info"); + + this.viewManager = viewManager; + this.boxOp = boxOp; + this.boxView = this.viewManager.addBoxView(this.ui); //this.editorUi.querySelector("#sub-views") + this.projectiveViewOps = new ProjectiveViewOps( + this.ui, //this.editorUi.querySelector("#sub-views"), + cfg, + this, + this.boxView.views, + this.boxOp, + func_on_box_changed, + func_on_box_remove + ); + + this.focusImageContext = new BoxImageContext( + this.ui.querySelector("#focuscanvas") + ); + + this.pseudoBox = { + position: { x: 0, y: 0, z: 0 }, + rotation: { x: 0, y: 0, z: 0 }, + scale: { x: 1, y: 1, z: 1 }, + }; + + this.copyPseudoBox = function (b) { + this.pseudoBox.position.x = b.position.x; + this.pseudoBox.position.y = b.position.y; + this.pseudoBox.position.z = b.position.z; + + this.pseudoBox.rotation.x = b.rotation.x; + this.pseudoBox.rotation.y = b.rotation.y; + this.pseudoBox.rotation.z = b.rotation.z; + + this.pseudoBox.scale.x = b.scale.x; + this.pseudoBox.scale.y = b.scale.y; + this.pseudoBox.scale.z = b.scale.z; + }; + + this.isInBatchMode = function () { + return !!this.boxEditorManager; + }; + + this.target = {}; + + this.setTarget = function (world, objTrackId, objType) { + this.target = { + world: world, + objTrackId: objTrackId, + objType: objType, + }; + + if (this.isInBatchMode()) { + this.pseudoBox.world = world; + this.boxView.attachBox(this.pseudoBox); } - this.target = {}; - - this.setTarget = function(world, objTrackId, objType){ - this.target = { - world: world, - objTrackId: objTrackId, - objType: objType, - } - - if (this.isInBatchMode()){ - - this.pseudoBox.world = world; - this.boxView.attachBox(this.pseudoBox); - } - - this.tryAttach(); - this.ui.style.display="inline-block"; - this.updateInfo(); - }; - - this.setIndex = function(index){ - this.index = index; // index as of in all editors. - }; - - this.setSelected = function(selected, eventId){ - if (selected) - { - this.ui.className = "selected"; - this.selected = true; - this.selectEventId = eventId; - } - else - { - if (!eventId || (this.selectEventId == eventId)) - { - // cancel only you selected. - this.ui.className = ""; - this.selected = false; - this.selectEventId = null; - } - } - - - }; - - // this.onContextMenu = (event)=>{ - // if (this.boxEditorManager) // there is no manager for box editor in main ui - // this.boxEditorManager.onContextMenu(event, this); - // }; - - // this.ui.oncontextmenu = this.onContextMenu; - - this.resetTarget = function(){ - if (this.target.world){ - //unload if it's not the main world + this.tryAttach(); + this.ui.style.display = "inline-block"; + this.updateInfo(); + }; + + this.setIndex = function (index) { + this.index = index; // index as of in all editors. + }; + + this.setSelected = function (selected, eventId) { + if (selected) { + this.ui.className = "selected"; + this.selected = true; + this.selectEventId = eventId; + } else { + if (!eventId || this.selectEventId == eventId) { + // cancel only you selected. + this.ui.className = ""; + this.selected = false; + this.selectEventId = null; + } + } + }; + // this.onContextMenu = (event)=>{ + // if (this.boxEditorManager) // there is no manager for box editor in main ui + // this.boxEditorManager.onContextMenu(event, this); + // }; - // if (this.target.world !== this.target.world.data.world) - // this.target.world.unload(); - } + // this.ui.oncontextmenu = this.onContextMenu; - this.detach(); - this.target = {}; - //this.ui.style.display="none"; - }; - - this.tryAttach = function(){ - // find target box, attach to me - if (this.target && this.target.world){ + this.resetTarget = function () { + if (this.target.world) { + //unload if it's not the main world + // if (this.target.world !== this.target.world.data.world) + // this.target.world.unload(); + } - let box = this.target.world.annotation.findBoxByTrackId(this.target.objTrackId); - if (box){ - this.attachBox(box); - } - } - }; - + this.detach(); + this.target = {}; + //this.ui.style.display="none"; + }; + + this.tryAttach = function () { + // find target box, attach to me + if (this.target && this.target.world) { + let box = this.target.world.annotation.findBoxByTrackId( + this.target.objTrackId + ); + if (box) { + this.attachBox(box); + } + } + }; - /* + /* the projectiveView tiggers zoomratio changing event. editormanager broaccasts it to all editors */ - this._setViewZoomRatio = function(viewIndex, ratio){ - this.boxView.views[viewIndex].zoom_ratio = ratio; - }; - - this.updateViewZoomRatio = function(viewIndex, ratio){ - //this.upate(); - if (this.boxEditorManager) - this.boxEditorManager.updateViewZoomRatio(viewIndex, ratio); - else{ - this._setViewZoomRatio(viewIndex, ratio); - this.update(); - //this.viewManager.render(); - } - }; - - this.onOpCmd = function(cmd){ - if (this.boxEditorManager) - this.boxEditorManager.onOpCmd(cmd, this); - else{ - this.executeOpCmd(cmd); - } - }; + this._setViewZoomRatio = function (viewIndex, ratio) { + this.boxView.views[viewIndex].zoom_ratio = ratio; + }; + + this.updateViewZoomRatio = function (viewIndex, ratio) { + //this.upate(); + if (this.boxEditorManager) + this.boxEditorManager.updateViewZoomRatio(viewIndex, ratio); + else { + this._setViewZoomRatio(viewIndex, ratio); + this.update(); + //this.viewManager.render(); + } + }; - this.executeOpCmd = function(cmd){ + this.onOpCmd = function (cmd) { + if (this.boxEditorManager) this.boxEditorManager.onOpCmd(cmd, this); + else { + this.executeOpCmd(cmd); + } + }; - if (!this.box) - { - return; - } + this.executeOpCmd = function (cmd) { + if (!this.box) { + return; + } - if (cmd.op == "translate") - { - for (let axis in cmd.params.delta) - { - this.boxOp.translate_box(this.box, axis, cmd.params.delta[axis]); - //this.boxOp.translate_box(this.box, "y", delta.y); - } + if (cmd.op == "translate") { + for (let axis in cmd.params.delta) { + this.boxOp.translate_box(this.box, axis, cmd.params.delta[axis]); + //this.boxOp.translate_box(this.box, "y", delta.y); + } - func_on_box_changed(this.box); - } + func_on_box_changed(this.box); + } + }; + + this.box = null; + this.attachBox = function (box) { + if (this.box && this.box !== box) { + this.box.boxEditor = null; + console.log("detach box editor"); + //todo de-highlight box } this.box = null; - this.attachBox = function(box){ - if (this.box && this.box !== box){ - this.box.boxEditor=null; - console.log("detach box editor"); - //todo de-highlight box - } - - this.box = null; - this.show(); - - if (box){ - box.boxEditor = this; - this.box=box; - //this.boxOp.highlightBox(box); - this.boxView.attachBox(box); - this.projectiveViewOps.attachBox(box); - this.focusImageContext.updateFocusedImageContext(box); - - //this.update(); - this.updateInfo(); - // this.boxView.render(); - - if (this.isInBatchMode()){ - this.boxEditorManager.onBoxChanged(this); - } - } - }; - - this.detach = function(dontHide){ - if (this.box){ - if (this.box.boxEditor === this){ - this.box.boxEditor = null; - } - //this.boxOp.unhighlightBox(this.box); - //todo de-highlight box - this.projectiveViewOps.detach(); - this.boxView.detach(); - - if (this.isInBatchMode()){ - this.copyPseudoBox(this.box); - this.boxView.attachBox(this.pseudoBox); - - } - - this.focusImageContext.clear_canvas(); - this.box = null; - } - - if (!dontHide) - this.hide(); - }; - - - - - this.hide = function(){ - this.ui.style.display="none"; - - - // this is a hack, if we don't have manager, this is the main editor - // hide parent ui - // todo, add a pseudo manager, hide itself when child hide - if (!this.boxEditorManager){ - this.parentUi.style.display="none"; - } + this.show(); + + if (box) { + box.boxEditor = this; + this.box = box; + //this.boxOp.highlightBox(box); + this.boxView.attachBox(box); + this.projectiveViewOps.attachBox(box); + this.focusImageContext.updateFocusedImageContext(box); + + //this.update(); + this.updateInfo(); + // this.boxView.render(); + + if (this.isInBatchMode()) { + this.boxEditorManager.onBoxChanged(this); + } } - this.show = function(){ - this.ui.style.display="";//"inline-block"; - if (!this.boxEditorManager){ - this.parentUi.style.display=""; - } + }; + + this.detach = function (dontHide) { + if (this.box) { + if (this.box.boxEditor === this) { + this.box.boxEditor = null; + } + //this.boxOp.unhighlightBox(this.box); + //todo de-highlight box + this.projectiveViewOps.detach(); + this.boxView.detach(); + + if (this.isInBatchMode()) { + this.copyPseudoBox(this.box); + this.boxView.attachBox(this.pseudoBox); + } + + this.focusImageContext.clear_canvas(); + this.box = null; } - this.onBoxChanged=function(){ - - this.projectiveViewOps.update_view_handle(); - this.focusImageContext.updateFocusedImageContext(this.box); - this.boxView.onBoxChanged(); - - // mark - delete this.box.annotator; // human annotator doesn't need a name - delete this.box.follows; - this.box.changed = true; - - // don't mark world's change flag, for it's hard to clear it. - - // inform boxEditorMgr to transfer annotation to other frames. - if (this.boxEditorManager) - this.boxEditorManager.onBoxChanged(this); - - this.updateInfo(); - - //this.boxView.render(); - }; + if (!dontHide) this.hide(); + }; - this.onDelBox = function(){ - let box = this.box; - this.detach("donthide"); - }; + this.hide = function () { + this.ui.style.display = "none"; - // windowresize... - this.update = function(dontRender=false){ + // this is a hack, if we don't have manager, this is the main editor + // hide parent ui + // todo, add a pseudo manager, hide itself when child hide + if (!this.boxEditorManager) { + this.parentUi.style.display = "none"; + } + }; + this.show = function () { + this.ui.style.display = ""; //"inline-block"; + if (!this.boxEditorManager) { + this.parentUi.style.display = ""; + } + }; - if (this.boxView){ - this.boxView.onBoxChanged(dontRender); + this.onBoxChanged = function () { + this.projectiveViewOps.update_view_handle(); + this.focusImageContext.updateFocusedImageContext(this.box); + this.boxView.onBoxChanged(); - // this.boxView.updateCameraRange(this.box); - // this.boxView.updateCameraPose(this.box); + // mark + delete this.box.annotator; // human annotator doesn't need a name + delete this.box.follows; + this.box.changed = true; - // if (!dontRender) - // this.boxView.render(); - } + // don't mark world's change flag, for it's hard to clear it. - // boxview should be updated for pseudobox. + // inform boxEditorMgr to transfer annotation to other frames. + if (this.boxEditorManager) this.boxEditorManager.onBoxChanged(this); - if (this.box === null) - return; + this.updateInfo(); - this.projectiveViewOps.update_view_handle(); - - - - // this is not needed somtime - this.focusImageContext.updateFocusedImageContext(this.box); + //this.boxView.render(); + }; - // should we update info? - this.updateInfo(); - }; + this.onDelBox = function () { + let box = this.box; + this.detach("donthide"); + }; - this.updateInfo = function(){ - let info = "" - if (this.target.world){ - info += String(this.target.world.frameInfo.frame); - - if (this.box && this.box.annotator) - info += ","+this.box.annotator; - - // if (this.box && this.box.changed) - // info += " *"; - } + // windowresize... + this.update = function (dontRender = false) { + if (this.boxView) { + this.boxView.onBoxChanged(dontRender); - this.boxInfoUi.innerHTML = info; - }; + // this.boxView.updateCameraRange(this.box); + // this.boxView.updateCameraPose(this.box); - this.updateBoxDimension = function(){ + // if (!dontRender) + // this.boxView.render(); + } - }; + // boxview should be updated for pseudobox. + if (this.box === null) return; - this.resize = function(width, height) - { - // if (height + "px" == this.ui.style.height && width + "px" == this.ui.style.width) - // { - // return; - // } + this.projectiveViewOps.update_view_handle(); - this.ui.style.width = width + "px"; - this.ui.style.height = height + "px"; - this.boxView.render(); - }; + // this is not needed somtime + this.focusImageContext.updateFocusedImageContext(this.box); - this.setResize = function(option){ - this.ui.style.resize=option; - this.ui.style["z-index"] = "0"; + // should we update info? + this.updateInfo(); + }; - if (option == 'both') - { + this.updateInfo = function () { + let info = ""; + if (this.target.world) { + info += String(this.target.world.frameInfo.frame); - this.lastSize= { - width: 0, - height: 0, - }; - - this.resizeObserver = new ResizeObserver(elements=>{ - - let rect = elements[0].contentRect; - console.log("sub-views resized.", rect); - - if (rect.height == 0 || rect.width == 0) - { - return; - } - - if (rect.height != this.lastSize.height || rect.width != this.lastSize.width) - { - // viewManager will clear backgound - // so this render is effectiveless. - //this.boxView.render(); - - // save - - if (this.boxEditorManager) // there is no manager for box editor in main ui - { - pointsGlobalConfig.setItem("batchModeSubviewSize", {width: rect.width, height: rect.height}); - this.boxEditorManager.onSubViewsResize(rect.width, rect.height); - } - else{ - this.boxView.render(); - } - - //save - this.lastSize.width = rect.width; - this.lastSize.height = rect.height; - } - - + if (this.box && this.box.annotator) info += "," + this.box.annotator; - }); - - this.resizeObserver.observe(this.ui); - } + // if (this.box && this.box.changed) + // info += " *"; } + this.boxInfoUi.innerHTML = info; + }; + this.updateBoxDimension = function () {}; -} - + this.resize = function (width, height) { + // if (height + "px" == this.ui.style.height && width + "px" == this.ui.style.width) + // { + // return; + // } -//parentUi #batch-box-editor -function BoxEditorManager(parentUi, viewManager, objectTrackView, - cfg, boxOp, globalHeader, contextMenu, configMenu, - func_on_box_changed, func_on_box_remove, func_on_annotation_reloaded){ - this.viewManager = viewManager; - this.objectTrackView = objectTrackView; - this.boxOp = boxOp; - this.activeIndex = 0; - this.editorList = []; - this.cfg = cfg; - this.globalHeader = globalHeader; - this.contextMenu = contextMenu; - this.parentUi = parentUi; //#batch-box-editor - this.boxEditorGroupUi = parentUi.querySelector("#batch-box-editor-group"); - this.boxEditorHeaderUi = parentUi.querySelector("#batch-box-editor-header"); - this.batchSize = cfg.batchModeInstNumber; - //this.configMenu = configMenu; - - this.activeEditorList = function(){ - return this.editorList.slice(0, this.activeIndex); - }; + this.ui.style.width = width + "px"; + this.ui.style.height = height + "px"; + this.boxView.render(); + }; - this.editingTarget = { - data: null, - sceneMeta: "", - objTrackId: "", - frame:"", - frameIndex: NaN, - }; - - this.onExit = null; - // frame specifies the center frame to edit - - - // this.parentUi.addEventListener("contextmenu", event=>{ - // this.contextMenu.show("boxEditorManager", event.clientX, event.clientY, this); - // event.stopPropagation(); - // event.preventDefault(); - // }) - - this.onSubViewsResize = function(width, height) - { - this.viewManager.mainView.clearView(); - this.editorList.forEach(e=>{ - e.resize(width, height); - }); + this.setResize = function (option) { + this.ui.style.resize = option; + this.ui.style["z-index"] = "0"; - //this.viewManager.render(); - }; + if (option == "both") { + this.lastSize = { + width: 0, + height: 0, + }; - this.calculateBestSubviewSize=function(batchSize) - { - let parentRect = this.parentUi.getBoundingClientRect(); - let headerRect = this.boxEditorHeaderUi.getBoundingClientRect(); - let editorsGroupRect = this.boxEditorGroupUi.getBoundingClientRect(); + this.resizeObserver = new ResizeObserver((elements) => { + let rect = elements[0].contentRect; + console.log("sub-views resized.", rect); - let availableHeight = parentRect.height - headerRect.height; - let availableWidth = parentRect.width; + if (rect.height == 0 || rect.width == 0) { + return; + } - if (availableHeight ==0 || availableWidth ==0) - { - this.batchSizeUpdated=true; - return; + if ( + rect.height != this.lastSize.height || + rect.width != this.lastSize.width + ) { + // viewManager will clear backgound + // so this render is effectiveless. + //this.boxView.render(); + + // save + + if (this.boxEditorManager) { + // there is no manager for box editor in main ui + pointsGlobalConfig.setItem("batchModeSubviewSize", { + width: rect.width, + height: rect.height, + }); + this.boxEditorManager.onSubViewsResize(rect.width, rect.height); + } else { + this.boxView.render(); + } + + //save + this.lastSize.width = rect.width; + this.lastSize.height = rect.height; } + }); + + this.resizeObserver.observe(this.ui); + } + }; +} +//parentUi #batch-box-editor +function BoxEditorManager( + parentUi, + viewManager, + objectTrackView, + cfg, + boxOp, + globalHeader, + contextMenu, + configMenu, + func_on_box_changed, + func_on_box_remove, + func_on_annotation_reloaded +) { + this.viewManager = viewManager; + this.objectTrackView = objectTrackView; + this.boxOp = boxOp; + this.activeIndex = 0; + this.editorList = []; + this.cfg = cfg; + this.globalHeader = globalHeader; + this.contextMenu = contextMenu; + this.parentUi = parentUi; //#batch-box-editor + this.boxEditorGroupUi = parentUi.querySelector("#batch-box-editor-group"); + this.boxEditorHeaderUi = parentUi.querySelector("#batch-box-editor-header"); + this.batchSize = cfg.batchModeInstNumber; + //this.configMenu = configMenu; + + this.activeEditorList = function () { + return this.editorList.slice(0, this.activeIndex); + }; + + this.editingTarget = { + data: null, + sceneMeta: "", + objTrackId: "", + frame: "", + frameIndex: NaN, + }; + + this.onExit = null; + // frame specifies the center frame to edit + + // this.parentUi.addEventListener("contextmenu", event=>{ + // this.contextMenu.show("boxEditorManager", event.clientX, event.clientY, this); + // event.stopPropagation(); + // event.preventDefault(); + // }) + + this.onSubViewsResize = function (width, height) { + this.viewManager.mainView.clearView(); + this.editorList.forEach((e) => { + e.resize(width, height); + }); + + //this.viewManager.render(); + }; + + this.calculateBestSubviewSize = function (batchSize) { + let parentRect = this.parentUi.getBoundingClientRect(); + let headerRect = this.boxEditorHeaderUi.getBoundingClientRect(); + let editorsGroupRect = this.boxEditorGroupUi.getBoundingClientRect(); + + let availableHeight = parentRect.height - headerRect.height; + let availableWidth = parentRect.width; + + if (availableHeight == 0 || availableWidth == 0) { + this.batchSizeUpdated = true; + return; + } - let defaultBoxWidth=130; - let defaultBoxHeight=450; + let defaultBoxWidth = 130; + let defaultBoxHeight = 450; + + let rows = 1; + let w = availableWidth / Math.ceil(batchSize / rows); + let h = availableHeight / rows; + let cost = Math.abs(w / h - defaultBoxWidth / defaultBoxHeight); + let minCost = cost; + let bestRows = rows; + while (true) { + rows += 1; + + let w = Math.floor(availableWidth / Math.ceil(batchSize / rows)); + let h = Math.floor(availableHeight / rows); + let cost = Math.abs(w / h - defaultBoxWidth / defaultBoxHeight); + + if (cost < minCost) { + minCost = cost; + bestRows = rows; + } else { + break; + } + } - let rows = 1; - let w = availableWidth/Math.ceil(batchSize/rows); - let h = availableHeight/rows; - let cost = Math.abs((w/h) - (defaultBoxWidth/defaultBoxHeight)); - let minCost = cost; - let bestRows = rows; - while(true) - { - rows +=1; - - let w = Math.floor(availableWidth/Math.ceil(batchSize/rows)); - let h = Math.floor(availableHeight/rows); - let cost = Math.abs((w/h) - (defaultBoxWidth/defaultBoxHeight)); - - if (cost < minCost) - { - minCost = cost; - bestRows = rows; - } - else{ - break; - } - } - - //bestRows - pointsGlobalConfig.batchModeSubviewSize = { - width: Math.floor(availableWidth/Math.ceil(batchSize/bestRows)), - height: Math.floor(availableHeight/bestRows), - } + //bestRows + pointsGlobalConfig.batchModeSubviewSize = { + width: Math.floor(availableWidth / Math.ceil(batchSize / bestRows)), + height: Math.floor(availableHeight / bestRows), + }; + }; + + this.setBatchSize = function (batchSize) { + this.calculateBestSubviewSize(batchSize); + + this.batchSize = batchSize; + if (this.parentUi.style.display != "none") { + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.frame, + this.editingTarget.objTrackId, + this.editingTarget.objType + ); } + }; - this.setBatchSize = function(batchSize) - { - this.calculateBestSubviewSize(batchSize); + this.onWindowResize = function () { + this.setBatchSize(this.batchSize); + }; - this.batchSize = batchSize; - if (this.parentUi.style.display != "none") - { - - - this.edit( this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.frame, - this.editingTarget.objTrackId, - this.editingTarget.objType - ); - } - }; + this.edit = function (data, sceneMeta, frame, objTrackId, objType, onExit) { + this.show(); + this.reset(); - this.onWindowResize = function() - { - this.setBatchSize(this.batchSize); - }; + if (this.batchSizeUpdated) { + this.batchSizeUpdated = false; + this.calculateBestSubviewSize(this.batchSize); + } - this.edit = function(data, sceneMeta, frame, objTrackId, objType, onExit){ - - this.show(); - this.reset(); + if (onExit) { + // next/prev call will not update onExit + this.onExit = onExit; + } + let sceneName = sceneMeta.scene; - if (this.batchSizeUpdated) - { - this.batchSizeUpdated=false; - this.calculateBestSubviewSize(this.batchSize); - } + this.editingTarget.data = data; + this.editingTarget.sceneMeta = sceneMeta; + this.editingTarget.objTrackId = objTrackId; + this.editingTarget.objType = objType; - if (onExit){ - // next/prev call will not update onExit - this.onExit = onExit; - } - let sceneName = sceneMeta.scene; + this.editingTarget.frame = frame; - this.editingTarget.data = data; - this.editingTarget.sceneMeta = sceneMeta; - this.editingTarget.objTrackId = objTrackId; + // this.parentUi.querySelector("#object-track-id-editor").value=objTrackId; + // this.parentUi.querySelector("#object-category-selector").value=objType; - - this.editingTarget.objType = objType; + let centerIndex = sceneMeta.frames.findIndex((f) => f == frame); + this.editingTarget.frameIndex = centerIndex; - this.editingTarget.frame = frame; + if (centerIndex < 0) { + centerIndex = 0; + } - // this.parentUi.querySelector("#object-track-id-editor").value=objTrackId; - // this.parentUi.querySelector("#object-category-selector").value=objType; - + let startIndex = Math.max(0, centerIndex - this.batchSize / 2); - let centerIndex = sceneMeta.frames.findIndex(f=>f==frame); - this.editingTarget.frameIndex = centerIndex; + if (startIndex > 0) { + if (startIndex + this.batchSize > sceneMeta.frames.length) { + startIndex -= startIndex + this.batchSize - sceneMeta.frames.length; - if (centerIndex < 0){ - centerIndex = 0; + if (startIndex < 0) { + startIndex = 0; } + } + } + let frames = sceneMeta.frames.slice( + startIndex, + startIndex + this.batchSize + ); + + //this.viewManager.mainView.clearView(); + + frames.forEach(async (frame, editorIndex) => { + let world = await data.getWorld(sceneName, frame); + let editor = this.addEditor(); + //editor.setTarget(world, objTrackId, objType); + editor.setIndex(editorIndex); + editor.resize( + pointsGlobalConfig.batchModeSubviewSize.width, + pointsGlobalConfig.batchModeSubviewSize.height + ); + + if (this.editingTarget.frame == frame) { + editor.setSelected(true); + } + + data.activate_world( + world, + () => { + //editor.tryAttach(); + + editor.setTarget(world, objTrackId, objType); + + // + //this.viewManager.render(); + }, + true + ); + }); + + // set obj selector + this.globalHeader.setObject(objTrackId); + }; + + this.onContextMenu = function (event, boxEditor) { + this.firingBoxEditor = boxEditor; + + if (boxEditor.selected) { + // ok + } else { + this.getSelectedEditors().forEach((e) => e.setSelected(false)); + boxEditor.setSelected(true); + } - let startIndex = Math.max(0, centerIndex - this.batchSize/2); + this.contextMenu.show("boxEditor", event.clientX, event.clientY, this); + event.stopPropagation(); + event.preventDefault(); + }; - if(startIndex > 0) - { - if (startIndex + this.batchSize > sceneMeta.frames.length) - { - startIndex -= startIndex + this.batchSize - sceneMeta.frames.length; - - if (startIndex < 0) - { - startIndex = 0; - } - } - } + this.parentUi.oncontextmenu = (event) => { + let ed = this.getEditorByMousePosition(event.clientX, event.clientY); - + this.onContextMenu(event, ed); + }; - let frames = sceneMeta.frames.slice(startIndex, startIndex+this.batchSize); + this.handleContextMenuKeydownEvent = function (event, menuPos) { + switch (event.key) { + case "s": + this.activeEditorList().forEach((e) => e.setSelected(true)); + return true; + break; + case "a": + this.autoAnnotateSelectedFrames(); + break; + case "f": + this.finalizeSelectedBoxes(); + break; + case "d": + this.deleteSelectedBoxes(menuPos); + break; + case "e": + this.interpolateSelectedFrames(); + break; + case "g": + this.gotoThisFrame(); + break; + case "t": + this.showTrajectory(); + break; + default: + return true; + } - - //this.viewManager.mainView.clearView(); + return false; + }; - frames.forEach(async (frame, editorIndex)=>{ - let world = await data.getWorld(sceneName, frame); - let editor = this.addEditor(); - //editor.setTarget(world, objTrackId, objType); - editor.setIndex(editorIndex); - editor.resize(pointsGlobalConfig.batchModeSubviewSize.width, pointsGlobalConfig.batchModeSubviewSize.height); - - if (this.editingTarget.frame == frame){ - editor.setSelected(true); - } + this.delayUpdateAutoGeneratedBoxesTimer = null; - data.activate_world(world, - ()=>{ - //editor.tryAttach(); + this.updateAutoGeneratedBoxes = function () { + if (this.delayUpdateAutoGeneratedBoxesTimer) { + clearTimeout(this.delayUpdateAutoGeneratedBoxesTimer); + } - editor.setTarget(world, objTrackId, objType); - - // - //this.viewManager.render(); - }, - true); - }); + this.delayUpdateAutoGeneratedBoxesTimer = setTimeout(async () => { + if (this.cfg.autoUpdateInterpolatedBoxes) { + await this.updateInterpolatedBoxes(); + } + + await this.updatePseudoBoxes(); + }, 500); + }; + + this.updateInterpolatedBoxes = async function () { + let editorList = this.activeEditorList(); + let applyIndList = editorList.map((e) => e.box && e.box.annotator == "i"); + + let boxList = editorList.map((e) => e.box); + let worldList = editorList.map((e) => e.target.world); + await this.boxOp.interpolateAsync(worldList, boxList, applyIndList); + //this.activeEditorList().forEach(e=>e.tryAttach()); + + this.globalHeader.updateModifiedStatus(); + //this.viewManager.render(); + editorList.forEach((e) => { + if (e.box && e.box.annotator == "i") { + e.boxView.onBoxChanged(); + } + }); + }; + + this.updatePseudoBoxes = async function () { + let editorList = this.activeEditorList(); + let boxList = editorList.map((e) => e.box); + let anns = boxList.map((b) => + b ? b.world.annotation.ann_to_vector_global(b) : null + ); + + let ret = await ml.interpolate_annotation(anns); + + editorList.forEach((e, i) => { + if (!e.box) { + let ann = e.target.world.annotation.vector_global_to_ann(ret[i]); + e.copyPseudoBox(ann); + e.boxView.onBoxChanged(); + } + }); + }; + + // manager + this.onBoxChanged = function (e) { + this.updateAutoGeneratedBoxes(); + // + }; + + let onBoxChangedInBatchMode = function (box) { + if (box.boxEditor) + // if in batch mode with less than 40 windows, some box don't have editor attached. + box.boxEditor.update(); //render. + + box.world.annotation.setModified(); + }; + + this.finalizeSelectedBoxes = function () { + this.getSelectedEditors().forEach((e) => { + if (e.box) { + if (e.box.annotator) { + delete e.box.annotator; + func_on_box_changed(e.box); + //e.box.world.annotation.setModified(); + e.updateInfo(); + } + } + }); + + this.globalHeader.updateModifiedStatus(); + }; + + this.interpolateSelectedFrames = function () { + let applyIndList = this.activeEditorList().map((e) => false); //all shoud be applied. + let selectedEditors = this.getSelectedEditors(); + + // if interpolate only one box, remove it if exist. + // no matter who is the annotator. + if (selectedEditors.length == 1) { + if (selectedEditors[0].box) { + func_on_box_remove(selectedEditors[0].box, true); + } + } - - // set obj selector - this.globalHeader.setObject(objTrackId); - }; - - this.onContextMenu = function(event, boxEditor) - { - this.firingBoxEditor = boxEditor; + selectedEditors.forEach((e) => (applyIndList[e.index] = true)); + this.interpolate(applyIndList); - if (boxEditor.selected) - { - // ok - } - else - { - this.getSelectedEditors().forEach(e=>e.setSelected(false)); - boxEditor.setSelected(true); - } + this.updateAutoGeneratedBoxes(); + }; - this.contextMenu.show("boxEditor", event.clientX, event.clientY, this); - event.stopPropagation(); - event.preventDefault(); - }; + this.deleteEmptyBoxes = function () { + let editors = this.activeEditorList(); + editors.forEach((e) => { + if (e.box) { + if (e.box.world.lidar.get_box_points_number(e.box) == 0) { + func_on_box_remove(e.box, true); + } + } + }); - this.parentUi.oncontextmenu = (event)=>{ - let ed = this.getEditorByMousePosition(event.clientX, event.clientY); + this.updateAutoGeneratedBoxes(); + }; - this.onContextMenu(event, ed); - }; + this.deleteIntersectedBoxes = function () { + let editors = this.getSelectedEditors(); + editors.forEach((e) => { + if (e.box) { + let boxes = e.box.world.annotation.findIntersectedBoxes(e.box); + boxes.forEach((b) => { + func_on_box_remove(b, true); + }); - this.handleContextMenuKeydownEvent = function(event, menuPos) - { - switch(event.key){ - case 's': - this.activeEditorList().forEach(e=>e.setSelected(true)); - return true; - break; - case 'a': - this.autoAnnotateSelectedFrames(); - break; - case 'f': - this.finalizeSelectedBoxes(); - break; - case 'd': - this.deleteSelectedBoxes(menuPos); - break; - case 'e': - this.interpolateSelectedFrames(); - break; - case 'g': - this.gotoThisFrame(); - break; - case 't': - this.showTrajectory(); - break; - default: - return true; - } + onBoxChangedInBatchMode(e.box); + } + }); + }; + + this.deleteSelectedBoxes = function (infoBoxPos) { + let selectedEditors = this.getSelectedEditors(); + + if (selectedEditors.length >= 2) { + window.editor.infoBox.show( + "Confirm", + `Delete ${selectedEditors.length} selected boxes?`, + ["yes", "no"], + (btn) => { + if (btn == "yes") { + selectedEditors.forEach((e) => { + if (e.box) func_on_box_remove(e.box, true); + }); - return false; - }; + this.updateAutoGeneratedBoxes(); + } + }, + infoBoxPos + ); + } else { + selectedEditors.forEach((e) => { + if (e.box) func_on_box_remove(e.box, true); + }); + + this.updateAutoGeneratedBoxes(); + } + }; + + this.autoAnnotateSelectedFrames = function () { + let applyIndList = this.activeEditorList().map((e) => false); //all shoud be applied. + this.getSelectedEditors().forEach((e) => (applyIndList[e.index] = true)); + this.autoAnnotate(applyIndList); + }; + + this.onOpCmd = function (cmd, firingEditor) { + firingEditor.executeOpCmd(cmd); + + if (this.cfg.linkEditorsInBatchMode) { + let editors = this.getSelectedEditors(); + + if (editors.includes(firingEditor)) { + editors + .filter((x) => x != firingEditor) + .forEach((e) => { + if (e.box && !e.box.annotator) { + e.executeOpCmd(cmd); + } + }); + } + } + }; + + this.handleContextMenuEvent = function (event) { + console.log(event.currentTarget.id, event.type); + switch (event.currentTarget.id) { + // manager + case "cm-increase-box-editor": + this.batchSize += 1; + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.sceneMeta.frame, + this.editingTarget.objTrackId, + this.editingTarget.objType + ); + break; - this.delayUpdateAutoGeneratedBoxesTimer = null; + case "cm-decrease-box-editor": + this.batchSize -= 1; + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.sceneMeta.frame, + this.editingTarget.objTrackId, + this.editingTarget.objType + ); + break; - this.updateAutoGeneratedBoxes = function(){ + /////////////////////// obj instance // - if (this.delayUpdateAutoGeneratedBoxesTimer) + case "cm-select-all": + this.activeEditorList().forEach((e) => e.setSelected(true)); + return false; //don't hide context menu + break; + case "cm-select-all-previous": + this.activeEditorList().forEach((e) => + e.setSelected(e.index <= this.firingBoxEditor.index) + ); + return false; //don't hide context menu + break; + case "cm-select-all-next": + this.activeEditorList().forEach((e) => + e.setSelected(e.index >= this.firingBoxEditor.index) + ); + return false; //don't hide context menu + break; + + case "cm-delete": + this.deleteSelectedBoxes({ x: event.clientX, y: event.clientY }); + break; + case "cm-delete-empty-boxes": + this.deleteEmptyBoxes(); + break; + case "cm-delete-intersected-boxes": + this.deleteIntersectedBoxes(); + break; + case "cm-interpolate": + this.interpolateSelectedFrames(); + break; + + case "cm-auto-annotate": + this.autoAnnotateSelectedFrames(); + break; + + case "cm-auto-annotate-wo-rotation": { - clearTimeout(this.delayUpdateAutoGeneratedBoxesTimer) + let applyIndList = this.activeEditorList().map((e) => false); //all shoud be applied. + this.getSelectedEditors().forEach( + (e) => (applyIndList[e.index] = true) + ); + this.autoAnnotate(applyIndList, "dontrotate"); } + break; - this.delayUpdateAutoGeneratedBoxesTimer = setTimeout(async ()=>{ - - if (this.cfg.autoUpdateInterpolatedBoxes){ - await this.updateInterpolatedBoxes(); - } - - await this.updatePseudoBoxes(); - }, - 500); - }; - - this.updateInterpolatedBoxes = async function(){ + case "cm-fit-moving-direction": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; - let editorList = this.activeEditorList(); - let applyIndList = editorList.map(e=>e.box&&e.box.annotator=="i"); - - let boxList = editorList.map(e=>e.box); - let worldList = editorList.map(e=>e.target.world); - await this.boxOp.interpolateAsync(worldList, boxList, applyIndList); - //this.activeEditorList().forEach(e=>e.tryAttach()); + let currentBox = e.box; + let estimatedRot = + boxOp.estimate_rotation_by_moving_direciton(currentBox); - this.globalHeader.updateModifiedStatus(); - //this.viewManager.render(); - editorList.forEach(e=>{ - if (e.box&&e.box.annotator=="i"){ - e.boxView.onBoxChanged(); - } + if (estimatedRot) { + currentBox.rotation.z = estimatedRot.z; + func_on_box_changed(currentBox); + } }); - }; - - - this.updatePseudoBoxes = async function(){ - let editorList = this.activeEditorList(); - let boxList = editorList.map(e=>e.box); - let anns = boxList.map(b=> b?b.world.annotation.ann_to_vector_global(b):null); + this.updateAutoGeneratedBoxes(); - let ret = await ml.interpolate_annotation(anns); + break; + case "cm-fit-size": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; - editorList.forEach((e,i)=>{ - if (!e.box){ - let ann = e.target.world.annotation.vector_global_to_ann(ret[i]); - e.copyPseudoBox(ann); - e.boxView.onBoxChanged(); - } + boxOp.fit_size(e.box, ["x", "y"]); + func_on_box_changed(e.box); }); - }; - - - // manager - this.onBoxChanged = function(e){ this.updateAutoGeneratedBoxes(); - // - }; - - - let onBoxChangedInBatchMode = function(box) - { - if (box.boxEditor) // if in batch mode with less than 40 windows, some box don't have editor attached. - box.boxEditor.update(); //render. + break; + case "cm-fit-position": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.auto_rotate_xyz( + e.box, + null, + null, //{x:false, y:false, z:true}, + func_on_box_changed, //onBoxChangedInBatchMode, + "noscaling", + "dontrotate" + ); + }); - box.world.annotation.setModified(); - }; + this.updateAutoGeneratedBoxes(); + break; + case "cm-fit-rotation": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.auto_rotate_xyz( + e.box, + null, + null, + func_on_box_changed, //onBoxChangedInBatchMode, // + "noscaling" + ); + }); + this.updateAutoGeneratedBoxes(); + break; + case "cm-fit-bottom": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.fit_bottom(e.box); - this.finalizeSelectedBoxes = function() - { - this.getSelectedEditors().forEach(e=>{ - - if (e.box){ - if (e.box.annotator) - { - delete e.box.annotator; - func_on_box_changed(e.box); - //e.box.world.annotation.setModified(); - e.updateInfo(); - } - } + func_on_box_changed(e.box); }); - this.globalHeader.updateModifiedStatus();; - }; + this.updateAutoGeneratedBoxes(); + break; + case "cm-fit-top": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.fit_top(e.box); - this.interpolateSelectedFrames = function(){ - let applyIndList = this.activeEditorList().map(e=>false); //all shoud be applied. - let selectedEditors = this.getSelectedEditors(); + func_on_box_changed(e.box); + }); - // if interpolate only one box, remove it if exist. - // no matter who is the annotator. - if (selectedEditors.length == 1) - { - if (selectedEditors[0].box) - { - func_on_box_remove(selectedEditors[0].box, true); - } - } + this.updateAutoGeneratedBoxes(); + break; + case "cm-fit-left": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.fit_left(e.box); - selectedEditors.forEach(e=>applyIndList[e.index] = true); - this.interpolate(applyIndList); - + func_on_box_changed(e.box); + }); this.updateAutoGeneratedBoxes(); - }; + break; + case "cm-fit-right": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.fit_right(e.box); - this.deleteEmptyBoxes = function() - { - let editors = this.activeEditorList(); - editors.forEach(e=>{ - if (e.box) - { - if (e.box.world.lidar.get_box_points_number(e.box) == 0) - { - func_on_box_remove(e.box, true); - } - } + func_on_box_changed(e.box); }); this.updateAutoGeneratedBoxes(); - }; + break; + case "cm-fit-rear": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.fit_rear(e.box); - this.deleteIntersectedBoxes = function(){ + func_on_box_changed(e.box); + }); - let editors = this.getSelectedEditors(); - editors.forEach(e=>{ - if (e.box) - { - let boxes = e.box.world.annotation.findIntersectedBoxes(e.box); + this.updateAutoGeneratedBoxes(); + break; + case "cm-fit-front": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + boxOp.fit_front(e.box); - boxes.forEach(b=>{ - func_on_box_remove(b, true); - }); + func_on_box_changed(e.box); + }); - onBoxChangedInBatchMode(e.box); - } + this.updateAutoGeneratedBoxes(); + break; + case "cm-reverse-direction": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + if (e.box.rotation.z > 0) { + e.box.rotation.z -= Math.PI; + } else { + e.box.rotation.z += Math.PI; + } + + onBoxChangedInBatchMode(e.box); }); - }; - this.deleteSelectedBoxes = function(infoBoxPos) - { - let selectedEditors = this.getSelectedEditors(); + //this.viewManager.render(); - if (selectedEditors.length >= 2) - { - window.editor.infoBox.show( - "Confirm", - `Delete ${selectedEditors.length} selected boxes?`, - ["yes", "no"], - (btn)=>{ - if (btn == "yes") - { - - selectedEditors.forEach(e=>{ - if (e.box) - func_on_box_remove(e.box, true); - }); - - this.updateAutoGeneratedBoxes(); - } - }, - infoBoxPos - ); - } - else{ - selectedEditors.forEach(e=>{ - if (e.box) - func_on_box_remove(e.box, true) - }); + this.updateAutoGeneratedBoxes(); - this.updateAutoGeneratedBoxes(); - } - }; + break; + case "cm-reset-roll-pitch": + this.getSelectedEditors().forEach((e) => { + if (!e.box) return; + e.box.rotation.x = 0; + e.box.rotation.y = 0; + e.update("dontrender"); + e.box.world.annotation.setModified(); - this.autoAnnotateSelectedFrames = function() - { - let applyIndList = this.activeEditorList().map(e=>false); //all shoud be applied. - this.getSelectedEditors().forEach(e=>applyIndList[e.index] = true); - this.autoAnnotate(applyIndList); - }; + onBoxChangedInBatchMode(e.box); + }); - this.onOpCmd = function(cmd, firingEditor){ + //this.viewManager.render(); + this.updateAutoGeneratedBoxes(); - firingEditor.executeOpCmd(cmd); + break; + case "cm-show-trajectory": + this.showTrajectory(); + break; - if (this.cfg.linkEditorsInBatchMode) + case "cm-check": { - let editors = this.getSelectedEditors(); - - if (editors.includes(firingEditor)) - { - editors.filter(x=>x!=firingEditor).forEach(e=>{ - if (e.box && !e.box.annotator) - { - e.executeOpCmd(cmd); - } - }); - } + let scene = this.editingTarget.sceneMeta.scene; + checkScene(scene); + logger.show(); + logger.errorBtn.onclick(); } - }; - - - this.handleContextMenuEvent = function(event) - { - console.log(event.currentTarget.id, event.type); - switch(event.currentTarget.id) - { - - // manager - case 'cm-increase-box-editor': - this.batchSize += 1; - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.sceneMeta.frame, - this.editingTarget.objTrackId, - this.editingTarget.objType - ); - break; - - case 'cm-decrease-box-editor': - this.batchSize -= 1; - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.sceneMeta.frame, - this.editingTarget.objTrackId, - this.editingTarget.objType - ); - break; - - /////////////////////// obj instance // - - case 'cm-select-all': - this.activeEditorList().forEach(e=>e.setSelected(true)); - return false;//don't hide context menu - break; - case 'cm-select-all-previous': - this.activeEditorList().forEach(e=> e.setSelected(e.index <= this.firingBoxEditor.index)); - return false;//don't hide context menu - break; - case 'cm-select-all-next': - this.activeEditorList().forEach(e=> e.setSelected(e.index >= this.firingBoxEditor.index)); - return false;//don't hide context menu - break - - case 'cm-delete': - this.deleteSelectedBoxes( {x: event.clientX, y: event.clientY}); - break; - case 'cm-delete-empty-boxes': - this.deleteEmptyBoxes(); - break; - case 'cm-delete-intersected-boxes': - this.deleteIntersectedBoxes(); - break; - case 'cm-interpolate': - this.interpolateSelectedFrames(); - break; - - case 'cm-auto-annotate': - this.autoAnnotateSelectedFrames(); - break; - - case 'cm-auto-annotate-wo-rotation': - { - let applyIndList = this.activeEditorList().map(e=>false); //all shoud be applied. - this.getSelectedEditors().forEach(e=>applyIndList[e.index] = true); - this.autoAnnotate(applyIndList, "dontrotate"); - } - break; - - case 'cm-fit-moving-direction': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - - let currentBox = e.box; - let estimatedRot = boxOp.estimate_rotation_by_moving_direciton(currentBox); - - if (estimatedRot){ - currentBox.rotation.z = estimatedRot.z; - func_on_box_changed(currentBox); - } - }); - - this.updateAutoGeneratedBoxes(); - - break; - case 'cm-fit-size': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - - boxOp.fit_size(e.box, ['x','y']); - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-position': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.auto_rotate_xyz(e.box, null, - null,//{x:false, y:false, z:true}, - func_on_box_changed, //onBoxChangedInBatchMode, - "noscaling", "dontrotate"); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-rotation': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.auto_rotate_xyz(e.box, null, - null, - func_on_box_changed,//onBoxChangedInBatchMode, // - "noscaling"); - - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-bottom': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.fit_bottom(e.box); - - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-top': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.fit_top(e.box); - - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-left': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.fit_left(e.box); - - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-right': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.fit_right(e.box); - - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-rear': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.fit_rear(e.box); - - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-fit-front': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - boxOp.fit_front(e.box); - - func_on_box_changed(e.box); - }); - - this.updateAutoGeneratedBoxes(); - break; - case 'cm-reverse-direction': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - if (e.box.rotation.z > 0){ - e.box.rotation.z -= Math.PI; - }else{ - e.box.rotation.z += Math.PI; - } - - onBoxChangedInBatchMode(e.box); - }); - - //this.viewManager.render(); - - this.updateAutoGeneratedBoxes(); - - break; - case 'cm-reset-roll-pitch': - this.getSelectedEditors().forEach(e=>{ - if (!e.box) - return; - e.box.rotation.x =0; - e.box.rotation.y =0; - e.update('dontrender'); - e.box.world.annotation.setModified(); - - onBoxChangedInBatchMode(e.box); - }); - - //this.viewManager.render(); - this.updateAutoGeneratedBoxes(); - - break; - case 'cm-show-trajectory': - this.showTrajectory(); - break; - - case 'cm-check': - { - let scene = this.editingTarget.sceneMeta.scene; - checkScene(scene); - logger.show(); - logger.errorBtn.onclick(); - } - break; - - case 'cm-finalize': - this.finalizeSelectedBoxes(); - break; - - case 'cm-sync-size': - editor.data.worldList.forEach(w=>{ - let box = w.annotation.boxes.find(b=>b.obj_track_id == this.firingBoxEditor.target.objTrackId); - if (box && box !== this.firingBoxEditor.box){ - box.scale.x = this.firingBoxEditor.box.scale.x; - box.scale.y = this.firingBoxEditor.box.scale.y; - box.scale.z = this.firingBoxEditor.box.scale.z; - //saveList.push(w); - w.annotation.setModified(); - - onBoxChangedInBatchMode(box); - } - }); - - //this.activeEditorList().forEach(e=>e.update('dontrender')); - //this.viewManager.render(); - this.updateAutoGeneratedBoxes(); - - break; - case 'cm-reload': - - { - let selectedEditors = this.getSelectedEditors(); - this.reloadAnnotation(selectedEditors); - - this.updateAutoGeneratedBoxes(); - - } - break; - - case 'cm-goto-this-frame': - { - this.gotoThisFrame(); - } - break; - case 'cm-follow-static-objects': - { - let b = this.firingBoxEditor.box; - editor.autoAdjust.followStaticObjects(b); - this.globalHeader.updateModifiedStatus(); - - this.activeEditorList().forEach(e=>{ - e.tryAttach(); - }); - - //this.viewManager.render(); - this.updateAutoGeneratedBoxes(); - - } - break; - }; - - - - return true; - }; - - this.reset = function(){ - this.activeEditorList().forEach(e=>{ - e.setSelected(false); - e.resetTarget(); + break; + + case "cm-finalize": + this.finalizeSelectedBoxes(); + break; + + case "cm-sync-size": + editor.data.worldList.forEach((w) => { + let box = w.annotation.boxes.find( + (b) => b.obj_track_id == this.firingBoxEditor.target.objTrackId + ); + if (box && box !== this.firingBoxEditor.box) { + box.scale.x = this.firingBoxEditor.box.scale.x; + box.scale.y = this.firingBoxEditor.box.scale.y; + box.scale.z = this.firingBoxEditor.box.scale.z; + //saveList.push(w); + w.annotation.setModified(); + + onBoxChangedInBatchMode(box); + } }); - - this.viewManager.mainView.clearView(); - - this.activeIndex = 0; - }; - - - this.keydownHandler = (event)=>{ - - switch(event.key){ - case 'a': - if (event.ctrlKey){ - this.activeEditorList().forEach(e=>e.setSelected(true)); - } - break; - - case 's': - if (event.ctrlKey){ - this._save(); - console.log("saved for batch editor"); - } - break; - case '+': - case '=': - this.editingTarget.data.scale_point_size(1.2); - this.viewManager.render(); - break; - case '-': - this.editingTarget.data.scale_point_size(0.8); - this.viewManager.render(); - break; - case 'v': - case 'Escape': - { - // let selected = this.getSelectedEditors(); - // if (selected.length >= 2){ - // selected.forEach(e=>e.setSelected(false)); - // } - // else - { - this.hide(); - this.reset(); - if (this.onExit) - this.onExit(); - } - } - break; - case 'PageUp': - case '3': - this.prevBatch(); - break; - case 'PageDown': - case '4': - this.nextBatch(); - break; - case 't': - this.showTrajectory(); - break; - default: - console.log(`key ${event.key} igonored`); - break; - } - - return false; - }; - - - let keydownHandler = (event)=>this.keydownHandler(event); + //this.activeEditorList().forEach(e=>e.update('dontrender')); + //this.viewManager.render(); + this.updateAutoGeneratedBoxes(); - this.hide =function(){ - if (this.parentUi.style.display != "none") + break; + case "cm-reload": { - this.parentUi.style.display = "none"; - this.toolbox.style.display = "none"; - //document.removeEventListener("keydown", keydownHandler); - globalKeyDownManager.deregister('batch-editor'); - } + let selectedEditors = this.getSelectedEditors(); + this.reloadAnnotation(selectedEditors); - }; - this.show = function(){ - if (this.parentUi.style.display == "none") - { - this.parentUi.style.display = ""; - //document.addEventListener("keydown", keydownHandler); - globalKeyDownManager.register(keydownHandler, 'batch-editor'); - this.toolbox.style.display = ""; + this.updateAutoGeneratedBoxes(); } - }; + break; - this.render =function() - { - if (this.parentUi.style.display != "none") + case "cm-goto-this-frame": { - this.viewManager.render(); + this.gotoThisFrame(); } - }; - - - this._addToolBox = function(){ - let template = document.getElementById("batch-editor-tools-template"); - let tool = template.content.cloneNode(true); - // this.boxEditorHeaderUi.appendChild(tool); - // return this.boxEditorHeaderUi.lastElementChild; - - document.getElementById("dynamic-buttons-placeholder").appendChild(tool); - return document.getElementById("dynamic-buttons-placeholder").lastElementChild; - }; - - this.toolbox = this._addToolBox(); - - this.reloadAnnotation = function(editorList){ - - let done = (anns)=>{ - // update editor - editorList.forEach(e=>{ - e.tryAttach(); - e.update("dontrender"); - }); - - // reload main view - if (func_on_annotation_reloaded) - func_on_annotation_reloaded(); - // render all, at last - - this.viewManager.render(); - - this.globalHeader.updateModifiedStatus(); - }; - - - let worldList = editorList.map(e=>e.target.world); - - let modifiedFrames =worldList.filter(w=>w && w.annotation.modified); - - if (modifiedFrames.length > 0) + break; + case "cm-follow-static-objects": { - window.editor.infoBox.show( - "Confirm", - `Discard changes to ${modifiedFrames.length} frames, continue to reload?`, - ["yes","no"], - (choice)=>{ - if (choice=="yes") - { - reloadWorldList(worldList, done); - } - } - ); - } - else{ - reloadWorldList(worldList, done); - } + let b = this.firingBoxEditor.box; + editor.autoAdjust.followStaticObjects(b); + this.globalHeader.updateModifiedStatus(); + this.activeEditorList().forEach((e) => { + e.tryAttach(); + }); - + //this.viewManager.render(); + this.updateAutoGeneratedBoxes(); + } + break; } - this.interpolate = async function(applyIndList){ - let boxList = this.activeEditorList().map(e=>e.box); - let worldList = this.activeEditorList().map(e=>e.target.world); - await this.boxOp.interpolateAsync(worldList, boxList, applyIndList); - this.activeEditorList().forEach(e=>e.tryAttach()); - - this.globalHeader.updateModifiedStatus(); - - this.viewManager.render(); - }; - - this.gotoThisFrame = function(){ - let targetFrame = this.firingBoxEditor.target.world.frameInfo.frame; - let targetTrackId = this.firingBoxEditor.target.objTrackId; - this.hide(); + return true; + }; - this.reset(); - if (this.onExit) - this.onExit(targetFrame, targetTrackId); - }; + this.reset = function () { + this.activeEditorList().forEach((e) => { + e.setSelected(false); + e.resetTarget(); + }); - this.autoAnnotate = async function(applyIndList, dontRotate){ - let editors = this.activeEditorList(); - let boxList = editors.map(e=>e.box); - let worldList = editors.map(e=>e.target.world); + this.viewManager.mainView.clearView(); - let onFinishOneBox = (i)=>{ - editors[i].tryAttach(); - editors[i].box.world.annotation.setModified(); - this.viewManager.render(); + this.activeIndex = 0; + }; - this.updateAutoGeneratedBoxes(); + this.keydownHandler = (event) => { + switch (event.key) { + case "a": + if (event.ctrlKey) { + this.activeEditorList().forEach((e) => e.setSelected(true)); } - - await this.boxOp.interpolateAndAutoAdjustAsync(worldList, boxList, onFinishOneBox, applyIndList, dontRotate); + break; - this.globalHeader.updateModifiedStatus(); + case "s": + if (event.ctrlKey) { + this._save(); + console.log("saved for batch editor"); + } + break; + case "+": + case "=": + this.editingTarget.data.scale_point_size(1.2); + this.viewManager.render(); + break; + case "-": + this.editingTarget.data.scale_point_size(0.8); + this.viewManager.render(); + break; + case "v": + case "Escape": + { + // let selected = this.getSelectedEditors(); + // if (selected.length >= 2){ + // selected.forEach(e=>e.setSelected(false)); + // } + // else + { + this.hide(); + this.reset(); + if (this.onExit) this.onExit(); + } + } + break; + case "PageUp": + case "3": + this.prevBatch(); + break; + case "PageDown": + case "4": + this.nextBatch(); + break; + case "t": + this.showTrajectory(); + break; + default: + console.log(`key ${event.key} igonored`); + break; } - // this.parentUi.querySelector("#object-track-id-editor").addEventListener("keydown", function(e){ - // e.stopPropagation();}); - - // this.parentUi.querySelector("#object-track-id-editor").addEventListener("keyup", function(e){ - // e.stopPropagation(); - // }); - - // this.parentUi.querySelector("#object-track-id-editor").onchange = (ev)=>this.object_track_id_changed(ev); - // this.parentUi.querySelector("#object-category-selector").onchange = (ev)=>this.object_category_changed(ev); - - - // this should follow addToolBox - - // this.parentUi.querySelector("#instance-number").value = this.batchSize; - // this.parentUi.querySelector("#instance-number").onchange = (ev)=>{ - // this.batchSize = parseInt(ev.currentTarget.value); - // this.edit( - // this.editingTarget.data, - // this.editingTarget.sceneMeta, - // this.editingTarget.frame, - // this.editingTarget.objTrackId, - // this.editingTarget.objType - // ); - // } + return false; + }; - this.showTrajectory = () =>{ - let tracks = this.editingTarget.data.worldList.map(w=>{ - let box = w.annotation.findBoxByTrackId(this.editingTarget.objTrackId); - let ann = null; - if (box){ - ann = w.annotation.boxToAnn(box); - ann.psr.position = w.lidarPosToUtm(ann.psr.position); - ann.psr.rotation = w.lidarRotToUtm(ann.psr.rotation); - } - return [w.frameInfo.frame, ann, false]; - }); + let keydownHandler = (event) => this.keydownHandler(event); - tracks.sort((a,b)=> (a[0] > b[0])? 1 : -1); + this.hide = function () { + if (this.parentUi.style.display != "none") { + this.parentUi.style.display = "none"; + this.toolbox.style.display = "none"; + //document.removeEventListener("keydown", keydownHandler); + globalKeyDownManager.deregister("batch-editor"); + } + }; + this.show = function () { + if (this.parentUi.style.display == "none") { + this.parentUi.style.display = ""; + //document.addEventListener("keydown", keydownHandler); + globalKeyDownManager.register(keydownHandler, "batch-editor"); + this.toolbox.style.display = ""; + } + }; - this.objectTrackView.setObject( - this.editingTarget.objType, - this.editingTarget.objTrackId, - tracks, - (targetFrame)=>{ //onExit - this.getSelectedEditors().forEach(e=>e.setSelected(false)); - this.activeEditorList().find(e=>e.target.world.frameInfo.frame == targetFrame).setSelected(true); - } - ); - }; + this.render = function () { + if (this.parentUi.style.display != "none") { + this.viewManager.render(); + } + }; - this.toolbox.querySelector("#trajectory").onclick = (e)=>{ - this.showTrajectory(); - }; + this._addToolBox = function () { + let template = document.getElementById("batch-editor-tools-template"); + let tool = template.content.cloneNode(true); + // this.boxEditorHeaderUi.appendChild(tool); + // return this.boxEditorHeaderUi.lastElementChild; - this.toolbox.querySelector("#reload").onclick = (e)=>{ + document.getElementById("dynamic-buttons-placeholder").appendChild(tool); + return document.getElementById("dynamic-buttons-placeholder") + .lastElementChild; + }; - let selectedEditors = this.activeEditorList(); - this.reloadAnnotation(selectedEditors); - }; + this.toolbox = this._addToolBox(); - this.toolbox.querySelector("#interpolate").onclick = async ()=>{ - //this.boxOp.interpolate_selected_object(this.editingTarget.scene, this.editingTarget.objTrackId, ""); - - let applyIndList = this.activeEditorList().map(e=>true); //all shoud be applied. - this.interpolate(applyIndList); - - }; + this.reloadAnnotation = function (editorList) { + let done = (anns) => { + // update editor + editorList.forEach((e) => { + e.tryAttach(); + e.update("dontrender"); + }); - this.toolbox.querySelector("#auto-annotate").onclick = async ()=>{ - let applyIndList = this.activeEditorList().map(e=>true); //all shoud be applied. - this.autoAnnotate(applyIndList); - }; + // reload main view + if (func_on_annotation_reloaded) func_on_annotation_reloaded(); + // render all, at last - this.toolbox.querySelector("#auto-annotate-translate-only").onclick = async ()=>{ - let applyIndList = this.activeEditorList().map(e=>true); //all shoud be applied. - this.autoAnnotate(applyIndList, "dontrotate"); - }; + this.viewManager.render(); - this.toolbox.querySelector("#exit").onclick = ()=>{ - this.hide(); - - this.reset(); - - if (this.onExit) - this.onExit(); + this.globalHeader.updateModifiedStatus(); }; - this.toolbox.querySelector("#next").onclick = ()=>{ - this.nextBatch(); - }; - - this.toolbox.querySelector("#prev").onclick = ()=>{ - this.prevBatch(); - }; + let worldList = editorList.map((e) => e.target.world); - this.nextBatch = function() - { - let maxFrameIndex = this.editingTarget.sceneMeta.frames.length-1; + let modifiedFrames = worldList.filter((w) => w && w.annotation.modified); - let editors = this.activeEditorList() - let lastEditor = editors[editors.length-1]; - if (lastEditor.target.world.frameInfo.frame_index == maxFrameIndex) - { - if (this.batchSize >= this.editingTarget.sceneMeta.frames.length) - { - this.nextObj(); - } - else - { - window.editor.infoBox.show("Info", "This is the last batch of frames."); - } - - } - else - { - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.sceneMeta.frames[Math.min(this.editingTarget.frameIndex + this.batchSize/2, maxFrameIndex)], - this.editingTarget.objTrackId, - this.editingTarget.objType - ); - } - }; - - this.prevBatch = function() - { - let firstEditor = this.activeEditorList()[0]; - if (firstEditor.target.world.frameInfo.frame_index == 0) - { - - if (this.batchSize >= this.editingTarget.sceneMeta.frames.length) - { - this.prevObj(); - } - else - { - window.editor.infoBox.show("Info", "This is the first batch of frames"); - } - - } - else - { - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.sceneMeta.frames[Math.max(this.editingTarget.frameIndex - this.batchSize/2, 0)], - this.editingTarget.objTrackId, - this.editingTarget.objType - ); + if (modifiedFrames.length > 0) { + window.editor.infoBox.show( + "Confirm", + `Discard changes to ${modifiedFrames.length} frames, continue to reload?`, + ["yes", "no"], + (choice) => { + if (choice == "yes") { + reloadWorldList(worldList, done); + } } - - }; - - this.prevObj = function(){ - let idx = objIdManager.objectList.findIndex(x=>x.id==this.editingTarget.objTrackId); - - let objNum = objIdManager.objectList.length; - - idx = (idx + objNum - 1) % objNum; - - let obj = objIdManager.objectList[idx]; - - - - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.sceneMeta.frames[this.editingTarget.frameIndex], - obj.id, - obj.category, + ); + } else { + reloadWorldList(worldList, done); + } + }; + + this.interpolate = async function (applyIndList) { + let boxList = this.activeEditorList().map((e) => e.box); + let worldList = this.activeEditorList().map((e) => e.target.world); + await this.boxOp.interpolateAsync(worldList, boxList, applyIndList); + this.activeEditorList().forEach((e) => e.tryAttach()); + + this.globalHeader.updateModifiedStatus(); + + this.viewManager.render(); + }; + + this.gotoThisFrame = function () { + let targetFrame = this.firingBoxEditor.target.world.frameInfo.frame; + let targetTrackId = this.firingBoxEditor.target.objTrackId; + this.hide(); + + this.reset(); + if (this.onExit) this.onExit(targetFrame, targetTrackId); + }; + + this.autoAnnotate = async function (applyIndList, dontRotate) { + let editors = this.activeEditorList(); + let boxList = editors.map((e) => e.box); + let worldList = editors.map((e) => e.target.world); + + let onFinishOneBox = (i) => { + editors[i].tryAttach(); + editors[i].box.world.annotation.setModified(); + this.viewManager.render(); + + this.updateAutoGeneratedBoxes(); + }; + + await this.boxOp.interpolateAndAutoAdjustAsync( + worldList, + boxList, + onFinishOneBox, + applyIndList, + dontRotate + ); + + this.globalHeader.updateModifiedStatus(); + }; + + // this.parentUi.querySelector("#object-track-id-editor").addEventListener("keydown", function(e){ + // e.stopPropagation();}); + + // this.parentUi.querySelector("#object-track-id-editor").addEventListener("keyup", function(e){ + // e.stopPropagation(); + // }); + + // this.parentUi.querySelector("#object-track-id-editor").onchange = (ev)=>this.object_track_id_changed(ev); + // this.parentUi.querySelector("#object-category-selector").onchange = (ev)=>this.object_category_changed(ev); + + // this should follow addToolBox + + // this.parentUi.querySelector("#instance-number").value = this.batchSize; + // this.parentUi.querySelector("#instance-number").onchange = (ev)=>{ + // this.batchSize = parseInt(ev.currentTarget.value); + // this.edit( + // this.editingTarget.data, + // this.editingTarget.sceneMeta, + // this.editingTarget.frame, + // this.editingTarget.objTrackId, + // this.editingTarget.objType + // ); + // } + + this.showTrajectory = () => { + let tracks = this.editingTarget.data.worldList.map((w) => { + let box = w.annotation.findBoxByTrackId(this.editingTarget.objTrackId); + let ann = null; + if (box) { + ann = w.annotation.boxToAnn(box); + ann.psr.position = w.lidarPosToUtm(ann.psr.position); + ann.psr.rotation = w.lidarRotToUtm(ann.psr.rotation); + } + return [w.frameInfo.frame, ann, false]; + }); + + tracks.sort((a, b) => (a[0] > b[0] ? 1 : -1)); + + this.objectTrackView.setObject( + this.editingTarget.objType, + this.editingTarget.objTrackId, + tracks, + (targetFrame) => { + //onExit + this.getSelectedEditors().forEach((e) => e.setSelected(false)); + this.activeEditorList() + .find((e) => e.target.world.frameInfo.frame == targetFrame) + .setSelected(true); + } + ); + }; + + this.toolbox.querySelector("#trajectory").onclick = (e) => { + this.showTrajectory(); + }; + + this.toolbox.querySelector("#reload").onclick = (e) => { + let selectedEditors = this.activeEditorList(); + this.reloadAnnotation(selectedEditors); + }; + + this.toolbox.querySelector("#interpolate").onclick = async () => { + //this.boxOp.interpolate_selected_object(this.editingTarget.scene, this.editingTarget.objTrackId, ""); + + let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. + this.interpolate(applyIndList); + }; + + this.toolbox.querySelector("#auto-annotate").onclick = async () => { + let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. + this.autoAnnotate(applyIndList); + }; + + this.toolbox.querySelector("#auto-annotate-translate-only").onclick = + async () => { + let applyIndList = this.activeEditorList().map((e) => true); //all shoud be applied. + this.autoAnnotate(applyIndList, "dontrotate"); + }; + + this.toolbox.querySelector("#exit").onclick = () => { + this.hide(); + + this.reset(); + + if (this.onExit) this.onExit(); + }; + + this.toolbox.querySelector("#next").onclick = () => { + this.nextBatch(); + }; + + this.toolbox.querySelector("#prev").onclick = () => { + this.prevBatch(); + }; + + this.nextBatch = function () { + let maxFrameIndex = this.editingTarget.sceneMeta.frames.length - 1; + + let editors = this.activeEditorList(); + let lastEditor = editors[editors.length - 1]; + if (lastEditor.target.world.frameInfo.frame_index == maxFrameIndex) { + if (this.batchSize >= this.editingTarget.sceneMeta.frames.length) { + this.nextObj(); + } else { + window.editor.infoBox.show("Info", "This is the last batch of frames."); + } + } else { + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.sceneMeta.frames[ + Math.min( + this.editingTarget.frameIndex + this.batchSize / 2, + maxFrameIndex + ) + ], + this.editingTarget.objTrackId, + this.editingTarget.objType + ); + } + }; + + this.prevBatch = function () { + let firstEditor = this.activeEditorList()[0]; + if (firstEditor.target.world.frameInfo.frame_index == 0) { + if (this.batchSize >= this.editingTarget.sceneMeta.frames.length) { + this.prevObj(); + } else { + window.editor.infoBox.show( + "Info", + "This is the first batch of frames" ); - }; - - - this.gotoFrame = function(frameID){ - this.getSelectedEditors().forEach(e=>e.setSelected(false)); - this.activeEditorList().find(e=>e.target.world.frameInfo.frame == frameID).setSelected(true); + } + } else { + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.sceneMeta.frames[ + Math.max(this.editingTarget.frameIndex - this.batchSize / 2, 0) + ], + this.editingTarget.objTrackId, + this.editingTarget.objType + ); + } + }; + + this.prevObj = function () { + let idx = objIdManager.objectList.findIndex( + (x) => x.id == this.editingTarget.objTrackId + ); + + let objNum = objIdManager.objectList.length; + + idx = (idx + objNum - 1) % objNum; + + let obj = objIdManager.objectList[idx]; + + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.sceneMeta.frames[this.editingTarget.frameIndex], + obj.id, + obj.category + ); + }; + + this.gotoFrame = function (frameID) { + this.getSelectedEditors().forEach((e) => e.setSelected(false)); + this.activeEditorList() + .find((e) => e.target.world.frameInfo.frame == frameID) + .setSelected(true); + }; + + this.gotoObjectFrame = function (frameId, objId) { + if (objId != this.editingTarget.objTrackId) { + let obj = objIdManager.getObjById(objId); + + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + frameId, + objId, + obj.category + ); } - this.gotoObjectFrame = function(frameId, objId){ - if (objId != this.editingTarget.objTrackId) - { - let obj = objIdManager.getObjById(objId); - - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - frameId, - objId, - obj.category, - ); + this.getSelectedEditors().forEach((e) => e.setSelected(false)); + this.activeEditorList() + .find((e) => e.target.world.frameInfo.frame == frameId) + .setSelected(true); + }; + + this.nextObj = function () { + let idx = objIdManager.objectList.findIndex( + (x) => + x.id == this.editingTarget.objTrackId && + x.category == this.editingTarget.objType + ); + + let objNum = objIdManager.objectList.length; + + idx = (idx + 1) % objNum; + + let obj = objIdManager.objectList[idx]; + + this.edit( + this.editingTarget.data, + this.editingTarget.sceneMeta, + this.editingTarget.sceneMeta.frames[this.editingTarget.frameIndex], + obj.id, + obj.category + ); + }; + + // this.toolbox.querySelector("#save").onclick = ()=>{ + // this._save(); + // }; + + this.toolbox.querySelector("#finalize").onclick = () => { + this.finalize(); + }; + + this.finalize = function () { + this.activeEditorList().forEach((e) => { + if (e.box) { + if (e.box.annotator) { + delete e.box.annotator; + func_on_box_changed(e.box); } + e.box.world.annotation.setModified(); + e.updateInfo(); + } + }); - this.getSelectedEditors().forEach(e=>e.setSelected(false)); - this.activeEditorList().find(e=>e.target.world.frameInfo.frame == frameId).setSelected(true); - } + this.globalHeader.updateModifiedStatus(); + }; - this.nextObj = function(){ - let idx = objIdManager.objectList.findIndex(x=>x.id==this.editingTarget.objTrackId && x.category == this.editingTarget.objType); + this.object_track_id_changed = function (event) { + var id = event.currentTarget.value; - let objNum = objIdManager.objectList.length; - - idx = (idx + 1) % objNum; - - let obj = objIdManager.objectList[idx]; + if (id == "new") { + id = objIdManager.generateNewUniqueId(); + this.parentUi.querySelector("#object-track-id-editor").value = id; + } - this.edit( - this.editingTarget.data, - this.editingTarget.sceneMeta, - this.editingTarget.sceneMeta.frames[this.editingTarget.frameIndex], - obj.id, - obj.category, - ); - }; + this.activeEditorList().forEach((e) => { + if (e.box) { + e.box.obj_track_id = id; + } + }); + }; + + this.object_category_changed = function (event) { + let obj_type = event.currentTarget.value; + this.activeEditorList().forEach((e) => { + if (e.box) { + e.box.obj_type = obj_type; + } + }); + }; + + this._save = function () { + let worldList = []; + let editorList = []; + this.activeEditorList().forEach((e) => { + worldList.push(e.target.world); + editorList.push(e); + }); + + saveWorldList(worldList); + }; + + this.updateViewZoomRatio = function (viewIndex, ratio) { + const dontRender = true; + this.activeEditorList().forEach((e) => { + e._setViewZoomRatio(viewIndex, ratio); + e.update(dontRender); + }); + + // render all + this.viewManager.render(); + }; + + this.addEditor = function () { + let editor = this.allocateEditor(); + this.activeIndex += 1; + return editor; + }; + + this.allocateEditor = function () { + if (this.activeIndex >= this.editorList.length) { + let editor = new BoxEditor( + this.boxEditorGroupUi, + this, + this.viewManager, + cfg, + this.boxOp, + func_on_box_changed, + func_on_box_remove, + String(this.activeIndex) + ); - // this.toolbox.querySelector("#save").onclick = ()=>{ - // this._save(); - // }; + // resizable for the first editor - this.toolbox.querySelector("#finalize").onclick = ()=>{ - this.finalize(); - }; + if (this.editorList.length == 0) { + editor.setResize("both"); + } - this.finalize = function(){ - this.activeEditorList().forEach(e=>{ - if (e.box){ + this.editorList.push(editor); - if (e.box.annotator){ - delete e.box.annotator; - func_on_box_changed(e.box); - } - e.box.world.annotation.setModified(); - e.updateInfo(); - } - }); + return editor; + } else { + return this.editorList[this.activeIndex]; + } + }; - this.globalHeader.updateModifiedStatus(); - }; + this.getEditorByMousePosition = function (x, y) { + return this.editorList.find((e) => { + let rect = e.ui.getBoundingClientRect(); - this.object_track_id_changed = function(event){ - var id = event.currentTarget.value; + return x > rect.left && x < rect.right && y > rect.top && y < rect.bottom; + }); + }; - if (id == "new"){ - id = objIdManager.generateNewUniqueId(); - this.parentUi.querySelector("#object-track-id-editor").value=id; - } + this.parentUi.onmousedown = (event) => { + if (event.which != 1) return; - this.activeEditorList().forEach(e=>{ - if (e.box){ - e.box.obj_track_id = id; - } - }); + let eventId = Date.now(); + let select_start_pos = { + x: event.clientX, + y: event.clientY, }; - this.object_category_changed = function(event){ - let obj_type = event.currentTarget.value; - this.activeEditorList().forEach(e=>{ - if (e.box){ - e.box.obj_type = obj_type; - } - }); + console.log("box editor manager, on mouse down.", select_start_pos); + + let select_end_pos = { + x: event.clientX, + y: event.clientY, }; + let leftMouseDown = true; - this._save = function(){ - let worldList = [] - let editorList = [] - this.activeEditorList().forEach(e=>{ - worldList.push(e.target.world); - editorList.push(e); - }); + // a1 a2) [a1, a2] = [a2, a1]; + if (b1 > b2) [b1, b2] = [b2, b1]; - - saveWorldList(worldList); + return ( + (a1 > b1 && a1 < b2) || + (a2 > b1 && a2 < b2) || + (b1 > a1 && b1 < a2) || + (b2 > a1 && b2 < a2) + ); } - - this.updateViewZoomRatio = function(viewIndex, ratio){ - const dontRender=true; - this.activeEditorList().forEach(e=>{ - e._setViewZoomRatio(viewIndex, ratio); - e.update(dontRender); - }) - - // render all - this.viewManager.render(); + // a,b: left, right, right, bottom + function intersect(domRect, mouseA, mouseB) { + return ( + lineIntersect( + select_end_pos.x, + select_start_pos.x, + domRect.left, + domRect.right + ) && + lineIntersect( + select_end_pos.y, + select_start_pos.y, + domRect.top, + domRect.bottom + ) + ); } - this.addEditor = function(){ - let editor = this.allocateEditor(); - this.activeIndex += 1; - return editor; - }; - - this.allocateEditor = function(){ - if (this.activeIndex >= this.editorList.length){ - let editor = new BoxEditor(this.boxEditorGroupUi, this, this.viewManager, cfg, this.boxOp, func_on_box_changed, func_on_box_remove, String(this.activeIndex)); - - // resizable for the first editor + this.parentUi.onmousemove = (event) => { + select_end_pos.x = event.clientX; + select_end_pos.y = event.clientY; - if (this.editorList.length == 0) - { - editor.setResize("both"); - } - - this.editorList.push(editor); - - return editor; - }else{ - return this.editorList[this.activeIndex]; - } - }; - - - this.getEditorByMousePosition = function(x,y){ - - return this.editorList.find(e=>{ - let rect = e.ui.getBoundingClientRect(); + this.editorList.forEach((e) => { + let rect = e.ui.getBoundingClientRect(); + let intersected = intersect(rect, select_start_pos, select_end_pos); - return x > rect.left && x < rect.right && y > rect.top && y < rect.bottom; - }) + e.setSelected(intersected, event.ctrlKey ? eventId : null); + }); }; - - - this.parentUi.onmousedown= (event)=>{ - - if (event.which!=1) - return; - - let eventId = Date.now(); - - let select_start_pos={ - x: event.clientX, - y: event.clientY, - } - - console.log("box editor manager, on mouse down.", select_start_pos); - - let select_end_pos={ - x: event.clientX, - y: event.clientY, - } - let leftMouseDown = true; - - // a1 a2) [a1,a2]=[a2,a1]; - if (b1 > b2) [b1,b2]=[b2,b1]; - - return (a1 > b1 && a1 < b2) || (a2 > b1 && a2 < b2) || (b1 > a1 && b1 < a2) || (b2 > a1 && b2 < a2) - } + this.parentUi.onmouseup = (event) => { + if (event.which != 1) return; - // a,b: left, right, right, bottom - function intersect(domRect, mouseA, mouseB){ - return (lineIntersect(select_end_pos.x, select_start_pos.x, domRect.left, domRect.right) && - lineIntersect(select_end_pos.y, select_start_pos.y, domRect.top, domRect.bottom)) - } + leftMouseDown = false; + this.parentUi.onmousemove = null; + this.parentUi.onmouseup = null; - + if ( + event.clientX == select_start_pos.x && + event.clientY == select_start_pos.y + ) { + // click - this.parentUi.onmousemove = (event)=>{ - select_end_pos.x = event.clientX; - select_end_pos.y = event.clientY; - - this.editorList.forEach(e=>{ - let rect = e.ui.getBoundingClientRect(); - let intersected = intersect(rect, select_start_pos, select_end_pos); - - e.setSelected(intersected, event.ctrlKey?eventId:null); - - }) - } + let ed = this.getEditorByMousePosition(event.clientX, event.clientY); - this.parentUi.onmouseup = (event) =>{ - if (event.which!=1) - return; - - leftMouseDown = false; - this.parentUi.onmousemove = null; - this.parentUi.onmouseup = null; - - - if (event.clientX == select_start_pos.x && event.clientY == select_start_pos.y) - { // click - - let ed = this.getEditorByMousePosition(event.clientX, event.clientY); - - - if (event.shiftKey) - { - let selectedEditors = this.getSelectedEditors(); - if (selectedEditors.length == 0) - { - - } - else if (ed.index < selectedEditors[0].index) - { - this.activeEditorList().forEach(e=>{ - if (e.index >= ed.index && e.index < selectedEditors[0].index){ - e.setSelected(true); - } - }); - } - else if (ed.index > selectedEditors[selectedEditors.length-1].index) - { - this.activeEditorList().forEach(e=>{ - if (e.index <= ed.index && e.index > selectedEditors[selectedEditors.length-1].index){ - e.setSelected(true); - } - }); - } - } - else if (event.ctrlKey) - { - ed.setSelected(!ed.selected); - } - else - { - let selectedEditors = this.getSelectedEditors(); - - - if (ed){ - if (ed.selected && selectedEditors.length == 1) - { - ed.setSelected(false); - } - else - { - selectedEditors.forEach(e=>e.setSelected(false)); - ed.setSelected(true); - } - } - else{ - selectedEditors.forEach(e=>e.setSelected(false)); - } - } + if (event.shiftKey) { + let selectedEditors = this.getSelectedEditors(); + if (selectedEditors.length == 0) { + } else if (ed.index < selectedEditors[0].index) { + this.activeEditorList().forEach((e) => { + if (e.index >= ed.index && e.index < selectedEditors[0].index) { + e.setSelected(true); + } + }); + } else if ( + ed.index > selectedEditors[selectedEditors.length - 1].index + ) { + this.activeEditorList().forEach((e) => { + if ( + e.index <= ed.index && + e.index > selectedEditors[selectedEditors.length - 1].index + ) { + e.setSelected(true); + } + }); + } + } else if (event.ctrlKey) { + ed.setSelected(!ed.selected); + } else { + let selectedEditors = this.getSelectedEditors(); + + if (ed) { + if (ed.selected && selectedEditors.length == 1) { + ed.setSelected(false); + } else { + selectedEditors.forEach((e) => e.setSelected(false)); + ed.setSelected(true); } + } else { + selectedEditors.forEach((e) => e.setSelected(false)); + } } - } - - this.getSelectedEditors = function(){ - return this.editorList.filter(e=>e.selected); - } + } + }; + }; + this.getSelectedEditors = function () { + return this.editorList.filter((e) => e.selected); + }; } -export {BoxEditorManager, BoxEditor}; \ No newline at end of file +export { BoxEditorManager, BoxEditor }; diff --git a/public/js/box_op.js b/public/js/box_op.js index 8f41f46..d1453a3 100644 --- a/public/js/box_op.js +++ b/public/js/box_op.js @@ -1,164 +1,195 @@ -import * as THREE from './lib/three.module.js'; +import * as THREE from "./lib/three.module.js"; -import {logger} from "./log.js" -import { - Quaternion, - Vector3 -} from "./lib/three.module.js"; - -import{ml} from "./ml.js"; -import {dotproduct, transpose, matmul, euler_angle_to_rotate_matrix_3by3} from "./util.js" - - -function BoxOp(){ - console.log("BoxOp called"); - this.grow_box_distance_threshold = 0.3; - this.init_scale_ratio = {x:2, y:2, z:3}; +import { logger } from "./log.js"; +import { Quaternion, Vector3 } from "./lib/three.module.js"; - this.fit_bottom = function(box) - { - let bottom = box.world.lidar.findBottom(box, {x:2, y:2, z:3}); - this.translate_box(box, 'z', bottom + box.scale.z/2); - } - - this.fit_top = function(box) - { - let top = box.world.lidar.findTop(box, {x:1.2, y:1.2, z:2}); - this.translate_box(box, 'z', top - box.scale.z/2); +import { ml } from "./ml.js"; +import { + dotproduct, + transpose, + matmul, + euler_angle_to_rotate_matrix_3by3, +} from "./util.js"; + +function BoxOp() { + console.log("BoxOp called"); + this.grow_box_distance_threshold = 0.3; + this.init_scale_ratio = { x: 2, y: 2, z: 3 }; + + this.fit_bottom = function (box) { + let bottom = box.world.lidar.findBottom(box, { x: 2, y: 2, z: 3 }); + this.translate_box(box, "z", bottom + box.scale.z / 2); + }; + + this.fit_top = function (box) { + let top = box.world.lidar.findTop(box, { x: 1.2, y: 1.2, z: 2 }); + this.translate_box(box, "z", top - box.scale.z / 2); + }; + + this.fit_left = function (box) { + var extreme = box.world.lidar.grow_box( + box, + this.grow_box_distance_threshold, + this.init_scale_ratio + ); + + if (extreme) { + this.translate_box(box, "y", extreme.max.y - box.scale.y / 2); } + }; - this.fit_left = function(box) - { - var extreme = box.world.lidar.grow_box(box, this.grow_box_distance_threshold, this.init_scale_ratio); + this.fit_right = function (box) { + var extreme = box.world.lidar.grow_box( + box, + this.grow_box_distance_threshold, + this.init_scale_ratio + ); - if (extreme){ - this.translate_box(box, 'y', extreme.max.y - box.scale.y/2); - } + if (extreme) { + this.translate_box(box, "y", extreme.min.y + box.scale.y / 2); } + }; - this.fit_right = function(box) - { - var extreme = box.world.lidar.grow_box(box, this.grow_box_distance_threshold, this.init_scale_ratio); + this.fit_front = function (box) { + var extreme = box.world.lidar.grow_box( + box, + this.grow_box_distance_threshold, + this.init_scale_ratio + ); - if (extreme){ - this.translate_box(box, 'y', extreme.min.y + box.scale.y/2); - } + if (extreme) { + this.translate_box(box, "x", extreme.max.x - box.scale.x / 2); } + }; - this.fit_front = function(box) - { - var extreme = box.world.lidar.grow_box(box, this.grow_box_distance_threshold, this.init_scale_ratio); + this.fit_rear = function (box) { + var extreme = box.world.lidar.grow_box( + box, + this.grow_box_distance_threshold, + this.init_scale_ratio + ); - if (extreme){ - this.translate_box(box, 'x', extreme.max.x - box.scale.x/2); - } + if (extreme) { + this.translate_box(box, "x", extreme.min.x + box.scale.x / 2); + } + }; + + this.fit_size = function (box, axies) { + this.grow_box( + box, + this.grow_box_distance_threshold, + { x: 2, y: 2, z: 3 }, + axies + ); + }; + + this.justifyAutoAdjResult = function (orgBox, box) { + let distance = Math.sqrt( + (box.position.x - orgBox.position.x) * + (box.position.x - orgBox.position.x) + + (box.position.y - orgBox.position.y) * + (box.position.y - orgBox.position.y) + + (box.position.z - orgBox.position.z) * + (box.position.z - orgBox.position.z) + ); + + if ( + distance > + Math.sqrt( + box.scale.x * box.scale.x + + box.scale.y * box.scale.y + + box.scale.z * box.scale.z + ) + ) { + return false; } - this.fit_rear = function(box) - { - var extreme = box.world.lidar.grow_box(box, this.grow_box_distance_threshold, this.init_scale_ratio); - - if (extreme){ - this.translate_box(box, 'x', extreme.min.x + box.scale.x/2); - } + // if (Math.abs(box.rotation.z - orgBox.rotation.z) > Math.PI/4) + // { + // return false; + // } + + if ( + box.scale.x > orgBox.scale.x * 3 || + box.scale.y > orgBox.scale.y * 3 || + box.scale.z > orgBox.scale.z * 3 + ) { + return false; } + return true; + }; + + this.auto_rotate_xyz = async function ( + box, + callback, + apply_mask, + on_box_changed, + noscaling, + rotate_method + ) { + let orgBox = box; + box = { + position: { x: box.position.x, y: box.position.y, z: box.position.z }, + rotation: { x: box.rotation.x, y: box.rotation.y, z: box.rotation.z }, + scale: { x: box.scale.x, y: box.scale.y, z: box.scale.z }, + world: box.world, + }; + // auto grow + // save scale + let grow = (box) => { + let org_scale = { + x: box.scale.x, + y: box.scale.y, + z: box.scale.z, + }; + this.grow_box(box, this.grow_box_distance_threshold, { + x: 2, + y: 2, + z: 3, + }); + this.auto_shrink_box(box); + // now box has been centered. + + let points_indices = box.world.lidar.get_points_of_box(box, 1.0).index; + let extreme = box.world.lidar.get_dimension_of_points( + points_indices, + box + ); + // restore scale + if (noscaling) { + box.scale.x = org_scale.x; + box.scale.y = org_scale.y; + box.scale.z = org_scale.z; + } + // + return extreme; + }; + //points is N*3 shape - this.fit_size = function(box,axies) - { - this.grow_box(box, this.grow_box_distance_threshold, {x:2, y:2, z:3}, axies); - } + let applyRotation = (ret, extreme_after_grow) => { + let angle = ret.angle; + if (!angle) { + console.log("prediction not implemented?"); + return; + } - + //var points_indices = box.world.get_points_indices_of_box(box); + let points_indices = box.world.lidar.get_points_of_box(box, 1.0).index; - this.justifyAutoAdjResult = function(orgBox, box) - { - let distance = Math.sqrt((box.position.x-orgBox.position.x)*(box.position.x-orgBox.position.x) + - (box.position.y-orgBox.position.y)*(box.position.y-orgBox.position.y) + - (box.position.z-orgBox.position.z)*(box.position.z-orgBox.position.z)); - - if (distance > Math.sqrt(box.scale.x*box.scale.x + box.scale.y*box.scale.y + box.scale.z*box.scale.z)) - { - return false; - } - - // if (Math.abs(box.rotation.z - orgBox.rotation.z) > Math.PI/4) - // { - // return false; - // } - - if (box.scale.x > orgBox.scale.x*3 || - box.scale.y > orgBox.scale.y*3 || - box.scale.z > orgBox.scale.z*3) - { - return false; - } - - return true; - } - - - this.auto_rotate_xyz= async function(box, callback, apply_mask, on_box_changed, noscaling, rotate_method){ - - let orgBox = box; - box = { - position: {x: box.position.x, y: box.position.y, z: box.position.z}, - rotation: {x: box.rotation.x, y: box.rotation.y, z: box.rotation.z}, - scale: {x: box.scale.x, y: box.scale.y, z: box.scale.z}, - world: box.world, - }; - - // auto grow - // save scale - let grow = (box)=>{ - let org_scale = { - x: box.scale.x, - y: box.scale.y, - z: box.scale.z, - }; - this.grow_box(box, this.grow_box_distance_threshold, {x:2, y:2, z:3}); - this.auto_shrink_box(box); - // now box has been centered. - - let points_indices = box.world.lidar.get_points_of_box(box,1.0).index; - let extreme = box.world.lidar.get_dimension_of_points(points_indices, box); - // restore scale - if (noscaling){ - box.scale.x = org_scale.x; - box.scale.y = org_scale.y; - box.scale.z = org_scale.z; - } - // - return extreme; - }; + var euler_delta = { + x: angle[0], + y: angle[1], + z: angle[2], + }; - //points is N*3 shape + if (euler_delta.z > Math.PI) { + euler_delta.z -= Math.PI * 2; + } - let applyRotation = (ret, extreme_after_grow)=>{ - - let angle = ret.angle; - if (!angle){ - console.log("prediction not implemented?"); - return; - } - - - //var points_indices = box.world.get_points_indices_of_box(box); - let points_indices = box.world.lidar.get_points_of_box(box,1.0).index; - - var euler_delta = { - x: angle[0], - y: angle[1], - z: angle[2] - }; - - if (euler_delta.z > Math.PI){ - euler_delta.z -= Math.PI*2; - }; - - /* + /* var composite_angel = linalg_std.euler_angle_composite(box.rotation, euler_delta); console.log("orig ", box.rotation.x, box.rotation.y, box.rotation.z); @@ -169,676 +200,722 @@ function BoxOp(){ box.rotation.y = composite_angel.y; box.rotation.z = composite_angel.z; */ - - if (apply_mask){ - if (apply_mask.x) - box.rotation.x = euler_delta.x; - if (apply_mask.y) - box.rotation.y = euler_delta.y; - if (apply_mask.z) - box.rotation.z = euler_delta.z; - } - else{ - box.rotation.x = euler_delta.x; - box.rotation.y = euler_delta.y; - box.rotation.z = euler_delta.z; - } - - - // rotation set, now rescaling the box - // important: should use original points before rotation set - var extreme = box.world.lidar.get_dimension_of_points(points_indices, box); - - let auto_adj_dimension = []; - - if (apply_mask){ - if (apply_mask.x || apply_mask.y) - auto_adj_dimension.push('z'); - - if (apply_mask.x || apply_mask.z) - auto_adj_dimension.push('y'); - - if (apply_mask.y || apply_mask.z) - auto_adj_dimension.push('x'); - } - else{ - auto_adj_dimension = ['x','y','z']; - } - - if (!noscaling){ - auto_adj_dimension.forEach((axis)=>{ - this.translate_box(box, axis, (extreme.max[axis] + extreme.min[axis])/2); - box.scale[axis] = extreme.max[axis] - extreme.min[axis]; - }) - }else { - //anyway, we move the box in a way - let trans = euler_angle_to_rotate_matrix_3by3(box.rotation); - trans = transpose(trans, 3); - - // compute the relative position of the origin point,that is, the lidar's position - // note the origin point is offseted, we need to restore first. - let boxpos = box.position; - let orgPoint = [ - - boxpos.x, - - boxpos.y, - - boxpos.z, - ]; - let orgPointInBoxCoord = matmul(trans, orgPoint, 3); - let relativePosition = { - x: orgPointInBoxCoord[0], - y: orgPointInBoxCoord[1], - z: 1, //orgPointInBoxCoord[2], - } - - if (extreme_after_grow) - extreme = extreme_after_grow; - - auto_adj_dimension.forEach((axis)=>{ - if (relativePosition[axis]>0){ - //stick to max - this.translate_box(box, axis, extreme.max[axis] - box.scale[axis]/2); - }else{ - //stick to min - this.translate_box(box, axis, extreme.min[axis] + box.scale[axis]/2); - } - - - - }) - - } - - return box; - }; - - - let postProc = (box)=>{ - - if (this.justifyAutoAdjResult(orgBox, box)) - { - // copy back - orgBox.position.x = box.position.x; - orgBox.position.y = box.position.y; - orgBox.position.z = box.position.z; - - orgBox.rotation.x = box.rotation.x; - orgBox.rotation.y = box.rotation.y; - orgBox.rotation.z = box.rotation.z; - - orgBox.scale.x = box.scale.x; - orgBox.scale.y = box.scale.y; - orgBox.scale.z = box.scale.z; - } - - if (on_box_changed) - on_box_changed(orgBox); - - if (callback){ - callback(); - } - return orgBox; + if (apply_mask) { + if (apply_mask.x) box.rotation.x = euler_delta.x; + if (apply_mask.y) box.rotation.y = euler_delta.y; + if (apply_mask.z) box.rotation.z = euler_delta.z; + } else { + box.rotation.x = euler_delta.x; + box.rotation.y = euler_delta.y; + box.rotation.z = euler_delta.z; + } + + // rotation set, now rescaling the box + // important: should use original points before rotation set + var extreme = box.world.lidar.get_dimension_of_points( + points_indices, + box + ); + + let auto_adj_dimension = []; + + if (apply_mask) { + if (apply_mask.x || apply_mask.y) auto_adj_dimension.push("z"); + + if (apply_mask.x || apply_mask.z) auto_adj_dimension.push("y"); + + if (apply_mask.y || apply_mask.z) auto_adj_dimension.push("x"); + } else { + auto_adj_dimension = ["x", "y", "z"]; + } + + if (!noscaling) { + auto_adj_dimension.forEach((axis) => { + this.translate_box( + box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } else { + //anyway, we move the box in a way + let trans = euler_angle_to_rotate_matrix_3by3(box.rotation); + trans = transpose(trans, 3); + + // compute the relative position of the origin point,that is, the lidar's position + // note the origin point is offseted, we need to restore first. + let boxpos = box.position; + let orgPoint = [-boxpos.x, -boxpos.y, -boxpos.z]; + let orgPointInBoxCoord = matmul(trans, orgPoint, 3); + let relativePosition = { + x: orgPointInBoxCoord[0], + y: orgPointInBoxCoord[1], + z: 1, //orgPointInBoxCoord[2], }; - let extreme_after_grow = grow(box); - - if (!rotate_method){ - let points = box.world.lidar.get_points_relative_coordinates_of_box_wo_rotation(box, 1); - //let points = box.world.get_points_relative_coordinates_of_box(box, 1.0); - - points = points.filter(function(p){ - return p[2] > - box.scale.z/2 + 0.3; - }) - - let retBox = await ml.predict_rotation(points) - .then(applyRotation) - .then(postProc); - - return retBox; - } - if (rotate_method == "moving-direction") - { - let estimatedRot = this.estimate_rotation_by_moving_direciton(box); - - applyRotation({ - angle:[ - box.rotation.x, // use original rotation - box.rotation.y, // use original rotation - estimatedRot? estimatedRot.z : box.rotation.z, // use original rotation - ] - }, - extreme_after_grow); - - postProc(box); - return box; - } - else{ //dont rotate, or null - applyRotation({ - angle:[ - box.rotation.x, // use original rotation - box.rotation.y, // use original rotation - box.rotation.z, // use original rotation - ] - }, - extreme_after_grow); - - postProc(box); - return box; - } - - - } - - this.auto_shrink_box= function(box){ - var extreme = box.world.lidar.get_points_dimmension_of_box(box); - - ['x', 'y','z'].forEach((axis)=>{ - - this.translate_box(box, axis, (extreme.max[axis] + extreme.min[axis])/2); - box.scale[axis] = extreme.max[axis]-extreme.min[axis]; - }) - - }; - - - this.estimate_rotation_by_moving_direciton = function(box) - { - let prevWorld = box.world.data.findWorld(box.world.frameInfo.scene, - box.world.frameInfo.frame_index-1); - - let nextWorld = box.world.data.findWorld(box.world.frameInfo.scene, - box.world.frameInfo.frame_index+1); - - let prevBox = prevWorld?prevWorld.annotation.findBoxByTrackId(box.obj_track_id): null; - let nextBox = nextWorld?nextWorld.annotation.findBoxByTrackId(box.obj_track_id): null; + if (extreme_after_grow) extreme = extreme_after_grow; + + auto_adj_dimension.forEach((axis) => { + if (relativePosition[axis] > 0) { + //stick to max + this.translate_box( + box, + axis, + extreme.max[axis] - box.scale[axis] / 2 + ); + } else { + //stick to min + this.translate_box( + box, + axis, + extreme.min[axis] + box.scale[axis] / 2 + ); + } + }); + } - if (prevBox && nextBox) - { - if ((prevBox.annotator && nextBox.annotator) || (!prevBox.annotator && !nextBox.annotator)) - { - // all annotated by machine or man, it's ok - } - else - { - // only one is manually annotated, use this one. - if (prevBox.annotator) - prevBox = null; - - if (nextBox.annotator) - nextBox = null; - } - } - - - if (!nextBox && !prevBox){ - logger.logcolor("red", "Cannot estimate direction: neither previous nor next frame/box loaded/annotated.") - return null; - } - - let currentP = box.world.lidarPosToUtm(box.position); - let nextP = nextBox?nextBox.world.lidarPosToUtm(nextBox.position) : null; - let prevP = prevBox?prevBox.world.lidarPosToUtm(prevBox.position) : null; - - if (!prevP) - prevP = currentP; - - if (!nextP) - nextP = currentP; - - let azimuth = Math.atan2(nextP.y-prevP.y, nextP.x-prevP.x) - - let estimatedRot = box.world.utmRotToLidar(new THREE.Euler(0,0,azimuth, "XYZ")); - - return estimatedRot; + return box; }; - this.grow_box= function(box, min_distance, init_scale_ratio, axies){ + let postProc = (box) => { + if (this.justifyAutoAdjResult(orgBox, box)) { + // copy back + orgBox.position.x = box.position.x; + orgBox.position.y = box.position.y; + orgBox.position.z = box.position.z; - if (!axies) - { - axies = ['x','y','z']; - } - - - var extreme = box.world.lidar.grow_box(box, min_distance, init_scale_ratio); + orgBox.rotation.x = box.rotation.x; + orgBox.rotation.y = box.rotation.y; + orgBox.rotation.z = box.rotation.z; - if (extreme){ + orgBox.scale.x = box.scale.x; + orgBox.scale.y = box.scale.y; + orgBox.scale.z = box.scale.z; + } - axies.forEach((axis)=>{ - this.translate_box(box, axis, (extreme.max[axis] + extreme.min[axis])/2); - box.scale[axis] = extreme.max[axis] - extreme.min[axis]; - }) - } + if (on_box_changed) on_box_changed(orgBox); + if (callback) { + callback(); + } + return orgBox; }; - this.change_rotation_y = function(box, theta, sticky, on_box_changed){ - //box.rotation.x += theta; - //on_box_changed(box); - - var points_indices = box.world.lidar.get_points_indices_of_box(box); - - var _tempQuaternion = new Quaternion(); - var rotationAxis = new Vector3(0, 1, 0); + let extreme_after_grow = grow(box); - // NOTE: the front/end subview is different from top/side view, that we look at the reverse direction of y-axis - // it's end view acturally. - // we could project front-view, but the translation (left, right) will be in reverse direction of top view. - /// that would be frustrating. - box.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, -theta ) ).normalize(); + if (!rotate_method) { + let points = + box.world.lidar.get_points_relative_coordinates_of_box_wo_rotation( + box, + 1 + ); + //let points = box.world.get_points_relative_coordinates_of_box(box, 1.0); - if (sticky){ - var extreme = box.world.lidar.get_dimension_of_points(points_indices, box); + points = points.filter(function (p) { + return p[2] > -box.scale.z / 2 + 0.3; + }); - ['x','z'].forEach((axis)=>{ + let retBox = await ml + .predict_rotation(points) + .then(applyRotation) + .then(postProc); - this.translate_box(box, axis, (extreme.max[axis] + extreme.min[axis])/2); - box.scale[axis] = extreme.max[axis] - extreme.min[axis]; - - }) - } - - if (on_box_changed) - on_box_changed(box); + return retBox; } + if (rotate_method == "moving-direction") { + let estimatedRot = this.estimate_rotation_by_moving_direciton(box); - - this.auto_rotate_y=function(box, on_box_changed){ - let points = box.world.lidar.get_points_of_box(box, 2.0); - - // 1. find surounding points - var side_indices = [] - var side_points = [] - points.position.forEach(function(p, i){ - if ((p[0] > box.scale.x/2 || p[0] < -box.scale.x/2) && (p[1] < box.scale.y/2 && p[1] > -box.scale.y/2)){ - side_indices.push(points.index[i]); - side_points.push(points.position[i]); - } - }) - - - var end_indices = [] - var end_points = [] - points.position.forEach(function(p, i){ - if ((p[0] < box.scale.x/2 && p[0] > -box.scale.x/2) && (p[1] > box.scale.y/2 || p[1] < -box.scale.y/2)){ - end_indices.push(points.index[i]); - end_points.push(points.position[i]); - } - }) - - - // 2. grid by 0.3 by 0.3 - - // compute slope (derivative) - // for side part (pitch/tilt), use y,z axis - // for end part (row), use x, z axis - - - - // box.world.lidar.set_spec_points_color(side_indices, {x:1,y:0,z:0}); - // box.world.lidar.set_spec_points_color(end_indices, {x:0,y:0,z:1}); - // box.world.lidar.update_points_color(); - - var x = end_points.map(function(x){return x[0]}); - //var y = side_points.map(function(x){return x[1]}); - var z = end_points.map(function(x){return x[2]}); - var z_mean = z.reduce(function(x,y){return x+y;}, 0)/z.length; - var z = z.map(function(x){return x-z_mean;}); - var theta = Math.atan2(dotproduct(x,z), dotproduct(x,x)); - console.log(theta); - - this.change_rotation_y(box, theta, false, on_box_changed); + applyRotation( + { + angle: [ + box.rotation.x, // use original rotation + box.rotation.y, // use original rotation + estimatedRot ? estimatedRot.z : box.rotation.z, // use original rotation + ], + }, + extreme_after_grow + ); + + postProc(box); + return box; + } else { + //dont rotate, or null + applyRotation( + { + angle: [ + box.rotation.x, // use original rotation + box.rotation.y, // use original rotation + box.rotation.z, // use original rotation + ], + }, + extreme_after_grow + ); + + postProc(box); + return box; + } + }; + + this.auto_shrink_box = function (box) { + var extreme = box.world.lidar.get_points_dimmension_of_box(box); + + ["x", "y", "z"].forEach((axis) => { + this.translate_box( + box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + }; + + this.estimate_rotation_by_moving_direciton = function (box) { + let prevWorld = box.world.data.findWorld( + box.world.frameInfo.scene, + box.world.frameInfo.frame_index - 1 + ); + + let nextWorld = box.world.data.findWorld( + box.world.frameInfo.scene, + box.world.frameInfo.frame_index + 1 + ); + + let prevBox = prevWorld + ? prevWorld.annotation.findBoxByTrackId(box.obj_track_id) + : null; + let nextBox = nextWorld + ? nextWorld.annotation.findBoxByTrackId(box.obj_track_id) + : null; + + if (prevBox && nextBox) { + if ( + (prevBox.annotator && nextBox.annotator) || + (!prevBox.annotator && !nextBox.annotator) + ) { + // all annotated by machine or man, it's ok + } else { + // only one is manually annotated, use this one. + if (prevBox.annotator) prevBox = null; + + if (nextBox.annotator) nextBox = null; + } } + if (!nextBox && !prevBox) { + logger.logcolor( + "red", + "Cannot estimate direction: neither previous nor next frame/box loaded/annotated." + ); + return null; + } + let currentP = box.world.lidarPosToUtm(box.position); + let nextP = nextBox ? nextBox.world.lidarPosToUtm(nextBox.position) : null; + let prevP = prevBox ? prevBox.world.lidarPosToUtm(prevBox.position) : null; - this.change_rotation_x=function(box, theta, sticky, on_box_changed){ - var points_indices = box.world.lidar.get_points_indices_of_box(box); - - //box.rotation.x += theta; - //on_box_changed(box); - var _tempQuaternion = new Quaternion(); - var rotationAxis = new Vector3(1,0,0); - box.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, theta ) ).normalize(); - - if (sticky){ - var extreme = box.world.lidar.get_dimension_of_points(points_indices, box); - - ['y','z'].forEach((axis)=>{ - - this.translate_box(box, axis, (extreme.max[axis] + extreme.min[axis])/2); - box.scale[axis] = extreme.max[axis] - extreme.min[axis]; - - }) - } - - if (on_box_changed) - on_box_changed(box); - - }; - - - this.auto_rotate_x=function(box, on_box_changed){ - console.log("x auto ratote"); - - let points = box.world.lidar.get_points_of_box(box, 2.0); - - // 1. find surounding points - var side_indices = [] - var side_points = [] - points.position.forEach(function(p, i){ - if ((p[0] > box.scale.x/2 || p[0] < -box.scale.x/2) && (p[1] < box.scale.y/2 && p[1] > -box.scale.y/2)){ - side_indices.push(points.index[i]); - side_points.push(points.position[i]); - } - }) - - - var end_indices = [] - var end_points = [] - points.position.forEach(function(p, i){ - if ((p[0] < box.scale.x/2 && p[0] > -box.scale.x/2) && (p[1] > box.scale.y/2 || p[1] < -box.scale.y/2)){ - end_indices.push(points.index[i]); - end_points.push(points.position[i]); - } - }) - - - // 2. grid by 0.3 by 0.3 - - // compute slope (derivative) - // for side part (pitch/tilt), use y,z axis - // for end part (row), use x, z axis - - - - // box.world.lidar.set_spec_points_color(side_indices, {x:1,y:0,z:0}); - // box.world.lidar.set_spec_points_color(end_indices, {x:0,y:0,z:1}); - // box.world.lidar.update_points_color(); - //render(); - - var x = side_points.map(function(x){return x[0]}); - var y = side_points.map(function(x){return x[1]}); - var z = side_points.map(function(x){return x[2]}); - var z_mean = z.reduce(function(x,y){return x+y;}, 0)/z.length; - var z = z.map(function(x){return x-z_mean;}); - var theta = Math.atan2(dotproduct(y,z), dotproduct(y,y)); - console.log(theta); - - this.change_rotation_x(box, theta, false, on_box_changed); - }; - - - this.translate_box=function(box, axis, delta){ - let t = {x:0, y:0, z:0}; - - t[axis] = delta; - - // switch (axis){ - // case 'x': - - // box.position.x += delta*Math.cos(box.rotation.z); - // box.position.y += delta*Math.sin(box.rotation.z); - // break; - // case 'y': - // box.position.x += delta*Math.cos(Math.PI/2 + box.rotation.z); - // box.position.y += delta*Math.sin(Math.PI/2 + box.rotation.z); - // break; - // case 'z': - // box.position.z += delta; - // break; - - // } - - let trans = this.translateBoxInBoxCoord(box.rotation, t); - box.position.x += trans.x; - box.position.y += trans.y; - box.position.z += trans.z; - - }; - - this.translateBoxInBoxCoord = function(rotation, t) - { - // euler - let euler = new THREE.Euler(rotation.x, rotation.y, rotation.z, "XYZ") - - let trans = new THREE.Vector3(t.x, t.y, t.z).applyEuler(euler); - - return trans; - }; - - this.rotate_z=function(box, theta, sticky){ - // points indices shall be obtained before rotation. - var points_indices = box.world.lidar.get_points_indices_of_box(box); - - - var _tempQuaternion = new Quaternion(); - var rotationAxis = new Vector3(0,0,1); - box.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, theta ) ).normalize(); - - if (sticky){ - - var extreme = box.world.lidar.get_dimension_of_points(points_indices, box); - - ['x','y'].forEach((axis)=>{ - - this.translate_box(box, axis, (extreme.max[axis] + extreme.min[axis])/2); - box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + if (!prevP) prevP = currentP; - }) - } - }, + if (!nextP) nextP = currentP; + let azimuth = Math.atan2(nextP.y - prevP.y, nextP.x - prevP.x); + let estimatedRot = box.world.utmRotToLidar( + new THREE.Euler(0, 0, azimuth, "XYZ") + ); - this.interpolate_selected_object= function(sceneName, objTrackId, currentFrame, done){ + return estimatedRot; + }; - // var xhr = new XMLHttpRequest(); - // // we defined the xhr - - // xhr.onreadystatechange = function () { - // if (this.readyState != 4) - // return; - - // if (this.status == 200) { - // var ret = JSON.parse(this.responseText); - // console.log(ret); + this.grow_box = function (box, min_distance, init_scale_ratio, axies) { + if (!axies) { + axies = ["x", "y", "z"]; + } - // if (done) - // done(sceneName, ret); - // } + var extreme = box.world.lidar.grow_box(box, min_distance, init_scale_ratio); + + if (extreme) { + axies.forEach((axis) => { + this.translate_box( + box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } + }; + + this.change_rotation_y = function (box, theta, sticky, on_box_changed) { + //box.rotation.x += theta; + //on_box_changed(box); + + var points_indices = box.world.lidar.get_points_indices_of_box(box); + + var _tempQuaternion = new Quaternion(); + var rotationAxis = new Vector3(0, 1, 0); + + // NOTE: the front/end subview is different from top/side view, that we look at the reverse direction of y-axis + // it's end view acturally. + // we could project front-view, but the translation (left, right) will be in reverse direction of top view. + /// that would be frustrating. + box.quaternion + .multiply(_tempQuaternion.setFromAxisAngle(rotationAxis, -theta)) + .normalize(); + + if (sticky) { + var extreme = box.world.lidar.get_dimension_of_points( + points_indices, + box + ); + + ["x", "z"].forEach((axis) => { + this.translate_box( + box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } - // }; - - // xhr.open('GET', "/interpolate?scene="+sceneName+"&frame="+currentFrame+"&obj_id="+objTrackId, true); - // xhr.send(); - }; + if (on_box_changed) on_box_changed(box); + }; + + this.auto_rotate_y = function (box, on_box_changed) { + let points = box.world.lidar.get_points_of_box(box, 2.0); + + // 1. find surounding points + var side_indices = []; + var side_points = []; + points.position.forEach(function (p, i) { + if ( + (p[0] > box.scale.x / 2 || p[0] < -box.scale.x / 2) && + p[1] < box.scale.y / 2 && + p[1] > -box.scale.y / 2 + ) { + side_indices.push(points.index[i]); + side_points.push(points.position[i]); + } + }); + + var end_indices = []; + var end_points = []; + points.position.forEach(function (p, i) { + if ( + p[0] < box.scale.x / 2 && + p[0] > -box.scale.x / 2 && + (p[1] > box.scale.y / 2 || p[1] < -box.scale.y / 2) + ) { + end_indices.push(points.index[i]); + end_points.push(points.position[i]); + } + }); + + // 2. grid by 0.3 by 0.3 + + // compute slope (derivative) + // for side part (pitch/tilt), use y,z axis + // for end part (row), use x, z axis + + // box.world.lidar.set_spec_points_color(side_indices, {x:1,y:0,z:0}); + // box.world.lidar.set_spec_points_color(end_indices, {x:0,y:0,z:1}); + // box.world.lidar.update_points_color(); + + var x = end_points.map(function (x) { + return x[0]; + }); + //var y = side_points.map(function(x){return x[1]}); + var z = end_points.map(function (x) { + return x[2]; + }); + var z_mean = + z.reduce(function (x, y) { + return x + y; + }, 0) / z.length; + var z = z.map(function (x) { + return x - z_mean; + }); + var theta = Math.atan2(dotproduct(x, z), dotproduct(x, x)); + console.log(theta); + + this.change_rotation_y(box, theta, false, on_box_changed); + }; + + this.change_rotation_x = function (box, theta, sticky, on_box_changed) { + var points_indices = box.world.lidar.get_points_indices_of_box(box); + + //box.rotation.x += theta; + //on_box_changed(box); + var _tempQuaternion = new Quaternion(); + var rotationAxis = new Vector3(1, 0, 0); + box.quaternion + .multiply(_tempQuaternion.setFromAxisAngle(rotationAxis, theta)) + .normalize(); + + if (sticky) { + var extreme = box.world.lidar.get_dimension_of_points( + points_indices, + box + ); + + ["y", "z"].forEach((axis) => { + this.translate_box( + box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } - this.highlightBox = function(box){ - if (box){ - box.material.color.r=1; - box.material.color.g=0; - box.material.color.b=1; - box.material.opacity=1; - } - }; + if (on_box_changed) on_box_changed(box); + }; + + this.auto_rotate_x = function (box, on_box_changed) { + console.log("x auto ratote"); + + let points = box.world.lidar.get_points_of_box(box, 2.0); + + // 1. find surounding points + var side_indices = []; + var side_points = []; + points.position.forEach(function (p, i) { + if ( + (p[0] > box.scale.x / 2 || p[0] < -box.scale.x / 2) && + p[1] < box.scale.y / 2 && + p[1] > -box.scale.y / 2 + ) { + side_indices.push(points.index[i]); + side_points.push(points.position[i]); + } + }); + + var end_indices = []; + var end_points = []; + points.position.forEach(function (p, i) { + if ( + p[0] < box.scale.x / 2 && + p[0] > -box.scale.x / 2 && + (p[1] > box.scale.y / 2 || p[1] < -box.scale.y / 2) + ) { + end_indices.push(points.index[i]); + end_points.push(points.position[i]); + } + }); + + // 2. grid by 0.3 by 0.3 + + // compute slope (derivative) + // for side part (pitch/tilt), use y,z axis + // for end part (row), use x, z axis + + // box.world.lidar.set_spec_points_color(side_indices, {x:1,y:0,z:0}); + // box.world.lidar.set_spec_points_color(end_indices, {x:0,y:0,z:1}); + // box.world.lidar.update_points_color(); + //render(); + + var x = side_points.map(function (x) { + return x[0]; + }); + var y = side_points.map(function (x) { + return x[1]; + }); + var z = side_points.map(function (x) { + return x[2]; + }); + var z_mean = + z.reduce(function (x, y) { + return x + y; + }, 0) / z.length; + var z = z.map(function (x) { + return x - z_mean; + }); + var theta = Math.atan2(dotproduct(y, z), dotproduct(y, y)); + console.log(theta); + + this.change_rotation_x(box, theta, false, on_box_changed); + }; + + this.translate_box = function (box, axis, delta) { + let t = { x: 0, y: 0, z: 0 }; + + t[axis] = delta; + + // switch (axis){ + // case 'x': + + // box.position.x += delta*Math.cos(box.rotation.z); + // box.position.y += delta*Math.sin(box.rotation.z); + // break; + // case 'y': + // box.position.x += delta*Math.cos(Math.PI/2 + box.rotation.z); + // box.position.y += delta*Math.sin(Math.PI/2 + box.rotation.z); + // break; + // case 'z': + // box.position.z += delta; + // break; + + // } + + let trans = this.translateBoxInBoxCoord(box.rotation, t); + box.position.x += trans.x; + box.position.y += trans.y; + box.position.z += trans.z; + }; + + this.translateBoxInBoxCoord = function (rotation, t) { + // euler + let euler = new THREE.Euler(rotation.x, rotation.y, rotation.z, "XYZ"); + + let trans = new THREE.Vector3(t.x, t.y, t.z).applyEuler(euler); + + return trans; + }; + + (this.rotate_z = function (box, theta, sticky) { + // points indices shall be obtained before rotation. + var points_indices = box.world.lidar.get_points_indices_of_box(box); + + var _tempQuaternion = new Quaternion(); + var rotationAxis = new Vector3(0, 0, 1); + box.quaternion + .multiply(_tempQuaternion.setFromAxisAngle(rotationAxis, theta)) + .normalize(); + + if (sticky) { + var extreme = box.world.lidar.get_dimension_of_points( + points_indices, + box + ); + + ["x", "y"].forEach((axis) => { + this.translate_box( + box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } + }), + (this.interpolate_selected_object = function ( + sceneName, + objTrackId, + currentFrame, + done + ) { + // var xhr = new XMLHttpRequest(); + // // we defined the xhr + // xhr.onreadystatechange = function () { + // if (this.readyState != 4) + // return; + // if (this.status == 200) { + // var ret = JSON.parse(this.responseText); + // console.log(ret); + // if (done) + // done(sceneName, ret); + // } + // }; + // xhr.open('GET', "/interpolate?scene="+sceneName+"&frame="+currentFrame+"&obj_id="+objTrackId, true); + // xhr.send(); + }); + + this.highlightBox = function (box) { + if (box) { + box.material.color.r = 1; + box.material.color.g = 0; + box.material.color.b = 1; + box.material.opacity = 1; + } + }; - this.unhighlightBox = function(box){ - if (box){ - // box.material.color = new THREE.Color(parseInt("0x"+get_obj_cfg_by_type(box.obj_type).color.slice(1))); + this.unhighlightBox = function (box) { + if (box) { + // box.material.color = new THREE.Color(parseInt("0x"+get_obj_cfg_by_type(box.obj_type).color.slice(1))); - // box.material.opacity = box.world.data.cfg.box_opacity; + // box.material.opacity = box.world.data.cfg.box_opacity; - box.world.annotation.color_box(box); - } + box.world.annotation.color_box(box); } - - this.interpolateAsync = async function(worldList, boxList, applyIndList){ - - // if annotator is not null, it's annotated by us algorithms - let anns = boxList.map(b=> (!b || b.annotator)? null : b.world.annotation.ann_to_vector_global(b)); - console.log(anns); - let ret = await ml.interpolate_annotation(anns); - console.log(ret); - - let refObj = boxList.find(b=>!!b); - let obj_type = refObj.obj_type; - let obj_track_id = refObj.obj_track_id; - let obj_attr = refObj.obj_attr; - - for (let i = 0; i< boxList.length; i++){ - if (!applyIndList[i]) - { - continue; - } - - // - let world = worldList[i]; - let ann = world.annotation.vector_global_to_ann(ret[i]); - - // don't roate x/y - if (!pointsGlobalConfig.enableAutoRotateXY) - { - ann.rotation.x = 0; - ann.rotation.y = 0; - } - - - // if (world.lidar.get_box_points_number(ann) == 0) - // { - // continue; - // } - - - if (!boxList[i]){ - // create new box - let newBox = world.annotation.add_box(ann.position, - ann.scale, - ann.rotation, - obj_type, - obj_track_id, - obj_attr); - newBox.annotator="i"; - world.annotation.load_box(newBox); - world.annotation.setModified(); - - } else if (boxList[i].annotator) { - // modify box attributes - let b = ann; - - boxList[i].position.x = b.position.x; - boxList[i].position.y = b.position.y; - boxList[i].position.z = b.position.z; - - boxList[i].scale.x = b.scale.x; - boxList[i].scale.y = b.scale.y; - boxList[i].scale.z = b.scale.z; - - boxList[i].rotation.x = b.rotation.x; - boxList[i].rotation.y = b.rotation.y; - boxList[i].rotation.z = b.rotation.z; - - boxList[i].annotator = "i"; - - boxList[i].world.annotation.setModified(); - } - } + }; + + this.interpolateAsync = async function (worldList, boxList, applyIndList) { + // if annotator is not null, it's annotated by us algorithms + let anns = boxList.map((b) => + !b || b.annotator ? null : b.world.annotation.ann_to_vector_global(b) + ); + console.log(anns); + let ret = await ml.interpolate_annotation(anns); + console.log(ret); + + let refObj = boxList.find((b) => !!b); + let obj_type = refObj.obj_type; + let obj_track_id = refObj.obj_track_id; + let obj_attr = refObj.obj_attr; + + for (let i = 0; i < boxList.length; i++) { + if (!applyIndList[i]) { + continue; + } + + // + let world = worldList[i]; + let ann = world.annotation.vector_global_to_ann(ret[i]); + + // don't roate x/y + if (!pointsGlobalConfig.enableAutoRotateXY) { + ann.rotation.x = 0; + ann.rotation.y = 0; + } + + // if (world.lidar.get_box_points_number(ann) == 0) + // { + // continue; + // } + + if (!boxList[i]) { + // create new box + let newBox = world.annotation.add_box( + ann.position, + ann.scale, + ann.rotation, + obj_type, + obj_track_id, + obj_attr + ); + newBox.annotator = "i"; + world.annotation.load_box(newBox); + world.annotation.setModified(); + } else if (boxList[i].annotator) { + // modify box attributes + let b = ann; + + boxList[i].position.x = b.position.x; + boxList[i].position.y = b.position.y; + boxList[i].position.z = b.position.z; + + boxList[i].scale.x = b.scale.x; + boxList[i].scale.y = b.scale.y; + boxList[i].scale.z = b.scale.z; + + boxList[i].rotation.x = b.rotation.x; + boxList[i].rotation.y = b.rotation.y; + boxList[i].rotation.z = b.rotation.z; + + boxList[i].annotator = "i"; + + boxList[i].world.annotation.setModified(); + } + } + }; + + this.interpolateAndAutoAdjustAsync = async function ( + worldList, + boxList, + onFinishOneBoxCB, + applyIndList, + dontRotate + ) { + // if annotator is not null, it's annotated by us algorithms + let anns = boxList.map((b, i) => { + if (!b) return null; + + if (b.annotator) return null; + + return b.world.annotation.ann_to_vector_global(b); + }); + + console.log("anns to interpolate", anns); + + let autoAdjAsync = async (index, newAnn) => { + //let box = boxList[index]; + let world = worldList[index]; + + let tempBox = world.annotation.vector_global_to_ann(newAnn); + tempBox.world = world; + + // autoadj is timecomsuming + // jump this step + let rotateThis = dontRotate; + if (!applyIndList[index]) { + rotateThis = "dontrotate"; + } + + let adjustedBox = await this.auto_rotate_xyz( + tempBox, + null, + null, + null, + true, + rotateThis + ); + return world.annotation.ann_to_vector_global(adjustedBox); }; - this.interpolateAndAutoAdjustAsync = async function(worldList, boxList, onFinishOneBoxCB, applyIndList, dontRotate){ - - - // if annotator is not null, it's annotated by us algorithms - let anns = boxList.map((b,i)=> { - - if (!b) - return null; - - if (b.annotator) - return null; - - return b.world.annotation.ann_to_vector_global(b); - }); - - console.log("anns to interpolate", anns); - - let autoAdjAsync = async (index, newAnn)=>{ - //let box = boxList[index]; - let world = worldList[index]; - - let tempBox = world.annotation.vector_global_to_ann(newAnn); - tempBox.world = world; - - // autoadj is timecomsuming - // jump this step - let rotateThis = dontRotate; - if (!applyIndList[index]){ - rotateThis = "dontrotate"; - } - - let adjustedBox = await this.auto_rotate_xyz(tempBox, null, null, null, true, rotateThis); - return world.annotation.ann_to_vector_global(adjustedBox); - }; - - - let refObj = boxList.find(b=>!!b); - let obj_type = refObj.obj_type; - let obj_track_id = refObj.obj_track_id; - let obj_attr = refObj.obj_attr; - - let onFinishOneBox= (index)=>{ - console.log(`auto insert ${index} ${worldList[index].frameInfo.frame}done`); - let i = index; - - if (!applyIndList[i]){ - return; - } - - if (!boxList[i]){ - // create new box - let world = worldList[i]; - let ann = world.annotation.vector_global_to_ann(anns[i]); - - let newBox = world.annotation.add_box(ann.position, - ann.scale, - ann.rotation, - obj_type, - obj_track_id, - obj_attr); - newBox.annotator="a"; - world.annotation.load_box(newBox); - - } else if (boxList[i].annotator) { - // modify box attributes - let b = boxList[i].world.annotation.vector_global_to_ann(anns[i]); - boxList[i].position.x = b.position.x; - boxList[i].position.y = b.position.y; - boxList[i].position.z = b.position.z; - - boxList[i].scale.x = b.scale.x; - boxList[i].scale.y = b.scale.y; - boxList[i].scale.z = b.scale.z; - - boxList[i].rotation.x = b.rotation.x; - boxList[i].rotation.y = b.rotation.y; - boxList[i].rotation.z = b.rotation.z; - - boxList[i].annotator="a"; - } - - if (onFinishOneBoxCB) - onFinishOneBoxCB(i); - }; - - let ret = await ml.interpolate_annotation(anns, autoAdjAsync, onFinishOneBox); - console.log(ret); - - // for (let i = 0; i< boxList.length; i++){ - // onFinishOneBox(i); - // } + let refObj = boxList.find((b) => !!b); + let obj_type = refObj.obj_type; + let obj_track_id = refObj.obj_track_id; + let obj_attr = refObj.obj_attr; + + let onFinishOneBox = (index) => { + console.log( + `auto insert ${index} ${worldList[index].frameInfo.frame}done` + ); + let i = index; + + if (!applyIndList[i]) { + return; + } + + if (!boxList[i]) { + // create new box + let world = worldList[i]; + let ann = world.annotation.vector_global_to_ann(anns[i]); + + let newBox = world.annotation.add_box( + ann.position, + ann.scale, + ann.rotation, + obj_type, + obj_track_id, + obj_attr + ); + newBox.annotator = "a"; + world.annotation.load_box(newBox); + } else if (boxList[i].annotator) { + // modify box attributes + let b = boxList[i].world.annotation.vector_global_to_ann(anns[i]); + boxList[i].position.x = b.position.x; + boxList[i].position.y = b.position.y; + boxList[i].position.z = b.position.z; + + boxList[i].scale.x = b.scale.x; + boxList[i].scale.y = b.scale.y; + boxList[i].scale.z = b.scale.z; + + boxList[i].rotation.x = b.rotation.x; + boxList[i].rotation.y = b.rotation.y; + boxList[i].rotation.z = b.rotation.z; + + boxList[i].annotator = "a"; + } + + if (onFinishOneBoxCB) onFinishOneBoxCB(i); }; - - + + let ret = await ml.interpolate_annotation( + anns, + autoAdjAsync, + onFinishOneBox + ); + console.log(ret); + + // for (let i = 0; i< boxList.length; i++){ + // onFinishOneBox(i); + // } + }; } -export {BoxOp} \ No newline at end of file +export { BoxOp }; diff --git a/public/js/calib.js b/public/js/calib.js index ab1ef38..609470e 100644 --- a/public/js/calib.js +++ b/public/js/calib.js @@ -1,153 +1,177 @@ - -import {rotation_matrix_to_euler_angle,euler_angle_to_rotate_matrix, matmul, transpose} from "./util.js" +import { + rotation_matrix_to_euler_angle, + euler_angle_to_rotate_matrix, + matmul, + transpose, +} from "./util.js"; //import {render_2d_image, update_image_box_projection} from "./image.js" -function Calib(data, editor){ - this.data = data; - this.editor = editor; +function Calib(data, editor) { + this.data = data; + this.editor = editor; - var euler_angle={x:0, y:0, y:0}; - var translate = {x:0, y:0, z:0}; - - this.save_calibration = function(){ - - - var scene_meta = data.meta[data.world.frameInfo.scene]; - - - var active_camera_name = data.world.cameras.active_name; - var calib = scene_meta.calib.camera[active_camera_name] - - var extrinsic = calib.extrinsic.map(function(x){return x*1.0;}); - - euler_angle = rotation_matrix_to_euler_angle(extrinsic); - translate = { - x: extrinsic[3]*1.0, - y: extrinsic[7]*1.0, - z: extrinsic[11]*1.0, - }; - - - console.log(extrinsic, euler_angle, translate); - - let matrix = euler_angle_to_rotate_matrix(euler_angle, translate) - console.log("restoreed matrix",matrix); - - - this.editor.infoBox.show("calib", JSON.stringify(matrix)); - } - - this.reset_calibration = function(){ - // to be done - this.editor.imageContextManager.render_2d_image(); - } - - this.calib_box = null; - - this.show_camera_pos = function(){ - this.editor.viewManager.mainView.dumpPose(); + var euler_angle = { x: 0, y: 0, y: 0 }; + var translate = { x: 0, y: 0, z: 0 }; + + this.save_calibration = function () { + var scene_meta = data.meta[data.world.frameInfo.scene]; + + var active_camera_name = data.world.cameras.active_name; + var calib = scene_meta.calib.camera[active_camera_name]; + + var extrinsic = calib.extrinsic.map(function (x) { + return x * 1.0; + }); + + euler_angle = rotation_matrix_to_euler_angle(extrinsic); + translate = { + x: extrinsic[3] * 1.0, + y: extrinsic[7] * 1.0, + z: extrinsic[11] * 1.0, }; - - // show a manipulating box - this.start_calibration = function(){ - var scene_meta = this.data.meta[data.world.frameInfo.scene]; - - var active_camera_name = this.data.world.cameras.active_name; - var calib = scene_meta.calib.camera[active_camera_name] - var extrinsic = calib.extrinsic.map(function(x){return x*1.0;}); - let viewMatrix = [0, -1, 0, 0, //row vector - 0, 0, -1, 0, - 1, 0, 0, 0, - 0, 0, 0, 1]; - function transpose_transmatrix(m){ - //m=4*4 - return [ - m[0],m[4],m[8],m[3], - m[1],m[5],m[9],m[7], - m[2],m[6],m[10],m[11], - m[12],m[13],m[14],m[15], - - ]; - } + console.log(extrinsic, euler_angle, translate); + + let matrix = euler_angle_to_rotate_matrix(euler_angle, translate); + console.log("restoreed matrix", matrix); + + this.editor.infoBox.show("calib", JSON.stringify(matrix)); + }; + + this.reset_calibration = function () { + // to be done + this.editor.imageContextManager.render_2d_image(); + }; + + this.calib_box = null; + + this.show_camera_pos = function () { + this.editor.viewManager.mainView.dumpPose(); + }; + + // show a manipulating box + this.start_calibration = function () { + var scene_meta = this.data.meta[data.world.frameInfo.scene]; + + var active_camera_name = this.data.world.cameras.active_name; + var calib = scene_meta.calib.camera[active_camera_name]; + var extrinsic = calib.extrinsic.map(function (x) { + return x * 1.0; + }); + let viewMatrix = [ + 0, + -1, + 0, + 0, //row vector + 0, + 0, + -1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + ]; + function transpose_transmatrix(m) { + //m=4*4 + return [ + m[0], + m[4], + m[8], + m[3], + m[1], + m[5], + m[9], + m[7], + m[2], + m[6], + m[10], + m[11], + m[12], + m[13], + m[14], + m[15], + ]; + } - var op_matrix = matmul (transpose_transmatrix(viewMatrix), - transpose_transmatrix(extrinsic), 4); + var op_matrix = matmul( + transpose_transmatrix(viewMatrix), + transpose_transmatrix(extrinsic), + 4 + ); + + var euler_angle = rotation_matrix_to_euler_angle(op_matrix); + var translate = { + x: extrinsic[3] * 1.0, + y: extrinsic[7] * 1.0, + z: extrinsic[11] * 1.0, + }; - var euler_angle = rotation_matrix_to_euler_angle(op_matrix); - var translate = { - x: extrinsic[3]*1.0, - y: extrinsic[7]*1.0, - z: extrinsic[11]*1.0, - }; - - console.log(euler_angle, translate); - this.show_camera_pos(); + console.log(euler_angle, translate); + this.show_camera_pos(); - - if (!this.calib_box) + if (!this.calib_box) { + this.calib_box = this.data.world.annotation.createCuboid( { - this.calib_box = this.data.world.annotation.createCuboid( - { - x: translate.x,// + this.data.world.coordinatesOffset[0], - y: translate.y,// + this.data.world.coordinatesOffset[1], - z: translate.z, // + this.data.world.coordinatesOffset[2] - }, - {x:1,y:1, z:1}, - { - x: euler_angle.x, - y: euler_angle.y, - z: euler_angle.z - }, - "camera", - "camera" - ); - - this.data.world.scene.add(this.calib_box); - - } - else{ - console.log("calib box exists."); - this.calib_box.position.x = translate.x;// + this.data.world.coordinatesOffset[0]; - this.calib_box.position.y = translate.y;// + this.data.world.coordinatesOffset[1]; - this.calib_box.position.z = translate.z;// + this.data.world.coordinatesOffset[2]; - - this.calib_box.rotation.x = euler_angle.x; - this.calib_box.rotation.y = euler_angle.y; - this.calib_box.rotation.z = euler_angle.z; - } + x: translate.x, // + this.data.world.coordinatesOffset[0], + y: translate.y, // + this.data.world.coordinatesOffset[1], + z: translate.z, // + this.data.world.coordinatesOffset[2] + }, + { x: 1, y: 1, z: 1 }, + { + x: euler_angle.x, + y: euler_angle.y, + z: euler_angle.z, + }, + "camera", + "camera" + ); + + this.data.world.scene.add(this.calib_box); + } else { + console.log("calib box exists."); + this.calib_box.position.x = translate.x; // + this.data.world.coordinatesOffset[0]; + this.calib_box.position.y = translate.y; // + this.data.world.coordinatesOffset[1]; + this.calib_box.position.z = translate.z; // + this.data.world.coordinatesOffset[2]; + + this.calib_box.rotation.x = euler_angle.x; + this.calib_box.rotation.y = euler_angle.y; + this.calib_box.rotation.z = euler_angle.z; + } - console.log(this.calib_box); - this.editor.render(); - - - this.calib_box.on_box_changed = ()=>{ - console.log("calib box changed."); - - let real_pos = { - x: this.calib_box.position.x,// - this.data.world.coordinatesOffset[0], - y: this.calib_box.position.y,// - this.data.world.coordinatesOffset[1], - z: this.calib_box.position.z,// - this.data.world.coordinatesOffset[2], - }; - - let extrinsic = euler_angle_to_rotate_matrix(this.calib_box.rotation, real_pos); - calib.extrinsic = transpose_transmatrix(matmul (viewMatrix, extrinsic, 4)); - console.log("extrinsic", calib.extrinsic) - console.log("euler", euler_angle, "translate", translate); - - this.editor.imageContextManager.render_2d_image(); - } + console.log(this.calib_box); + this.editor.render(); + this.calib_box.on_box_changed = () => { + console.log("calib box changed."); - - }; - - function stop_calibration() - { - //tbd + let real_pos = { + x: this.calib_box.position.x, // - this.data.world.coordinatesOffset[0], + y: this.calib_box.position.y, // - this.data.world.coordinatesOffset[1], + z: this.calib_box.position.z, // - this.data.world.coordinatesOffset[2], + }; + + let extrinsic = euler_angle_to_rotate_matrix( + this.calib_box.rotation, + real_pos + ); + calib.extrinsic = transpose_transmatrix(matmul(viewMatrix, extrinsic, 4)); + console.log("extrinsic", calib.extrinsic); + console.log("euler", euler_angle, "translate", translate); + + this.editor.imageContextManager.render_2d_image(); }; - - /* + }; + + function stop_calibration() { + //tbd + } + + /* function calibrate(ax, value){ var scene_meta = data.meta[data.world.frameInfo.scene]; @@ -189,8 +213,6 @@ function Calib(data, editor){ update_image_box_projection(selected_box); } */ - -}; - +} -export {Calib} \ No newline at end of file +export { Calib }; diff --git a/public/js/config.js b/public/js/config.js index 66418ad..72da19e 100644 --- a/public/js/config.js +++ b/public/js/config.js @@ -1,121 +1,106 @@ +class Config { + //dataCfg = { -class Config{ - - //dataCfg = { - - //disableLabels: true, - enablePreload = true; - color_points = "mono"; - enableRadar = false; - enableAuxLidar = false; - enableDynamicGroundLevel = true; - - coordinateSystem = 'utm'; - - point_size = 1; - point_brightness = 0.6; - box_opacity = 1; - show_background = true; - color_obj = "category"; - theme = "dark"; - - enableFilterPoints = false; - filterPointsZ = 2.0; - - batchModeInstNumber = 20; - batchModeSubviewSize = {width: 130, height: 450}; - - - // edit on one box, apply to all selected boxes. - linkEditorsInBatchMode = false; - - // only rotate z in 'auto/interpolate' algs - enableAutoRotateXY = false; - autoSave = true; - - autoUpdateInterpolatedBoxes = true; - - hideId = false; - hideCategory = false; - - moveStep = 0.01; // ratio, percentage - rotateStep = Math.PI/360; - - ignoreDistantObject = true; - - ///editorCfg - - //disableSceneSelector = true; - //disableFrameSelector = true; - //disableCameraSelector = true; - //disableFastToolbox= true; - //disableMainView= true; - //disableMainImageContext = true; - //disableGrid = true; - //disableRangeCircle = true; - //disableAxis = true; - //disableMainViewKeyDown = true; - //projectRadarToImage = true; - //projectLidarToImage = true; - - constructor() - { - - } + //disableLabels: true, + enablePreload = true; + color_points = "mono"; + enableRadar = false; + enableAuxLidar = false; + enableDynamicGroundLevel = true; - readItem(name, defaultValue, castFunc){ - let ret = window.localStorage.getItem(name); - - if (ret) - { - if (castFunc) - return castFunc(ret); - else - return ret; - } - else - { - return defaultValue; - } - } + coordinateSystem = "utm"; - setItem(name, value) - { - this[name] = value; - if (typeof value == 'object') - value = JSON.stringify(value); - window.localStorage.setItem(name, value); - } + point_size = 1; + point_brightness = 0.6; + box_opacity = 1; + show_background = true; + color_obj = "category"; + theme = "dark"; - toBool(v) - { - return v==="true"; - } + enableFilterPoints = false; + filterPointsZ = 2.0; - saveItems = [ - ["theme", null], - ["enableRadar", this.toBool], - ["enablePreload", this.toBool], - ["enableAuxLidar", this.toBool], - ["enableFilterPoints", this.toBool], - ["filterPointsZ", parseFloat], - ["color_points", null], - ["coordinateSystem", null], - ["batchModeInstNumber", parseInt], - ["batchModeSubviewSize", JSON.parse], - ["enableAutoRotateXY", this.toBool], - ["autoUpdateInterpolatedBoxes", this.toBool], - ]; - - load() - { - this.saveItems.forEach(item=>{ - let key = item[0]; - let castFunc = item[1]; - - this[key] = this.readItem(key, this[key], castFunc); - }) - } -}; + batchModeInstNumber = 20; + batchModeSubviewSize = { width: 130, height: 450 }; + + // edit on one box, apply to all selected boxes. + linkEditorsInBatchMode = false; + + // only rotate z in 'auto/interpolate' algs + enableAutoRotateXY = false; + autoSave = true; + + autoUpdateInterpolatedBoxes = true; -export {Config}; \ No newline at end of file + hideId = false; + hideCategory = false; + + moveStep = 0.01; // ratio, percentage + rotateStep = Math.PI / 360; + + ignoreDistantObject = true; + + ///editorCfg + + //disableSceneSelector = true; + //disableFrameSelector = true; + //disableCameraSelector = true; + //disableFastToolbox= true; + //disableMainView= true; + //disableMainImageContext = true; + //disableGrid = true; + //disableRangeCircle = true; + //disableAxis = true; + //disableMainViewKeyDown = true; + //projectRadarToImage = true; + //projectLidarToImage = true; + + constructor() {} + + readItem(name, defaultValue, castFunc) { + let ret = window.localStorage.getItem(name); + + if (ret) { + if (castFunc) return castFunc(ret); + else return ret; + } else { + return defaultValue; + } + } + + setItem(name, value) { + this[name] = value; + if (typeof value == "object") value = JSON.stringify(value); + window.localStorage.setItem(name, value); + } + + toBool(v) { + return v === "true"; + } + + saveItems = [ + ["theme", null], + ["enableRadar", this.toBool], + ["enablePreload", this.toBool], + ["enableAuxLidar", this.toBool], + ["enableFilterPoints", this.toBool], + ["filterPointsZ", parseFloat], + ["color_points", null], + ["coordinateSystem", null], + ["batchModeInstNumber", parseInt], + ["batchModeSubviewSize", JSON.parse], + ["enableAutoRotateXY", this.toBool], + ["autoUpdateInterpolatedBoxes", this.toBool], + ]; + + load() { + this.saveItems.forEach((item) => { + let key = item[0]; + let castFunc = item[1]; + + this[key] = this.readItem(key, this[key], castFunc); + }); + } +} + +export { Config }; diff --git a/public/js/config_ui.js b/public/js/config_ui.js index 2bae001..b8119c7 100644 --- a/public/js/config_ui.js +++ b/public/js/config_ui.js @@ -1,347 +1,335 @@ import { globalKeyDownManager } from "./keydown_manager.js"; -import {logger} from "./log.js"; - -class ConfigUi{ - - clickableItems = { - "#cfg-increase-size": (event)=>{ - this.editor.data.scale_point_size(1.2); - this.editor.render(); - this.editor.boxEditorManager.render(); - return false; - }, - - "#cfg-decrease-size": (event)=>{ - this.editor.data.scale_point_size(0.8); - this.editor.render(); - this.editor.boxEditorManager.render(); - return false; - }, - - "#cfg-increase-brightness": (event)=>{ - this.editor.data.scale_point_brightness(1.2); - this.editor.render(); - this.editor.boxEditorManager.render(); - return false; - }, - - "#cfg-decrease-brightness": (event)=>{ - this.editor.data.scale_point_brightness(0.8); - this.editor.render(); - this.editor.boxEditorManager.render(); - return false; - }, - - "#cfg-take-screenshot": (event)=>{ - this.editor.downloadWebglScreenShot(); - return true; - }, - - "#cfg-show-log": (event)=>{ - logger.show(); - return true; - }, - - "#cfg-start-calib":(event)=>{ - this.editor.calib.start_calibration(); - return true; - }, - - "#cfg-show-calib":(event)=>{ - this.editor.calib.save_calibration(); - return true; - }, - - // "#cfg-reset-calib":(event)=>{ - // this.editor.calib.reset_calibration(); - // return true; - // } - - "#cfg-crop-scene": (event)=>{ - this.editor.cropScene.show(); - - return true; - }, - +import { logger } from "./log.js"; + +class ConfigUi { + clickableItems = { + "#cfg-increase-size": (event) => { + this.editor.data.scale_point_size(1.2); + this.editor.render(); + this.editor.boxEditorManager.render(); + return false; + }, + + "#cfg-decrease-size": (event) => { + this.editor.data.scale_point_size(0.8); + this.editor.render(); + this.editor.boxEditorManager.render(); + return false; + }, + + "#cfg-increase-brightness": (event) => { + this.editor.data.scale_point_brightness(1.2); + this.editor.render(); + this.editor.boxEditorManager.render(); + return false; + }, + + "#cfg-decrease-brightness": (event) => { + this.editor.data.scale_point_brightness(0.8); + this.editor.render(); + this.editor.boxEditorManager.render(); + return false; + }, + + "#cfg-take-screenshot": (event) => { + this.editor.downloadWebglScreenShot(); + return true; + }, + + "#cfg-show-log": (event) => { + logger.show(); + return true; + }, + + "#cfg-start-calib": (event) => { + this.editor.calib.start_calibration(); + return true; + }, + + "#cfg-show-calib": (event) => { + this.editor.calib.save_calibration(); + return true; + }, + + // "#cfg-reset-calib":(event)=>{ + // this.editor.calib.reset_calibration(); + // return true; + // } + + "#cfg-crop-scene": (event) => { + this.editor.cropScene.show(); + + return true; + }, + }; + + changeableItems = { + "#cfg-theme-select": (event) => { + let theme = event.currentTarget.value; + + //let scheme = document.documentElement.className; + + document.documentElement.className = "theme-" + theme; + + pointsGlobalConfig.setItem("theme", theme); + + this.editor.viewManager.setColorScheme(); + this.editor.render(); + this.editor.boxEditorManager.render(); + + return false; + }, + + "#cfg-hide-box-checkbox": (event) => { + let checked = event.currentTarget.checked; + + //let scheme = document.documentElement.className; + + if (checked) this.editor.data.set_box_opacity(0); + else this.editor.data.set_box_opacity(1); + + this.editor.render(); + this.editor.boxEditorManager.render(); + + return false; + }, + + "#cfg-hide-id-checkbox": (event) => { + let checked = event.currentTarget.checked; + this.editor.floatLabelManager.show_id(!checked); + return false; + }, + + "#cfg-hide-category-checkbox": (event) => { + let checked = event.currentTarget.checked; + this.editor.floatLabelManager.show_category(!checked); + return false; + }, + + "#cfg-hide-circle-ruler-checkbox": (event) => { + let checked = event.currentTarget.checked; + this.editor.showRangeCircle(!checked); + return false; + }, + + "#cfg-auto-rotate-xy-checkbox": (event) => { + let checked = event.currentTarget.checked; + pointsGlobalConfig.setItem("enableAutoRotateXY", checked); + return false; + }, + + "#cfg-auto-update-interpolated-boxes-checkbox": (event) => { + let checked = event.currentTarget.checked; + pointsGlobalConfig.setItem("autoUpdateInterpolatedBoxes", checked); + return false; + }, + + "#cfg-color-points-select": (event) => { + let value = event.currentTarget.value; + pointsGlobalConfig.setItem("color_points", value); + + this.editor.data.worldList.forEach((w) => { + w.lidar.color_points(); + w.lidar.update_points_color(); + }); + this.editor.render(); + return false; + }, + + "#cfg-color-object-scheme": (event) => { + let value = event.currentTarget.value; + this.editor.data.set_obj_color_scheme(value); + this.editor.render(); + this.editor.imageContextManager.render_2d_image(); + + this.editor.floatLabelManager.set_color_scheme(value); + this.editor.render2dLabels(this.editor.data.world); + this.editor.boxEditorManager.render(); + + return false; + }, + + "#cfg-batch-mode-inst-number": (event) => { + let batchSize = parseInt(event.currentTarget.value); + + pointsGlobalConfig.setItem("batchModeInstNumber", batchSize); + + this.editor.boxEditorManager.setBatchSize(batchSize); + return false; + }, + + "#cfg-coordinate-system-select": (event) => { + let coord = event.currentTarget.value; + pointsGlobalConfig.setItem("coordinateSystem", coord); + + this.editor.data.worldList.forEach((w) => { + w.calcTransformMatrix(); + }); + this.editor.render(); + }, + + "#cfg-data-aux-lidar-checkbox": (event) => { + let checked = event.currentTarget.checked; + + pointsGlobalConfig.setItem("enableAuxLidar", checked); + return false; + }, + + "#cfg-data-radar-checkbox": (event) => { + let checked = event.currentTarget.checked; + + pointsGlobalConfig.setItem("enableRadar", checked); + return false; + }, + + "#cfg-data-filter-points-checkbox": (event) => { + let checked = event.currentTarget.checked; + + pointsGlobalConfig.setItem("enableFilterPoints", checked); + return false; + }, + + "#cfg-data-filter-points-z": (event) => { + let z = event.currentTarget.value; + + pointsGlobalConfig.setItem("filterPointsZ", z); + return false; + }, + + "#cfg-data-preload-checkbox": (event) => { + let checked = event.currentTarget.checked; + pointsGlobalConfig.setItem("enablePreload", checked); + return false; + }, + }; + + ignoreItems = [ + "#cfg-point-size", + "#cfg-point-brightness", + "#cfg-theme", + "#cfg-color-object", + "#cfg-menu-batch-mode-inst-number", + "#cfg-hide-box", + "#cfg-calib-camera-LiDAR", + "#cfg-experimental", + "#cfg-data", + ]; + + subMenus = ["#cfg-experimental", "#cfg-data"]; + + constructor(button, wrapper, editor) { + this.button = button; + this.wrapper = wrapper; + this.editor = editor; + this.editorCfg = editor.editorCfg; + this.dataCfg = editor.data.cfg; + this.menu = this.wrapper.querySelector("#config-menu"); + + this.wrapper.onclick = () => { + this.hide(); }; - changeableItems = { - - "#cfg-theme-select":(event)=>{ - let theme = event.currentTarget.value; - - //let scheme = document.documentElement.className; - - - document.documentElement.className = "theme-"+theme; - - pointsGlobalConfig.setItem("theme", theme); - - this.editor.viewManager.setColorScheme(); - this.editor.render(); - this.editor.boxEditorManager.render(); - - return false; - }, - - "#cfg-hide-box-checkbox":(event)=>{ - let checked = event.currentTarget.checked; - - //let scheme = document.documentElement.className; - - if (checked) - this.editor.data.set_box_opacity(0); - else - this.editor.data.set_box_opacity(1); - - this.editor.render(); - this.editor.boxEditorManager.render(); - - - return false; - }, - - - "#cfg-hide-id-checkbox":(event)=>{ - let checked = event.currentTarget.checked; - this.editor.floatLabelManager.show_id(!checked); - return false; - }, - - - - "#cfg-hide-category-checkbox":(event)=>{ - let checked = event.currentTarget.checked; - this.editor.floatLabelManager.show_category(!checked); - return false; - }, - - "#cfg-hide-circle-ruler-checkbox": (event)=>{ - let checked = event.currentTarget.checked; - this.editor.showRangeCircle(!checked); - return false; - }, - - "#cfg-auto-rotate-xy-checkbox": (event)=>{ - let checked = event.currentTarget.checked; - pointsGlobalConfig.setItem("enableAutoRotateXY", checked); - return false; - }, - - '#cfg-auto-update-interpolated-boxes-checkbox': (event)=>{ - let checked = event.currentTarget.checked; - pointsGlobalConfig.setItem("autoUpdateInterpolatedBoxes", checked); - return false; - }, - - "#cfg-color-points-select": (event)=>{ - let value = event.currentTarget.value; - pointsGlobalConfig.setItem("color_points", value); - - this.editor.data.worldList.forEach(w=>{ - w.lidar.color_points(); - w.lidar.update_points_color(); - }); - this.editor.render(); - return false; - }, - - "#cfg-color-object-scheme":(event)=>{ - let value = event.currentTarget.value; - this.editor.data.set_obj_color_scheme(value); - this.editor.render(); - this.editor.imageContextManager.render_2d_image(); - - this.editor.floatLabelManager.set_color_scheme(value); - this.editor.render2dLabels(this.editor.data.world); - this.editor.boxEditorManager.render(); - - return false; - }, - - "#cfg-batch-mode-inst-number":(event)=>{ - let batchSize = parseInt(event.currentTarget.value); - - pointsGlobalConfig.setItem("batchModeInstNumber", batchSize); - - this.editor.boxEditorManager.setBatchSize(batchSize); - return false; - }, - - "#cfg-coordinate-system-select": (event)=>{ - let coord = event.currentTarget.value; - pointsGlobalConfig.setItem("coordinateSystem", coord); - - this.editor.data.worldList.forEach(w=>{ - w.calcTransformMatrix(); - }); - this.editor.render(); - }, - - "#cfg-data-aux-lidar-checkbox": (event)=>{ - let checked = event.currentTarget.checked; + this.button.onclick = (event) => { + this.show(event.currentTarget); + }; - pointsGlobalConfig.setItem("enableAuxLidar", checked); - return false; - }, - - "#cfg-data-radar-checkbox": (event)=>{ - let checked = event.currentTarget.checked; - - pointsGlobalConfig.setItem("enableRadar", checked); - return false; - }, - - "#cfg-data-filter-points-checkbox": (event)=>{ - let checked = event.currentTarget.checked; - - pointsGlobalConfig.setItem("enableFilterPoints", checked); - return false; - }, - - "#cfg-data-filter-points-z": (event)=>{ - let z = event.currentTarget.value; - - pointsGlobalConfig.setItem("filterPointsZ", z); - return false; - }, - - - "#cfg-data-preload-checkbox": (event)=>{ - let checked = event.currentTarget.checked; - pointsGlobalConfig.setItem("enablePreload", checked); - return false; + for (let item in this.clickableItems) { + this.menu.querySelector(item).onclick = (event) => { + let ret = this.clickableItems[item](event); + if (ret) { + this.hide(); } - }; + event.stopPropagation(); + }; + } - ignoreItems = [ - "#cfg-point-size", - "#cfg-point-brightness", - "#cfg-theme", - "#cfg-color-object", - "#cfg-menu-batch-mode-inst-number", - "#cfg-hide-box", - "#cfg-calib-camera-LiDAR", - "#cfg-experimental", - "#cfg-data", - ]; - - subMenus = [ - "#cfg-experimental", - "#cfg-data", - ]; - - constructor(button, wrapper, editor) - { - this.button = button; - this.wrapper = wrapper; - this.editor = editor; - this.editorCfg = editor.editorCfg; - this.dataCfg = editor.data.cfg; - this.menu = this.wrapper.querySelector("#config-menu"); - - this.wrapper.onclick = ()=>{ - this.hide(); + for (let item in this.changeableItems) { + this.menu.querySelector(item).onchange = (event) => { + let ret = this.changeableItems[item](event); + if (ret) { + this.hide(); } - this.button.onclick = (event)=>{ - this.show(event.currentTarget); - } + event.stopPropagation(); + }; + } - for (let item in this.clickableItems) + this.ignoreItems.forEach((item) => { + this.menu.querySelector(item).onclick = (event) => { { - this.menu.querySelector(item).onclick = (event)=>{ - let ret = this.clickableItems[item](event); - if (ret) - { - this.hide(); - } - - event.stopPropagation(); - } + event.stopPropagation(); } - - for (let item in this.changeableItems) - { - this.menu.querySelector(item).onchange = (event)=>{ - let ret = this.changeableItems[item](event); - if (ret) - { - this.hide(); - } - - event.stopPropagation(); - } + }; + }); + + this.subMenus.forEach((item) => { + this.menu.querySelector(item).onmouseenter = function (event) { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = null; } - this.ignoreItems.forEach(item=>{ - this.menu.querySelector(item).onclick = (event)=>{ - { - event.stopPropagation(); - } - } - }); - - this.subMenus.forEach(item=>{ - this.menu.querySelector(item).onmouseenter = function(event){ - if (this.timerId) - { - clearTimeout(this.timerId); - this.timerId = null; - } - - event.currentTarget.querySelector(item +"-submenu").style.display="inherit"; - } - - this.menu.querySelector(item).onmouseleave = function(event){ - let ui = event.currentTarget.querySelector(item +"-submenu"); - this.timerId = setTimeout(()=>{ - ui.style.display="none"; - this.timerId = null; - }, - 200); - } - }); - - this.menu.onclick = (event)=>{ - event.stopPropagation(); - }; - - - - // init ui - this.menu.querySelector("#cfg-theme-select").value = pointsGlobalConfig.theme; - this.menu.querySelector("#cfg-data-aux-lidar-checkbox").checked = pointsGlobalConfig.enableAuxLidar; - this.menu.querySelector("#cfg-data-radar-checkbox").checked = pointsGlobalConfig.enableRadar; - this.menu.querySelector("#cfg-color-points-select").value = pointsGlobalConfig.color_points; - this.menu.querySelector("#cfg-coordinate-system-select").value = pointsGlobalConfig.coordinateSystem; - this.menu.querySelector("#cfg-batch-mode-inst-number").value = pointsGlobalConfig.batchModeInstNumber; - this.menu.querySelector("#cfg-data-filter-points-checkbox").checked = pointsGlobalConfig.enableFilterPoints; - this.menu.querySelector("#cfg-data-filter-points-z").value = pointsGlobalConfig.filterPointsZ; - this.menu.querySelector("#cfg-hide-id-checkbox").value = pointsGlobalConfig.hideId; - this.menu.querySelector("#cfg-hide-category-checkbox").value = pointsGlobalConfig.hideCategory; - this.menu.querySelector("#cfg-data-preload-checkbox").checked = pointsGlobalConfig.enablePreload; - this.menu.querySelector("#cfg-auto-rotate-xy-checkbox").checked = pointsGlobalConfig.enableAutoRotateXY; - this.menu.querySelector("#cfg-auto-update-interpolated-boxes-checkbox").checked = pointsGlobalConfig.autoUpdateInterpolatedBoxes; - } - - - show(target){ - this.wrapper.style.display="inherit"; - - this.menu.style.right = "0px"; - this.menu.style.top = target.offsetHeight + "px"; - - globalKeyDownManager.register((event)=>false, 'config'); - } - - hide(){ - globalKeyDownManager.deregister('config'); - this.wrapper.style.display="none"; - } + event.currentTarget.querySelector(item + "-submenu").style.display = + "inherit"; + }; + + this.menu.querySelector(item).onmouseleave = function (event) { + let ui = event.currentTarget.querySelector(item + "-submenu"); + this.timerId = setTimeout(() => { + ui.style.display = "none"; + this.timerId = null; + }, 200); + }; + }); + + this.menu.onclick = (event) => { + event.stopPropagation(); + }; + // init ui + this.menu.querySelector("#cfg-theme-select").value = + pointsGlobalConfig.theme; + this.menu.querySelector("#cfg-data-aux-lidar-checkbox").checked = + pointsGlobalConfig.enableAuxLidar; + this.menu.querySelector("#cfg-data-radar-checkbox").checked = + pointsGlobalConfig.enableRadar; + this.menu.querySelector("#cfg-color-points-select").value = + pointsGlobalConfig.color_points; + this.menu.querySelector("#cfg-coordinate-system-select").value = + pointsGlobalConfig.coordinateSystem; + this.menu.querySelector("#cfg-batch-mode-inst-number").value = + pointsGlobalConfig.batchModeInstNumber; + this.menu.querySelector("#cfg-data-filter-points-checkbox").checked = + pointsGlobalConfig.enableFilterPoints; + this.menu.querySelector("#cfg-data-filter-points-z").value = + pointsGlobalConfig.filterPointsZ; + this.menu.querySelector("#cfg-hide-id-checkbox").value = + pointsGlobalConfig.hideId; + this.menu.querySelector("#cfg-hide-category-checkbox").value = + pointsGlobalConfig.hideCategory; + this.menu.querySelector("#cfg-data-preload-checkbox").checked = + pointsGlobalConfig.enablePreload; + this.menu.querySelector("#cfg-auto-rotate-xy-checkbox").checked = + pointsGlobalConfig.enableAutoRotateXY; + this.menu.querySelector( + "#cfg-auto-update-interpolated-boxes-checkbox" + ).checked = pointsGlobalConfig.autoUpdateInterpolatedBoxes; + } + + show(target) { + this.wrapper.style.display = "inherit"; + + this.menu.style.right = "0px"; + this.menu.style.top = target.offsetHeight + "px"; + + globalKeyDownManager.register((event) => false, "config"); + } + + hide() { + globalKeyDownManager.deregister("config"); + this.wrapper.style.display = "none"; + } } - -export {ConfigUi} \ No newline at end of file +export { ConfigUi }; diff --git a/public/js/context_menu.js b/public/js/context_menu.js index 5cfadde..44b460f 100644 --- a/public/js/context_menu.js +++ b/public/js/context_menu.js @@ -1,199 +1,168 @@ import { globalKeyDownManager } from "./keydown_manager.js"; - class ContextMenu { - constructor(ui) - { - this.wrapperUi = ui; - - this.menus = { - world: ui.querySelector("#context-menu"), - object: ui.querySelector("#object-context-menu"), - boxEditor: ui.querySelector("#box-editor-context-menu"), - boxEditorManager: ui.querySelector("#box-editor-manager-context-menu"), - playSubMenu: ui.querySelector("#play-submenu"), - gotoSubMenu: ui.querySelector("#goto-submenu"), - fitSubMenu: ui.querySelector("#cm-fit-submenu"), - //thisSubMenu: ui.querySelector("#cm-this-submenu"), - }; - - for (let m in this.menus){ - for (let i = 0; i < this.menus[m].children.length; i++) - { - this.menus[m].children[i].onclick = (event) => - { - //event.preventDefault(); - event.stopPropagation(); - - let ret = this.handler.handleContextMenuEvent(event); - if (ret) - { - this.hide(); - } - } - } - } - - let motherMenu = { - "#cm-goto": "#goto-submenu", - "#cm-new": "#new-submenu", - "#cm-play": "#play-submenu", - "#cm-fit": "#cm-fit-submenu", - //"#cm-this": "#cm-this-submenu", - }; - - - for (let item in motherMenu) - { - let menu = ui.querySelector(item); - menu.onclick = (event)=>{ - return false; - } - - let self = this; - menu.onmouseenter = function(event){ - if (this.timerId) - { - clearTimeout(this.timerId); - this.timerId = null; - } - - let menu = event.currentTarget.querySelector(motherMenu[item]); - menu.style.display="inherit"; - - let motherMenuRect = event.currentTarget.getBoundingClientRect(); - let posX = motherMenuRect.right; - let posY = motherMenuRect.bottom; - - if (self.wrapperUi.clientHeight < posY + menu.clientHeight){ - menu.style.bottom = "0%"; - menu.style.top = ""; - } - else{ - menu.style.top = "0%"; - menu.style.bottom = ""; - } - - - if (self.wrapperUi.clientWidth < posX + menu.clientWidth){ - menu.style.right = "100%"; - menu.style.left = ""; - } - else{ - menu.style.left = "100%"; - menu.style.right = ""; - } - } - - menu.onmouseleave = function(event){ - let ui = event.currentTarget.querySelector(motherMenu[item]); - this.timerId = setTimeout(()=>{ - ui.style.display="none"; - this.timerId = null; - }, - 200); - } - } - - - this.wrapperUi.onclick = (event)=>{ - this.hide(); - event.preventDefault(); - event.stopPropagation(); - }; - - this.wrapperUi.oncontextmenu = (event)=>{ - //event.currentTarget.style.display="none"; - event.preventDefault(); - event.stopPropagation(); - }; + constructor(ui) { + this.wrapperUi = ui; + + this.menus = { + world: ui.querySelector("#context-menu"), + object: ui.querySelector("#object-context-menu"), + boxEditor: ui.querySelector("#box-editor-context-menu"), + boxEditorManager: ui.querySelector("#box-editor-manager-context-menu"), + playSubMenu: ui.querySelector("#play-submenu"), + gotoSubMenu: ui.querySelector("#goto-submenu"), + fitSubMenu: ui.querySelector("#cm-fit-submenu"), + //thisSubMenu: ui.querySelector("#cm-this-submenu"), + }; + + for (let m in this.menus) { + for (let i = 0; i < this.menus[m].children.length; i++) { + this.menus[m].children[i].onclick = (event) => { + //event.preventDefault(); + event.stopPropagation(); + + let ret = this.handler.handleContextMenuEvent(event); + if (ret) { + this.hide(); + } + }; + } } - // install dynamic menu, like object new - installMenu(name, ui, funcHandler) - { - this.menus[name] = ui; - - for (let i = 0; i < ui.children.length; i++){ - ui.children[i].onclick = (event) => - { - //event.preventDefault(); - event.stopPropagation(); - - let ret = funcHandler(event); - if (ret) - { - this.hide(); - } - } + let motherMenu = { + "#cm-goto": "#goto-submenu", + "#cm-new": "#new-submenu", + "#cm-play": "#play-submenu", + "#cm-fit": "#cm-fit-submenu", + //"#cm-this": "#cm-this-submenu", + }; + + for (let item in motherMenu) { + let menu = ui.querySelector(item); + menu.onclick = (event) => { + return false; + }; + + let self = this; + menu.onmouseenter = function (event) { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = null; } - } - hide() - { - this.wrapperUi.style.display = "none"; - globalKeyDownManager.deregister('context menu'); - - } + let menu = event.currentTarget.querySelector(motherMenu[item]); + menu.style.display = "inherit"; - show(name, posX, posY, handler, funcSetPos) - { - this.handler = handler; + let motherMenuRect = event.currentTarget.getBoundingClientRect(); + let posX = motherMenuRect.right; + let posY = motherMenuRect.bottom; - //hide all others - for (let m in this.menus) { - if (m !== name) - this.menus[m].style.display = 'none'; + if (self.wrapperUi.clientHeight < posY + menu.clientHeight) { + menu.style.bottom = "0%"; + menu.style.top = ""; + } else { + menu.style.top = "0%"; + menu.style.bottom = ""; } - // show - this.wrapperUi.style.display = "block"; + if (self.wrapperUi.clientWidth < posX + menu.clientWidth) { + menu.style.right = "100%"; + menu.style.left = ""; + } else { + menu.style.left = "100%"; + menu.style.right = ""; + } + }; + + menu.onmouseleave = function (event) { + let ui = event.currentTarget.querySelector(motherMenu[item]); + this.timerId = setTimeout(() => { + ui.style.display = "none"; + this.timerId = null; + }, 200); + }; + } - let menu = this.menus[name] - menu.style.display = "inherit"; + this.wrapperUi.onclick = (event) => { + this.hide(); + event.preventDefault(); + event.stopPropagation(); + }; + + this.wrapperUi.oncontextmenu = (event) => { + //event.currentTarget.style.display="none"; + event.preventDefault(); + event.stopPropagation(); + }; + } + + // install dynamic menu, like object new + installMenu(name, ui, funcHandler) { + this.menus[name] = ui; + + for (let i = 0; i < ui.children.length; i++) { + ui.children[i].onclick = (event) => { + //event.preventDefault(); + event.stopPropagation(); + + let ret = funcHandler(event); + if (ret) { + this.hide(); + } + }; + } + } - this.currentMenu = menu; + hide() { + this.wrapperUi.style.display = "none"; + globalKeyDownManager.deregister("context menu"); + } - if (funcSetPos) - { - funcSetPos(menu); - } - else{ - - if (this.wrapperUi.clientHeight < posY + menu.clientHeight){ - menu.style.top = (this.wrapperUi.clientHeight - menu.clientHeight) + "px"; - } - else{ - menu.style.top = posY+"px"; - } - - - if (this.wrapperUi.clientWidth < posX + menu.clientWidth){ - menu.style.left = (this.wrapperUi.clientWidth - menu.clientWidth) + "px"; - } - else{ - menu.style.left = posX+"px"; - } - - } + show(name, posX, posY, handler, funcSetPos) { + this.handler = handler; + //hide all others + for (let m in this.menus) { + if (m !== name) this.menus[m].style.display = "none"; + } - globalKeyDownManager.register((event)=>{ + // show + this.wrapperUi.style.display = "block"; - let menuRect = this.currentMenu.getBoundingClientRect(); - let ret = this.handler.handleContextMenuKeydownEvent(event, - {x: menuRect.left, y: menuRect.top}); - if (!ret) - { - this.hide(); - } + let menu = this.menus[name]; + menu.style.display = "inherit"; - return false; // false means don't propogate - }, 'context menu'); - - } + this.currentMenu = menu; + if (funcSetPos) { + funcSetPos(menu); + } else { + if (this.wrapperUi.clientHeight < posY + menu.clientHeight) { + menu.style.top = this.wrapperUi.clientHeight - menu.clientHeight + "px"; + } else { + menu.style.top = posY + "px"; + } -}; + if (this.wrapperUi.clientWidth < posX + menu.clientWidth) { + menu.style.left = this.wrapperUi.clientWidth - menu.clientWidth + "px"; + } else { + menu.style.left = posX + "px"; + } + } -export {ContextMenu}; + globalKeyDownManager.register((event) => { + let menuRect = this.currentMenu.getBoundingClientRect(); + let ret = this.handler.handleContextMenuKeydownEvent(event, { + x: menuRect.left, + y: menuRect.top, + }); + if (!ret) { + this.hide(); + } + + return false; // false means don't propogate + }, "context menu"); + } +} + +export { ContextMenu }; diff --git a/public/js/crop_scene.js b/public/js/crop_scene.js index d967b05..6b3d737 100644 --- a/public/js/crop_scene.js +++ b/public/js/crop_scene.js @@ -1,64 +1,54 @@ import { PopupDialog } from "./popup_dialog.js"; - -class CropScene extends PopupDialog{ - - - - - constructor(ui, editor) - { - super(ui); - - this.ui = ui; //wrapper - this.editor = editor; - - this.contentUi = this.ui.querySelector("#content"); - - - let self = this; - - this.ui.querySelector("#btn-generate").onclick = (event)=>{ - var xhr = new XMLHttpRequest(); - // we defined the xhr - xhr.onreadystatechange = function () { - if (this.readyState != 4) return; - - if (this.status == 200) { - let ret = JSON.parse(this.responseText); - self.contentUi.querySelector("#log").innerText = JSON.stringify(ret, null,"\t"); - } - }; - - xhr.open('POST', "/cropscene", true); - - let para={ - rawSceneId: this.editor.data.world.frameInfo.scene, - //id: this.ui.querySelector("#scene-id").value, - desc: this.ui.querySelector("#scene-desc").value, - startTime: this.ui.querySelector("#scene-start-time").value, - seconds: this.ui.querySelector("#scene-seconds").value - }; - - xhr.send(JSON.stringify(para)); +class CropScene extends PopupDialog { + constructor(ui, editor) { + super(ui); + + this.ui = ui; //wrapper + this.editor = editor; + + this.contentUi = this.ui.querySelector("#content"); + + let self = this; + + this.ui.querySelector("#btn-generate").onclick = (event) => { + var xhr = new XMLHttpRequest(); + // we defined the xhr + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + + if (this.status == 200) { + let ret = JSON.parse(this.responseText); + self.contentUi.querySelector("#log").innerText = JSON.stringify( + ret, + null, + "\t" + ); } - } - - - - show() - { - - let frameInfo = this.editor.data.world.frameInfo; - this.ui.querySelector("#scene-start-time").value=parseInt(frameInfo.frame)-10; - this.ui.querySelector("#scene-seconds").value=20; - this.contentUi.querySelector("#log").innerText = ""; - super.show(); - } - - - + }; + + xhr.open("POST", "/cropscene", true); + + let para = { + rawSceneId: this.editor.data.world.frameInfo.scene, + //id: this.ui.querySelector("#scene-id").value, + desc: this.ui.querySelector("#scene-desc").value, + startTime: this.ui.querySelector("#scene-start-time").value, + seconds: this.ui.querySelector("#scene-seconds").value, + }; + + xhr.send(JSON.stringify(para)); + }; + } + + show() { + let frameInfo = this.editor.data.world.frameInfo; + this.ui.querySelector("#scene-start-time").value = + parseInt(frameInfo.frame) - 10; + this.ui.querySelector("#scene-seconds").value = 20; + this.contentUi.querySelector("#log").innerText = ""; + super.show(); + } } - -export {CropScene}; \ No newline at end of file +export { CropScene }; diff --git a/public/js/data.js b/public/js/data.js index 043ddda..6a15494 100644 --- a/public/js/data.js +++ b/public/js/data.js @@ -1,468 +1,436 @@ - - -import {World} from "./world.js"; -import {Debug} from "./debug.js"; -import {logger} from "./log.js" - -class Data -{ - - constructor(cfg) - { - this.cfg = cfg; - - } - - async readSceneList() - { - const req = new Request("/get_all_scene_desc"); - let init = { - method: 'GET', - //body: JSON.stringify({"points": data}) - }; - // we defined the xhr - - return fetch(req, init) - .then(response=>{ - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - }else{ - return response.json(); - } - }) - .then(ret=> - { - console.log(ret); - this.sceneDescList = ret; - return ret; - }) - .catch(reject=>{ - console.log("error read scene list!"); - }); - } - - async init(){ - await this.readSceneList(); - } - - // multiple world support - // place world by a offset so they don't overlap - dbg = new Debug(); - - worldGap=1000.0; - worldList=[]; - MaxWorldNumber=80; - createWorldIndex = 0; // this index shall not repeat, so it increases permanently - - async getWorld(sceneName, frame, on_preload_finished){ - // find in list - - if (!this.meta[sceneName]){ - await this.readSceneMetaData(sceneName) - } - - if (!this.meta[sceneName]) - { - logger.log("load scene failed", sceneName); - return null; - } - - let world = this.worldList.find((w)=>{ - return w.frameInfo.scene == sceneName && w.frameInfo.frame == frame; - }) - if (world) // found! - return world; - - - world = this._createWorld(sceneName, frame, on_preload_finished); - - return world; - }; - - _createWorld(sceneName, frame, on_preload_finished){ - - let [x,y,z] = this.allocateOffset(); - console.log("create world",x,y,z); - let world = new World(this, sceneName, frame, [this.worldGap*x, this.worldGap*y, this.worldGap*z], on_preload_finished); - world.offsetIndex = [x,y,z]; - this.createWorldIndex++; - this.worldList.push(world); - - return world; - +import { World } from "./world.js"; +import { Debug } from "./debug.js"; +import { logger } from "./log.js"; + +class Data { + constructor(cfg) { + this.cfg = cfg; + } + + async readSceneList() { + const req = new Request("/get_all_scene_desc"); + let init = { + method: "GET", + //body: JSON.stringify({"points": data}) }; - - findWorld(sceneName, frameIndex){ - let world = this.worldList.find((w)=>{ - return w.frameInfo.scene == sceneName && w.frameInfo.frame_index == frameIndex; - }) - if (world) // found! - return world; - else - return null; - }; - - offsetList = [[0,0,0]]; - lastSeedOffset = [0,0,0]; - offsetsAliveCount = 0; - allocateOffset() - { - - // we need to make sure the first frame loaded in a scene - // got to locate in [0,0,0] - - if (this.offsetsAliveCount == 0) - { - //reset offsets. - this.offsetList = [[0,0,0]]; - this.lastSeedOffset = [0,0,0]; - } - - - - if (this.offsetList.length == 0) - { - let [x,y,z] = this.lastSeedOffset; - - if (x == y) - { - x = x+1; - y = 0; - } - else - { - y = y+1; - } - - this.lastSeedOffset = [x, y, 0]; - - this.offsetList.push([x,y,0]); - - if (x != 0) this.offsetList.push([-x,y,0]); - if (y != 0) this.offsetList.push([x,-y,0]); - if (x * y != 0) this.offsetList.push([-x,-y,0]); - - if (x != y) { - this.offsetList.push([y,x,0]); - - if (y != 0) this.offsetList.push([-y,x,0]); - if (x != 0) this.offsetList.push([y,-x,0]); - if (x * y != 0) this.offsetList.push([-y,-x,0]); - } + // we defined the xhr + + return fetch(req, init) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } else { + return response.json(); } - - let ret = this.offsetList.pop(); - this.offsetsAliveCount++; - + }) + .then((ret) => { + console.log(ret); + this.sceneDescList = ret; return ret; - }; + }) + .catch((reject) => { + console.log("error read scene list!"); + }); + } + + async init() { + await this.readSceneList(); + } + + // multiple world support + // place world by a offset so they don't overlap + dbg = new Debug(); + + worldGap = 1000.0; + worldList = []; + MaxWorldNumber = 80; + createWorldIndex = 0; // this index shall not repeat, so it increases permanently + + async getWorld(sceneName, frame, on_preload_finished) { + // find in list + + if (!this.meta[sceneName]) { + await this.readSceneMetaData(sceneName); + } - returnOffset(offset) - { - this.offsetList.push(offset); - this.offsetsAliveCount--; - }; + if (!this.meta[sceneName]) { + logger.log("load scene failed", sceneName); + return null; + } - deleteDistantWorlds(world){ - let currentWorldIndex = world.frameInfo.frame_index; - - let disposable = (w)=>{ - let distant = Math.abs(w.frameInfo.frame_index - currentWorldIndex)>this.MaxWorldNumber; - let active = w.everythingDone; - if (w.annotation.modified) - { - console.log("deleting world not saved. stop."); - } - - return distant && !active && !w.annotation.modified; - } + let world = this.worldList.find((w) => { + return w.frameInfo.scene == sceneName && w.frameInfo.frame == frame; + }); + if (world) + // found! + return world; + + world = this._createWorld(sceneName, frame, on_preload_finished); + + return world; + } + + _createWorld(sceneName, frame, on_preload_finished) { + let [x, y, z] = this.allocateOffset(); + console.log("create world", x, y, z); + let world = new World( + this, + sceneName, + frame, + [this.worldGap * x, this.worldGap * y, this.worldGap * z], + on_preload_finished + ); + world.offsetIndex = [x, y, z]; + this.createWorldIndex++; + this.worldList.push(world); + + return world; + } + + findWorld(sceneName, frameIndex) { + let world = this.worldList.find((w) => { + return ( + w.frameInfo.scene == sceneName && w.frameInfo.frame_index == frameIndex + ); + }); + if (world) + // found! + return world; + else return null; + } + + offsetList = [[0, 0, 0]]; + lastSeedOffset = [0, 0, 0]; + offsetsAliveCount = 0; + allocateOffset() { + // we need to make sure the first frame loaded in a scene + // got to locate in [0,0,0] + + if (this.offsetsAliveCount == 0) { + //reset offsets. + this.offsetList = [[0, 0, 0]]; + this.lastSeedOffset = [0, 0, 0]; + } - let distantWorldList = this.worldList.filter(w=>disposable(w)); + if (this.offsetList.length == 0) { + let [x, y, z] = this.lastSeedOffset; - distantWorldList.forEach(w=>{ - this.returnOffset(w.offsetIndex); - w.deleteAll(); - }); + if (x == y) { + x = x + 1; + y = 0; + } else { + y = y + 1; + } - - this.worldList = this.worldList.filter(w=>!disposable(w)); + this.lastSeedOffset = [x, y, 0]; - }; + this.offsetList.push([x, y, 0]); - deleteOtherWorldsExcept=function(keepScene){ - // release resources if scene changed - this.worldList.forEach(w=>{ - if (w.frameInfo.scene != keepScene){ - this.returnOffset(w.offsetIndex); - w.deleteAll(); - - this.removeRefEgoPoseOfScene(w.frameInfo.scene); - } - }) - this.worldList = this.worldList.filter(w=>w.frameInfo.scene==keepScene); - }; - + if (x != 0) this.offsetList.push([-x, y, 0]); + if (y != 0) this.offsetList.push([x, -y, 0]); + if (x * y != 0) this.offsetList.push([-x, -y, 0]); - refEgoPose={}; - getRefEgoPose(sceneName, currentPose) - { - if (this.refEgoPose[sceneName]){ - return this.refEgoPose[sceneName]; - } - else{ - this.refEgoPose[sceneName] = currentPose; - return currentPose; - } - } + if (x != y) { + this.offsetList.push([y, x, 0]); - removeRefEgoPoseOfScene(sceneName) - { - if (this.refEgoPose[sceneName]) - delete this.refEgoPose[sceneName]; + if (y != 0) this.offsetList.push([-y, x, 0]); + if (x != 0) this.offsetList.push([y, -x, 0]); + if (x * y != 0) this.offsetList.push([-y, -x, 0]); + } } - forcePreloadScene(sceneName, currentWorld){ - //this.deleteOtherWorldsExcept(sceneName); - let meta = currentWorld.sceneMeta; + let ret = this.offsetList.pop(); + this.offsetsAliveCount++; - let currentWorldIndex = currentWorld.frameInfo.frame_index; - let startIndex = Math.max(0, currentWorldIndex - this.MaxWorldNumber/2); - let endIndex = Math.min(meta.frames.length, startIndex + this.MaxWorldNumber); + return ret; + } - this._doPreload(sceneName, startIndex, endIndex); - - logger.log(`${endIndex - startIndex} frames created`); - } + returnOffset(offset) { + this.offsetList.push(offset); + this.offsetsAliveCount--; + } - preloadScene(sceneName, currentWorld){ + deleteDistantWorlds(world) { + let currentWorldIndex = world.frameInfo.frame_index; - // clean other scenes. - this.deleteOtherWorldsExcept(sceneName); - this.deleteDistantWorlds(currentWorld); + let disposable = (w) => { + let distant = + Math.abs(w.frameInfo.frame_index - currentWorldIndex) > + this.MaxWorldNumber; + let active = w.everythingDone; + if (w.annotation.modified) { + console.log("deleting world not saved. stop."); + } - if (!this.cfg.enablePreload) - return; - - this.forcePreloadScene(sceneName, currentWorld); - + return distant && !active && !w.annotation.modified; }; - _doPreload(sceneName, startIndex, endIndex) - { - let meta = this.getMetaBySceneName(sceneName); - - let numLoaded = 0; - let _need_create = (frame)=>{ - let world = this.worldList.find((w)=>{ - return w.frameInfo.scene == sceneName && w.frameInfo.frame == frame; - }) - - return !world; - } - - let _do_create = (frame)=>{ - this._createWorld(sceneName, frame); - numLoaded++; - }; - - let pendingFrames = meta.frames.slice(startIndex, endIndex).filter(_need_create); - - logger.log(`preload ${meta.scene} ${pendingFrames}`); - // if (numLoaded > 0){ - // meta.frames.slice(endIndex, Math.min(endIndex+5, meta.frames.length)).forEach(_do_create); - // meta.frames.slice(Math.max(0, startIndex-5), startIndex).forEach(_do_create); - // } - - pendingFrames.forEach(_do_create); + let distantWorldList = this.worldList.filter((w) => disposable(w)); + + distantWorldList.forEach((w) => { + this.returnOffset(w.offsetIndex); + w.deleteAll(); + }); + + this.worldList = this.worldList.filter((w) => !disposable(w)); + } + + deleteOtherWorldsExcept = function (keepScene) { + // release resources if scene changed + this.worldList.forEach((w) => { + if (w.frameInfo.scene != keepScene) { + this.returnOffset(w.offsetIndex); + w.deleteAll(); + + this.removeRefEgoPoseOfScene(w.frameInfo.scene); + } + }); + this.worldList = this.worldList.filter( + (w) => w.frameInfo.scene == keepScene + ); + }; + + refEgoPose = {}; + getRefEgoPose(sceneName, currentPose) { + if (this.refEgoPose[sceneName]) { + return this.refEgoPose[sceneName]; + } else { + this.refEgoPose[sceneName] = currentPose; + return currentPose; } + } + removeRefEgoPoseOfScene(sceneName) { + if (this.refEgoPose[sceneName]) delete this.refEgoPose[sceneName]; + } - reloadAllAnnotation=function(done){ - this.worldList.forEach(w=>w.reloadAnnotation(done)); - }; - - onAnnotationUpdatedByOthers(scene, frames){ - frames.forEach(f=>{ - let world = this.worldList.find(w=>(w.frameInfo.scene==scene && w.frameInfo.frame==f)); - if (world) - world.annotation.reloadAnnotation(); - }) - }; + forcePreloadScene(sceneName, currentWorld) { + //this.deleteOtherWorldsExcept(sceneName); + let meta = currentWorld.sceneMeta; - webglScene = null; - set_webglScene=function(scene, mainScene){ - this.webglScene = scene; - this.webglMainScene = mainScene; - }; - - scale_point_size(v){ - this.cfg.point_size *= v; - // if (this.world){ - // this.world.lidar.set_point_size(this.cfg.point_size); - // } - - this.worldList.forEach(w=>{ - w.lidar.set_point_size(this.cfg.point_size); - }); - }; + let currentWorldIndex = currentWorld.frameInfo.frame_index; + let startIndex = Math.max(0, currentWorldIndex - this.MaxWorldNumber / 2); + let endIndex = Math.min( + meta.frames.length, + startIndex + this.MaxWorldNumber + ); - scale_point_brightness(v){ - this.cfg.point_brightness *= v; + this._doPreload(sceneName, startIndex, endIndex); - // if (this.world){ - // this.world.lidar.recolor_all_points(); - // } + logger.log(`${endIndex - startIndex} frames created`); + } - this.worldList.forEach(w=>{ - w.lidar.recolor_all_points(); - }) - }; + preloadScene(sceneName, currentWorld) { + // clean other scenes. + this.deleteOtherWorldsExcept(sceneName); + this.deleteDistantWorlds(currentWorld); - set_box_opacity(opacity){ - this.cfg.box_opacity = opacity; + if (!this.cfg.enablePreload) return; - this.worldList.forEach(w=>{ - w.annotation.set_box_opacity(this.cfg.box_opacity); - }); - }; + this.forcePreloadScene(sceneName, currentWorld); + } - toggle_background(){ - this.cfg.show_background = !this.cfg.show_background; + _doPreload(sceneName, startIndex, endIndex) { + let meta = this.getMetaBySceneName(sceneName); - if (this.cfg.show_background){ - this.world.lidar.cancel_highlight(); - } - else{ - this.world.lidar.hide_background(); - } - }; + let numLoaded = 0; + let _need_create = (frame) => { + let world = this.worldList.find((w) => { + return w.frameInfo.scene == sceneName && w.frameInfo.frame == frame; + }); - set_obj_color_scheme(scheme){ - - - pointsGlobalConfig.color_obj = scheme; - - // if (pointsGlobalConfig.color_obj != "no"){ - // this.world.lidar.color_points(); - // } else { - // this.world.lidar.set_points_color({ - // x: this.cfg.point_brightness, - // y: this.cfg.point_brightness, - // z: this.cfg.point_brightness, - // }); - // } - - // this.world.lidar.update_points_color(); - // this.world.annotation.color_boxes(); - - - // toto: move to world - this.worldList.forEach(w=>{ - if (pointsGlobalConfig.color_obj == "no") - { - w.lidar.color_points(); - } - else - { - w.lidar.color_objects(); - } - - w.lidar.update_points_color(); - - w.annotation.color_boxes(); - }) + return !world; }; - // active_camera_name = ""; - - // // return null means not changed. - // set_active_image(name){ - // if (name === this.active_camera_name){ - // return null; - // } - - // this.active_camera_name = name; - // if (this.world){ - // this.world.cameras.activate(name); - // } - // this.worldList.forEach(w=>w.cameras.activate(name)); - - // return name; - // }; - - world=null; - - // this.future_world_buffer = []; - // this.put_world_into_buffer= function(world){ - // this.future_world_buffer.push(world); - // }; - - // this.reset_world_buffer= function(){ - // this.future_world_buffer=[]; - // }; - - // this.activateMultiWorld=function(world, on_finished){ - // world.activate(this.webglScene, - // null, //don't destroy old world - // on_finished); - // this.worldList.push(world); - // }; - - activate_world= function(world, on_finished, dontDestroyOldWorld){ - - if (dontDestroyOldWorld){ - world.activate(this.webglScene, null, on_finished); - } - else{ - var old_world = this.world; // current world, should we get current world later? - this.world = world; // swich when everything is ready. otherwise data.world is half-baked, causing mysterious problems. - - world.activate(this.webglMainScene, - function(){ - if (old_world) - old_world.unload(); - }, - on_finished); - } + let _do_create = (frame) => { + this._createWorld(sceneName, frame); + numLoaded++; }; + let pendingFrames = meta.frames + .slice(startIndex, endIndex) + .filter(_need_create); + + logger.log(`preload ${meta.scene} ${pendingFrames}`); + // if (numLoaded > 0){ + // meta.frames.slice(endIndex, Math.min(endIndex+5, meta.frames.length)).forEach(_do_create); + // meta.frames.slice(Math.max(0, startIndex-5), startIndex).forEach(_do_create); + // } + + pendingFrames.forEach(_do_create); + } + + reloadAllAnnotation = function (done) { + this.worldList.forEach((w) => w.reloadAnnotation(done)); + }; + + onAnnotationUpdatedByOthers(scene, frames) { + frames.forEach((f) => { + let world = this.worldList.find( + (w) => w.frameInfo.scene == scene && w.frameInfo.frame == f + ); + if (world) world.annotation.reloadAnnotation(); + }); + } + + webglScene = null; + set_webglScene = function (scene, mainScene) { + this.webglScene = scene; + this.webglMainScene = mainScene; + }; + + scale_point_size(v) { + this.cfg.point_size *= v; + // if (this.world){ + // this.world.lidar.set_point_size(this.cfg.point_size); + // } + + this.worldList.forEach((w) => { + w.lidar.set_point_size(this.cfg.point_size); + }); + } + + scale_point_brightness(v) { + this.cfg.point_brightness *= v; + + // if (this.world){ + // this.world.lidar.recolor_all_points(); + // } + + this.worldList.forEach((w) => { + w.lidar.recolor_all_points(); + }); + } + + set_box_opacity(opacity) { + this.cfg.box_opacity = opacity; + + this.worldList.forEach((w) => { + w.annotation.set_box_opacity(this.cfg.box_opacity); + }); + } + + toggle_background() { + this.cfg.show_background = !this.cfg.show_background; + + if (this.cfg.show_background) { + this.world.lidar.cancel_highlight(); + } else { + this.world.lidar.hide_background(); + } + } + + set_obj_color_scheme(scheme) { + pointsGlobalConfig.color_obj = scheme; + + // if (pointsGlobalConfig.color_obj != "no"){ + // this.world.lidar.color_points(); + // } else { + // this.world.lidar.set_points_color({ + // x: this.cfg.point_brightness, + // y: this.cfg.point_brightness, + // z: this.cfg.point_brightness, + // }); + // } + + // this.world.lidar.update_points_color(); + // this.world.annotation.color_boxes(); + + // toto: move to world + this.worldList.forEach((w) => { + if (pointsGlobalConfig.color_obj == "no") { + w.lidar.color_points(); + } else { + w.lidar.color_objects(); + } + + w.lidar.update_points_color(); + + w.annotation.color_boxes(); + }); + } + + // active_camera_name = ""; + + // // return null means not changed. + // set_active_image(name){ + // if (name === this.active_camera_name){ + // return null; + // } + + // this.active_camera_name = name; + // if (this.world){ + // this.world.cameras.activate(name); + // } + // this.worldList.forEach(w=>w.cameras.activate(name)); + + // return name; + // }; + + world = null; + + // this.future_world_buffer = []; + // this.put_world_into_buffer= function(world){ + // this.future_world_buffer.push(world); + // }; + + // this.reset_world_buffer= function(){ + // this.future_world_buffer=[]; + // }; + + // this.activateMultiWorld=function(world, on_finished){ + // world.activate(this.webglScene, + // null, //don't destroy old world + // on_finished); + // this.worldList.push(world); + // }; + + activate_world = function (world, on_finished, dontDestroyOldWorld) { + if (dontDestroyOldWorld) { + world.activate(this.webglScene, null, on_finished); + } else { + var old_world = this.world; // current world, should we get current world later? + this.world = world; // swich when everything is ready. otherwise data.world is half-baked, causing mysterious problems. + + world.activate( + this.webglMainScene, + function () { + if (old_world) old_world.unload(); + }, + on_finished + ); + } + }; - meta = {}; //meta data - - getMetaBySceneName = (sceneName)=>{ - return this.meta[sceneName]; - }; + meta = {}; //meta data + getMetaBySceneName = (sceneName) => { + return this.meta[sceneName]; + }; - get_current_world_scene_meta(){ - return this.getMetaBySceneName(this.world.frameInfo.scene); - }; + get_current_world_scene_meta() { + return this.getMetaBySceneName(this.world.frameInfo.scene); + } + readSceneMetaData(sceneName) { + let self = this; + return new Promise(function (resolve, reject) { + let xhr = new XMLHttpRequest(); - readSceneMetaData(sceneName) - { - let self =this; - return new Promise(function(resolve, reject){ - let xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = function () { - if (this.readyState != 4) - return; - - if (this.status == 200) { - let sceneMeta = JSON.parse(this.responseText); - self.meta[sceneName] = sceneMeta; - resolve(sceneMeta); - } - - }; - - xhr.open('GET', `/scenemeta?scene=${sceneName}`, true); - xhr.send(); - }); - } -}; + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + if (this.status == 200) { + let sceneMeta = JSON.parse(this.responseText); + self.meta[sceneName] = sceneMeta; + resolve(sceneMeta); + } + }; -export {Data}; + xhr.open("GET", `/scenemeta?scene=${sceneName}`, true); + xhr.send(); + }); + } +} +export { Data }; diff --git a/public/js/debug.js b/public/js/debug.js index 5850cb1..e4eb8b6 100644 --- a/public/js/debug.js +++ b/public/js/debug.js @@ -1,18 +1,17 @@ -function Debug(){ - this.res_count = 0; +function Debug() { + this.res_count = 0; - this.alloc = function(){ - this.res_count++; - }; + this.alloc = function () { + this.res_count++; + }; - this.free = function(){ - this.res_count--; - }; + this.free = function () { + this.res_count--; + }; - this.dump = function(){ - console.log(`number of resources: ${this.res_count}`); - } -}; - -export {Debug}; + this.dump = function () { + console.log(`number of resources: ${this.res_count}`); + }; +} +export { Debug }; diff --git a/public/js/editor.js b/public/js/editor.js index 8ea6b39..b9e1051 100644 --- a/public/js/editor.js +++ b/public/js/editor.js @@ -1,1389 +1,1436 @@ -import * as THREE from './lib/three.module.js'; - -import {ViewManager} from "./view.js"; -import {FastToolBox, FloatLabelManager} from "./floatlabel.js"; -import {Mouse} from "./mouse.js"; -import {BoxEditor, BoxEditorManager} from "./box_editor.js"; -import {ImageContextManager} from "./image.js"; -import {globalObjectCategory} from "./obj_cfg.js"; - -import {objIdManager} from "./obj_id_list.js"; -import {Header} from "./header.js"; -import {BoxOp} from './box_op.js'; -import {AutoAdjust} from "./auto-adjust.js"; -import {PlayControl} from "./play.js"; -import {reloadWorldList, saveWorldList} from "./save.js"; -import {logger, create_logger} from "./log.js"; -import {autoAnnotate} from "./auto_annotate.js"; -import {Calib} from "./calib.js"; -import {Trajectory} from "./trajectory.js"; -import { ContextMenu } from './context_menu.js'; -import { InfoBox } from './info_box.js'; -import {CropScene} from './crop_scene.js'; -import { ConfigUi } from './config_ui.js'; -import { MovableView } from './popup_dialog.js'; -import {globalKeyDownManager} from './keydown_manager.js'; -import {vector_range} from "./util.js" -import { checkScene } from './error_check.js'; - -function Editor(editorUi, wrapperUi, editorCfg, data, name="editor"){ - - // create logger before anything else. - create_logger(editorUi.querySelector("#log-wrapper"), editorUi.querySelector("#log-button")); - this.logger = logger; - - this.editorCfg = editorCfg; - this.sideview_enabled = true; - this.editorUi = editorUi; - this.wrapperUi = wrapperUi; - this.container = null; - this.name = name; - - this.data = data; - this.scene = null; - this.renderer = null; - this.selected_box = null; - this.windowWidth = null; - this.windowHeight= null; - this.floatLabelManager = null; - this.operation_state = { - key_pressed : false, - box_navigate_index:0, - }; - this.view_state = { - lock_obj_track_id : "", - lock_obj_in_highlight : false, // focus mode - autoLock: function(trackid, focus){ - this.lock_obj_track_id = trackid; - this.lock_obj_in_highlight = focus; - } - }; - this.calib = new Calib(this.data, this); - - this.header = null; - this.imageContextManager = null; - this.boxOp = null; - this.boxEditorManager = null; - this.params={}; +import * as THREE from "./lib/three.module.js"; + +import { ViewManager } from "./view.js"; +import { FastToolBox, FloatLabelManager } from "./floatlabel.js"; +import { Mouse } from "./mouse.js"; +import { BoxEditor, BoxEditorManager } from "./box_editor.js"; +import { ImageContextManager } from "./image.js"; +import { globalObjectCategory } from "./obj_cfg.js"; + +import { objIdManager } from "./obj_id_list.js"; +import { Header } from "./header.js"; +import { BoxOp } from "./box_op.js"; +import { AutoAdjust } from "./auto-adjust.js"; +import { PlayControl } from "./play.js"; +import { reloadWorldList, saveWorldList } from "./save.js"; +import { logger, create_logger } from "./log.js"; +import { autoAnnotate } from "./auto_annotate.js"; +import { Calib } from "./calib.js"; +import { Trajectory } from "./trajectory.js"; +import { ContextMenu } from "./context_menu.js"; +import { InfoBox } from "./info_box.js"; +import { CropScene } from "./crop_scene.js"; +import { ConfigUi } from "./config_ui.js"; +import { MovableView } from "./popup_dialog.js"; +import { globalKeyDownManager } from "./keydown_manager.js"; +import { vector_range } from "./util.js"; +import { checkScene } from "./error_check.js"; + +function Editor(editorUi, wrapperUi, editorCfg, data, name = "editor") { + // create logger before anything else. + create_logger( + editorUi.querySelector("#log-wrapper"), + editorUi.querySelector("#log-button") + ); + this.logger = logger; + + this.editorCfg = editorCfg; + this.sideview_enabled = true; + this.editorUi = editorUi; + this.wrapperUi = wrapperUi; + this.container = null; + this.name = name; + + this.data = data; + this.scene = null; + this.renderer = null; + this.selected_box = null; + this.windowWidth = null; + this.windowHeight = null; + this.floatLabelManager = null; + this.operation_state = { + key_pressed: false, + box_navigate_index: 0, + }; + this.view_state = { + lock_obj_track_id: "", + lock_obj_in_highlight: false, // focus mode + autoLock: function (trackid, focus) { + this.lock_obj_track_id = trackid; + this.lock_obj_in_highlight = focus; + }, + }; + this.calib = new Calib(this.data, this); - this.currentMainEditor = this; // who is on focus, this or batch-editor-manager? + this.header = null; + this.imageContextManager = null; + this.boxOp = null; + this.boxEditorManager = null; + this.params = {}; - this.init = function(editorUi) { - - let self = this; - this.editorUi = editorUi; - - + this.currentMainEditor = this; // who is on focus, this or batch-editor-manager? + this.init = function (editorUi) { + let self = this; + this.editorUi = editorUi; - this.playControl = new PlayControl(this.data); + this.playControl = new PlayControl(this.data); + + this.configUi = new ConfigUi( + editorUi.querySelector("#config-button"), + editorUi.querySelector("#config-wrapper"), + this + ); + + this.header = new Header( + editorUi.querySelector("#header"), + this.data, + this.editorCfg, + (e) => { + this.scene_changed(e.currentTarget.value); + //event.currentTarget.blur(); + }, + (e) => { + this.frame_changed(e); + }, + (e) => { + this.object_changed(e); + }, + (e) => { + this.camera_changed(e); + } + ); + + // + // that way, the operation speed may be better + // if we load all worlds, we can speed up batch-mode operations, but the singl-world operations slows down. + // if we use two seperate scenes. can we solve this problem? + // + this.scene = new THREE.Scene(); + this.mainScene = this.scene; //new THREE.Scene(); + + this.data.set_webglScene(this.scene, this.mainScene); + + this.renderer = new THREE.WebGLRenderer({ + antialias: true, + preserveDrawingBuffer: true, + }); + this.renderer.setPixelRatio(window.devicePixelRatio); + + this.container = editorUi.querySelector("#container"); + this.container.appendChild(this.renderer.domElement); + + this.boxOp = new BoxOp(this.data); + this.viewManager = new ViewManager( + this.container, + this.scene, + this.mainScene, + this.renderer, + function () { + self.render(); + }, + function (box) { + self.on_box_changed(box); + }, + this.editorCfg + ); + + this.imageContextManager = new ImageContextManager( + this.editorUi.querySelector("#content"), + this.editorUi.querySelector("#camera-selector"), + this.editorCfg, + (lidar_points) => this.on_img_click(lidar_points) + ); + + if (!this.editorCfg.disableRangeCircle) this.addRangeCircle(); + + this.floatLabelManager = new FloatLabelManager( + this.editorUi, + this.container, + this.viewManager.mainView, + function (box) { + self.selectBox(box); + } + ); + this.fastToolBox = new FastToolBox( + this.editorUi.querySelector("#obj-editor"), + (event) => this.handleFastToolEvent(event) + ); + //this.controlGui = this.init_gui(); + + this.axis = new THREE.AxesHelper(1); + + this.scene.add(this.axis); + + window.addEventListener( + "resize", + function () { + self.onWindowResize(); + }, + false + ); + + if (!this.editorCfg.disableMainViewKeyDown) { + // this.container.onmouseenter = (event)=>{ + // this.container.focus(); + // }; + + // this.container.onmouseleave = (event)=>{ + // this.container.blur(); + // }; + + //this.container.addEventListener( 'keydown', function(e){self.keydown(e);} ); + //this.editorUi.addEventListener( 'keydown', e=>this.keydown(e); ); + + this.keydownHandler = (event) => this.keydown(event); + //this.keydownDisabled = false; + //document.removeEventListener('keydown', this.keydownHandler); + //document.addEventListener( 'keydown', this.keydownHandler); + globalKeyDownManager.register(this.keydownHandler, "main editor"); + } - this.configUi = new ConfigUi(editorUi.querySelector("#config-button"), editorUi.querySelector("#config-wrapper"), this); + this.globalKeyDownManager = globalKeyDownManager; + + this.objectTrackView = new Trajectory( + this.editorUi.querySelector("#object-track-wrapper") + ); + + this.infoBox = new InfoBox(this.editorUi.querySelector("#info-wrapper")); + + this.cropScene = new CropScene( + this.editorUi.querySelector("#crop-scene-wrapper"), + this + ); + + this.contextMenu = new ContextMenu( + this.editorUi.querySelector("#context-menu-wrapper") + ); + + this.boxEditorManager = new BoxEditorManager( + document.querySelector("#batch-box-editor"), + this.viewManager, + this.objectTrackView, + this.editorCfg, + this.boxOp, + this.header, + this.contextMenu, + this.configUi, + (b) => this.on_box_changed(b), + (b, r) => this.remove_box(b, r), // on box remove + () => { + // this.on_load_world_finished(this.data.world); + // this.imageContextManager.hide(); + // this.floatLabelManager.hide(); + // this.viewManager.mainView.disable(); + // this.boxEditor.hide(); + // this.hideGridLines(); + // this.controlGui.hide(); + } + ); //func_on_annotation_reloaded + this.boxEditorManager.hide(); + + let boxEditorUi = this.editorUi.querySelector("#main-box-editor-wrapper"); + this.boxEditor = new BoxEditor( + boxEditorUi, + null, // no box editor manager + this.viewManager, + this.editorCfg, + this.boxOp, + (b) => this.on_box_changed(b), + (b) => this.remove_box(b), + "main-boxe-ditor" + ); + this.boxEditor.detach(); // hide it + this.boxEditor.setResize("both"); + this.boxEditor.moveHandle = new MovableView( + boxEditorUi.querySelector("#focuscanvas"), + boxEditorUi.querySelector("#sub-views"), + () => { + this.boxEditor.update(); + this.render(); + } + ); + + this.mouse = new Mouse( + this.viewManager.mainView, + this.operation_state, + this.container, + this.editorUi, + function (ev) { + self.handleLeftClick(ev); + }, + function (ev) { + self.handleRightClick(ev); + }, + function (x, y, w, h, ctl, shift) { + self.handleSelectRect(x, y, w, h, ctl, shift); + } + ); + + this.autoAdjust = new AutoAdjust(this.boxOp, this.mouse, this.header); + + //this.projectiveViewOps.hide(); + + if (!this.editorCfg.disableGrid) this.installGridLines(); + + window.onbeforeunload = function () { + return "Exit?"; + //if we return nothing here (just calling return;) then there will be no pop-up question at all + //return; + }; + + this.onWindowResize(); + }; + + this.run = function () { + //this.animate(); + this.render(); + //$( "#maincanvas" ).resizable(); + + this.imageContextManager.init_image_op(() => this.selected_box); + + this.add_global_obj_type(); + }; + + this.hide = function () { + this.wrapperUi.style.display = "none"; + }; + this.show = function () { + this.wrapperUi.style.display = "block"; + }; + + this.moveRangeCircle = function (world) { + if (this.rangeCircle.parent) { + world.webglGroup.add(this.rangeCircle); + } + }; - this.header = new Header(editorUi.querySelector("#header"), this.data, this.editorCfg, - (e)=>{ - this.scene_changed(e.currentTarget.value); - //event.currentTarget.blur(); - }, - (e)=>{this.frame_changed(e)}, - (e)=>{this.object_changed(e)}, - (e)=>{this.camera_changed(e)} - ); + this.addRangeCircle = function () { + var h = 1; + var body = []; - // - // that way, the operation speed may be better - // if we load all worlds, we can speed up batch-mode operations, but the singl-world operations slows down. - // if we use two seperate scenes. can we solve this problem? - // - this.scene = new THREE.Scene(); - this.mainScene = this.scene; //new THREE.Scene(); - - this.data.set_webglScene(this.scene, this.mainScene); + var segments = 64; + for (var i = 0; i < segments; i++) { + var theta1 = ((2 * Math.PI) / segments) * i; + var x1 = Math.cos(theta1); + var y1 = Math.sin(theta1); - + var theta2 = ((2 * Math.PI) / segments) * ((i + 1) % segments); + var x2 = Math.cos(theta2); + var y2 = Math.sin(theta2); - this.renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } ); - this.renderer.setPixelRatio( window.devicePixelRatio ); - - this.container = editorUi.querySelector("#container"); - this.container.appendChild( this.renderer.domElement ); - - + body.push(x1, y1, h, x2, y2, h); + body.push(0.6 * x1, 0.6 * y1, h, 0.6 * x2, 0.6 * y2, h); + body.push(2.0 * x1, 2.0 * y1, h, 2.0 * x2, 2.0 * y2, h); + } - this.boxOp = new BoxOp(this.data); - this.viewManager = new ViewManager(this.container, this.scene, this.mainScene, this.renderer, - function(){self.render();}, - function(box){self.on_box_changed(box)}, - this.editorCfg); - + this.data.dbg.alloc(); + var bbox = new THREE.BufferGeometry(); + bbox.setAttribute("position", new THREE.Float32BufferAttribute(body, 3)); + + var box = new THREE.LineSegments( + bbox, + new THREE.LineBasicMaterial({ + color: 0x888800, + linewidth: 1, + opacity: 0.5, + transparent: true, + }) + ); + + box.scale.x = 50; + box.scale.y = 50; + box.scale.z = -3; + box.position.x = 0; + box.position.y = 0; + box.position.z = 0; + box.computeLineDistances(); + this.rangeCircle = box; + this.scene.add(box); + }; + + this.showRangeCircle = function (show) { + if (show) { + if (this.data.world) { + this.data.world.webglGroup.add(this.rangeCircle); + } + } else { + if (this.rangeCircle.parent) + this.rangeCircle.parent.remove(this.rangeCircle); + } - this.imageContextManager = new ImageContextManager( - this.editorUi.querySelector("#content"), - this.editorUi.querySelector("#camera-selector"), - this.editorCfg, - (lidar_points)=>this.on_img_click(lidar_points)); + this.render(); + }; + + this.hideGridLines = function () { + var svg = this.editorUi.querySelector("#grid-lines-wrapper"); + svg.style.display = "none"; + }; + this.showGridLines = function () { + var svg = this.editorUi.querySelector("#grid-lines-wrapper"); + svg.style.display = ""; + }; + this.installGridLines = function () { + var svg = this.editorUi.querySelector("#grid-lines-wrapper"); + + for (var i = 1; i < 10; i++) { + const line = document.createElementNS( + "http://www.w3.org/2000/svg", + "line" + ); + line.setAttribute("x1", "0%"); + line.setAttribute("y1", String(i * 10) + "%"); + line.setAttribute("x2", "100%"); + line.setAttribute("y2", String(i * 10) + "%"); + line.setAttribute("class", "grid-line"); + svg.appendChild(line); + } + for (var i = 1; i < 10; i++) { + const line = document.createElementNS( + "http://www.w3.org/2000/svg", + "line" + ); + line.setAttribute("y1", "0%"); + line.setAttribute("x1", String(i * 10) + "%"); + line.setAttribute("y2", "100%"); + line.setAttribute("x2", String(i * 10) + "%"); + line.setAttribute("class", "grid-line"); + svg.appendChild(line); + } + }; + + this.handleFastToolEvent = function (event) { + let self = this; + switch (event.currentTarget.id) { + case "label-del": + self.remove_selected_box(); + self.header.updateModifiedStatus(); + break; + case "label-gen-id": + //self.autoAdjust.mark_bbox(self.selected_box); + //event.currentTarget.blur(); + let id = objIdManager.generateNewUniqueId(); + self.fastToolBox.setValue( + self.selected_box.obj_type, + id, + self.selected_box.obj_attr + ); - if (!this.editorCfg.disableRangeCircle) - this.addRangeCircle(); - - this.floatLabelManager = new FloatLabelManager(this.editorUi, this.container, this.viewManager.mainView,function(box){self.selectBox(box);}); - this.fastToolBox = new FastToolBox(this.editorUi.querySelector("#obj-editor"), (event)=>this.handleFastToolEvent(event)); - //this.controlGui = this.init_gui(); - - this.axis = new THREE.AxesHelper(1); + self.setObjectId(id); + break; + case "label-copy": + if (!this.selected_box.obj_track_id) { + this.infoBox.show("Error", "Please assign object track ID."); + } else { + self.autoAdjust.mark_bbox(self.selected_box); + } + break; + + case "label-paste": + //this.autoAdjust.smart_paste(self.selected_box, null, (b)=>this.on_box_changed(b)); + this.boxOp.auto_rotate_xyz( + this.selected_box, + null, + null, + (b) => this.on_box_changed(b), + "noscaling" + ); + //event.currentTarget.blur(); + break; - this.scene.add(this.axis); - - window.addEventListener( 'resize', function(){self.onWindowResize();}, false ); - + case "label-batchedit": + { + if (!this.ensureBoxTrackIdExist()) break; - if (!this.editorCfg.disableMainViewKeyDown){ - // this.container.onmouseenter = (event)=>{ - // this.container.focus(); - // }; + if (!this.ensurePreloaded()) break; - // this.container.onmouseleave = (event)=>{ - // this.container.blur(); - // }; + this.header.setObject(this.selected_box.obj_track_id); + this.editBatch( + this.data.world.frameInfo.scene, + this.data.world.frameInfo.frame, + this.selected_box.obj_track_id, + this.selected_box.obj_type + ); + } + break; - //this.container.addEventListener( 'keydown', function(e){self.keydown(e);} ); - //this.editorUi.addEventListener( 'keydown', e=>this.keydown(e); ); + case "label-trajectory": + this.showTrajectory(); + break; - this.keydownHandler = (event)=>this.keydown(event); - //this.keydownDisabled = false; - //document.removeEventListener('keydown', this.keydownHandler); - //document.addEventListener( 'keydown', this.keydownHandler); - globalKeyDownManager.register(this.keydownHandler, "main editor"); + case "label-edit": + event.currentTarget.blur(); + self.selectBox(self.selected_box); + break; + + // case "label-reset": + // event.currentTarget.blur(); + // if (self.selected_box){ + // //switch_bbox_type(this.selected_box.obj_type); + // self.transform_bbox("reset"); + // } + // break; + + case "label-highlight": + event.currentTarget.blur(); + if (self.selected_box.in_highlight) { + self.cancelFocus(self.selected_box); + self.view_state.lock_obj_in_highlight = false; + } else { + self.focusOnSelectedBox(self.selected_box); } + break; - this.globalKeyDownManager = globalKeyDownManager; + case "label-rotate": + event.currentTarget.blur(); + self.transform_bbox("z_rotate_reverse"); + break; + + case "object-category-selector": + this.object_category_changed(event); + break; + case "object-track-id-editor": + this.object_track_id_changed(event); + break; + case "attr-input": + this.object_attribute_changed(event.currentTarget.value); + break; + default: + this.handleContextMenuEvent(event); + break; + } + }; - this.objectTrackView = new Trajectory( - this.editorUi.querySelector("#object-track-wrapper") - ); + this.cancelFocus = function (box) { + box.in_highlight = false; + //view_state.lock_obj_in_highlight = false; // when user unhighlight explicitly, set it to false + this.data.world.lidar.cancel_highlight(box); + this.floatLabelManager.restore_all(); - this.infoBox = new InfoBox( - this.editorUi.querySelector("#info-wrapper") - ); + this.viewManager.mainView.save_orbit_state(box.scale); + this.viewManager.mainView.orbit.reset(); + }; - this.cropScene = new CropScene( - this.editorUi.querySelector("#crop-scene-wrapper"), - this - ); + this.focusOnSelectedBox = function (box) { + if (this.editorCfg.disableMainView) return; - this.contextMenu = new ContextMenu(this.editorUi.querySelector("#context-menu-wrapper")); + if (box) { + this.data.world.lidar.highlight_box_points(box); - + this.floatLabelManager.hide_all(); + this.viewManager.mainView.orbit.saveState(); - this.boxEditorManager = new BoxEditorManager( - document.querySelector("#batch-box-editor"), - this.viewManager, - this.objectTrackView, - this.editorCfg, - this.boxOp, - this.header, - this.contextMenu, - this.configUi, - (b)=>this.on_box_changed(b), - (b,r)=>this.remove_box(b,r), // on box remove - ()=>{ - // this.on_load_world_finished(this.data.world); - // this.imageContextManager.hide(); - // this.floatLabelManager.hide(); - - // this.viewManager.mainView.disable(); - // this.boxEditor.hide(); - // this.hideGridLines(); - // this.controlGui.hide(); - - }); //func_on_annotation_reloaded - this.boxEditorManager.hide(); - - let boxEditorUi = this.editorUi.querySelector("#main-box-editor-wrapper"); - this.boxEditor= new BoxEditor( - boxEditorUi, - null, // no box editor manager - this.viewManager, - this.editorCfg, - this.boxOp, - (b)=>this.on_box_changed(b), - (b)=>this.remove_box(b), - "main-boxe-ditor"); - this.boxEditor.detach(); // hide it - this.boxEditor.setResize("both"); - this.boxEditor.moveHandle = new MovableView( - boxEditorUi.querySelector("#focuscanvas"), - boxEditorUi.querySelector("#sub-views"), - ()=>{ - this.boxEditor.update(); - this.render(); - } - ); + //this.viewManager.mainView.camera.position.set(this.selected_box.position.x+this.selected_box.scale.x*3, this.selected_box.position.y+this.selected_box.scale.y*3, this.selected_box.position.z+this.selected_box.scale.z*3); - this.mouse = new Mouse( - this.viewManager.mainView, - this.operation_state, - this.container, - this.editorUi, - function(ev){self.handleLeftClick(ev);}, - function(ev){self.handleRightClick(ev);}, - function(x,y,w,h,ctl,shift){self.handleSelectRect(x,y,w,h,ctl,shift);}); - - this.autoAdjust=new AutoAdjust(this.boxOp, this.mouse, this.header); - - - //this.projectiveViewOps.hide(); - - if (!this.editorCfg.disableGrid) - this.installGridLines() - - window.onbeforeunload = function() { - return "Exit?"; - //if we return nothing here (just calling return;) then there will be no pop-up question at all - //return; - }; - - this.onWindowResize(); - }; + let posG = this.data.world.lidarPosToScene(box.position); + this.viewManager.mainView.orbit.target.x = posG.x; + this.viewManager.mainView.orbit.target.y = posG.y; + this.viewManager.mainView.orbit.target.z = posG.z; + this.viewManager.mainView.restore_relative_orbit_state(box.scale); + this.viewManager.mainView.orbit.update(); + this.render(); + box.in_highlight = true; + this.view_state.lock_obj_in_highlight = true; + } + }; - this.run = function(){ - //this.animate(); - this.render(); - //$( "#maincanvas" ).resizable(); - - - this.imageContextManager.init_image_op(()=>this.selected_box); + this.showTrajectory = function () { + if (!this.selected_box) return; - this.add_global_obj_type(); - }; + if (!this.selected_box.obj_track_id) { + console.error("no track id"); + return; + } - this.hide = function(){ - this.wrapperUi.style.display="none"; - }; - this.show = function(){ - this.wrapperUi.style.display="block"; - }; + let tracks = this.data.worldList.map((w) => { + let box = w.annotation.findBoxByTrackId(this.selected_box.obj_track_id); + let ann = null; + if (box) { + ann = w.annotation.boxToAnn(box); + ann.psr.position = w.lidarPosToUtm(ann.psr.position); + ann.psr.rotation = w.lidarRotToUtm(ann.psr.rotation); + } + return [w.frameInfo.frame, ann, w === this.data.world]; + }); + + tracks.sort((a, b) => (a[0] > b[0] ? 1 : -1)); + + this.objectTrackView.setObject( + this.selected_box.obj_type, + this.selected_box.obj_track_id, + tracks, + (targetFrame) => { + //onExit + this.load_world(this.data.world.frameInfo.scene, targetFrame); + } + ); + }; + + // return true to close contextmenu + // return false to keep contextmenu + this.handleContextMenuEvent = function (event) { + switch (event.currentTarget.id) { + case "cm-play-2fps": + this.playControl.play((w) => { + this.on_load_world_finished(w); + }, 2); + break; + case "cm-play-10fps": + this.playControl.play((w) => { + this.on_load_world_finished(w); + }, 10); + break; + case "cm-play-20fps": + this.playControl.play((w) => { + this.on_load_world_finished(w); + }, 20); + break; + case "cm-play-50fps": + this.playControl.play((w) => { + this.on_load_world_finished(w); + }, 50); + break; + case "cm-paste": + { + let box = this.add_box_on_mouse_pos_by_ref(); + + if (!event.shiftKey) { + logger.log("paste without auto-adjusting"); + this.boxOp.auto_rotate_xyz( + box, + null, + null, + (b) => this.on_box_changed(b), + "noscaling" + ); + } + } + break; + case "cm-prev-frame": + this.previous_frame(); + break; + case "cm-next-frame": + this.next_frame(); + break; + case "cm-last-frame": + this.last_frame(); + break; + case "cm-first-frame": + this.first_frame(); + break; + case "cm-go-to-10hz": + this.load_world( + this.data.world.frameInfo.scene + "_10hz", + this.data.world.frameInfo.frame + ); + // { + // let link = document.createElement("a"); + // //link.download=`${this.data.world.frameInfo.scene}-${this.data.world.frameInfo.frame}-webgl`; + // link.href="http://localhost"; + // link.target="_blank"; + // link.click(); + // } + break; + case "cm-go-to-full-2hz": + this.load_world( + this.data.world.frameInfo.scene + "_full_2hz", + this.data.world.frameInfo.frame + ); + break; + case "cm-go-to-2hz": + this.load_world( + this.data.world.frameInfo.scene.split("_")[0], + this.data.world.frameInfo.frame + ); + break; + case "cm-save": + saveWorldList(this.data.worldList); + break; - this.moveRangeCircle = function(world){ - if (this.rangeCircle.parent){ - world.webglGroup.add(this.rangeCircle); + case "cm-reload": + { + reloadWorldList([this.data.world], () => { + this.on_load_world_finished(this.data.world); + this.header.updateModifiedStatus(); + }); } - }; + break; - this.addRangeCircle= function(){ - - var h = 1; - - var body = [ - ]; - - var segments=64; - for (var i = 0; i w.annotation.modified + ); + + if (modifiedFrames.length > 0) { + this.infoBox.show( + "Confirm", + `Discard changes to ${modifiedFrames.length} frames, continue to reload?`, + ["yes", "no"], + (choice) => { + if (choice == "yes") { + reloadWorldList(this.data.worldList, () => { + this.on_load_world_finished(this.data.world); + this.header.updateModifiedStatus(); + }); + } + } + ); + } else { + reloadWorldList(this.data.worldList, () => { + this.on_load_world_finished(this.data.world); + this.header.updateModifiedStatus(); + }); + + objIdManager.forceUpdate(); + } } + break; - this.data.dbg.alloc(); - var bbox = new THREE.BufferGeometry(); - bbox.setAttribute( 'position', new THREE.Float32BufferAttribute(body, 3 ) ); - - var box = new THREE.LineSegments( bbox, - new THREE.LineBasicMaterial( { color: 0x888800, linewidth: 1, opacity: 0.5, transparent: true } ) ); - - box.scale.x=50; - box.scale.y=50; - box.scale.z=-3; - box.position.x=0; - box.position.y=0; - box.position.z=0; - box.computeLineDistances(); - this.rangeCircle = box; - this.scene.add(box); - }; + case "cm-stop": + this.playControl.stop_play(); + break; + case "cm-pause": + this.playControl.pause_resume_play(); + break; + case "cm-prev-object": + this.select_previous_object(); + break; - this.showRangeCircle = function(show){ + case "cm-next-object": + this.select_previous_object(); + break; - if (show){ - if (this.data.world) - { - this.data.world.webglGroup.add(this.rangeCircle); - } - } - else + case "cm-show-frame-info": { - if (this.rangeCircle.parent) - this.rangeCircle.parent.remove(this.rangeCircle); + let info = { + "scend-id": this.data.world.frameInfo.scene, + frame: this.data.world.frameInfo.frame, + }; + + if (this.data.world.frameInfo.sceneMeta.desc) { + info = { + ...info, + ...this.data.world.frameInfo.sceneMeta.desc, + }; + } + + this.infoBox.show( + "Frame info - " + this.data.world.frameInfo.scene, + JSON.stringify(info, null, "
") + ); + } + break; + + case "cm-show-stat": + { + let scene = this.data.world.frameInfo.scene; + objIdManager.load_obj_ids_of_scene(scene, (objs) => { + let info = { + objects: objs.length, + boxes: objs.reduce((a, b) => a + b.count, 0), + frames: this.data.world.frameInfo.sceneMeta.frames.length, + }; + + this.infoBox.show( + "Stat - " + scene, + JSON.stringify(info, null, "
") + ); + }); } + break; + /// object - this.render(); - }; + case "cm-check-scene": + { + let scene = this.data.world.frameInfo.scene; + checkScene(scene); + logger.show(); + logger.errorBtn.onclick(); + } + break; + case "cm-reset-view": + this.resetView(); + break; + case "cm-delete": + this.remove_selected_box(); + this.header.updateModifiedStatus(); + break; - this.hideGridLines = function(){ - var svg = this.editorUi.querySelector("#grid-lines-wrapper"); - svg.style.display="none"; - }; - this.showGridLines = function(){ - var svg = this.editorUi.querySelector("#grid-lines-wrapper"); - svg.style.display=""; - }; - this.installGridLines= function(){ - - var svg = this.editorUi.querySelector("#grid-lines-wrapper"); - - for (var i=1; i<10; i++){ - const line = document. createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("x1", "0%"); - line.setAttribute("y1", String(i*10)+"%"); - line.setAttribute("x2", "100%"); - line.setAttribute("y2", String(i*10)+"%"); - line.setAttribute("class", "grid-line"); - svg.appendChild(line); - } + case "cm-edit-multiple-instances": + this.enterBatchEditMode(); - for (var i=1; i<10; i++){ - const line = document. createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("y1", "0%"); - line.setAttribute("x1", String(i*10)+"%"); - line.setAttribute("y2", "100%"); - line.setAttribute("x2", String(i*10)+"%"); - line.setAttribute("class", "grid-line"); - svg.appendChild(line); + break; + case "cm-auto-ann-background": + { + this.autoAnnInBackground(); } - - }; - - this.handleFastToolEvent= function(event){ - - let self = this; - switch (event.currentTarget.id){ - case "label-del": - self.remove_selected_box(); - self.header.updateModifiedStatus(); - break; - case "label-gen-id": - //self.autoAdjust.mark_bbox(self.selected_box); - //event.currentTarget.blur(); - let id = objIdManager.generateNewUniqueId(); - self.fastToolBox.setValue(self.selected_box.obj_type, id, self.selected_box.obj_attr); - - self.setObjectId(id); - break; - case "label-copy": - if (!this.selected_box.obj_track_id) - { - this.infoBox.show("Error", "Please assign object track ID."); - - } - else - { - self.autoAdjust.mark_bbox(self.selected_box); - } - break; - - case "label-paste": - //this.autoAdjust.smart_paste(self.selected_box, null, (b)=>this.on_box_changed(b)); - this.boxOp.auto_rotate_xyz(this.selected_box, null, null, - (b)=>this.on_box_changed(b), - "noscaling"); - //event.currentTarget.blur(); - break; - - case "label-batchedit": - { - - if (!this.ensureBoxTrackIdExist()) - break; - - if (!this.ensurePreloaded()) - break; - - this.header.setObject(this.selected_box.obj_track_id); - this.editBatch( - this.data.world.frameInfo.scene, - this.data.world.frameInfo.frame, - this.selected_box.obj_track_id, - this.selected_box.obj_type - ); - } - break; - - - case "label-trajectory": - this.showTrajectory(); - break; - - case "label-edit": - event.currentTarget.blur(); - self.selectBox(self.selected_box); - break; - - - // case "label-reset": - // event.currentTarget.blur(); - // if (self.selected_box){ - // //switch_bbox_type(this.selected_box.obj_type); - // self.transform_bbox("reset"); - // } - // break; - - case "label-highlight": - event.currentTarget.blur(); - if (self.selected_box.in_highlight){ - self.cancelFocus(self.selected_box); - self.view_state.lock_obj_in_highlight = false - } - else { - self.focusOnSelectedBox(self.selected_box); - } - break; - - case "label-rotate": - event.currentTarget.blur(); - self.transform_bbox("z_rotate_reverse"); - break; - - case "object-category-selector": - this.object_category_changed(event); - break; - case "object-track-id-editor": - this.object_track_id_changed(event); - break; - case "attr-input": - this.object_attribute_changed(event.currentTarget.value); - break; - default: - this.handleContextMenuEvent(event); - break; + break; + case "cm-interpolate-background": + { + this.interpolateInBackground(); } + break; + case "cm-show-trajectory": + this.showTrajectory(); + break; - }; - - this.cancelFocus= function(box){ - - box.in_highlight = false; - //view_state.lock_obj_in_highlight = false; // when user unhighlight explicitly, set it to false - this.data.world.lidar.cancel_highlight(box); - this.floatLabelManager.restore_all(); - - this.viewManager.mainView.save_orbit_state(box.scale); - this.viewManager.mainView.orbit.reset(); - }; - - this.focusOnSelectedBox = function(box){ - if (this.editorCfg.disableMainView) - return; - - if (box){ - this.data.world.lidar.highlight_box_points(box); - - this.floatLabelManager.hide_all(); - this.viewManager.mainView.orbit.saveState(); - - //this.viewManager.mainView.camera.position.set(this.selected_box.position.x+this.selected_box.scale.x*3, this.selected_box.position.y+this.selected_box.scale.y*3, this.selected_box.position.z+this.selected_box.scale.z*3); - - let posG = this.data.world.lidarPosToScene(box.position); - this.viewManager.mainView.orbit.target.x = posG.x; - this.viewManager.mainView.orbit.target.y = posG.y; - this.viewManager.mainView.orbit.target.z = posG.z; + case "cm-select-as-ref": + if (!this.selected_box.obj_track_id) { + this.infoBox.show("Error", "Please assign object track ID."); + return false; + } else { + this.autoAdjust.mark_bbox(this.selected_box); + } + break; - this.viewManager.mainView.restore_relative_orbit_state(box.scale); - this.viewManager.mainView.orbit.update(); + case "cm-change-id-to-ref": + if (!this.ensureRefObjExist()) break; - this.render(); - box.in_highlight=true; - this.view_state.lock_obj_in_highlight = true; - } - }; - - this.showTrajectory = function(){ - - if (!this.selected_box) - return; - - if (!this.selected_box.obj_track_id){ - console.error("no track id"); - return; - } + this.setObjectId(this.autoAdjust.marked_object.ann.obj_id); + this.fastToolBox.setValue( + this.selected_box.obj_type, + this.selected_box.obj_track_id, + this.selected_box.obj_attr + ); - let tracks = this.data.worldList.map(w=>{ - let box = w.annotation.findBoxByTrackId(this.selected_box.obj_track_id); - let ann = null; - if (box){ - ann = w.annotation.boxToAnn(box); - ann.psr.position = w.lidarPosToUtm(ann.psr.position); - ann.psr.rotation = w.lidarRotToUtm(ann.psr.rotation); - } - return [w.frameInfo.frame, ann, w===this.data.world] + break; + case "cm-change-id-to-ref-in-scene": + if (!this.ensureBoxTrackIdExist()) break; + if (!this.ensurePreloaded()) break; + if (!this.ensureRefObjExist()) break; + + this.data.worldList.forEach((w) => { + let box = w.annotation.boxes.find( + (b) => + b.obj_track_id === this.selected_box.obj_track_id && + b.obj_type === this.selected_box.obj_type + ); + if (box && box !== this.selected_box) { + box.obj_track_id = this.autoAdjust.marked_object.ann.obj_id; + w.annotation.setModified(); + } }); + this.setObjectId(this.autoAdjust.marked_object.ann.obj_id); + this.fastToolBox.setValue( + this.selected_box.obj_type, + this.selected_box.obj_track_id, + this.selected_box.obj_attr + ); - tracks.sort((a,b)=> (a[0] > b[0])? 1 : -1); + break; + case "cm-follow-ref": + if (!this.ensureBoxTrackIdExist()) break; + if (!this.ensurePreloaded()) break; + this.autoAdjust.followsRef(this.selected_box); + this.header.updateModifiedStatus(); + this.editBatch( + this.data.world.frameInfo.scene, + this.data.world.frameInfo.frame, + this.selected_box.obj_track_id, + this.selected_box.obj_type + ); + break; + case "cm-follow-static-objects": + if (!this.ensureBoxTrackIdExist()) break; + if (!this.ensurePreloaded()) break; + this.autoAdjust.followStaticObjects(this.selected_box); + this.header.updateModifiedStatus(); - this.objectTrackView.setObject( - this.selected_box.obj_type, - this.selected_box.obj_track_id, - tracks, - (targetFrame)=>{ //onExit - this.load_world(this.data.world.frameInfo.scene, targetFrame); - } + this.editBatch( + this.data.world.frameInfo.scene, + this.data.world.frameInfo.frame, + this.selected_box.obj_track_id, + this.selected_box.obj_type ); - } - // return true to close contextmenu - // return false to keep contextmenu - this.handleContextMenuEvent = function(event){ + break; + case "cm-sync-followers": + if (!this.ensurePreloaded()) break; + this.autoAdjust.syncFollowers(this.selected_box); + this.header.updateModifiedStatus(); + this.render(); + break; - switch(event.currentTarget.id) + case "cm-delete-obj": { - - case "cm-play-2fps": - this.playControl.play((w)=>{this.on_load_world_finished(w)}, 2); - break; - case "cm-play-10fps": - this.playControl.play((w)=>{this.on_load_world_finished(w)}, 10); - break; - case "cm-play-20fps": - this.playControl.play((w)=>{this.on_load_world_finished(w)}, 20); - break; - case "cm-play-50fps": - this.playControl.play((w)=>{this.on_load_world_finished(w)}, 50); - break; - case 'cm-paste': - { - let box = this.add_box_on_mouse_pos_by_ref(); - - if (!event.shiftKey) - { - logger.log('paste without auto-adjusting'); - this.boxOp.auto_rotate_xyz(box, null, null, - b=>this.on_box_changed(b), - "noscaling"); - } - } - break; - case 'cm-prev-frame': - this.previous_frame(); - break; - case 'cm-next-frame': - this.next_frame(); - break; - case 'cm-last-frame': - this.last_frame(); - break; - case 'cm-first-frame': - this.first_frame(); - break; - case 'cm-go-to-10hz': - this.load_world(this.data.world.frameInfo.scene+"_10hz", this.data.world.frameInfo.frame) - - // { - // let link = document.createElement("a"); - // //link.download=`${this.data.world.frameInfo.scene}-${this.data.world.frameInfo.frame}-webgl`; - // link.href="http://localhost"; - // link.target="_blank"; - // link.click(); - // } - break; - case 'cm-go-to-full-2hz': - this.load_world(this.data.world.frameInfo.scene+"_full_2hz", this.data.world.frameInfo.frame) - break; - - case 'cm-go-to-2hz': - this.load_world(this.data.world.frameInfo.scene.split("_")[0], this.data.world.frameInfo.frame) - break; - - - - case 'cm-save': - saveWorldList(this.data.worldList); - break; - - case "cm-reload": - { - reloadWorldList([this.data.world], ()=>{ - this.on_load_world_finished(this.data.world); - this.header.updateModifiedStatus(); - }); - - } - break; - - case "cm-reload-all": - { - let modifiedFrames = this.data.worldList.filter(w=>w.annotation.modified); - - if (modifiedFrames.length > 0) - { - this.infoBox.show( - "Confirm", - `Discard changes to ${modifiedFrames.length} frames, continue to reload?`, - ["yes","no"], - (choice)=>{ - if (choice=="yes") - { - reloadWorldList(this.data.worldList, ()=>{ - this.on_load_world_finished(this.data.world); - this.header.updateModifiedStatus(); - }); - } - } - ); - } - else - { - reloadWorldList(this.data.worldList, ()=>{ - this.on_load_world_finished(this.data.world); - this.header.updateModifiedStatus(); - }); - - objIdManager.forceUpdate(); - } - } - break; - - - case "cm-stop": - this.playControl.stop_play(); - break; - case "cm-pause": - this.playControl.pause_resume_play(); - break; - - case "cm-prev-object": - this.select_previous_object(); - break; - - case "cm-next-object": - this.select_previous_object(); - break; - - case "cm-show-frame-info": - { - let info = {"scend-id": this.data.world.frameInfo.scene, - "frame": this.data.world.frameInfo.frame - }; - - if (this.data.world.frameInfo.sceneMeta.desc) - { - info = { - ...info, - ...this.data.world.frameInfo.sceneMeta.desc, - }; - } - - this.infoBox.show("Frame info - " + this.data.world.frameInfo.scene, JSON.stringify(info,null,"
")); - } - break; - - case "cm-show-stat": - { - let scene = this.data.world.frameInfo.scene; - objIdManager.load_obj_ids_of_scene(scene, (objs)=>{ - let info = { - objects: objs.length, - boxes: objs.reduce((a,b)=>a+b.count, 0), - frames: this.data.world.frameInfo.sceneMeta.frames.length, - }; - - this.infoBox.show("Stat - " + scene, JSON.stringify(info, null,"
")); - }); - } - break; - /// object - - case 'cm-check-scene': - { - let scene = this.data.world.frameInfo.scene; - checkScene(scene); - logger.show(); - logger.errorBtn.onclick(); - } - break; - case "cm-reset-view": - this.resetView(); - break; - case "cm-delete": - this.remove_selected_box(); - this.header.updateModifiedStatus(); - break; - - case "cm-edit-multiple-instances": - this.enterBatchEditMode(); - - break; - case "cm-auto-ann-background": - { - this.autoAnnInBackground(); - - } - break; - case "cm-interpolate-background": - { - this.interpolateInBackground(); - } - break; - case "cm-show-trajectory": - - this.showTrajectory(); - break; - - case "cm-select-as-ref": - if (!this.selected_box.obj_track_id) - { - this.infoBox.show("Error", "Please assign object track ID."); - return false; - } - else - { - this.autoAdjust.mark_bbox(this.selected_box); + //let saveList=[]; + this.data.worldList.forEach((w) => { + let box = w.annotation.boxes.find( + (b) => b.obj_track_id === this.selected_box.obj_track_id + ); + if (box && box !== this.selected_box) { + w.annotation.unload_box(box); + w.annotation.remove_box(box); + //saveList.push(w); + w.annotation.setModified(); } - break; - - case "cm-change-id-to-ref": - if (!this.ensureRefObjExist()) - break; - - this.setObjectId(this.autoAdjust.marked_object.ann.obj_id); - this.fastToolBox.setValue(this.selected_box.obj_type, - this.selected_box.obj_track_id, - this.selected_box.obj_attr); - - break; - case "cm-change-id-to-ref-in-scene": - - if (!this.ensureBoxTrackIdExist()) - break; - if (!this.ensurePreloaded()) - break; - if (!this.ensureRefObjExist()) - break; - - this.data.worldList.forEach(w=>{ - let box = w.annotation.boxes.find(b=>b.obj_track_id === this.selected_box.obj_track_id && b.obj_type === this.selected_box.obj_type); - if (box && box !== this.selected_box){ - box.obj_track_id = this.autoAdjust.marked_object.ann.obj_id; - w.annotation.setModified(); - } - }); + }); - - this.setObjectId(this.autoAdjust.marked_object.ann.obj_id); - this.fastToolBox.setValue(this.selected_box.obj_type, - this.selected_box.obj_track_id, - this.selected_box.obj_attr); - - break; - case "cm-follow-ref": - - if (!this.ensureBoxTrackIdExist()) - break; - if (!this.ensurePreloaded()) - break; - this.autoAdjust.followsRef(this.selected_box); - this.header.updateModifiedStatus(); - this.editBatch( - this.data.world.frameInfo.scene, - this.data.world.frameInfo.frame, - this.selected_box.obj_track_id, - this.selected_box.obj_type - ); - break; - case 'cm-follow-static-objects': - if (!this.ensureBoxTrackIdExist()) - break; - if (!this.ensurePreloaded()) - break; - this.autoAdjust.followStaticObjects(this.selected_box); - this.header.updateModifiedStatus(); + //saveWorldList(saveList); + this.remove_selected_box(); + this.header.updateModifiedStatus(); + } + break; - this.editBatch( - this.data.world.frameInfo.scene, - this.data.world.frameInfo.frame, - this.selected_box.obj_track_id, - this.selected_box.obj_type + case "cm-modify-obj-type": + { + if (!this.ensurePreloaded()) break; + //let saveList=[]; + this.data.worldList.forEach((w) => { + let box = w.annotation.boxes.find( + (b) => b.obj_track_id === this.selected_box.obj_track_id ); - - break; - case "cm-sync-followers": - - if (!this.ensurePreloaded()) - break; - this.autoAdjust.syncFollowers(this.selected_box); - this.header.updateModifiedStatus(); - this.render(); - break; - - - case "cm-delete-obj": - { - //let saveList=[]; - this.data.worldList.forEach(w=>{ - let box = w.annotation.boxes.find(b=>b.obj_track_id === this.selected_box.obj_track_id); - if (box && box !== this.selected_box){ - w.annotation.unload_box(box); - w.annotation.remove_box(box); - //saveList.push(w); - w.annotation.setModified(); - } - }); - - //saveWorldList(saveList); - this.remove_selected_box(); - this.header.updateModifiedStatus(); - } - break; - - case "cm-modify-obj-type": - { - if (!this.ensurePreloaded()) - break; - //let saveList=[]; - this.data.worldList.forEach(w=>{ - let box = w.annotation.boxes.find(b=>b.obj_track_id === this.selected_box.obj_track_id); - if (box && box !== this.selected_box){ - box.obj_type = this.selected_box.obj_type; - box.obj_attr = this.selected_box.obj_attr; - //saveList.push(w); - w.annotation.setModified(); - } - - }); - - //saveWorldList(saveList); - this.header.updateModifiedStatus(); - } - break; - - case "cm-modify-obj-size": - { - if (!this.ensurePreloaded()) - break; - //let saveList=[]; - this.data.worldList.forEach(w=>{ - let box = w.annotation.boxes.find(b=>b.obj_track_id == this.selected_box.obj_track_id); - if (box && box !== this.selected_box){ - box.scale.x = this.selected_box.scale.x; - box.scale.y = this.selected_box.scale.y; - box.scale.z = this.selected_box.scale.z; - //saveList.push(w); - - w.annotation.setModified(); - } - - }); - - //saveWorldList(saveList); - this.header.updateModifiedStatus(); + if (box && box !== this.selected_box) { + box.obj_type = this.selected_box.obj_type; + box.obj_attr = this.selected_box.obj_attr; + //saveList.push(w); + w.annotation.setModified(); } - break; + }); - - default: - console.log('unhandled', event.currentTarget.id, event.type); + //saveWorldList(saveList); + this.header.updateModifiedStatus(); } + break; - return true; - }; - - // this.animate= function() { - // let self=this; - // requestAnimationFrame( function(){self.animate();} ); - // this.viewManager.mainView.orbit_orth.update(); - // }; - - - - this.render= function(){ - - this.viewManager.mainView.render(); - this.boxEditor.boxView.render(); - - this.floatLabelManager.update_all_position(); - if (this.selected_box){ - this.fastToolBox.setPos(this.floatLabelManager.getLabelEditorPos(this.selected_box.obj_local_id)); - } - }; + case "cm-modify-obj-size": + { + if (!this.ensurePreloaded()) break; + //let saveList=[]; + this.data.worldList.forEach((w) => { + let box = w.annotation.boxes.find( + (b) => b.obj_track_id == this.selected_box.obj_track_id + ); + if (box && box !== this.selected_box) { + box.scale.x = this.selected_box.scale.x; + box.scale.y = this.selected_box.scale.y; + box.scale.z = this.selected_box.scale.z; + //saveList.push(w); - - this.resetView = function(targetPos){ + w.annotation.setModified(); + } + }); - if (!targetPos){ - let center = this.data.world.lidar.computeCenter(); - targetPos = {...center};//{x:0, y:0, z:50}; - targetPos.z += 50; + //saveWorldList(saveList); + this.header.updateModifiedStatus(); } - else - targetPos.z = 50; - - let pos = this.data.world.lidarPosToScene(targetPos); - this.viewManager.mainView.orbit.object.position.set(pos.x, pos.y, pos.z); //object is camera - this.viewManager.mainView.orbit.target.set(pos.x, pos.y, 0); - this.viewManager.mainView.orbit.update(); - this.render(); - }; + break; - this.scene_changed= async function(sceneName){ - - //var sceneName = event.currentTarget.value; + default: + console.log("unhandled", event.currentTarget.id, event.type); + } - if (sceneName.length == 0){ - return; - } - - console.log("choose sceneName " + sceneName); - var meta = this.data.getMetaBySceneName(sceneName); + return true; + }; - if (!meta) - { - this.editorUi.querySelector("#frame-selector").innerHTML = ""; - meta = await this.data.readSceneMetaData(sceneName); - } + // this.animate= function() { + // let self=this; + // requestAnimationFrame( function(){self.animate();} ); + // this.viewManager.mainView.orbit_orth.update(); + // }; - var frame_selector_str = meta.frames.map(function(f){ - return ""; - }).reduce(function(x,y){return x+y;}, ""); + this.render = function () { + this.viewManager.mainView.render(); + this.boxEditor.boxView.render(); - this.editorUi.querySelector("#frame-selector").innerHTML = frame_selector_str; - - - if (meta.camera){ - this.imageContextManager.updateCameraList(meta.camera); - } + this.floatLabelManager.update_all_position(); + if (this.selected_box) { + this.fastToolBox.setPos( + this.floatLabelManager.getLabelEditorPos(this.selected_box.obj_local_id) + ); + } + }; + + this.resetView = function (targetPos) { + if (!targetPos) { + let center = this.data.world.lidar.computeCenter(); + targetPos = { ...center }; //{x:0, y:0, z:50}; + targetPos.z += 50; + } else targetPos.z = 50; + + let pos = this.data.world.lidarPosToScene(targetPos); + this.viewManager.mainView.orbit.object.position.set(pos.x, pos.y, pos.z); //object is camera + this.viewManager.mainView.orbit.target.set(pos.x, pos.y, 0); + this.viewManager.mainView.orbit.update(); + this.render(); + }; + + this.scene_changed = async function (sceneName) { + //var sceneName = event.currentTarget.value; + + if (sceneName.length == 0) { + return; + } - //load_obj_ids_of_scene(sceneName); - }; + console.log("choose sceneName " + sceneName); + var meta = this.data.getMetaBySceneName(sceneName); - this.frame_changed= function(event){ - var sceneName = this.editorUi.querySelector("#scene-selector").value; + if (!meta) { + this.editorUi.querySelector("#frame-selector").innerHTML = + ""; + meta = await this.data.readSceneMetaData(sceneName); + } - if (sceneName.length == 0 && this.data.world) - { - sceneName = this.data.world.frameInfo.scene; - } + var frame_selector_str = meta.frames + .map(function (f) { + return ""; + }) + .reduce(function (x, y) { + return x + y; + }, ""); - if (sceneName.length == 0){ - return; - } + this.editorUi.querySelector("#frame-selector").innerHTML = + frame_selector_str; - var frame = event.currentTarget.value; - console.log(sceneName, frame); - this.load_world(sceneName, frame); - event.currentTarget.blur(); - }; + if (meta.camera) { + this.imageContextManager.updateCameraList(meta.camera); + } + //load_obj_ids_of_scene(sceneName); + }; - this.ensureBoxTrackIdExist = function() - { - if (!this.selected_box.obj_track_id) - { - this.infoBox.show("Error", "Please assign object track ID."); - return false; - } + this.frame_changed = function (event) { + var sceneName = this.editorUi.querySelector("#scene-selector").value; - return true; + if (sceneName.length == 0 && this.data.world) { + sceneName = this.data.world.frameInfo.scene; } - this.ensureRefObjExist = function() - { - if (!this.autoAdjust.marked_object) - { - this.infoBox.show("Notice", 'No reference object was selected'); - return false; - } - - - return true; + if (sceneName.length == 0) { + return; } - this.ensurePreloaded = function() - { - let worldList = this.data.worldList.filter(w=>w.frameInfo.scene == this.data.world.frameInfo.scene); - worldList = worldList.sort((a,b)=>a.frameInfo.frame_index - b.frameInfo.frame_index); - - let meta = this.data.get_current_world_scene_meta(); - - let allLoaded = worldList.map(w=>w.preloaded()).reduce((a,b)=>a && b, true); + var frame = event.currentTarget.value; + console.log(sceneName, frame); + this.load_world(sceneName, frame); + event.currentTarget.blur(); + }; - if ((worldList.length < meta.frames.length && worldList.length <= 60) || (!allLoaded)) - { - this.data.forcePreloadScene(this.data.world.frameInfo.scene, this.data.world); + this.ensureBoxTrackIdExist = function () { + if (!this.selected_box.obj_track_id) { + this.infoBox.show("Error", "Please assign object track ID."); + return false; + } - this.infoBox.show("Notice", - `Loading scene in background. Please try again later.`); - return false; - } + return true; + }; - - return true; + this.ensureRefObjExist = function () { + if (!this.autoAdjust.marked_object) { + this.infoBox.show("Notice", "No reference object was selected"); + return false; } + return true; + }; + this.ensurePreloaded = function () { + let worldList = this.data.worldList.filter( + (w) => w.frameInfo.scene == this.data.world.frameInfo.scene + ); + worldList = worldList.sort( + (a, b) => a.frameInfo.frame_index - b.frameInfo.frame_index + ); + + let meta = this.data.get_current_world_scene_meta(); + + let allLoaded = worldList + .map((w) => w.preloaded()) + .reduce((a, b) => a && b, true); + + if ( + (worldList.length < meta.frames.length && worldList.length <= 60) || + !allLoaded + ) { + this.data.forcePreloadScene( + this.data.world.frameInfo.scene, + this.data.world + ); + + this.infoBox.show( + "Notice", + `Loading scene in background. Please try again later.` + ); + return false; + } - this.interpolateInBackground = function() - { - - if (!this.ensureBoxTrackIdExist()) - return; + return true; + }; - if (!this.ensurePreloaded()) - return; + this.interpolateInBackground = function () { + if (!this.ensureBoxTrackIdExist()) return; - let worldList = this.data.worldList.filter(w=>w.frameInfo.scene == this.data.world.frameInfo.scene); - worldList = worldList.sort((a,b)=>a.frameInfo.frame_index - b.frameInfo.frame_index); - let boxList = worldList.map(w=>w.annotation.findBoxByTrackId(this.selected_box.obj_track_id)); + if (!this.ensurePreloaded()) return; - let applyIndList = boxList.map(b=>true); - this.boxOp.interpolateAsync(worldList, boxList, applyIndList).then(ret=>{ - this.header.updateModifiedStatus(); - this.viewManager.render(); - }); - }; - this.enterBatchEditMode = function() - { - if (!this.ensureBoxTrackIdExist()) - return; + let worldList = this.data.worldList.filter( + (w) => w.frameInfo.scene == this.data.world.frameInfo.scene + ); + worldList = worldList.sort( + (a, b) => a.frameInfo.frame_index - b.frameInfo.frame_index + ); + let boxList = worldList.map((w) => + w.annotation.findBoxByTrackId(this.selected_box.obj_track_id) + ); - if (!this.ensurePreloaded()) - return; + let applyIndList = boxList.map((b) => true); + this.boxOp + .interpolateAsync(worldList, boxList, applyIndList) + .then((ret) => { + this.header.updateModifiedStatus(); + this.viewManager.render(); + }); + }; + this.enterBatchEditMode = function () { + if (!this.ensureBoxTrackIdExist()) return; + + if (!this.ensurePreloaded()) return; + + this.header.setObject(this.selected_box.obj_track_id); + + this.editBatch( + this.data.world.frameInfo.scene, + this.data.world.frameInfo.frame, + this.selected_box.obj_track_id, + this.selected_box.obj_type + ); + }; + + this.autoAnnInBackground = function () { + if (!this.ensureBoxTrackIdExist()) return; + + if (!this.ensurePreloaded()) return; + + let worldList = this.data.worldList.filter( + (w) => w.frameInfo.scene == this.data.world.frameInfo.scene + ); + worldList = worldList.sort( + (a, b) => a.frameInfo.frame_index - b.frameInfo.frame_index + ); + + let boxList = worldList.map((w) => + w.annotation.findBoxByTrackId(this.selected_box.obj_track_id) + ); + + let onFinishOneBox = (i) => { + this.viewManager.render(); + }; + let applyIndList = boxList.map((b) => true); + let dontRotate = false; + + this.boxOp + .interpolateAndAutoAdjustAsync( + worldList, + boxList, + onFinishOneBox, + applyIndList, + dontRotate + ) + .then((ret) => { + this.header.updateModifiedStatus(); + }); + }; + + this.editBatch = function (sceneName, frame, objectTrackId, objectType) { + //this.keydownDisabled = true; + // hide something + this.imageContextManager.hide(); + this.floatLabelManager.hide(); + + //this.floatLabelManager.showFastToolbox(); + + this.viewManager.mainView.disable(); + this.boxEditor.hide(); + this.hideGridLines(); + //this.controlGui.hide(); + this.editorUi.querySelector("#selectors").style.display = "none"; + //this.editorUi.querySelector("#object-selector").style.display='none'; + this.currentMainEditor = this.boxEditorManager; + + this.boxEditorManager.edit( + this.data, + this.data.getMetaBySceneName(sceneName), + frame, + objectTrackId, + objectType, + (targetFrame, targetTrackId) => { + //on exit + this.currentMainEditor = this; + //this.keydownDisabled = false; + this.viewManager.mainView.enable(); + + this.imageContextManager.show(); + this.floatLabelManager.show(); + + if (targetTrackId) this.view_state.lock_obj_track_id = targetTrackId; + + this.on_load_world_finished(this.data.world); + + // if (this.selected_box){ + // // attach again, restore box.boxEditor + // // obj type/id may have changed in batch mode + // this.floatLabelManager.set_object_track_id(this.selected_box.obj_local_id, this.selected_box.obj_track_id); + // this.boxEditor.attachBox(this.selected_box); + // this.boxEditor.update(); + + // // update fasttoolbox + // this.fastToolBox.setValue(this.selected_box.obj_type, this.selected_box.obj_track_id, this.selected_box.obj_attr); + // } - this.header.setObject(this.selected_box.obj_track_id); + this.showGridLines(); + this.render(); + //this.controlGui.show(); + this.editorUi.querySelector("#selectors").style.display = "inherit"; + + if (targetFrame) { + this.load_world(this.data.world.frameInfo.scene, targetFrame, () => { + // onfinished + this.makeVisible(targetTrackId); + }); + } + } + ); + }; + + this.gotoObjectFrame = function (frame, objId) { + this.load_world(this.data.world.frameInfo.scene, frame, () => { + // onfinished + this.makeVisible(objId); + }); + }; + + this.makeVisible = function (targetTrackId) { + let box = this.data.world.annotation.findBoxByTrackId(targetTrackId); + + if (box) { + if (this.selected_box != box) { + this.selectBox(box); + } - this.editBatch( - this.data.world.frameInfo.scene, - this.data.world.frameInfo.frame, - this.selected_box.obj_track_id, - this.selected_box.obj_type - ); - }; + this.resetView({ x: box.position.x, y: box.position.y, z: 50 }); + } + }; + + this.object_changed = function (event) { + var sceneName = this.data.world.frameInfo.scene; //this.editorUi.querySelector("#scene-selector").value; + + let objectTrackId = event.currentTarget.value; + let obj = objIdManager.getObjById(objectTrackId); + + this.editBatch(sceneName, null, objectTrackId, obj.category); + }; + + this.camera_changed = function (event) { + var camera_name = event.currentTarget.value; + + this.data.set_active_image(camera_name); + this.imageContextManager.render_2d_image(); + + event.currentTarget.blur(); + }; + + this.downloadWebglScreenShot = function () { + let link = document.createElement("a"); + link.download = `${this.data.world.frameInfo.scene}-${this.data.world.frameInfo.frame}-webgl`; + link.href = this.renderer.domElement.toDataURL("image/png", 1); + link.click(); + }; + + this.showLog = function () {}; + + this.annotateByAlg1 = function () { + autoAnnotate(this.data.world, () => + this.on_load_world_finished(this.data.world) + ); + }; + + this.object_category_changed = function (event) { + if (this.selected_box) { + let category = event.currentTarget.value; + + this.selected_box.obj_type = category; + this.floatLabelManager.set_object_type( + this.selected_box.obj_local_id, + this.selected_box.obj_type + ); + // this.header.mark_changed_flag(); + // this.updateBoxPointsColor(this.selected_box); + // this.imageContextManager.boxes_manager.update_obj_type(this.selected_box.obj_local_id, this.selected_box.obj_type); + + // this.render(); + this.on_box_changed(this.selected_box); + + //todo: we don't know if the old one is already deleted. + // could use object count number? + objIdManager.addObject({ + category: this.selected_box.obj_type, + id: this.selected_box.obj_track_id, + }); + } + }; + + this.setObjectId = function (id) { + this.selected_box.obj_track_id = id; + this.floatLabelManager.set_object_track_id( + this.selected_box.obj_local_id, + this.selected_box.obj_track_id + ); + + this.view_state.lock_obj_track_id = id; + + //this.header.mark_changed_flag(); + this.on_box_changed(this.selected_box); + + // + objIdManager.addObject({ + category: this.selected_box.obj_type, + id: this.selected_box.obj_track_id, + }); + }; + + this.object_track_id_changed = function (event) { + if (this.selected_box) { + var id = event.currentTarget.value; + this.setObjectId(id); + } + }; + + this.object_attribute_changed = function (value) { + if (this.selected_box) { + this.selected_box.obj_attr = value; + this.floatLabelManager.set_object_attr( + this.selected_box.obj_local_id, + value + ); + this.data.world.annotation.setModified(); + this.header.updateModifiedStatus(); + } + }; - this.autoAnnInBackground = function() - { - if (!this.ensureBoxTrackIdExist()) - return; + // this.updateSubviewRangeByWindowResize= function(box){ - if (!this.ensurePreloaded()) - return; + // if (box === null) + // return; - let worldList = this.data.worldList.filter(w=>w.frameInfo.scene == this.data.world.frameInfo.scene); - worldList = worldList.sort((a,b)=>a.frameInfo.frame_index - b.frameInfo.frame_index); + // if (box.boxEditor) + // box.boxEditor.onWindowResize(); + // this.render(); + // }; + this.handleRightClick = function (event) { + // select new object - let boxList = worldList.map(w=>w.annotation.findBoxByTrackId(this.selected_box.obj_track_id)); + if (!this.data.world) { + return; + } - let onFinishOneBox = (i)=>{ - this.viewManager.render(); - } - let applyIndList = boxList.map(b=>true); - let dontRotate = false; + if (event.shiftKey || event.ctrlKey) { + // if ctrl or shift hold, don't select any object. + this.contextMenu.show("world", event.layerX, event.layerY, this); + return; + } - this.boxOp.interpolateAndAutoAdjustAsync(worldList, boxList, onFinishOneBox, applyIndList, dontRotate).then(ret=>{ - this.header.updateModifiedStatus(); - }); - }; - - - this.editBatch = function(sceneName, frame, objectTrackId, objectType){ - - - - //this.keydownDisabled = true; - // hide something - this.imageContextManager.hide(); - this.floatLabelManager.hide(); - - //this.floatLabelManager.showFastToolbox(); - - this.viewManager.mainView.disable(); - this.boxEditor.hide(); - this.hideGridLines(); - //this.controlGui.hide(); - this.editorUi.querySelector("#selectors").style.display='none'; - //this.editorUi.querySelector("#object-selector").style.display='none'; - this.currentMainEditor = this.boxEditorManager; - - this.boxEditorManager.edit(this.data, - this.data.getMetaBySceneName(sceneName), - frame, - objectTrackId, - objectType, - (targetFrame, targetTrackId)=>{ //on exit - this.currentMainEditor = this - //this.keydownDisabled = false; - this.viewManager.mainView.enable(); - - this.imageContextManager.show(); - this.floatLabelManager.show(); - - if (targetTrackId) - this.view_state.lock_obj_track_id = targetTrackId; - - this.on_load_world_finished(this.data.world); - - // if (this.selected_box){ - // // attach again, restore box.boxEditor - // // obj type/id may have changed in batch mode - // this.floatLabelManager.set_object_track_id(this.selected_box.obj_local_id, this.selected_box.obj_track_id); - // this.boxEditor.attachBox(this.selected_box); - // this.boxEditor.update(); - - // // update fasttoolbox - // this.fastToolBox.setValue(this.selected_box.obj_type, this.selected_box.obj_track_id, this.selected_box.obj_attr); - // } - - - this.showGridLines(); - this.render(); - //this.controlGui.show(); - this.editorUi.querySelector("#selectors").style.display='inherit'; - - if (targetFrame) - { - this.load_world(this.data.world.frameInfo.scene, targetFrame, ()=>{ // onfinished - this.makeVisible(targetTrackId); - }); - } - } - ); - }; - - this.gotoObjectFrame = function(frame, objId) - { - this.load_world(this.data.world.frameInfo.scene, frame, ()=>{ // onfinished - this.makeVisible(objId); - }); - }; - - this.makeVisible = function(targetTrackId){ - let box = this.data.world.annotation.findBoxByTrackId(targetTrackId); - - if (box){ - if (this.selected_box != box){ - this.selectBox(box); - } - - this.resetView({x:box.position.x, y:box.position.y, z:50}); - } - - }; - - this.object_changed = function(event){ - var sceneName = this.data.world.frameInfo.scene; //this.editorUi.querySelector("#scene-selector").value; - - let objectTrackId = event.currentTarget.value; - let obj = objIdManager.getObjById(objectTrackId); - - this.editBatch(sceneName, null, objectTrackId, obj.category); - }; - - this.camera_changed= function(event){ - var camera_name = event.currentTarget.value; - - this.data.set_active_image(camera_name); - this.imageContextManager.render_2d_image(); - - event.currentTarget.blur(); - }; - - this.downloadWebglScreenShot = function(){ - let link = document.createElement("a"); - link.download=`${this.data.world.frameInfo.scene}-${this.data.world.frameInfo.frame}-webgl`; - link.href=this.renderer.domElement.toDataURL("image/png", 1); - link.click(); - }; - - this.showLog = function() { - - }; - - this.annotateByAlg1 = function(){ - autoAnnotate(this.data.world, ()=>this.on_load_world_finished(this.data.world)); - }; - - - - this.object_category_changed= function(event){ - if (this.selected_box){ - - let category = event.currentTarget.value; - - this.selected_box.obj_type = category; - this.floatLabelManager.set_object_type(this.selected_box.obj_local_id, this.selected_box.obj_type); - // this.header.mark_changed_flag(); - // this.updateBoxPointsColor(this.selected_box); - // this.imageContextManager.boxes_manager.update_obj_type(this.selected_box.obj_local_id, this.selected_box.obj_type); - - // this.render(); - this.on_box_changed(this.selected_box); - - //todo: we don't know if the old one is already deleted. - // could use object count number? - objIdManager.addObject({ - category: this.selected_box.obj_type, - id: this.selected_box.obj_track_id, - }); - - } - }; - - this.setObjectId = function(id) - { - this.selected_box.obj_track_id = id; - this.floatLabelManager.set_object_track_id(this.selected_box.obj_local_id, this.selected_box.obj_track_id); - - this.view_state.lock_obj_track_id = id; - - //this.header.mark_changed_flag(); - this.on_box_changed(this.selected_box); - - // - objIdManager.addObject({ - category: this.selected_box.obj_type, - id: this.selected_box.obj_track_id, - }); + var intersects = this.mouse.getIntersects( + this.mouse.onUpPosition, + this.data.world.annotation.boxes + ); + if (intersects.length > 0) { + //var object = intersects[ 0 ].object; + var object = intersects[0].object; + let target_obj = object.userData.object; + if (target_obj == undefined) { + // helper + target_obj = object; + } + + if (target_obj != this.selected_box) { + this.selectBox(target_obj); + } + + // this.hide_world_context_menu(); + // this.show_object_context_menu(event.layerX, event.layerY); + this.contextMenu.show("object", event.layerX, event.layerY, this); + } else { + // if no object is selected, popup context menu + //var pos = getMousePosition(renderer.domElement, event.clientX, event.clientY ); + this.contextMenu.show("world", event.layerX, event.layerY, this); } - - this.object_track_id_changed= function(event){ - if (this.selected_box){ - var id = event.currentTarget.value; - this.setObjectId(id); - } - }; - - this.object_attribute_changed = function(value){ - if (this.selected_box){ - this.selected_box.obj_attr = value; - this.floatLabelManager.set_object_attr(this.selected_box.obj_local_id, value); - this.data.world.annotation.setModified(); - this.header.updateModifiedStatus(); - } - }; - - // this.updateSubviewRangeByWindowResize= function(box){ - - // if (box === null) - // return; - - // if (box.boxEditor) - // box.boxEditor.onWindowResize(); - - // this.render(); - // }; - - this.handleRightClick= function(event){ - - // select new object - - if (!this.data.world){ - return; - } - - - if (event.shiftKey || event.ctrlKey) - { - // if ctrl or shift hold, don't select any object. - this.contextMenu.show("world",event.layerX, event.layerY, this); - return; - } - - - var intersects = this.mouse.getIntersects( this.mouse.onUpPosition, this.data.world.annotation.boxes ); - if ( intersects.length > 0 ) { - //var object = intersects[ 0 ].object; - var object = intersects[ 0 ].object; - let target_obj = object.userData.object; - if ( target_obj == undefined ) { - // helper - target_obj = object; - } - - if (target_obj != this.selected_box){ - this.selectBox(target_obj); - } - - // this.hide_world_context_menu(); - // this.show_object_context_menu(event.layerX, event.layerY); - this.contextMenu.show("object",event.layerX, event.layerY, this); - - } else { - // if no object is selected, popup context menu - //var pos = getMousePosition(renderer.domElement, event.clientX, event.clientY ); - this.contextMenu.show("world",event.layerX, event.layerY, this); - } - }; - - this.show_world_context_menu= function(posX, posY){ - let menu = this.editorUi.querySelector("#context-menu"); - menu.style.display = "inherit"; - menu.style.left = posX+"px"; - menu.style.top = posY+"px"; - this.editorUi.querySelector("#context-menu-wrapper").style.display = "block"; - }; - - this.hide_world_context_menu= function(){ - let menu = this.editorUi.querySelector("#context-menu"); - menu.style.display = "none"; - }; - - this.show_object_context_menu= function(posX, posY){ - let menu = this.editorUi.querySelector("#object-context-menu"); - menu.style.display = "inherit"; - menu.style.left = posX+"px"; - menu.style.top = posY+"px"; - this.editorUi.querySelector("#context-menu-wrapper").style.display = "block"; - }; - - this.hide_object_context_menu= function(){ - let menu = this.editorUi.querySelector("#object-context-menu"); - menu.style.display = "none"; - }; - - this.on_img_click = function(lidar_point_indices){ - - console.log(lidar_point_indices); - - var self=this; - let obj_type = "Car"; - this.data.world.lidar.set_spec_points_color(lidar_point_indices, {x:0,y:0,z:1}); - this.data.world.lidar.update_points_color(); - this.render(); - //return; - - let pos = this.data.world.lidar.get_centroid(lidar_point_indices); - pos.z = 0; - - let rotation = {x:0, y:0, z:this.viewManager.mainView.camera.rotation.z+Math.PI/2}; - - let obj_cfg = globalObjectCategory.get_obj_cfg_by_type(obj_type); - let scale = { - x: obj_cfg.size[0], - y: obj_cfg.size[1], - z: obj_cfg.size[2] - }; - - let box = this.add_box(pos, scale, rotation, obj_type, ""); - self.boxOp.auto_rotate_xyz(box, null, null, function(b){ - self.on_box_changed(b); - }); - - return; - /* + }; + + this.show_world_context_menu = function (posX, posY) { + let menu = this.editorUi.querySelector("#context-menu"); + menu.style.display = "inherit"; + menu.style.left = posX + "px"; + menu.style.top = posY + "px"; + this.editorUi.querySelector("#context-menu-wrapper").style.display = + "block"; + }; + + this.hide_world_context_menu = function () { + let menu = this.editorUi.querySelector("#context-menu"); + menu.style.display = "none"; + }; + + this.show_object_context_menu = function (posX, posY) { + let menu = this.editorUi.querySelector("#object-context-menu"); + menu.style.display = "inherit"; + menu.style.left = posX + "px"; + menu.style.top = posY + "px"; + this.editorUi.querySelector("#context-menu-wrapper").style.display = + "block"; + }; + + this.hide_object_context_menu = function () { + let menu = this.editorUi.querySelector("#object-context-menu"); + menu.style.display = "none"; + }; + + this.on_img_click = function (lidar_point_indices) { + console.log(lidar_point_indices); + + var self = this; + let obj_type = "Car"; + this.data.world.lidar.set_spec_points_color(lidar_point_indices, { + x: 0, + y: 0, + z: 1, + }); + this.data.world.lidar.update_points_color(); + this.render(); + //return; + + let pos = this.data.world.lidar.get_centroid(lidar_point_indices); + pos.z = 0; + + let rotation = { + x: 0, + y: 0, + z: this.viewManager.mainView.camera.rotation.z + Math.PI / 2, + }; + + let obj_cfg = globalObjectCategory.get_obj_cfg_by_type(obj_type); + let scale = { + x: obj_cfg.size[0], + y: obj_cfg.size[1], + z: obj_cfg.size[2], + }; + + let box = this.add_box(pos, scale, rotation, obj_type, ""); + self.boxOp.auto_rotate_xyz(box, null, null, function (b) { + self.on_box_changed(b); + }); + + return; + /* var box = this.data.world.lidar.create_box_by_points(lidar_point_indices, this.viewManager.mainView.camera); @@ -1394,1290 +1441,1357 @@ function Editor(editorUi, wrapperUi, editorCfg, data, name="editor"){ this.boxOp.auto_shrink_box(box); - - // guess obj type here - - box.obj_type = guess_obj_type_by_dimension(box.scale); - - this.floatLabelManager.add_label(box); - - this.selectBox(box); - this.on_box_changed(box); - - - this.boxOp.auto_rotate_xyz(box, function(){ - box.obj_type = guess_obj_type_by_dimension(box.scale); - self.floatLabelManager.set_object_type(box.obj_local_id, box.obj_type); - self.floatLabelManager.update_label_editor(box.obj_type, box.obj_track_id); - self.on_box_changed(box); - }); - */ - - }; - - this.handleSelectRect= function(x,y,w,h, ctrl, shift){ - // y = y+h; - // x = x*2-1; - // y = -y*2+1; - // w *= 2; - // h *= 2; - - // x,y: start cornor, w: width, h: height - - /* - console.log("main select rect", x,y,w,h); - - this.viewManager.mainView.camera.updateProjectionMatrix(); - this.data.world.select_points_by_view_rect(x,y,w,h, this.viewManager.mainView.camera); - render(); - render_2d_image(); - */ - - // check if any box is inside the rectangle - - this.viewManager.mainView.camera.updateProjectionMatrix(); - - let boxes = this.data.world.annotation.find_boxes_inside_rect(x,y,w,h, this.viewManager.mainView.camera); - if (boxes.length > 0) { - - if (boxes.length == 1){ - this.selectBox(boxes[0]) - } - else{ - // this is dangerous - // for (let b in boxes){ - // this.remove_box(boxes[b],false) - // } - // this.render(); - } - - return; - } - - let points = this.data.world.lidar.select_points_by_view_rect(x,y,w,h, this.viewManager.mainView.camera); - - // show color - //this.render(); - - // return; - // // create new box - // var self=this; - var center_pos = this.mouse.get_screen_location_in_world(x+w/2, y+h/2); - center_pos = this.data.world.scenePosToLidar(center_pos); - - let initRoationZ = this.viewManager.mainView.camera.rotation.z + Math.PI/2; - - let box = this.create_box_by_points(points, initRoationZ); - - let id = objIdManager.generateNewUniqueId(); - box.obj_track_id = id; - - - - //this.scene.add(box); - - - - if (!shift){ - try{ - this.boxOp.auto_shrink_box(box); - } - catch(e) - { - logger.log(e); - } - } - - // guess obj type here - - box.obj_type = globalObjectCategory.guess_obj_type_by_dimension(box.scale); - - objIdManager.addObject({ - category: box.obj_type, - id: box.obj_track_id, - }); - - - - this.imageContextManager.boxes_manager.add_box(box); - this.floatLabelManager.add_label(box); - - this.selectBox(box); - this.on_box_changed(box); - - if (!shift){ - this.boxOp.auto_rotate_xyz(box, ()=>{ - box.obj_type = globalObjectCategory.guess_obj_type_by_dimension(box.scale); - this.floatLabelManager.set_object_type(box.obj_local_id, box.obj_type); - this.fastToolBox.setValue(box.obj_type, box.obj_track_id, box.obj_attr); - this.on_box_changed(box); - }); - } - - - //floatLabelManager.add_label(box); - - - }; - - - - this.create_box_by_points=function(points, rotationZ){ - - let localRot = this.data.world.sceneRotToLidar(new THREE.Euler(0,0,rotationZ, "XYZ")); - - let transToBoxMatrix = new THREE.Matrix4().makeRotationFromEuler(localRot) - .setPosition(0, 0, 0) - .invert(); - - // var trans = transpose(euler_angle_to_rotate_matrix({x:0,y:0,z:rotation_z}, {x:0, y:0, z:0}), 4); - - let relative_position = []; - let v = new THREE.Vector3(); - points.forEach(function(p){ - v.set(p[0],p[1],p[2]); - let boxP = v.applyMatrix4(transToBoxMatrix); - relative_position.push([boxP.x,boxP.y, boxP.z]); - }); - - var relative_extreme = vector_range(relative_position); - var scale = { - x: relative_extreme.max[0] - relative_extreme.min[0], - y: relative_extreme.max[1] - relative_extreme.min[1], - z: relative_extreme.max[2] - relative_extreme.min[2], - }; - - // enlarge scale a little - - let center = this.boxOp.translateBoxInBoxCoord( - localRot, - { - x: (relative_extreme.max[0] + relative_extreme.min[0])/2, - y: (relative_extreme.max[1] + relative_extreme.min[1])/2, - z: (relative_extreme.max[2] + relative_extreme.min[2])/2, - } - ); - - return this.data.world.annotation.add_box(center, scale, localRot, "Unknown", ""); - }; - - - this.handleLeftClick= function(event) { - - if (event.ctrlKey){ - //Ctrl+left click to smart paste! - //smart_paste(); - } - else{ - //select box /unselect box - if (!this.data.world || (!this.data.world.annotation.boxes && this.data.world.radars.radarList.length==0 && !this.calib.calib_box)){ - return; - } - - let all_boxes = this.data.world.annotation.boxes.concat(this.data.world.radars.getAllBoxes()); - all_boxes = all_boxes.concat(this.data.world.aux_lidars.getAllBoxes()); - - if (this.calib.calib_box){ - all_boxes.push(this.calib.calib_box); - } - - let intersects = this.mouse.getIntersects( this.mouse.onUpPosition, all_boxes); - - if (intersects.length == 0){ - if (this.data.world.radar_box){ - intersects = this.mouse.getIntersects( this.mouse.onUpPosition, [this.data.world.radar_box]); - } - } - - if ( intersects.length > 0 ) { - //var object = intersects[ 0 ].object; - var object = intersects[ 0 ].object; - if ( object.userData.object !== undefined ) { - // helper - this.selectBox( object.userData.object ); - } else { - this.selectBox( object ); - } - } else { - this.unselectBox(null); - } - - //render(); - } - - - }; - - this.select_locked_object= function(){ - var self=this; - if (this.view_state.lock_obj_track_id != ""){ - var box = this.data.world.annotation.boxes.find(function(x){ - return x.obj_track_id == self.view_state.lock_obj_track_id; - }) - - if (box){ - this.selectBox(box); - - if (self.view_state.lock_obj_in_highlight){ - this.focusOnSelectedBox(box); - } - } - } - }; - - // new_object - this.unselectBox = function(new_object, keep_lock){ - - if (new_object==null){ - if (this.viewManager.mainView && this.viewManager.mainView.transform_control.visible) - { - //unselect first time - this.viewManager.mainView.transform_control.detach(); - }else{ - //unselect second time - if (this.selected_box){ - // restore from highlight - if (this.selected_box.in_highlight){ - this.cancelFocus(this.selected_box); - - if (!keep_lock){ - this.view_state.lock_obj_in_highlight = false; - } - } else{ - // unselected finally - //this.selected_box.material.color = new THREE.Color(parseInt("0x"+get_obj_cfg_by_type(this.selected_box.obj_type).color.slice(1))); - //this.selected_box.material.opacity = this.data.cfg.box_opacity; - this.boxOp.unhighlightBox(this.selected_box); - //this.floatLabelManager.unselect_box(this.selected_box.obj_local_id, this.selected_box.obj_type); - this.fastToolBox.hide(); - - if (!keep_lock){ - this.view_state.lock_obj_track_id = ""; - } - - this.imageContextManager.boxes_manager.onBoxUnselected(this.selected_box.obj_local_id, this.selected_box.obj_type); - this.selected_box = null; - this.boxEditor.detach(); - - this.onSelectedBoxChanged(null); - } - } - else{ - // just an empty click - return; - } - } - } - else{ - // selected other box - //unselect all - this.viewManager.mainView.transform_control.detach(); - - - if (this.selected_box){ - - // restore from highlight - - if (this.selected_box.in_highlight){ - this.cancelFocus(this.selected_box); - if (!keep_lock){ - this.view_state.lock_obj_in_highlight = false; - } - } - - this.selected_box.material.color = new THREE.Color(parseInt("0x"+globalObjectCategory.get_obj_cfg_by_type(this.selected_box.obj_type).color.slice(1))); - this.selected_box.material.opacity = this.data.cfg.box_opacity; - //this.floatLabelManager.unselect_box(this.selected_box.obj_local_id); - this.fastToolBox.hide(); - this.imageContextManager.boxes_manager.onBoxUnselected(this.selected_box.obj_local_id, this.selected_box.obj_type); - - this.selected_box = null; - this.boxEditor.detach(); - if (!keep_lock) - this.view_state.lock_obj_track_id = ""; - } - } - - - - this.render(); - - }; - - - - this.selectBox = function(object){ - - if (this.selected_box != object){ - // unselect old bbox - - var in_highlight = false; - - if (this.selected_box){ - in_highlight = this.selected_box.in_highlight; - this.unselectBox(this.selected_box); - } - - // select me, the first time - this.selected_box = object; - - // switch camera - if (!this.editorCfg.disableMainImageContext){ - var best_camera = this.imageContextManager.choose_best_camera_for_point( - this.selected_box.world.frameInfo.sceneMeta, - this.selected_box.position); - - if (best_camera){ - - //var image_changed = this.data.set_active_image(best_camera); - - // if (image_changed){ - // this.editorUi.querySelector("#camera-selector").value=best_camera; - // this.imageContextManager.boxes_manager.display_image(); - // } - - this.imageContextManager.setBestCamera(best_camera); - } - } - - // highlight box - // shold change this id if the current selected box changed id. - this.view_state.lock_obj_track_id = object.obj_track_id; - - //this.floatLabelManager.select_box(this.selected_box.obj_local_id); - - this.fastToolBox.setPos(this.floatLabelManager.getLabelEditorPos(this.selected_box.obj_local_id)); - this.fastToolBox.setValue(object.obj_type, object.obj_track_id, object.obj_attr); - this.fastToolBox.show(); - - this.boxOp.highlightBox(this.selected_box); - - if (in_highlight){ - this.focusOnSelectedBox(this.selected_box); - } - - this.save_box_info(object); // this is needed since when a frame is loaded, all box haven't saved anything. - // we could move this to when a frame is loaded. - this.boxEditor.attachBox(object); - this.onSelectedBoxChanged(object); - - } - else { - //reselect the same box - if (this.viewManager.mainView.transform_control.visible){ - this.change_transform_control_view(); - } - else{ - //select me the second time - //object.add(this.viewManager.mainView.transform_control); - this.viewManager.mainView.transform_control.attach( object ); - } - } - - this.render(); - - - }; - - this.adjustContainerSize = function() - { - let editorRect = this.editorUi.getBoundingClientRect(); - let headerRect = this.editorUi.querySelector("#header").getBoundingClientRect(); - - this.container.style.height = editorRect.height - headerRect.height + "px"; - } - - - this.onWindowResize= function() { - - this.adjustContainerSize(); - this.boxEditorManager.onWindowResize(); - - // use clientwidth and clientheight to resize container - // but use scrollwidth/height to place other things. - if ( this.windowWidth != this.container.clientWidth || this.windowHeight != this.container.clientHeight ) { - - //update_mainview(); - if (this.viewManager.mainView) - this.viewManager.mainView.onWindowResize(); - - if (this.boxEditor) - this.boxEditor.update("dontrender"); - - this.windowWidth = this.container.clientWidth; - this.windowHeight = this.container.clientHeight; - this.renderer.setSize( this.windowWidth, this.windowHeight ); - - //this.viewManager.updateViewPort(); - - // update sideview svg if there exists selected box - // the following update is called in updateSubviewRangeByWindowResize - // if (this.selected_box){ - // this.projectiveViewOps.update_view_handle(this.selected_box); - // } - } - - this.viewManager.render(); - }; - - this.change_transform_control_view= function(){ - if (this.viewManager.mainView.transform_control.mode=="scale"){ - this.viewManager.mainView.transform_control.setMode( "translate" ); - this.viewManager.mainView.transform_control.showY=true; - this.viewManager.mainView.transform_control.showX=true; - this.viewManager.mainView.transform_control.showz=true; - }else if (this.viewManager.mainView.transform_control.mode=="translate"){ - this.viewManager.mainView.transform_control.setMode( "rotate" ); - this.viewManager.mainView.transform_control.showY=false; - this.viewManager.mainView.transform_control.showX=false; - this.viewManager.mainView.transform_control.showz=true; - }else if (this.viewManager.mainView.transform_control.mode=="rotate"){ - this.viewManager.mainView.transform_control.setMode( "scale" ); - this.viewManager.mainView.transform_control.showY=true; - this.viewManager.mainView.transform_control.showX=true; - this.viewManager.mainView.transform_control.showz=true; - } - }; - - this.add_box_on_mouse_pos_by_ref = function(){ - - let globalP = this.mouse.get_mouse_location_in_world(); - // trans pos to world local pos - let pos = this.data.world.scenePosToLidar(globalP); - - let refbox = this.autoAdjust.marked_object.ann; - pos.z = refbox.psr.position.z; - - let id = refbox.obj_id; - - if (this.autoAdjust.marked_object.frame == this.data.world.frameInfo.frame) - { - id = ""; - } - - let box = this.add_box(pos, refbox.psr.scale, refbox.psr.rotation, refbox.obj_type, id, refbox.obj_attr); - - return box; - }; - - this.add_box_on_mouse_pos= function(obj_type){ - // todo: move to this.data.world - let globalP = this.mouse.get_mouse_location_in_world(); - - // trans pos to world local pos - let pos = this.data.world.scenePosToLidar(globalP); - - var rotation = new THREE.Euler(0, 0, this.viewManager.mainView.camera.rotation.z+Math.PI/2, "XYZ"); - rotation = this.data.world.sceneRotToLidar(rotation); - - var obj_cfg = globalObjectCategory.get_obj_cfg_by_type(obj_type); - var scale = { - x: obj_cfg.size[0], - y: obj_cfg.size[1], - z: obj_cfg.size[2] - }; - - pos.z = -1.8 + scale.z/2; // -1.8 is height of lidar - - let id = objIdManager.generateNewUniqueId(); - - objIdManager.addObject({ - category: obj_type, - id: id, - }); - - let box = this.add_box(pos, scale, rotation, obj_type, id); - - return box; - }; - - this.add_box= function(pos, scale, rotation, obj_type, obj_track_id, obj_attr){ - let box = this.data.world.annotation.add_box(pos, scale, rotation, obj_type, obj_track_id, obj_attr); - - this.floatLabelManager.add_label(box); - - this.imageContextManager.boxes_manager.add_box(box); - - this.selectBox(box); - return box; - }; - - this.save_box_info= function(box){ - box.last_info = { - //obj_type: box.obj_type, - position: { - x: box.position.x, - y: box.position.y, - z: box.position.z, - }, - rotation: { - x: box.rotation.x, - y: box.rotation.y, - z: box.rotation.z, - }, - scale: { - x: box.scale.x, - y: box.scale.y, - z: box.scale.z, - } - } - }; - - - // axix, xyz, action: scale, move, direction, up/down - this.transform_bbox= function(command){ - if (!this.selected_box) - return; - - switch (command){ - case 'x_move_up': - this.boxOp.translate_box(this.selected_box, 'x', 0.05); - break; - case 'x_move_down': - this.boxOp.translate_box(this.selected_box, 'x', -0.05); - break; - case 'x_scale_up': - this.selected_box.scale.x *= 1.01; - break; - case 'x_scale_down': - this.selected_box.scale.x /= 1.01; - break; - - case 'y_move_up': - this.boxOp.translate_box(this.selected_box, 'y', 0.05); - break; - case 'y_move_down': - this.boxOp.translate_box(this.selected_box, 'y', -0.05); - break; - case 'y_scale_up': - this.selected_box.scale.y *= 1.01; - break; - case 'y_scale_down': - this.selected_box.scale.y /= 1.01; - break; - - case 'z_move_up': - this.selected_box.position.z += 0.05; - break; - case 'z_move_down': - this.selected_box.position.z -= 0.05; - break; - case 'z_scale_up': - this.selected_box.scale.z *= 1.01; - break; - case 'z_scale_down': - this.selected_box.scale.z /= 1.01; - break; - - case 'z_rotate_left': - this.selected_box.rotation.z += 0.01; - break; - case 'z_rotate_right': - this.selected_box.rotation.z -= 0.01; - break; - - case 'z_rotate_reverse': - if (this.selected_box.rotation.z > 0){ - this.selected_box.rotation.z -= Math.PI; - }else{ - this.selected_box.rotation.z += Math.PI; - } - break; - case 'reset': - this.selected_box.rotation.x = 0; - this.selected_box.rotation.y = 0; - this.selected_box.rotation.z = 0; - this.selected_box.position.z = 0; - break; + + // guess obj type here + + box.obj_type = guess_obj_type_by_dimension(box.scale); + + this.floatLabelManager.add_label(box); - } + this.selectBox(box); + this.on_box_changed(box); - this.on_box_changed(this.selected_box); - }; + this.boxOp.auto_rotate_xyz(box, function(){ + box.obj_type = guess_obj_type_by_dimension(box.scale); + self.floatLabelManager.set_object_type(box.obj_local_id, box.obj_type); + self.floatLabelManager.update_label_editor(box.obj_type, box.obj_track_id); + self.on_box_changed(box); + }); + */ + }; + this.handleSelectRect = function (x, y, w, h, ctrl, shift) { + // y = y+h; + // x = x*2-1; + // y = -y*2+1; + // w *= 2; + // h *= 2; - // function switch_bbox_type(target_type){ - // if (!this.selected_box) - // return; + // x,y: start cornor, w: width, h: height - // if (!target_type){ - // target_type = get_next_obj_type_name(this.selected_box.obj_type); - // } + /* + console.log("main select rect", x,y,w,h); - // this.selected_box.obj_type = target_type; - // var obj_cfg = get_obj_cfg_by_type(target_type); - // this.selected_box.scale.x=obj_cfg.size[0]; - // this.selected_box.scale.y=obj_cfg.size[1]; - // this.selected_box.scale.z=obj_cfg.size[2]; + this.viewManager.mainView.camera.updateProjectionMatrix(); + this.data.world.select_points_by_view_rect(x,y,w,h, this.viewManager.mainView.camera); + render(); + render_2d_image(); + */ - - // this.floatLabelManager.set_object_type(this.selected_box.obj_local_id, this.selected_box.obj_type); - // this.floatLabelManager.update_label_editor(this.selected_box.obj_type, this.selected_box.obj_track_id); + // check if any box is inside the rectangle + + this.viewManager.mainView.camera.updateProjectionMatrix(); + + let boxes = this.data.world.annotation.find_boxes_inside_rect( + x, + y, + w, + h, + this.viewManager.mainView.camera + ); + if (boxes.length > 0) { + if (boxes.length == 1) { + this.selectBox(boxes[0]); + } else { + // this is dangerous + // for (let b in boxes){ + // this.remove_box(boxes[b],false) + // } + // this.render(); + } - - - // } + return; + } - + let points = this.data.world.lidar.select_points_by_view_rect( + x, + y, + w, + h, + this.viewManager.mainView.camera + ); - this.keydown= function( ev ) { + // show color + //this.render(); - // if (this.keydownDisabled) - // return; + // return; + // // create new box + // var self=this; + var center_pos = this.mouse.get_screen_location_in_world( + x + w / 2, + y + h / 2 + ); + center_pos = this.data.world.scenePosToLidar(center_pos); - this.operation_state.key_pressed = true; + let initRoationZ = + this.viewManager.mainView.camera.rotation.z + Math.PI / 2; - switch ( ev.key) { - case '+': - case '=': - this.data.scale_point_size(1.2); - this.render(); - break; - case '-': - this.data.scale_point_size(0.8); - this.render(); - break; - case '1': - this.select_previous_object(); - break; - case '2': - this.select_next_object(); - break; - case '3': - case 'PageUp': - this.previous_frame(); - break; - case 'PageDown': - case '4': - this.next_frame(); - break; - case 'p': - this.downloadWebglScreenShot(); - break; - - // case 'v': - // this.change_transform_control_view(); - // break; - /* - case 'm': - case 'M': - smart_paste(); - break; - case 'N': - case 'n': - //add_bbox(); - //header.mark_changed_flag(); - break; - case 'B': - case 'b': - switch_bbox_type(); - self.header.mark_changed_flag(); - self.on_box_changed(this.selected_box); - break; - */ - case 'z': // X - this.viewManager.mainView.transform_control.showX = ! this.viewManager.mainView.transform_control.showX; - break; - case 'x': // Y - this.viewManager.mainView.transform_control.showY = ! this.viewManager.mainView.transform_control.showY; - break; - case 'c': // Z - if (ev.ctrlKey){ - this.mark_bbox(this.selected_box); - } else { - this.viewManager.mainView.transform_control.showZ = ! this.viewManager.mainView.transform_control.showZ; - } - break; - case ' ': // Spacebar - //this.viewManager.mainView.transform_control.enabled = ! this.viewManager.mainView.transform_control.enabled; - this.playControl.pause_resume_play(); - break; - - case '5': - case '6': - case '7': - this.boxEditor.boxView.views[ev.key-5].cameraHelper.visible = !this.boxEditor.boxView.views[ev.key-5].cameraHelper.visible; - this.render(); - break; - - - case 's': - if (ev.ctrlKey){ - saveWorldList(this.data.worldList); - } - else if (this.selected_box) - { - let v = Math.max(this.editorCfg.moveStep * this.selected_box.scale.x, 0.02); - this.boxOp.translate_box(this.selected_box, 'x', -v); - this.on_box_changed(this.selected_box); - } - break; - case 'w': - if (this.selected_box){ - let v = Math.max(this.editorCfg.moveStep * this.selected_box.scale.x, 0.02); - this.boxOp.translate_box(this.selected_box, 'x', v); - this.on_box_changed(this.selected_box); - } - break; - case 'a': - if (this.selected_box){ - let v = Math.max(this.editorCfg.moveStep * this.selected_box.scale.y, 0.02); - this.boxOp.translate_box(this.selected_box, 'y', v); - this.on_box_changed(this.selected_box); - } - break; - case 'd': - if (this.selected_box){ - let v = Math.max(this.editorCfg.moveStep * this.selected_box.scale.y, 0.02); - this.boxOp.translate_box(this.selected_box, 'y', -v); - this.on_box_changed(this.selected_box); - } - break; + let box = this.create_box_by_points(points, initRoationZ); - case 'q': - if (this.selected_box){ - this.boxOp.rotate_z(this.selected_box, this.editorCfg.rotateStep, false); - this.on_box_changed(this.selected_box); - } - break; - case 'e': - if (this.selected_box){ - this.boxOp.rotate_z(this.selected_box, -this.editorCfg.rotateStep, false); - this.on_box_changed(this.selected_box); - } - break; - case 'r': - if (this.selected_box){ - //this.transform_bbox("z_rotate_left"); - this.boxOp.rotate_z(this.selected_box, this.editorCfg.rotateStep, true); - this.on_box_changed(this.selected_box); - } - break; - - case 'f': - if (this.selected_box){ - //this.transform_bbox("z_rotate_right"); - this.boxOp.rotate_z(this.selected_box, -this.editorCfg.rotateStep, true); - this.on_box_changed(this.selected_box); - } - break; - case 'g': - this.transform_bbox("z_rotate_reverse"); - break; - case 't': - //this.transform_bbox("reset"); - this.showTrajectory(); - break; - case 'v': - this.enterBatchEditMode(); - break; - case 'd': - case 'D': - if (ev.ctrlKey){ - this.remove_selected_box(); - this.header.updateModifiedStatus(); - } - break; - case 'Delete': - this.remove_selected_box(); - this.header.updateModifiedStatus(); - break; - case 'Escape': - if (this.selected_box){ - this.unselectBox(null); - } - break; - } - }; + let id = objIdManager.generateNewUniqueId(); + box.obj_track_id = id; + //this.scene.add(box); + if (!shift) { + try { + this.boxOp.auto_shrink_box(box); + } catch (e) { + logger.log(e); + } + } - this.previous_frame= function(){ + // guess obj type here + box.obj_type = globalObjectCategory.guess_obj_type_by_dimension(box.scale); + objIdManager.addObject({ + category: box.obj_type, + id: box.obj_track_id, + }); - if (!this.data.meta) - return; + this.imageContextManager.boxes_manager.add_box(box); + this.floatLabelManager.add_label(box); - var scene_meta = this.data.get_current_world_scene_meta(); + this.selectBox(box); + this.on_box_changed(box); - var frame_index = this.data.world.frameInfo.frame_index-1; + if (!shift) { + this.boxOp.auto_rotate_xyz(box, () => { + box.obj_type = globalObjectCategory.guess_obj_type_by_dimension( + box.scale + ); + this.floatLabelManager.set_object_type(box.obj_local_id, box.obj_type); + this.fastToolBox.setValue(box.obj_type, box.obj_track_id, box.obj_attr); + this.on_box_changed(box); + }); + } - if (frame_index < 0){ - console.log("first frame"); - this.infoBox.show("Notice", "This is the first frame"); - return; + //floatLabelManager.add_label(box); + }; + + this.create_box_by_points = function (points, rotationZ) { + let localRot = this.data.world.sceneRotToLidar( + new THREE.Euler(0, 0, rotationZ, "XYZ") + ); + + let transToBoxMatrix = new THREE.Matrix4() + .makeRotationFromEuler(localRot) + .setPosition(0, 0, 0) + .invert(); + + // var trans = transpose(euler_angle_to_rotate_matrix({x:0,y:0,z:rotation_z}, {x:0, y:0, z:0}), 4); + + let relative_position = []; + let v = new THREE.Vector3(); + points.forEach(function (p) { + v.set(p[0], p[1], p[2]); + let boxP = v.applyMatrix4(transToBoxMatrix); + relative_position.push([boxP.x, boxP.y, boxP.z]); + }); + + var relative_extreme = vector_range(relative_position); + var scale = { + x: relative_extreme.max[0] - relative_extreme.min[0], + y: relative_extreme.max[1] - relative_extreme.min[1], + z: relative_extreme.max[2] - relative_extreme.min[2], + }; + + // enlarge scale a little + + let center = this.boxOp.translateBoxInBoxCoord(localRot, { + x: (relative_extreme.max[0] + relative_extreme.min[0]) / 2, + y: (relative_extreme.max[1] + relative_extreme.min[1]) / 2, + z: (relative_extreme.max[2] + relative_extreme.min[2]) / 2, + }); + + return this.data.world.annotation.add_box( + center, + scale, + localRot, + "Unknown", + "" + ); + }; + + this.handleLeftClick = function (event) { + if (event.ctrlKey) { + //Ctrl+left click to smart paste! + //smart_paste(); + } else { + //select box /unselect box + if ( + !this.data.world || + (!this.data.world.annotation.boxes && + this.data.world.radars.radarList.length == 0 && + !this.calib.calib_box) + ) { + return; + } + + let all_boxes = this.data.world.annotation.boxes.concat( + this.data.world.radars.getAllBoxes() + ); + all_boxes = all_boxes.concat(this.data.world.aux_lidars.getAllBoxes()); + + if (this.calib.calib_box) { + all_boxes.push(this.calib.calib_box); + } + + let intersects = this.mouse.getIntersects( + this.mouse.onUpPosition, + all_boxes + ); + + if (intersects.length == 0) { + if (this.data.world.radar_box) { + intersects = this.mouse.getIntersects(this.mouse.onUpPosition, [ + this.data.world.radar_box, + ]); + } + } + + if (intersects.length > 0) { + //var object = intersects[ 0 ].object; + var object = intersects[0].object; + if (object.userData.object !== undefined) { + // helper + this.selectBox(object.userData.object); + } else { + this.selectBox(object); } + } else { + this.unselectBox(null); + } - this.load_world(scene_meta.scene, scene_meta.frames[frame_index]); + //render(); + } + }; - + this.select_locked_object = function () { + var self = this; + if (this.view_state.lock_obj_track_id != "") { + var box = this.data.world.annotation.boxes.find(function (x) { + return x.obj_track_id == self.view_state.lock_obj_track_id; + }); - }; + if (box) { + this.selectBox(box); - this.last_frame = function() - { - let scene_meta = this.data.get_current_world_scene_meta(); - this.load_world(scene_meta.scene, scene_meta.frames[scene_meta.frames.length-1]); - }; - this.first_frame = function() - { - let scene_meta = this.data.get_current_world_scene_meta(); - this.load_world(scene_meta.scene, scene_meta.frames[0]); - }; + if (self.view_state.lock_obj_in_highlight) { + this.focusOnSelectedBox(box); + } + } + } + }; + + // new_object + this.unselectBox = function (new_object, keep_lock) { + if (new_object == null) { + if ( + this.viewManager.mainView && + this.viewManager.mainView.transform_control.visible + ) { + //unselect first time + this.viewManager.mainView.transform_control.detach(); + } else { + //unselect second time + if (this.selected_box) { + // restore from highlight + if (this.selected_box.in_highlight) { + this.cancelFocus(this.selected_box); - this.next_frame= function(){ + if (!keep_lock) { + this.view_state.lock_obj_in_highlight = false; + } + } else { + // unselected finally + //this.selected_box.material.color = new THREE.Color(parseInt("0x"+get_obj_cfg_by_type(this.selected_box.obj_type).color.slice(1))); + //this.selected_box.material.opacity = this.data.cfg.box_opacity; + this.boxOp.unhighlightBox(this.selected_box); + //this.floatLabelManager.unselect_box(this.selected_box.obj_local_id, this.selected_box.obj_type); + this.fastToolBox.hide(); + + if (!keep_lock) { + this.view_state.lock_obj_track_id = ""; + } + this.imageContextManager.boxes_manager.onBoxUnselected( + this.selected_box.obj_local_id, + this.selected_box.obj_type + ); + this.selected_box = null; + this.boxEditor.detach(); + this.onSelectedBoxChanged(null); + } + } else { + // just an empty click + return; + } + } + } else { + // selected other box + //unselect all + this.viewManager.mainView.transform_control.detach(); + + if (this.selected_box) { + // restore from highlight + + if (this.selected_box.in_highlight) { + this.cancelFocus(this.selected_box); + if (!keep_lock) { + this.view_state.lock_obj_in_highlight = false; + } + } + + this.selected_box.material.color = new THREE.Color( + parseInt( + "0x" + + globalObjectCategory + .get_obj_cfg_by_type(this.selected_box.obj_type) + .color.slice(1) + ) + ); + this.selected_box.material.opacity = this.data.cfg.box_opacity; + //this.floatLabelManager.unselect_box(this.selected_box.obj_local_id); + this.fastToolBox.hide(); + this.imageContextManager.boxes_manager.onBoxUnselected( + this.selected_box.obj_local_id, + this.selected_box.obj_type + ); - if (!this.data.meta) - return; - - var scene_meta = this.data.get_current_world_scene_meta(); + this.selected_box = null; + this.boxEditor.detach(); + if (!keep_lock) this.view_state.lock_obj_track_id = ""; + } + } - var num_frames = scene_meta.frames.length; + this.render(); + }; - var frame_index = (this.data.world.frameInfo.frame_index +1); + this.selectBox = function (object) { + if (this.selected_box != object) { + // unselect old bbox - if (frame_index >= num_frames){ - console.log("last frame"); - this.infoBox.show("Notice", "This is the last frame"); - return; - } + var in_highlight = false; - this.load_world(scene_meta.scene, scene_meta.frames[frame_index]); - }; + if (this.selected_box) { + in_highlight = this.selected_box.in_highlight; + this.unselectBox(this.selected_box); + } - this.select_next_object= function(){ + // select me, the first time + this.selected_box = object; - var self=this; - if (this.data.world.annotation.boxes.length<=0) - return; + // switch camera + if (!this.editorCfg.disableMainImageContext) { + var best_camera = this.imageContextManager.choose_best_camera_for_point( + this.selected_box.world.frameInfo.sceneMeta, + this.selected_box.position + ); - if (this.selected_box){ - this.operation_state.box_navigate_index = this.data.world.annotation.boxes.findIndex(function(x){ - return self.selected_box == x; - }); - } - - this.operation_state.box_navigate_index += 1; - this.operation_state.box_navigate_index %= this.data.world.annotation.boxes.length; - - this.selectBox(this.data.world.annotation.boxes[this.operation_state.box_navigate_index]); + if (best_camera) { + //var image_changed = this.data.set_active_image(best_camera); + + // if (image_changed){ + // this.editorUi.querySelector("#camera-selector").value=best_camera; + // this.imageContextManager.boxes_manager.display_image(); + // } + + this.imageContextManager.setBestCamera(best_camera); + } + } + + // highlight box + // shold change this id if the current selected box changed id. + this.view_state.lock_obj_track_id = object.obj_track_id; + + //this.floatLabelManager.select_box(this.selected_box.obj_local_id); + + this.fastToolBox.setPos( + this.floatLabelManager.getLabelEditorPos(this.selected_box.obj_local_id) + ); + this.fastToolBox.setValue( + object.obj_type, + object.obj_track_id, + object.obj_attr + ); + this.fastToolBox.show(); + + this.boxOp.highlightBox(this.selected_box); + + if (in_highlight) { + this.focusOnSelectedBox(this.selected_box); + } + + this.save_box_info(object); // this is needed since when a frame is loaded, all box haven't saved anything. + // we could move this to when a frame is loaded. + this.boxEditor.attachBox(object); + this.onSelectedBoxChanged(object); + } else { + //reselect the same box + if (this.viewManager.mainView.transform_control.visible) { + this.change_transform_control_view(); + } else { + //select me the second time + //object.add(this.viewManager.mainView.transform_control); + this.viewManager.mainView.transform_control.attach(object); + } + } - }; + this.render(); + }; - this.select_previous_object= function(){ - var self=this; - if (this.data.world.annotation.boxes.length<=0) - return; + this.adjustContainerSize = function () { + let editorRect = this.editorUi.getBoundingClientRect(); + let headerRect = this.editorUi + .querySelector("#header") + .getBoundingClientRect(); - if (this.selected_box){ - this.operation_state.box_navigate_index = this.data.world.annotation.boxes.findIndex(function(x){ - return self.selected_box == x; - }); - } - - this.operation_state.box_navigate_index += this.data.world.annotation.boxes.length-1; - this.operation_state.box_navigate_index %= this.data.world.annotation.boxes.length; - - this.selectBox(this.data.world.annotation.boxes[this.operation_state.box_navigate_index]); - }; + this.container.style.height = editorRect.height - headerRect.height + "px"; + }; - // this.centerMainView =function(){ - // let offset = this.data.world.coordinatesOffset; - // this.viewManager.mainView.orbit.target.x += offset[0]; - // this.viewManager.mainView.orbit.target.y += offset[1]; - // this.viewManager.mainView.orbit.target.z += offset[2]; - // }; - - this.on_load_world_finished= function(world){ - - document.title = "SUSTech POINTS-" + world.frameInfo.scene; - // switch view positoin - this.moveAxisHelper(world); - this.moveRangeCircle(world); - this.lookAtWorld(world); - this.unselectBox(null, true); - this.unselectBox(null, true); - this.render(); - this.imageContextManager.attachWorld(world); - this.imageContextManager.render_2d_image(); - this.render2dLabels(world); - this.update_frame_info(world.frameInfo.scene, world.frameInfo.frame); + this.onWindowResize = function () { + this.adjustContainerSize(); + this.boxEditorManager.onWindowResize(); - this.select_locked_object(); - - //load_obj_ids_of_scene(world.frameInfo.scene); - objIdManager.setCurrentScene(world.frameInfo.scene); + // use clientwidth and clientheight to resize container + // but use scrollwidth/height to place other things. + if ( + this.windowWidth != this.container.clientWidth || + this.windowHeight != this.container.clientHeight + ) { + //update_mainview(); + if (this.viewManager.mainView) this.viewManager.mainView.onWindowResize(); - // preload after the first world loaded - // otherwise the loading of the first world would be too slow - this.data.preloadScene(world.frameInfo.scene, world); - }; - this.moveAxisHelper = function(world) { - world.webglGroup.add(this.axis); - }; + if (this.boxEditor) this.boxEditor.update("dontrender"); - this.mainViewOffset = [0,0,0]; + this.windowWidth = this.container.clientWidth; + this.windowHeight = this.container.clientHeight; + this.renderer.setSize(this.windowWidth, this.windowHeight); - this.lookAtWorld = function(world){ - let newOffset = [ - world.coordinatesOffset[0] - this.mainViewOffset[0], - world.coordinatesOffset[1] - this.mainViewOffset[1], - world.coordinatesOffset[2] - this.mainViewOffset[2], - ]; - - this.mainViewOffset = world.coordinatesOffset; - - this.viewManager.mainView.orbit.target.x += newOffset[0]; - this.viewManager.mainView.orbit.target.y += newOffset[1]; - this.viewManager.mainView.orbit.target.z += newOffset[2]; + //this.viewManager.updateViewPort(); - this.viewManager.mainView.camera.position.x += newOffset[0]; - this.viewManager.mainView.camera.position.y += newOffset[1]; - this.viewManager.mainView.camera.position.z += newOffset[2]; + // update sideview svg if there exists selected box + // the following update is called in updateSubviewRangeByWindowResize + // if (this.selected_box){ + // this.projectiveViewOps.update_view_handle(this.selected_box); + // } + } - this.viewManager.mainView.orbit.update(); - - }; + this.viewManager.render(); + }; + + this.change_transform_control_view = function () { + if (this.viewManager.mainView.transform_control.mode == "scale") { + this.viewManager.mainView.transform_control.setMode("translate"); + this.viewManager.mainView.transform_control.showY = true; + this.viewManager.mainView.transform_control.showX = true; + this.viewManager.mainView.transform_control.showz = true; + } else if ( + this.viewManager.mainView.transform_control.mode == "translate" + ) { + this.viewManager.mainView.transform_control.setMode("rotate"); + this.viewManager.mainView.transform_control.showY = false; + this.viewManager.mainView.transform_control.showX = false; + this.viewManager.mainView.transform_control.showz = true; + } else if (this.viewManager.mainView.transform_control.mode == "rotate") { + this.viewManager.mainView.transform_control.setMode("scale"); + this.viewManager.mainView.transform_control.showY = true; + this.viewManager.mainView.transform_control.showX = true; + this.viewManager.mainView.transform_control.showz = true; + } + }; - this.load_world = async function(sceneName, frame, onFinished){ + this.add_box_on_mouse_pos_by_ref = function () { + let globalP = this.mouse.get_mouse_location_in_world(); + // trans pos to world local pos + let pos = this.data.world.scenePosToLidar(globalP); - this.data.dbg.dump(); + let refbox = this.autoAdjust.marked_object.ann; + pos.z = refbox.psr.position.z; - logger.log(`load ${sceneName}, ${frame}`); + let id = refbox.obj_id; - var self=this; - //stop if current world is not ready! - if (this.data.world && !this.data.world.preloaded()){ - console.error("current world is still loading."); - return; - } + if ( + this.autoAdjust.marked_object.frame == this.data.world.frameInfo.frame + ) { + id = ""; + } - if (this.selected_box && this.selected_box.in_highlight){ - this.cancelFocus(this.selected_box); - } + let box = this.add_box( + pos, + refbox.psr.scale, + refbox.psr.rotation, + refbox.obj_type, + id, + refbox.obj_attr + ); + + return box; + }; + + this.add_box_on_mouse_pos = function (obj_type) { + // todo: move to this.data.world + let globalP = this.mouse.get_mouse_location_in_world(); + + // trans pos to world local pos + let pos = this.data.world.scenePosToLidar(globalP); + + var rotation = new THREE.Euler( + 0, + 0, + this.viewManager.mainView.camera.rotation.z + Math.PI / 2, + "XYZ" + ); + rotation = this.data.world.sceneRotToLidar(rotation); + + var obj_cfg = globalObjectCategory.get_obj_cfg_by_type(obj_type); + var scale = { + x: obj_cfg.size[0], + y: obj_cfg.size[1], + z: obj_cfg.size[2], + }; + + pos.z = -1.8 + scale.z / 2; // -1.8 is height of lidar + + let id = objIdManager.generateNewUniqueId(); + + objIdManager.addObject({ + category: obj_type, + id: id, + }); + + let box = this.add_box(pos, scale, rotation, obj_type, id); + + return box; + }; + + this.add_box = function ( + pos, + scale, + rotation, + obj_type, + obj_track_id, + obj_attr + ) { + let box = this.data.world.annotation.add_box( + pos, + scale, + rotation, + obj_type, + obj_track_id, + obj_attr + ); + + this.floatLabelManager.add_label(box); + + this.imageContextManager.boxes_manager.add_box(box); + + this.selectBox(box); + return box; + }; + + this.save_box_info = function (box) { + box.last_info = { + //obj_type: box.obj_type, + position: { + x: box.position.x, + y: box.position.y, + z: box.position.z, + }, + rotation: { + x: box.rotation.x, + y: box.rotation.y, + z: box.rotation.z, + }, + scale: { + x: box.scale.x, + y: box.scale.y, + z: box.scale.z, + }, + }; + }; + + // axix, xyz, action: scale, move, direction, up/down + this.transform_bbox = function (command) { + if (!this.selected_box) return; + + switch (command) { + case "x_move_up": + this.boxOp.translate_box(this.selected_box, "x", 0.05); + break; + case "x_move_down": + this.boxOp.translate_box(this.selected_box, "x", -0.05); + break; + case "x_scale_up": + this.selected_box.scale.x *= 1.01; + break; + case "x_scale_down": + this.selected_box.scale.x /= 1.01; + break; + + case "y_move_up": + this.boxOp.translate_box(this.selected_box, "y", 0.05); + break; + case "y_move_down": + this.boxOp.translate_box(this.selected_box, "y", -0.05); + break; + case "y_scale_up": + this.selected_box.scale.y *= 1.01; + break; + case "y_scale_down": + this.selected_box.scale.y /= 1.01; + break; + + case "z_move_up": + this.selected_box.position.z += 0.05; + break; + case "z_move_down": + this.selected_box.position.z -= 0.05; + break; + case "z_scale_up": + this.selected_box.scale.z *= 1.01; + break; + case "z_scale_down": + this.selected_box.scale.z /= 1.01; + break; + + case "z_rotate_left": + this.selected_box.rotation.z += 0.01; + break; + case "z_rotate_right": + this.selected_box.rotation.z -= 0.01; + break; + + case "z_rotate_reverse": + if (this.selected_box.rotation.z > 0) { + this.selected_box.rotation.z -= Math.PI; + } else { + this.selected_box.rotation.z += Math.PI; + } + break; + case "reset": + this.selected_box.rotation.x = 0; + this.selected_box.rotation.y = 0; + this.selected_box.rotation.z = 0; + this.selected_box.position.z = 0; + break; + } - if (this.viewManager.mainView && this.viewManager.mainView.transform_control.visible) - { - //unselect first time - this.viewManager.mainView.transform_control.detach(); - } + this.on_box_changed(this.selected_box); + }; - var world = await this.data.getWorld(sceneName, frame); + // function switch_bbox_type(target_type){ + // if (!this.selected_box) + // return; - if (world) - { - this.data.activate_world( - world, - function(){ - self.on_load_world_finished(world); - if (onFinished) - onFinished(); - - } - ); - } + // if (!target_type){ + // target_type = get_next_obj_type_name(this.selected_box.obj_type); + // } - - }; + // this.selected_box.obj_type = target_type; + // var obj_cfg = get_obj_cfg_by_type(target_type); + // this.selected_box.scale.x=obj_cfg.size[0]; + // this.selected_box.scale.y=obj_cfg.size[1]; + // this.selected_box.scale.z=obj_cfg.size[2]; + // this.floatLabelManager.set_object_type(this.selected_box.obj_local_id, this.selected_box.obj_type); + // this.floatLabelManager.update_label_editor(this.selected_box.obj_type, this.selected_box.obj_track_id); - this.remove_box = function(box, render=true){ - if (box === this.selected_box){ - this.unselectBox(null,true); - this.unselectBox(null,true); //twice to safely unselect. - this.selected_box = null; - //this.remove_selected_box(); - } - + // } + this.keydown = function (ev) { + // if (this.keydownDisabled) + // return; - this.do_remove_box(box, false); // render later. + this.operation_state.key_pressed = true; - // this should be after do-remove-box - // subview renderings don't need to be done again after - // the box is removed. - if (box.boxEditor) - { - if (box.boxEditor){ - box.boxEditor.detach("donthide"); - } - else{ - console.error("what?"); - } + switch (ev.key) { + case "+": + case "=": + this.data.scale_point_size(1.2); + this.render(); + break; + case "-": + this.data.scale_point_size(0.8); + this.render(); + break; + case "1": + this.select_previous_object(); + break; + case "2": + this.select_next_object(); + break; + case "3": + case "PageUp": + this.previous_frame(); + break; + case "PageDown": + case "4": + this.next_frame(); + break; + case "p": + this.downloadWebglScreenShot(); + break; + + // case 'v': + // this.change_transform_control_view(); + // break; + /* + case 'm': + case 'M': + smart_paste(); + break; + case 'N': + case 'n': + //add_bbox(); + //header.mark_changed_flag(); + break; + case 'B': + case 'b': + switch_bbox_type(); + self.header.mark_changed_flag(); + self.on_box_changed(this.selected_box); + break; + */ + case "z": // X + this.viewManager.mainView.transform_control.showX = + !this.viewManager.mainView.transform_control.showX; + break; + case "x": // Y + this.viewManager.mainView.transform_control.showY = + !this.viewManager.mainView.transform_control.showY; + break; + case "c": // Z + if (ev.ctrlKey) { + this.mark_bbox(this.selected_box); + } else { + this.viewManager.mainView.transform_control.showZ = + !this.viewManager.mainView.transform_control.showZ; + } + break; + case " ": // Spacebar + //this.viewManager.mainView.transform_control.enabled = ! this.viewManager.mainView.transform_control.enabled; + this.playControl.pause_resume_play(); + break; + + case "5": + case "6": + case "7": + this.boxEditor.boxView.views[ev.key - 5].cameraHelper.visible = + !this.boxEditor.boxView.views[ev.key - 5].cameraHelper.visible; + this.render(); + break; + + case "s": + if (ev.ctrlKey) { + saveWorldList(this.data.worldList); + } else if (this.selected_box) { + let v = Math.max( + this.editorCfg.moveStep * this.selected_box.scale.x, + 0.02 + ); + this.boxOp.translate_box(this.selected_box, "x", -v); + this.on_box_changed(this.selected_box); + } + break; + case "w": + if (this.selected_box) { + let v = Math.max( + this.editorCfg.moveStep * this.selected_box.scale.x, + 0.02 + ); + this.boxOp.translate_box(this.selected_box, "x", v); + this.on_box_changed(this.selected_box); + } + break; + case "a": + if (this.selected_box) { + let v = Math.max( + this.editorCfg.moveStep * this.selected_box.scale.y, + 0.02 + ); + this.boxOp.translate_box(this.selected_box, "y", v); + this.on_box_changed(this.selected_box); + } + break; + case "d": + if (this.selected_box) { + let v = Math.max( + this.editorCfg.moveStep * this.selected_box.scale.y, + 0.02 + ); + this.boxOp.translate_box(this.selected_box, "y", -v); + this.on_box_changed(this.selected_box); + } + break; + + case "q": + if (this.selected_box) { + this.boxOp.rotate_z( + this.selected_box, + this.editorCfg.rotateStep, + false + ); + this.on_box_changed(this.selected_box); + } + break; + case "e": + if (this.selected_box) { + this.boxOp.rotate_z( + this.selected_box, + -this.editorCfg.rotateStep, + false + ); + this.on_box_changed(this.selected_box); + } + break; + case "r": + if (this.selected_box) { + //this.transform_bbox("z_rotate_left"); + this.boxOp.rotate_z( + this.selected_box, + this.editorCfg.rotateStep, + true + ); + this.on_box_changed(this.selected_box); + } + break; + + case "f": + if (this.selected_box) { + //this.transform_bbox("z_rotate_right"); + this.boxOp.rotate_z( + this.selected_box, + -this.editorCfg.rotateStep, + true + ); + this.on_box_changed(this.selected_box); + } + break; + case "g": + this.transform_bbox("z_rotate_reverse"); + break; + case "t": + //this.transform_bbox("reset"); + this.showTrajectory(); + break; + case "v": + this.enterBatchEditMode(); + break; + case "d": + case "D": + if (ev.ctrlKey) { + this.remove_selected_box(); + this.header.updateModifiedStatus(); + } + break; + case "Delete": + this.remove_selected_box(); + this.header.updateModifiedStatus(); + break; + case "Escape": + if (this.selected_box) { + this.unselectBox(null); } + break; + } + }; + this.previous_frame = function () { + if (!this.data.meta) return; - this.header.updateModifiedStatus(); + var scene_meta = this.data.get_current_world_scene_meta(); - if (render) - this.render(); - - }; + var frame_index = this.data.world.frameInfo.frame_index - 1; - this.remove_selected_box= function(){ - this.remove_box(this.selected_box); - }; + if (frame_index < 0) { + console.log("first frame"); + this.infoBox.show("Notice", "This is the first frame"); + return; + } - this.do_remove_box = function(box, render=true){ + this.load_world(scene_meta.scene, scene_meta.frames[frame_index]); + }; - if (!box.annotator) - this.restore_box_points_color(box, render); + this.last_frame = function () { + let scene_meta = this.data.get_current_world_scene_meta(); + this.load_world( + scene_meta.scene, + scene_meta.frames[scene_meta.frames.length - 1] + ); + }; + this.first_frame = function () { + let scene_meta = this.data.get_current_world_scene_meta(); + this.load_world(scene_meta.scene, scene_meta.frames[0]); + }; - this.imageContextManager.boxes_manager.remove_box(box.obj_local_id); + this.next_frame = function () { + if (!this.data.meta) return; - this.floatLabelManager.remove_box(box); - this.fastToolBox.hide(); - - //this.selected_box.dispose(); - - box.world.annotation.unload_box(box); - box.world.annotation.remove_box(box); + var scene_meta = this.data.get_current_world_scene_meta(); - box.world.annotation.setModified(); - }, + var num_frames = scene_meta.frames.length; - this.clear= function(){ + var frame_index = this.data.world.frameInfo.frame_index + 1; - this.header.clear_box_info(); - //this.editorUi.querySelector("#image").innerHTML = ''; - - this.unselectBox(null); - this.unselectBox(null); + if (frame_index >= num_frames) { + console.log("last frame"); + this.infoBox.show("Notice", "This is the last frame"); + return; + } - this.header.clear_frame_info(); + this.load_world(scene_meta.scene, scene_meta.frames[frame_index]); + }; - this.imageContextManager.clear_main_canvas(); - this.boxEditor.detach(); + this.select_next_object = function () { + var self = this; + if (this.data.world.annotation.boxes.length <= 0) return; + if (this.selected_box) { + this.operation_state.box_navigate_index = + this.data.world.annotation.boxes.findIndex(function (x) { + return self.selected_box == x; + }); + } - this.data.world.unload(); - this.data.world= null; //dump it - this.floatLabelManager.remove_all_labels(); - this.fastToolBox.hide(); - this.render(); - }; + this.operation_state.box_navigate_index += 1; + this.operation_state.box_navigate_index %= + this.data.world.annotation.boxes.length; - this.update_frame_info= function(scene, frame){ - var self = this; - this.header.set_frame_info(scene, frame, function(sceneName){ - self.scene_changed(sceneName)}); - }; + this.selectBox( + this.data.world.annotation.boxes[this.operation_state.box_navigate_index] + ); + }; - //box edited - this.on_box_changed = function(box){ + this.select_previous_object = function () { + var self = this; + if (this.data.world.annotation.boxes.length <= 0) return; - if (!this.imageContextManager.hidden()) - this.imageContextManager.boxes_manager.update_box(box); + if (this.selected_box) { + this.operation_state.box_navigate_index = + this.data.world.annotation.boxes.findIndex(function (x) { + return self.selected_box == x; + }); + } - this.header.update_box_info(box); - //floatLabelManager.update_position(box, false); don't update position, or the ui is annoying. - - box.world.annotation.setModified(); - - + this.operation_state.box_navigate_index += + this.data.world.annotation.boxes.length - 1; + this.operation_state.box_navigate_index %= + this.data.world.annotation.boxes.length; + + this.selectBox( + this.data.world.annotation.boxes[this.operation_state.box_navigate_index] + ); + }; + + // this.centerMainView =function(){ + // let offset = this.data.world.coordinatesOffset; + // this.viewManager.mainView.orbit.target.x += offset[0]; + // this.viewManager.mainView.orbit.target.y += offset[1]; + // this.viewManager.mainView.orbit.target.z += offset[2]; + // }; + + this.on_load_world_finished = function (world) { + document.title = "SUSTech POINTS-" + world.frameInfo.scene; + // switch view positoin + this.moveAxisHelper(world); + this.moveRangeCircle(world); + this.lookAtWorld(world); + this.unselectBox(null, true); + this.unselectBox(null, true); + this.render(); + this.imageContextManager.attachWorld(world); + this.imageContextManager.render_2d_image(); + this.render2dLabels(world); + this.update_frame_info(world.frameInfo.scene, world.frameInfo.frame); + + this.select_locked_object(); + + //load_obj_ids_of_scene(world.frameInfo.scene); + objIdManager.setCurrentScene(world.frameInfo.scene); + + // preload after the first world loaded + // otherwise the loading of the first world would be too slow + this.data.preloadScene(world.frameInfo.scene, world); + }; + this.moveAxisHelper = function (world) { + world.webglGroup.add(this.axis); + }; + + this.mainViewOffset = [0, 0, 0]; + + this.lookAtWorld = function (world) { + let newOffset = [ + world.coordinatesOffset[0] - this.mainViewOffset[0], + world.coordinatesOffset[1] - this.mainViewOffset[1], + world.coordinatesOffset[2] - this.mainViewOffset[2], + ]; + + this.mainViewOffset = world.coordinatesOffset; + + this.viewManager.mainView.orbit.target.x += newOffset[0]; + this.viewManager.mainView.orbit.target.y += newOffset[1]; + this.viewManager.mainView.orbit.target.z += newOffset[2]; + + this.viewManager.mainView.camera.position.x += newOffset[0]; + this.viewManager.mainView.camera.position.y += newOffset[1]; + this.viewManager.mainView.camera.position.z += newOffset[2]; + + this.viewManager.mainView.orbit.update(); + }; + + this.load_world = async function (sceneName, frame, onFinished) { + this.data.dbg.dump(); + + logger.log(`load ${sceneName}, ${frame}`); + + var self = this; + //stop if current world is not ready! + if (this.data.world && !this.data.world.preloaded()) { + console.error("current world is still loading."); + return; + } - this.updateBoxPointsColor(box); - this.save_box_info(box); - - + if (this.selected_box && this.selected_box.in_highlight) { + this.cancelFocus(this.selected_box); + } - if (box.boxEditor){ - box.boxEditor.onBoxChanged(); - } - else{ - console.error("what?"); - } + if ( + this.viewManager.mainView && + this.viewManager.mainView.transform_control.visible + ) { + //unselect first time + this.viewManager.mainView.transform_control.detach(); + } - this.autoAdjust.syncFollowers(box); + var world = await this.data.getWorld(sceneName, frame); - // if (box === this.data.world.radar_box){ - // this.data.world.move_radar(box); - // } + if (world) { + this.data.activate_world(world, function () { + self.on_load_world_finished(world); + if (onFinished) onFinished(); + }); + } + }; + + this.remove_box = function (box, render = true) { + if (box === this.selected_box) { + this.unselectBox(null, true); + this.unselectBox(null, true); //twice to safely unselect. + this.selected_box = null; + //this.remove_selected_box(); + } - if (box.on_box_changed){ - box.on_box_changed(); - } + this.do_remove_box(box, false); // render later. + + // this should be after do-remove-box + // subview renderings don't need to be done again after + // the box is removed. + if (box.boxEditor) { + if (box.boxEditor) { + box.boxEditor.detach("donthide"); + } else { + console.error("what?"); + } + } - this.header.updateModifiedStatus(); - this.render(); - }; + this.header.updateModifiedStatus(); - // box removed, restore points color. - this.restore_box_points_color= function(box,render=true){ - if (this.data.cfg.color_obj != "no"){ - box.world.lidar.reset_box_points_color(box); - box.world.lidar.update_points_color(); - if (render) - this.render(); - } - - }; + if (render) this.render(); + }; - this.updateBoxPointsColor= function(box){ - if (this.data.cfg.color_obj != "no"){ - if (box.last_info){ - box.world.lidar.set_box_points_color(box.last_info, {x: this.data.cfg.point_brightness, y: this.data.cfg.point_brightness, z: this.data.cfg.point_brightness}); - } + this.remove_selected_box = function () { + this.remove_box(this.selected_box); + }; - box.world.lidar.set_box_points_color(box); - box.world.lidar.update_points_color(); - } - }; + (this.do_remove_box = function (box, render = true) { + if (!box.annotator) this.restore_box_points_color(box, render); - this.onSelectedBoxChanged= function(box){ + this.imageContextManager.boxes_manager.remove_box(box.obj_local_id); - if (box){ - this.header.update_box_info(box); - // this.floatLabelManager.update_position(box, true); - // this.fastToolBox.setPos(this.floatLabelManager.getLabelEditorPos(box.obj_local_id)); - this.imageContextManager.boxes_manager.onBoxSelected(box.obj_local_id, box.obj_type); + this.floatLabelManager.remove_box(box); + this.fastToolBox.hide(); + //this.selected_box.dispose(); - //this.boxEditor.attachBox(box); + box.world.annotation.unload_box(box); + box.world.annotation.remove_box(box); - this.render(); - //this.boxEditor.boxView.render(); + box.world.annotation.setModified(); + }), + (this.clear = function () { + this.header.clear_box_info(); + //this.editorUi.querySelector("#image").innerHTML = ''; - //this.updateSubviewRangeByWindowResize(box); - - } else { - this.header.clear_box_info(); - } + this.unselectBox(null); + this.unselectBox(null); - }; + this.header.clear_frame_info(); - this.render2dLabels= function(world){ - if (this.editorCfg.disableMainView) - return; + this.imageContextManager.clear_main_canvas(); + this.boxEditor.detach(); - this.floatLabelManager.remove_all_labels(); - var self=this; - world.annotation.boxes.forEach(function(b){ - self.floatLabelManager.add_label(b); - }) + this.data.world.unload(); + this.data.world = null; //dump it + this.floatLabelManager.remove_all_labels(); + this.fastToolBox.hide(); + this.render(); + }); - if (this.selected_box){ - //this.floatLabelManager.select_box(this.selected_box.obj_local_id) - this.fastToolBox.show(); - this.fastToolBox.setValue(this.selected_box.obj_type, this.selected_box.obj_track_id, this.selected_box.obj_attr); - } - }; + this.update_frame_info = function (scene, frame) { + var self = this; + this.header.set_frame_info(scene, frame, function (sceneName) { + self.scene_changed(sceneName); + }); + }; - this.add_global_obj_type= function(){ + //box edited + this.on_box_changed = function (box) { + if (!this.imageContextManager.hidden()) + this.imageContextManager.boxes_manager.update_box(box); - var self = this; - var sheet = window.document.styleSheets[1]; + this.header.update_box_info(box); + //floatLabelManager.update_position(box, false); don't update position, or the ui is annoying. - let obj_type_map = globalObjectCategory.obj_type_map; + box.world.annotation.setModified(); - for (var o in obj_type_map){ - var rule = '.'+o+ '{color:'+obj_type_map[o].color+';'+ - 'stroke:' +obj_type_map[o].color+ ';'+ - 'fill:' +obj_type_map[o].color+ '22' + ';'+ - '}'; - sheet.insertRule(rule, sheet.cssRules.length); - } + this.updateBoxPointsColor(box); + this.save_box_info(box); - function color_str(v){ - let c = Math.round(v*255); - if (c < 16) - return "0" + c.toString(16); - else - return c.toString(16); - } + if (box.boxEditor) { + box.boxEditor.onBoxChanged(); + } else { + console.error("what?"); + } - for (let idx=0; idx<=32; idx++){ - let c = globalObjectCategory.get_color_by_id(idx); - let color = "#" + color_str(c.x) + color_str(c.y) + color_str(c.z); + this.autoAdjust.syncFollowers(box); - var rule = '.color-'+idx+ '{color:'+color+';'+ - 'stroke:' +color+ ';'+ - 'fill:' +color+ '22' + ';'+ - '}'; - sheet.insertRule(rule, sheet.cssRules.length); - } + // if (box === this.data.world.radar_box){ + // this.data.world.move_radar(box); + // } - // obj type selector - var options = ""; - for (var o in obj_type_map){ - options += ''; - } + if (box.on_box_changed) { + box.on_box_changed(); + } - this.editorUi.querySelector("#floating-things #object-category-selector").innerHTML = options; - //this.editorUi.querySelector("#batch-editor-tools-wrapper #object-category-selector").innerHTML = options; + this.header.updateModifiedStatus(); + this.render(); + }; - // submenu of new - var items = ""; - for (var o in obj_type_map){ - items += ''; - } + // box removed, restore points color. + this.restore_box_points_color = function (box, render = true) { + if (this.data.cfg.color_obj != "no") { + box.world.lidar.reset_box_points_color(box); + box.world.lidar.update_points_color(); + if (render) this.render(); + } + }; + + this.updateBoxPointsColor = function (box) { + if (this.data.cfg.color_obj != "no") { + if (box.last_info) { + box.world.lidar.set_box_points_color(box.last_info, { + x: this.data.cfg.point_brightness, + y: this.data.cfg.point_brightness, + z: this.data.cfg.point_brightness, + }); + } + + box.world.lidar.set_box_points_color(box); + box.world.lidar.update_points_color(); + } + }; + + this.onSelectedBoxChanged = function (box) { + if (box) { + this.header.update_box_info(box); + // this.floatLabelManager.update_position(box, true); + // this.fastToolBox.setPos(this.floatLabelManager.getLabelEditorPos(box.obj_local_id)); + this.imageContextManager.boxes_manager.onBoxSelected( + box.obj_local_id, + box.obj_type + ); + + //this.boxEditor.attachBox(box); + + this.render(); + //this.boxEditor.boxView.render(); + + //this.updateSubviewRangeByWindowResize(box); + } else { + this.header.clear_box_info(); + } + }; + + this.render2dLabels = function (world) { + if (this.editorCfg.disableMainView) return; + + this.floatLabelManager.remove_all_labels(); + var self = this; + world.annotation.boxes.forEach(function (b) { + self.floatLabelManager.add_label(b); + }); + + if (this.selected_box) { + //this.floatLabelManager.select_box(this.selected_box.obj_local_id) + this.fastToolBox.show(); + this.fastToolBox.setValue( + this.selected_box.obj_type, + this.selected_box.obj_track_id, + this.selected_box.obj_attr + ); + } + }; + + this.add_global_obj_type = function () { + var self = this; + var sheet = window.document.styleSheets[1]; + + let obj_type_map = globalObjectCategory.obj_type_map; + + for (var o in obj_type_map) { + var rule = + "." + + o + + "{color:" + + obj_type_map[o].color + + ";" + + "stroke:" + + obj_type_map[o].color + + ";" + + "fill:" + + obj_type_map[o].color + + "22" + + ";" + + "}"; + sheet.insertRule(rule, sheet.cssRules.length); + } + + function color_str(v) { + let c = Math.round(v * 255); + if (c < 16) return "0" + c.toString(16); + else return c.toString(16); + } - this.editorUi.querySelector("#new-submenu").innerHTML = items; + for (let idx = 0; idx <= 32; idx++) { + let c = globalObjectCategory.get_color_by_id(idx); + let color = "#" + color_str(c.x) + color_str(c.y) + color_str(c.z); + + var rule = + ".color-" + + idx + + "{color:" + + color + + ";" + + "stroke:" + + color + + ";" + + "fill:" + + color + + "22" + + ";" + + "}"; + sheet.insertRule(rule, sheet.cssRules.length); + } - this.contextMenu.installMenu("newSubMenu", this.editorUi.querySelector("#new-submenu"), (event)=>{ - let obj_type = event.currentTarget.getAttribute("uservalue"); - let box = self.add_box_on_mouse_pos(obj_type); - //switch_bbox_type(event.currentTarget.getAttribute("uservalue")); - //self.boxOp.grow_box(box, 0.2, {x:2, y:2, z:3}); - //self.auto_shrink_box(box); - //self.on_box_changed(box); + // obj type selector + var options = ""; + for (var o in obj_type_map) { + options += + '"; + } - let noscaling = event.shiftKey; + this.editorUi.querySelector( + "#floating-things #object-category-selector" + ).innerHTML = options; + //this.editorUi.querySelector("#batch-editor-tools-wrapper #object-category-selector").innerHTML = options; + + // submenu of new + var items = ""; + for (var o in obj_type_map) { + items += + '"; + } - self.boxOp.auto_rotate_xyz(box, null, null, function(b){ - self.on_box_changed(b); - }, noscaling); - return true; - }); + this.editorUi.querySelector("#new-submenu").innerHTML = items; + + this.contextMenu.installMenu( + "newSubMenu", + this.editorUi.querySelector("#new-submenu"), + (event) => { + let obj_type = event.currentTarget.getAttribute("uservalue"); + let box = self.add_box_on_mouse_pos(obj_type); + //switch_bbox_type(event.currentTarget.getAttribute("uservalue")); + //self.boxOp.grow_box(box, 0.2, {x:2, y:2, z:3}); + //self.auto_shrink_box(box); + //self.on_box_changed(box); + + let noscaling = event.shiftKey; + + self.boxOp.auto_rotate_xyz( + box, + null, + null, + function (b) { + self.on_box_changed(b); + }, + noscaling + ); + return true; + } + ); - // // install click actions - // for (var o in obj_type_map){ - // this.editorUi.querySelector("#cm-new-"+o).onclick = (event)=>{ - - // // hide context men - // // let context menu object handle this. - // // this.editorUi.querySelector("#context-menu-wrapper").style.display="none"; - - // // process event - // var obj_type = event.currentTarget.getAttribute("uservalue"); - // let box = self.add_box_on_mouse_pos(obj_type); - // //switch_bbox_type(event.currentTarget.getAttribute("uservalue")); - // //self.boxOp.grow_box(box, 0.2, {x:2, y:2, z:3}); - // //self.auto_shrink_box(box); - // //self.on_box_changed(box); - - // self.boxOp.auto_rotate_xyz(box, null, null, function(b){ - // self.on_box_changed(b); - // }); - - // } - // } + // // install click actions + // for (var o in obj_type_map){ + // this.editorUi.querySelector("#cm-new-"+o).onclick = (event)=>{ - }; + // // hide context men + // // let context menu object handle this. + // // this.editorUi.querySelector("#context-menu-wrapper").style.display="none"; - this.interpolate_selected_object= function(){ + // // process event + // var obj_type = event.currentTarget.getAttribute("uservalue"); + // let box = self.add_box_on_mouse_pos(obj_type); + // //switch_bbox_type(event.currentTarget.getAttribute("uservalue")); + // //self.boxOp.grow_box(box, 0.2, {x:2, y:2, z:3}); + // //self.auto_shrink_box(box); + // //self.on_box_changed(box); - let scene = this.data.world.frameInfo.scene; - let frame = this.data.world.frameInfo.frame; - let obj_id = this.selected_box.obj_track_id; + // self.boxOp.auto_rotate_xyz(box, null, null, function(b){ + // self.on_box_changed(b); + // }); - this.boxOp.interpolate_selected_object(scene, obj_id, frame, (s,fs)=>{ - this.onAnnotationUpdatedByOthers(s, fs); - }); + // } + // } + }; - - }; + this.interpolate_selected_object = function () { + let scene = this.data.world.frameInfo.scene; + let frame = this.data.world.frameInfo.frame; + let obj_id = this.selected_box.obj_track_id; - this.onAnnotationUpdatedByOthers = function(scene, frames){ - this.data.onAnnotationUpdatedByOthers(scene, frames); - } + this.boxOp.interpolate_selected_object(scene, obj_id, frame, (s, fs) => { + this.onAnnotationUpdatedByOthers(s, fs); + }); + }; - this.init(editorUi); + this.onAnnotationUpdatedByOthers = function (scene, frames) { + this.data.onAnnotationUpdatedByOthers(scene, frames); + }; -}; + this.init(editorUi); +} -export{Editor} \ No newline at end of file +export { Editor }; diff --git a/public/js/ego_pose.js b/public/js/ego_pose.js index 3eebf5c..a12f953 100644 --- a/public/js/ego_pose.js +++ b/public/js/ego_pose.js @@ -1,78 +1,65 @@ - - - - -class EgoPose -{ - constructor(sceneMeta, world, frameInfo) - { - this.world = world; - this.data = this.world.data; - this.sceneMeta = sceneMeta; - } - - - preload(on_preload_finished) - { - this.on_preload_finished = on_preload_finished; - this.load_ego_pose(); - }; - - - load_ego_pose(){ - - var xhr = new XMLHttpRequest(); - // we defined the xhr - var _self = this; - xhr.onreadystatechange = function () { - if (this.readyState != 4) return; - - if (this.status == 200) { - let egoPose = JSON.parse(this.responseText); - _self.egoPose = egoPose; - } - - console.log(_self.world.frameInfo.frame, "egopose", "loaded"); - _self.preloaded = true; - - if (_self.on_preload_finished){ - _self.on_preload_finished(); - } - if (_self.go_cmd_received){ - _self.go(this.webglScene, this.on_go_finished); - } - - // end of state change: it can be after some time (async) - }; - - xhr.open('GET', "/load_ego_pose"+"?scene="+this.world.frameInfo.scene+"&frame="+this.world.frameInfo.frame, true); - xhr.send(); +class EgoPose { + constructor(sceneMeta, world, frameInfo) { + this.world = world; + this.data = this.world.data; + this.sceneMeta = sceneMeta; + } + + preload(on_preload_finished) { + this.on_preload_finished = on_preload_finished; + this.load_ego_pose(); + } + + load_ego_pose() { + var xhr = new XMLHttpRequest(); + // we defined the xhr + var _self = this; + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + + if (this.status == 200) { + let egoPose = JSON.parse(this.responseText); + _self.egoPose = egoPose; + } + + console.log(_self.world.frameInfo.frame, "egopose", "loaded"); + _self.preloaded = true; + + if (_self.on_preload_finished) { + _self.on_preload_finished(); + } + if (_self.go_cmd_received) { + _self.go(this.webglScene, this.on_go_finished); + } + + // end of state change: it can be after some time (async) }; + xhr.open( + "GET", + "/load_ego_pose" + + "?scene=" + + this.world.frameInfo.scene + + "&frame=" + + this.world.frameInfo.frame, + true + ); + xhr.send(); + } + + go_cmd_received = false; + on_go_finished = null; + + go(webglScene, on_go_finished) { + if (this.preloaded) { + if (on_go_finished) on_go_finished(); + } else { + this.go_cmd_received = true; + this.on_go_finished = on_go_finished; + } + } - go_cmd_received = false; - on_go_finished = null; - - go(webglScene, on_go_finished) - { - if (this.preloaded){ - if (on_go_finished) - on_go_finished(); - } else { - this.go_cmd_received = true; - this.on_go_finished = on_go_finished; - } - }; - - - - - unload() - { - - }; - + unload() {} } - -export{EgoPose} \ No newline at end of file +export { EgoPose }; diff --git a/public/js/error_check.js b/public/js/error_check.js index b64aea4..4bc6bf3 100644 --- a/public/js/error_check.js +++ b/public/js/error_check.js @@ -1,30 +1,27 @@ import { logger } from "./log.js"; +function checkScene(scene) { + const req = new Request(`/checkscene?scene=${scene}`); + let init = { + method: "GET", + //body: JSON.stringify({"points": data}) + }; + // we defined the xhr -function checkScene(scene) -{ - const req = new Request(`/checkscene?scene=${scene}`); - let init = { - method: 'GET', - //body: JSON.stringify({"points": data}) - }; - // we defined the xhr - - return fetch(req, init) - .then(response=>{ - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - }else{ - return response.json(); - } + return fetch(req, init) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } else { + return response.json(); + } }) - .then(ret=> - { - logger.setErrorsContent(ret); + .then((ret) => { + logger.setErrorsContent(ret); }) - .catch(reject=>{ - console.log("error check scene!"); + .catch((reject) => { + console.log("error check scene!"); }); } -export {checkScene} \ No newline at end of file +export { checkScene }; diff --git a/public/js/floatlabel.js b/public/js/floatlabel.js index edf1079..e30d2af 100644 --- a/public/js/floatlabel.js +++ b/public/js/floatlabel.js @@ -1,592 +1,545 @@ - - -import {psr_to_xyz} from "./util.js" -import * as THREE from './lib/three.module.js'; +import { psr_to_xyz } from "./util.js"; +import * as THREE from "./lib/three.module.js"; import { globalObjectCategory } from "./obj_cfg.js"; -class FastToolBox{ - constructor(ui, eventHandler) - { - let self = this; - this.ui = ui; - this.eventHandler = eventHandler; - - this.installEventHandler(); - - this.ui.querySelector("#attr-editor").onmouseenter=function(event){ - if (this.timerId) - { - clearTimeout(this.timerId); - this.timerId = null; - } - - event.target.querySelector("#attr-selector").style.display=""; - - }; - - - this.ui.querySelector("#attr-editor").onmouseleave=function(event){ - let ui = event.target.querySelector("#attr-selector"); - - this.timerId = setTimeout(()=>{ - ui.style.display="none"; - this.timerId = null; - }, - 200); - - }; - - this.ui.querySelector("#label-more").onmouseenter=function(event){ - if (this.timerId) - { - clearTimeout(this.timerId); - this.timerId = null; - } - let ui = event.target.querySelector("#object-dropdown-menu"); - ui.style.display="inherit"; - ui.style.top = "100%"; - ui.style.left = "0%"; - ui.style.right = null; - ui.style.bottom = null; - - let rect = ui.getClientRects()[0]; - if (window.innerHeight < rect.y+rect.height) - { - ui.style.top = null; - ui.style.bottom = "100%"; - } - - if (window.innerWidth < rect.x+rect.width) - { - ui.style.left = null; - ui.style.right = "0%"; - } - - }; - - this.ui.querySelector("#label-more").onmouseleave=function(event){ - let ui = event.target.querySelector("#object-dropdown-menu"); - this.timerId = setTimeout(()=>{ - ui.style.display="none"; - this.timerId = null; - }, - 200); - }; - - - let dropdownMenu = this.ui.querySelector("#object-dropdown-menu"); - for (let i = 0; i < dropdownMenu.children.length; i++) - { - dropdownMenu.children[i].onclick = (event) => - { - //event.preventDefault(); - event.stopPropagation(); - - this.eventHandler(event); - - this.ui.querySelector("#object-dropdown-menu").style.display="none"; - } - } +class FastToolBox { + constructor(ui, eventHandler) { + let self = this; + this.ui = ui; + this.eventHandler = eventHandler; + + this.installEventHandler(); + + this.ui.querySelector("#attr-editor").onmouseenter = function (event) { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = null; + } + + event.target.querySelector("#attr-selector").style.display = ""; + }; + + this.ui.querySelector("#attr-editor").onmouseleave = function (event) { + let ui = event.target.querySelector("#attr-selector"); + + this.timerId = setTimeout(() => { + ui.style.display = "none"; + this.timerId = null; + }, 200); + }; + + this.ui.querySelector("#label-more").onmouseenter = function (event) { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = null; + } + let ui = event.target.querySelector("#object-dropdown-menu"); + ui.style.display = "inherit"; + ui.style.top = "100%"; + ui.style.left = "0%"; + ui.style.right = null; + ui.style.bottom = null; + + let rect = ui.getClientRects()[0]; + if (window.innerHeight < rect.y + rect.height) { + ui.style.top = null; + ui.style.bottom = "100%"; + } + + if (window.innerWidth < rect.x + rect.width) { + ui.style.left = null; + ui.style.right = "0%"; + } + }; + + this.ui.querySelector("#label-more").onmouseleave = function (event) { + let ui = event.target.querySelector("#object-dropdown-menu"); + this.timerId = setTimeout(() => { + ui.style.display = "none"; + this.timerId = null; + }, 200); + }; + + let dropdownMenu = this.ui.querySelector("#object-dropdown-menu"); + for (let i = 0; i < dropdownMenu.children.length; i++) { + dropdownMenu.children[i].onclick = (event) => { + //event.preventDefault(); + event.stopPropagation(); + + this.eventHandler(event); + + this.ui.querySelector("#object-dropdown-menu").style.display = "none"; + }; } + } - hide() - { - this.ui.style.display = "none"; - } + hide() { + this.ui.style.display = "none"; + } - show() - { - this.ui.style.display = "inline-block"; - this.ui.querySelector("#attr-selector").style.display="none"; - } + show() { + this.ui.style.display = "inline-block"; + this.ui.querySelector("#attr-selector").style.display = "none"; + } + setValue(obj_type, obj_track_id, obj_attr) { + this.ui.querySelector("#object-category-selector").value = obj_type; - setValue(obj_type, obj_track_id, obj_attr){ - this.ui.querySelector("#object-category-selector").value = obj_type; + this.setAttrOptions(obj_type, obj_attr); - this.setAttrOptions(obj_type, obj_attr); + this.ui.querySelector("#object-track-id-editor").value = obj_track_id; - this.ui.querySelector("#object-track-id-editor").value = obj_track_id; + if (obj_attr) this.ui.querySelector("#attr-input").value = obj_attr; + else this.ui.querySelector("#attr-input").value = ""; + } - if (obj_attr) - this.ui.querySelector("#attr-input").value = obj_attr; - else - this.ui.querySelector("#attr-input").value = ""; - + setPos(pos) { + if (pos) { + this.ui.style.top = pos.top; + this.ui.style.left = pos.left; } + } - setPos(pos) - { - if (pos) - { - this.ui.style.top = pos.top; - this.ui.style.left = pos.left; - } - } + setAttrOptions(obj_type, obj_attr) { + let attrs = ["static"]; - setAttrOptions(obj_type, obj_attr) - { - - let attrs = ["static"]; - - - if (globalObjectCategory.obj_type_map[obj_type] && globalObjectCategory.obj_type_map[obj_type].attr) - attrs = attrs.concat(globalObjectCategory.obj_type_map[obj_type].attr); - - // merge attrs - let objAttrs = []; - - if (obj_attr){ - objAttrs = obj_attr.split(",").map(a=>a.trim()); - objAttrs.forEach(a=>{ - if (!attrs.find(x=>x==a)) - { - attrs.push(a); - } - }) - } + if ( + globalObjectCategory.obj_type_map[obj_type] && + globalObjectCategory.obj_type_map[obj_type].attr + ) + attrs = attrs.concat(globalObjectCategory.obj_type_map[obj_type].attr); + // merge attrs + let objAttrs = []; - let items = ``; - - - attrs.forEach(a=>{ - if (objAttrs.find(x=>x==a)){ - items+= `
${a}
` - } - else { - items+= `
${a}
` - } - }); - - - this.ui.querySelector("#attr-selector").innerHTML = items; - - this.ui.querySelector("#attr-selector").onclick = (event)=>{ - - let attrs = this.ui.querySelector("#attr-input").value; - - let objCurrentAttrs = []; - if (attrs) - objCurrentAttrs = attrs.split(",").map(a=>a.trim()); - - - let clickedAttr = event.target.innerText; - - if (objCurrentAttrs.find(x=>x==clickedAttr)) - { - objCurrentAttrs = objCurrentAttrs.filter(x => x!= clickedAttr); - event.target.className = 'attr-item'; - } - else - { - objCurrentAttrs.push(clickedAttr); - event.target.className = 'attr-item attr-selected'; - } - - attrs = ""; - if (objCurrentAttrs.length > 0) - { - attrs = objCurrentAttrs.reduce((a,b)=>a+ (a?",":"") + b); - } - - this.ui.querySelector("#attr-input").value = attrs; - - this.eventHandler({ - currentTarget:{ - id: "attr-input", - value: attrs - } - }); - + if (obj_attr) { + objAttrs = obj_attr.split(",").map((a) => a.trim()); + objAttrs.forEach((a) => { + if (!attrs.find((x) => x == a)) { + attrs.push(a); } - + }); } - installEventHandler(){ - - let btns = [ - "#label-del", - "#label-gen-id", - "#label-copy", - "#label-paste", - "#label-batchedit", - "#label-trajectory", - "#label-edit", - "#label-highlight", - "#label-rotate", - ]; - - btns.forEach(btn=>{ - this.ui.querySelector(btn).onclick = (event)=>{ - this.eventHandler(event); - }; - }); - - this.ui.querySelector("#object-category-selector").onchange = event=>{ - - //this.ui.querySelector("#attr-input").value=""; - this.setAttrOptions(event.currentTarget.value, this.ui.querySelector("#attr-input").value); - this.eventHandler(event); - }; - - - this.ui.querySelector("#object-track-id-editor").onchange = event=>this.eventHandler(event); - this.ui.querySelector("#object-track-id-editor").addEventListener("keydown", e=>e.stopPropagation()); - this.ui.querySelector("#object-track-id-editor").addEventListener("keyup", event=>{ - event.stopPropagation(); - this.eventHandler(event); - }); - - this.ui.querySelector("#attr-input").onchange = event=>this.eventHandler(event); - this.ui.querySelector("#attr-input").addEventListener("keydown", e=>e.stopPropagation()); - this.ui.querySelector("#attr-input").addEventListener("keyup", event=>{ - event.stopPropagation(); - this.eventHandler(event); - }); - } + let items = ``; + + attrs.forEach((a) => { + if (objAttrs.find((x) => x == a)) { + items += `
${a}
`; + } else { + items += `
${a}
`; + } + }); + + this.ui.querySelector("#attr-selector").innerHTML = items; + + this.ui.querySelector("#attr-selector").onclick = (event) => { + let attrs = this.ui.querySelector("#attr-input").value; + + let objCurrentAttrs = []; + if (attrs) objCurrentAttrs = attrs.split(",").map((a) => a.trim()); + + let clickedAttr = event.target.innerText; + + if (objCurrentAttrs.find((x) => x == clickedAttr)) { + objCurrentAttrs = objCurrentAttrs.filter((x) => x != clickedAttr); + event.target.className = "attr-item"; + } else { + objCurrentAttrs.push(clickedAttr); + event.target.className = "attr-item attr-selected"; + } + + attrs = ""; + if (objCurrentAttrs.length > 0) { + attrs = objCurrentAttrs.reduce((a, b) => a + (a ? "," : "") + b); + } + + this.ui.querySelector("#attr-input").value = attrs; + + this.eventHandler({ + currentTarget: { + id: "attr-input", + value: attrs, + }, + }); + }; + } + + installEventHandler() { + let btns = [ + "#label-del", + "#label-gen-id", + "#label-copy", + "#label-paste", + "#label-batchedit", + "#label-trajectory", + "#label-edit", + "#label-highlight", + "#label-rotate", + ]; + + btns.forEach((btn) => { + this.ui.querySelector(btn).onclick = (event) => { + this.eventHandler(event); + }; + }); + + this.ui.querySelector("#object-category-selector").onchange = (event) => { + //this.ui.querySelector("#attr-input").value=""; + this.setAttrOptions( + event.currentTarget.value, + this.ui.querySelector("#attr-input").value + ); + this.eventHandler(event); + }; + + this.ui.querySelector("#object-track-id-editor").onchange = (event) => + this.eventHandler(event); + this.ui + .querySelector("#object-track-id-editor") + .addEventListener("keydown", (e) => e.stopPropagation()); + this.ui + .querySelector("#object-track-id-editor") + .addEventListener("keyup", (event) => { + event.stopPropagation(); + this.eventHandler(event); + }); + + this.ui.querySelector("#attr-input").onchange = (event) => + this.eventHandler(event); + this.ui + .querySelector("#attr-input") + .addEventListener("keydown", (e) => e.stopPropagation()); + this.ui.querySelector("#attr-input").addEventListener("keyup", (event) => { + event.stopPropagation(); + this.eventHandler(event); + }); + } } - - class FloatLabelManager { - - id_enabled = true; - category_enabled = true; - color_scheme = "category"; - - constructor(editor_ui, container_div, view, func_on_label_clicked) - { - this.view = view; //access camera by view, since camera is dynamic - this.editor_ui = editor_ui; - this.container = container_div; - this.labelsUi = editor_ui.querySelector("#floating-labels"); - this.floatingUi = editor_ui.querySelector("#floating-things"); - - - - this.style = document.createElement('style'); - this.temp_style = document.createElement('style'); - this.on_label_clicked = func_on_label_clicked; - - document.head.appendChild(this.style); - document.head.appendChild(this.temp_style); - - this.id_enabled = !pointsGlobalConfig.hideId; - this.category_enabled = !pointsGlobalConfig.hideCategory; + id_enabled = true; + category_enabled = true; + color_scheme = "category"; + + constructor(editor_ui, container_div, view, func_on_label_clicked) { + this.view = view; //access camera by view, since camera is dynamic + this.editor_ui = editor_ui; + this.container = container_div; + this.labelsUi = editor_ui.querySelector("#floating-labels"); + this.floatingUi = editor_ui.querySelector("#floating-things"); + + this.style = document.createElement("style"); + this.temp_style = document.createElement("style"); + this.on_label_clicked = func_on_label_clicked; + + document.head.appendChild(this.style); + document.head.appendChild(this.temp_style); + + this.id_enabled = !pointsGlobalConfig.hideId; + this.category_enabled = !pointsGlobalConfig.hideCategory; + } + + hide() { + this.floatingUi.style.display = "none"; + } + + show() { + this.floatingUi.style.display = ""; + } + + show_id(show) { + this.id_enabled = show; + + if (!show) { + this.temp_style.sheet.insertRule(".label-obj-id-text {display: none}"); + } else { + for (let i = this.temp_style.sheet.cssRules.length - 1; i >= 0; i--) { + var r = this.temp_style.sheet.cssRules[i]; + if (r.selectorText === ".label-obj-id-text") { + this.temp_style.sheet.deleteRule(i); + } + } } - - hide(){ - this.floatingUi.style.display="none"; + } + + show_category(show) { + this.category_enabled = show; + + if (!show) { + this.temp_style.sheet.insertRule(".label-obj-type-text {display: none}"); + this.temp_style.sheet.insertRule(".label-obj-attr-text {display: none}"); + } else { + for (let i = this.temp_style.sheet.cssRules.length - 1; i >= 0; i--) { + var r = this.temp_style.sheet.cssRules[i]; + if ( + r.selectorText === ".label-obj-type-text" || + r.selectorText === ".label-obj-attr-text" + ) { + this.temp_style.sheet.deleteRule(i); + } + } } - - show(){ - this.floatingUi.style.display=""; + } + + // hide all temporarily when zoom in one object. + hide_all() { + // if (this.temp_style.sheet.cssRules.length == 0){ + // this.temp_style.sheet.insertRule(".label-obj-id-text {display: none}"); + // this.temp_style.sheet.insertRule(".label-obj-type-text {display: none}"); + // this.temp_style.sheet.insertRule(".label-obj-attr-text {display: none}"); + // } + this.labelsUi.style.display = "none"; + } + + restore_all() { + // this.show_category(this.category_enabled); + // this.show_id(this.id_enabled); + this.labelsUi.style.display = ""; + } + + remove_all_labels() { + var _self = this; + + if (this.labelsUi.children.length > 0) { + for (var c = this.labelsUi.children.length - 1; c >= 0; c--) { + this.labelsUi.children[c].remove(); + } } + } + update_all_position() { + if (this.labelsUi.children.length > 0) { + for (var c = 0; c < this.labelsUi.children.length; c++) { + var element = this.labelsUi.children[c]; - + var best_pos = this.compute_best_position(element.vertices); + var pos = this.coord_to_pixel(best_pos); - show_id(show){ - - this.id_enabled = show; + element.style.top = Math.round(pos.y) + "px"; + element.style.left = Math.round(pos.x) + "px"; - if (!show){ - this.temp_style.sheet.insertRule(".label-obj-id-text {display: none}"); + element.className = element.orgClassName; + if (pos.out_view) { + element.className += " label-out-view"; } - else{ - for (let i = this.temp_style.sheet.cssRules.length-1; i >= 0; i--){ - var r = this.temp_style.sheet.cssRules[i]; - if (r.selectorText === ".label-obj-id-text"){ - this.temp_style.sheet.deleteRule(i); - } - } - - } - + } } - - show_category(show){ - - this.category_enabled = show; - - if (!show){ - this.temp_style.sheet.insertRule(".label-obj-type-text {display: none}"); - this.temp_style.sheet.insertRule(".label-obj-attr-text {display: none}"); - } - else{ - for (let i = this.temp_style.sheet.cssRules.length-1; i >= 0; i--){ - var r = this.temp_style.sheet.cssRules[i]; - if (r.selectorText === ".label-obj-type-text" || r.selectorText === ".label-obj-attr-text"){ - this.temp_style.sheet.deleteRule(i); - } - } - } - + } + + getLabelEditorPos(local_id) { + let label = this.editor_ui.querySelector("#obj-local-" + local_id); + if (label) { + // if label is hidden, we can't use its pos directly. + let best_pos = this.compute_best_position(label.vertices); + let pos = this.coord_to_pixel(best_pos); + + return { + top: Math.round(pos.y) + label.offsetHeight + "px", + left: Math.round(pos.x) + 30 + "px", + }; } - - // hide all temporarily when zoom in one object. - hide_all(){ - // if (this.temp_style.sheet.cssRules.length == 0){ - // this.temp_style.sheet.insertRule(".label-obj-id-text {display: none}"); - // this.temp_style.sheet.insertRule(".label-obj-type-text {display: none}"); - // this.temp_style.sheet.insertRule(".label-obj-attr-text {display: none}"); - // } - this.labelsUi.style.display = "none"; + } + + set_object_type(local_id, obj_type) { + var label = this.editor_ui.querySelector("#obj-local-" + local_id); + if (label) { + label.obj_type = obj_type; + label.update_text(); + this.update_color(label); } - - restore_all(){ - // this.show_category(this.category_enabled); - // this.show_id(this.id_enabled); - this.labelsUi.style.display = ""; + } + + set_object_attr(local_id, obj_attr) { + var label = this.editor_ui.querySelector("#obj-local-" + local_id); + if (label) { + label.obj_attr = obj_attr; + label.update_text(); + this.update_color(label); } + } - remove_all_labels(){ - - var _self = this; + set_object_track_id(local_id, track_id) { + var label = this.editor_ui.querySelector("#obj-local-" + local_id); - if (this.labelsUi.children.length>0){ - for (var c=this.labelsUi.children.length-1; c >= 0; c--){ - this.labelsUi.children[c].remove(); - } - } + if (label) { + label.obj_track_id = track_id; + label.update_text(); + this.update_color(label); } - - - update_all_position(){ - if (this.labelsUi.children.length>0){ - for (var c=0; c < this.labelsUi.children.length; c++){ - var element = this.labelsUi.children[c]; - - var best_pos = this.compute_best_position(element.vertices); - var pos = this.coord_to_pixel(best_pos); - - element.style.top = Math.round(pos.y) + 'px'; - element.style.left = Math.round(pos.x) + 'px'; - - - element.className = element.orgClassName; - if (pos.out_view){ - element.className += " label-out-view"; - } - - } - } + } + + translate_vertices_to_global(world, vertices) { + let ret = []; + for (let i = 0; i < vertices.length; i += 4) { + let p = new THREE.Vector4() + .fromArray(vertices, i) + .applyMatrix4(world.webglGroup.matrix); + ret.push(p.x); + ret.push(p.y); + ret.push(p.z); + ret.push(p.w); } - getLabelEditorPos(local_id) - { - let label = this.editor_ui.querySelector("#obj-local-"+local_id); - if (label) - { - // if label is hidden, we can't use its pos directly. - let best_pos = this.compute_best_position(label.vertices); - let pos = this.coord_to_pixel(best_pos); - - - return { - top: Math.round(pos.y) + label.offsetHeight + "px", - left: Math.round(pos.x) + 30 + "px", - }; - } - } + return ret; + } - - set_object_type(local_id, obj_type){ - var label = this.editor_ui.querySelector("#obj-local-"+local_id); - if (label){ - label.obj_type = obj_type; - label.update_text(); - this.update_color(label); - } - } + update_position(box, refresh) { + var label = this.editor_ui.querySelector("#obj-local-" + box.obj_local_id); - set_object_attr(local_id, obj_attr){ - var label = this.editor_ui.querySelector("#obj-local-"+local_id); - if (label){ - label.obj_attr = obj_attr; - label.update_text(); - this.update_color(label); - } - } + if (label) { + label.vertices = this.translate_vertices_to_global( + box.world, + psr_to_xyz(box.position, box.scale, box.rotation) + ); - - set_object_track_id(local_id, track_id){ - var label = this.editor_ui.querySelector("#obj-local-"+local_id); + if (refresh) { + var best_pos = this.compute_best_position(label.vertices); + var pos = this.coord_to_pixel(best_pos); - if (label){ - label.obj_track_id = track_id; - label.update_text(); - this.update_color(label); - } - } + label.style.top = Math.round(pos.y) + "px"; + label.style.left = Math.round(pos.x) + "px"; - translate_vertices_to_global(world, vertices) { - let ret = []; - for (let i = 0; i< vertices.length; i+=4) - { - let p = new THREE.Vector4().fromArray(vertices, i).applyMatrix4(world.webglGroup.matrix); - ret.push(p.x); - ret.push(p.y); - ret.push(p.z); - ret.push(p.w); + label.className = label.orgClassName; + if (pos.out_view) { + label.className += " label-out-view"; } - - return ret; - + } } - - update_position(box, refresh){ - var label = this.editor_ui.querySelector("#obj-local-"+box.obj_local_id); - - if (label){ - - label.vertices = this.translate_vertices_to_global(box.world, psr_to_xyz(box.position, box.scale, box.rotation)); - - if (refresh){ - var best_pos = this.compute_best_position(label.vertices); - var pos = this.coord_to_pixel(best_pos); - - label.style.top = Math.round(pos.y) + 'px'; - label.style.left = Math.round(pos.x) + 'px'; - - label.className = label.orgClassName; - if (pos.out_view){ - label.className += " label-out-view"; - } - } - } + } + + remove_box(box) { + var label = this.editor_ui.querySelector("#obj-local-" + box.obj_local_id); + + if (label) label.remove(); + } + + set_color_scheme(color_scheme) { + this.color_scheme = color_scheme; + } + update_color(label) { + if (this.color_scheme == "id") { + label.className = "float-label color-" + (label.obj_track_id % 33); + } // by id + else { + label.className = "float-label " + label.obj_type; } - + label.orgClassName = label.className; + } - remove_box(box){ - var label = this.editor_ui.querySelector("#obj-local-"+box.obj_local_id); + add_label(box) { + var label = document.createElement("div"); - if (label) - label.remove(); - } + label.id = "obj-local-" + box.obj_local_id; - set_color_scheme(color_scheme){ - this.color_scheme = color_scheme; - } - update_color(label) - { - if (this.color_scheme == "id") - { - label.className = "float-label color-"+ (label.obj_track_id % 33); - } - else // by id - { - label.className = "float-label "+label.obj_type; - } - - label.orgClassName = label.className; - } + var _self = this; - add_label(box){ - - var label = document.createElement('div'); - - - - label.id = "obj-local-"+box.obj_local_id; - - var _self =this; - - label.update_text = function(){ - let label_text = '
'; - label_text += this.obj_type; - label_text += '
'; - - if (this.obj_attr) - { - label_text += '
'; - label_text += this.obj_attr; - label_text += '
'; - } - - label_text += '
'; - label_text += this.obj_track_id; - label_text += '
'; - - this.innerHTML = label_text; - } - - label.obj_type = box.obj_type; - label.obj_local_id = box.obj_local_id; - label.obj_track_id = box.obj_track_id; - label.obj_attr = box.obj_attr; - label.update_text(); - this.update_color(label); + label.update_text = function () { + let label_text = '
'; + label_text += this.obj_type; + label_text += "
"; - label.vertices = this.translate_vertices_to_global(box.world, psr_to_xyz(box.position, box.scale, box.rotation)); + if (this.obj_attr) { + label_text += '
'; + label_text += this.obj_attr; + label_text += "
"; + } - var best_pos = this.compute_best_position(label.vertices); - best_pos = this.coord_to_pixel(best_pos); - - var pos = best_pos; - - label.style.top = Math.round(pos.y) + 'px'; - label.style.left = Math.round(pos.x) + 'px'; - - if (pos.out_view){ - label.className += " label-out-view"; - } + label_text += '
'; + label_text += this.obj_track_id; + label_text += "
"; - this.labelsUi.appendChild(label); + this.innerHTML = label_text; + }; - let self = this; - label.onclick = ()=>{ - this.on_label_clicked(box); - }; - } + label.obj_type = box.obj_type; + label.obj_local_id = box.obj_local_id; + label.obj_track_id = box.obj_track_id; + label.obj_attr = box.obj_attr; + label.update_text(); + this.update_color(label); + label.vertices = this.translate_vertices_to_global( + box.world, + psr_to_xyz(box.position, box.scale, box.rotation) + ); - coord_to_pixel(p){ - var width = this.container.clientWidth, height = this.container.clientHeight; - var widthHalf = width / 2, heightHalf = height / 2; + var best_pos = this.compute_best_position(label.vertices); + best_pos = this.coord_to_pixel(best_pos); - var ret={ - x: ( p.x * widthHalf ) + widthHalf + 10, - y: - ( p.y * heightHalf ) + heightHalf - 10, - out_view: p.x>0.9 || p.x<-0.6 || p.y<-0.9 || p.y>0.9 || p.z< -1 || p.z > 1, - // p.x<-0.6 to prevent it from appearing ontop of sideviews. - } + var pos = best_pos; - return ret; - } + label.style.top = Math.round(pos.y) + "px"; + label.style.left = Math.round(pos.x) + "px"; - compute_best_position(vertices){ - var _self = this; - var camera_p = [0,1,2,3,4,5,6,7].map(function(i){ - return new THREE.Vector3(vertices[i*4+0], vertices[i*4+1], vertices[i*4+2]); - }); - - camera_p.forEach(function(x){ - x.project(_self.view.camera); - }); - - var visible_p = camera_p; - - var best_p = {x:-1, y: -1, z: -2}; - - visible_p.forEach(function(p){ - if (p.x > best_p.x){ - best_p.x = p.x; - } - - if (p.y > best_p.y){ - best_p.y = p.y; - } - - if (p.z > best_p.z){ - best_p.z = p.z; - } - }) - - return best_p; + if (pos.out_view) { + label.className += " label-out-view"; } -} + this.labelsUi.appendChild(label); + + let self = this; + label.onclick = () => { + this.on_label_clicked(box); + }; + } + + coord_to_pixel(p) { + var width = this.container.clientWidth, + height = this.container.clientHeight; + var widthHalf = width / 2, + heightHalf = height / 2; + + var ret = { + x: p.x * widthHalf + widthHalf + 10, + y: -(p.y * heightHalf) + heightHalf - 10, + out_view: + p.x > 0.9 || + p.x < -0.6 || + p.y < -0.9 || + p.y > 0.9 || + p.z < -1 || + p.z > 1, + // p.x<-0.6 to prevent it from appearing ontop of sideviews. + }; + + return ret; + } + + compute_best_position(vertices) { + var _self = this; + var camera_p = [0, 1, 2, 3, 4, 5, 6, 7].map(function (i) { + return new THREE.Vector3( + vertices[i * 4 + 0], + vertices[i * 4 + 1], + vertices[i * 4 + 2] + ); + }); + + camera_p.forEach(function (x) { + x.project(_self.view.camera); + }); + + var visible_p = camera_p; + + var best_p = { x: -1, y: -1, z: -2 }; + + visible_p.forEach(function (p) { + if (p.x > best_p.x) { + best_p.x = p.x; + } + + if (p.y > best_p.y) { + best_p.y = p.y; + } + + if (p.z > best_p.z) { + best_p.z = p.z; + } + }); + + return best_p; + } +} -export {FloatLabelManager, FastToolBox}; \ No newline at end of file +export { FloatLabelManager, FastToolBox }; diff --git a/public/js/header.js b/public/js/header.js index b97930f..35952d3 100644 --- a/public/js/header.js +++ b/public/js/header.js @@ -1,156 +1,192 @@ - import { CubeRefractionMapping } from "./lib/three.module.js"; -import {saveWorldList} from "./save.js" - -var Header=function(ui, data, cfg, onSceneChanged, onFrameChanged, onObjectSelected, onCameraChanged){ - - this.ui = ui; - this.data = data; - this.cfg = cfg; - this.boxUi = ui.querySelector("#box"); - this.refObjUi = ui.querySelector("#ref-obj"); - this.sceneSelectorUi = ui.querySelector("#scene-selector"); - this.frameSelectorUi = ui.querySelector("#frame-selector"); - this.objectSelectorUi = ui.querySelector("#object-selector"); - this.cameraSelectorUi = ui.querySelector("#camera-selector"); - this.changedMarkUi = ui.querySelector("#changed-mark"); - - this.onSceneChanged = onSceneChanged; - this.onFrameChanged = onFrameChanged; - this.onObjectSelected = onObjectSelected; - this.onCameraChanged = onCameraChanged; - - - if (cfg.disableSceneSelector){ - this.sceneSelectorUi.style.display="none"; - } - - if (cfg.disableFrameSelector){ - this.frameSelectorUi.style.display="none"; - } - - if (cfg.disableCameraSelector){ - this.cameraSelectorUi.style.display="none"; - } - - // update scene selector ui - - - - - this.updateSceneList = function(sceneDescList){ - let scene_selector_str = ""; - for (let scene in sceneDescList) - { - if (data.sceneDescList[scene]) - scene_selector_str += ""; - else - scene_selector_str += ""; - } - - this.ui.querySelector("#scene-selector").innerHTML = scene_selector_str; - } - - this.updateSceneList(this.data.sceneDescList); - - this.ui.querySelector("#btn-reload-scene-list").onclick = (event)=>{ - let curentValue = this.sceneSelectorUi.value; - - this.data.readSceneList().then((sceneDescList=>{ - this.updateSceneList(sceneDescList); - this.sceneSelectorUi.value = curentValue; - })) - } - - - - this.sceneSelectorUi.onchange = (e)=>{this.onSceneChanged(e);}; - this.objectSelectorUi.onchange = (e)=>{this.onObjectSelected(e);}; - this.frameSelectorUi.onchange = (e)=>{this.onFrameChanged(e);}; - this.cameraSelectorUi.onchange = (e)=>{this.onCameraChanged(e);}; - - this.setObject = function(id) - { - this.objectSelectorUi.value = id; +import { saveWorldList } from "./save.js"; + +var Header = function ( + ui, + data, + cfg, + onSceneChanged, + onFrameChanged, + onObjectSelected, + onCameraChanged +) { + this.ui = ui; + this.data = data; + this.cfg = cfg; + this.boxUi = ui.querySelector("#box"); + this.refObjUi = ui.querySelector("#ref-obj"); + this.sceneSelectorUi = ui.querySelector("#scene-selector"); + this.frameSelectorUi = ui.querySelector("#frame-selector"); + this.objectSelectorUi = ui.querySelector("#object-selector"); + this.cameraSelectorUi = ui.querySelector("#camera-selector"); + this.changedMarkUi = ui.querySelector("#changed-mark"); + + this.onSceneChanged = onSceneChanged; + this.onFrameChanged = onFrameChanged; + this.onObjectSelected = onObjectSelected; + this.onCameraChanged = onCameraChanged; + + if (cfg.disableSceneSelector) { + this.sceneSelectorUi.style.display = "none"; + } + + if (cfg.disableFrameSelector) { + this.frameSelectorUi.style.display = "none"; + } + + if (cfg.disableCameraSelector) { + this.cameraSelectorUi.style.display = "none"; + } + + // update scene selector ui + + this.updateSceneList = function (sceneDescList) { + let scene_selector_str = ""; + for (let scene in sceneDescList) { + if (data.sceneDescList[scene]) + scene_selector_str += + ""; + else + scene_selector_str += + ""; } - this.clear_box_info = function(){ - this.boxUi.innerHTML = ''; - }; - - this.update_box_info = function(box){ - var scale = box.scale; - var pos = box.position; - var rotation = box.rotation; - var points_number = box.world.lidar.get_box_points_number(box); - let distance = Math.sqrt(pos.x*pos.x + pos.y*pos.y).toFixed(2); - - this.boxUi.innerHTML = "" + box.obj_type +"-"+box.obj_track_id + - (box.annotator? (" | " + box.annotator) : "") + - " | " + distance + - " | "+pos.x.toFixed(2) +" "+pos.y.toFixed(2) + " " + pos.z.toFixed(2) + - " | " +scale.x.toFixed(2) +" "+scale.y.toFixed(2) + " " + scale.z.toFixed(2) + - " | " + - (rotation.x*180/Math.PI).toFixed(2)+" "+(rotation.y*180/Math.PI).toFixed(2)+" "+(rotation.z*180/Math.PI).toFixed(2)+ - " | " + - points_number + " "; - if (box.follows){ - this.boxUi.innerHTML += "| F:"+box.follows.obj_track_id; - } - }, - - this.set_ref_obj = function(marked_object){ - this.refObjUi.innerHTML="| Ref: "+marked_object.scene+"/"+marked_object.frame+": "+marked_object.ann.obj_type+"-"+marked_object.ann.obj_id; - }, - - this.set_frame_info =function(scene, frame, on_scene_changed){ - - if (this.sceneSelectorUi.value != scene){ - this.sceneSelectorUi.value = scene; - on_scene_changed(scene); - } - - this.frameSelectorUi.value = frame; - }, - - this.clear_frame_info = function(scene, frame){ - - }, - - this.updateModifiedStatus = function(){ - let frames = this.data.worldList.filter(w=>w.annotation.modified); - if (frames.length > 0) - { - this.ui.querySelector("#changed-mark").className = 'ui-button alarm-mark'; - } - else - { - this.ui.querySelector("#changed-mark").className = 'ui-button'; - } + this.ui.querySelector("#scene-selector").innerHTML = scene_selector_str; + }; + + this.updateSceneList(this.data.sceneDescList); + + this.ui.querySelector("#btn-reload-scene-list").onclick = (event) => { + let curentValue = this.sceneSelectorUi.value; + + this.data.readSceneList().then((sceneDescList) => { + this.updateSceneList(sceneDescList); + this.sceneSelectorUi.value = curentValue; + }); + }; + + this.sceneSelectorUi.onchange = (e) => { + this.onSceneChanged(e); + }; + this.objectSelectorUi.onchange = (e) => { + this.onObjectSelected(e); + }; + this.frameSelectorUi.onchange = (e) => { + this.onFrameChanged(e); + }; + this.cameraSelectorUi.onchange = (e) => { + this.onCameraChanged(e); + }; + + this.setObject = function (id) { + this.objectSelectorUi.value = id; + }; + + this.clear_box_info = function () { + this.boxUi.innerHTML = ""; + }; + + (this.update_box_info = function (box) { + var scale = box.scale; + var pos = box.position; + var rotation = box.rotation; + var points_number = box.world.lidar.get_box_points_number(box); + let distance = Math.sqrt(pos.x * pos.x + pos.y * pos.y).toFixed(2); + + this.boxUi.innerHTML = + "" + + box.obj_type + + "-" + + box.obj_track_id + + (box.annotator + ? " | " + box.annotator + : "") + + " | " + + distance + + " | " + + pos.x.toFixed(2) + + " " + + pos.y.toFixed(2) + + " " + + pos.z.toFixed(2) + + " | " + + scale.x.toFixed(2) + + " " + + scale.y.toFixed(2) + + " " + + scale.z.toFixed(2) + + " | " + + ((rotation.x * 180) / Math.PI).toFixed(2) + + " " + + ((rotation.y * 180) / Math.PI).toFixed(2) + + " " + + ((rotation.z * 180) / Math.PI).toFixed(2) + + " | " + + points_number + + " "; + if (box.follows) { + this.boxUi.innerHTML += "| F:" + box.follows.obj_track_id; } - - this.ui.querySelector("#changed-mark").onmouseenter = ()=>{ - - let items = ""; - let frames = this.data.worldList.filter(w=>w.annotation.modified).map(w=>w.frameInfo); - frames.forEach(f=>{ - items += "
" + f.frame + '
'; - }); - - if (frames.length > 0){ - this.ui.querySelector("#changed-world-list").innerHTML = items; - this.ui.querySelector("#changed-world-list-wrapper").style.display = 'inherit'; - } + }), + (this.set_ref_obj = function (marked_object) { + this.refObjUi.innerHTML = + "| Ref: " + + marked_object.scene + + "/" + + marked_object.frame + + ": " + + marked_object.ann.obj_type + + "-" + + marked_object.ann.obj_id; + }), + (this.set_frame_info = function (scene, frame, on_scene_changed) { + if (this.sceneSelectorUi.value != scene) { + this.sceneSelectorUi.value = scene; + on_scene_changed(scene); + } + + this.frameSelectorUi.value = frame; + }), + (this.clear_frame_info = function (scene, frame) {}), + (this.updateModifiedStatus = function () { + let frames = this.data.worldList.filter((w) => w.annotation.modified); + if (frames.length > 0) { + this.ui.querySelector("#changed-mark").className = + "ui-button alarm-mark"; + } else { + this.ui.querySelector("#changed-mark").className = "ui-button"; + } + }); + + this.ui.querySelector("#changed-mark").onmouseenter = () => { + let items = ""; + let frames = this.data.worldList + .filter((w) => w.annotation.modified) + .map((w) => w.frameInfo); + frames.forEach((f) => { + items += "
" + f.frame + "
"; + }); + + if (frames.length > 0) { + this.ui.querySelector("#changed-world-list").innerHTML = items; + this.ui.querySelector("#changed-world-list-wrapper").style.display = + "inherit"; } + }; - this.ui.querySelector("#changed-mark").onmouseleave = ()=>{ - this.ui.querySelector("#changed-world-list-wrapper").style.display = 'none'; - } + this.ui.querySelector("#changed-mark").onmouseleave = () => { + this.ui.querySelector("#changed-world-list-wrapper").style.display = "none"; + }; - this.ui.querySelector("#save-button").onclick = ()=>{ - saveWorldList(this.data.worldList); - } + this.ui.querySelector("#save-button").onclick = () => { + saveWorldList(this.data.worldList); + }; }; - -export {Header} \ No newline at end of file +export { Header }; diff --git a/public/js/image.js b/public/js/image.js index 816b8aa..e1be52b 100644 --- a/public/js/image.js +++ b/public/js/image.js @@ -1,699 +1,718 @@ - -import {vector4to3, vector3_nomalize, psr_to_xyz, matmul} from "./util.js" -import {globalObjectCategory, } from './obj_cfg.js'; +import { vector4to3, vector3_nomalize, psr_to_xyz, matmul } from "./util.js"; +import { globalObjectCategory } from "./obj_cfg.js"; import { MovableView } from "./popup_dialog.js"; -function BoxImageContext(ui){ - - this.ui = ui; - - // draw highlighted box - this.updateFocusedImageContext = function(box){ - var scene_meta = box.world.frameInfo.sceneMeta; - - - let bestImage = choose_best_camera_for_point( - scene_meta, - box.position); +function BoxImageContext(ui) { + this.ui = ui; - if (!bestImage){ - return; - } - - if (!scene_meta.calib.camera){ - return; - } - - var calib = scene_meta.calib.camera[bestImage] - if (!calib){ - return; - } - - if (calib){ - var img = box.world.cameras.getImageByName(bestImage); - if (img && (img.naturalWidth > 0)){ + // draw highlighted box + this.updateFocusedImageContext = function (box) { + var scene_meta = box.world.frameInfo.sceneMeta; - this.clear_canvas(); + let bestImage = choose_best_camera_for_point(scene_meta, box.position); - var imgfinal = box_to_2d_points(box, calib) - - if (imgfinal != null){ // if projection is out of range of the image, stop drawing. - var ctx = this.ui.getContext("2d"); - ctx.lineWidth = 0.5; - - // note: 320*240 should be adjustable - var crop_area = crop_image(img.naturalWidth, img.naturalHeight, ctx.canvas.width, ctx.canvas.height, imgfinal); - - ctx.drawImage(img, crop_area[0], crop_area[1],crop_area[2], crop_area[3], 0, 0, ctx.canvas.width, ctx.canvas.height);// ctx.canvas.clientHeight); - //ctx.drawImage(img, 0,0,img.naturalWidth, img.naturalHeight, 0, 0, 320, 180);// ctx.canvas.clientHeight); - var imgfinal = vectorsub(imgfinal, [crop_area[0],crop_area[1]]); - var trans_ratio = { - x: ctx.canvas.height/crop_area[3], - y: ctx.canvas.height/crop_area[3], - } - - draw_box_on_image(ctx, box, imgfinal, trans_ratio, true); - } - } - } + if (!bestImage) { + return; } - this.clear_canvas = function(){ - var c = this.ui; - var ctx = c.getContext("2d"); - ctx.clearRect(0, 0, c.width, c.height); + if (!scene_meta.calib.camera) { + return; } - - function vectorsub(vs, v){ - var ret = []; - var vl = v.length; - - for (var i = 0; imaxx) maxx=x; - else if (xmaxy) maxy=y; - else if (y 0) { + this.clear_canvas(); + + var imgfinal = box_to_2d_points(box, calib); + + if (imgfinal != null) { + // if projection is out of range of the image, stop drawing. + var ctx = this.ui.getContext("2d"); + ctx.lineWidth = 0.5; + + // note: 320*240 should be adjustable + var crop_area = crop_image( + img.naturalWidth, + img.naturalHeight, + ctx.canvas.width, + ctx.canvas.height, + imgfinal + ); + + ctx.drawImage( + img, + crop_area[0], + crop_area[1], + crop_area[2], + crop_area[3], + 0, + 0, + ctx.canvas.width, + ctx.canvas.height + ); // ctx.canvas.clientHeight); + //ctx.drawImage(img, 0,0,img.naturalWidth, img.naturalHeight, 0, 0, 320, 180);// ctx.canvas.clientHeight); + var imgfinal = vectorsub(imgfinal, [crop_area[0], crop_area[1]]); + var trans_ratio = { + x: ctx.canvas.height / crop_area[3], + y: ctx.canvas.height / crop_area[3], + }; + + draw_box_on_image(ctx, box, imgfinal, trans_ratio, true); } - - var targetWidth= (maxx-minx)*1.5; - var targetHeight= (maxy-miny)*1.5; - - if (targetWidth/targetHeight > clientWidth/clientHeight){ - //increate height - targetHeight = targetWidth*clientHeight/clientWidth; - } - else{ - targetWidth = targetHeight*clientWidth/clientHeight; - } - - var centerx = (maxx+minx)/2; - var centery = (maxy+miny)/2; - - return [ - centerx - targetWidth/2, - centery - targetHeight/2, - targetWidth, - targetHeight - ]; + } } + }; - function draw_box_on_image(ctx, box, box_corners, trans_ratio, selected){ - var imgfinal = box_corners; - - if (!selected){ - let target_color = null; - if (box.world.data.cfg.color_obj == "category") - { - target_color = globalObjectCategory.get_color_by_category(box.obj_type); - } - else // by id - { - let idx = (box.obj_track_id)?parseInt(box.obj_track_id): box.obj_local_id; - target_color = globalObjectCategory.get_color_by_id(idx); - } - - + this.clear_canvas = function () { + var c = this.ui; + var ctx = c.getContext("2d"); + ctx.clearRect(0, 0, c.width, c.height); + }; - //ctx.strokeStyle = get_obj_cfg_by_type(box.obj_type).color; + function vectorsub(vs, v) { + var ret = []; + var vl = v.length; - //var c = get_obj_cfg_by_type(box.obj_type).color; - var r ="0x"+(target_color.x*256).toString(16); - var g ="0x"+(target_color.y*256).toString(16);; - var b ="0x"+(target_color.z*256).toString(16);; - - ctx.fillStyle="rgba("+parseInt(r)+","+parseInt(g)+","+parseInt(b)+",0.2)"; - } - else{ - ctx.strokeStyle="#ff00ff"; - ctx.fillStyle="rgba(255,0,255,0.2)"; - } + for (var i = 0; i < vs.length / vl; i++) { + for (var j = 0; j < vl; j++) ret[i * vl + j] = vs[i * vl + j] - v[j]; + } - // front panel - ctx.beginPath(); - ctx.moveTo(imgfinal[3*2]*trans_ratio.x,imgfinal[3*2+1]*trans_ratio.y); + return ret; + } - for (var i=0; i < imgfinal.length/2/2; i++) - { - ctx.lineTo(imgfinal[i*2+0]*trans_ratio.x, imgfinal[i*2+1]*trans_ratio.y); - } + function crop_image(imgWidth, imgHeight, clientWidth, clientHeight, corners) { + var maxx = 0, + maxy = 0, + minx = imgWidth, + miny = imgHeight; - ctx.closePath(); - ctx.fill(); - - // frame - ctx.beginPath(); + for (var i = 0; i < corners.length / 2; i++) { + var x = corners[i * 2]; + var y = corners[i * 2 + 1]; - ctx.moveTo(imgfinal[3*2]*trans_ratio.x,imgfinal[3*2+1]*trans_ratio.y); + if (x > maxx) maxx = x; + else if (x < minx) minx = x; - for (var i=0; i < imgfinal.length/2/2; i++) - { - ctx.lineTo(imgfinal[i*2+0]*trans_ratio.x, imgfinal[i*2+1]*trans_ratio.y); - } - //ctx.stroke(); + if (y > maxy) maxy = y; + else if (y < miny) miny = y; + } + var targetWidth = (maxx - minx) * 1.5; + var targetHeight = (maxy - miny) * 1.5; - //ctx.strokeStyle="#ff00ff"; - //ctx.beginPath(); + if (targetWidth / targetHeight > clientWidth / clientHeight) { + //increate height + targetHeight = (targetWidth * clientHeight) / clientWidth; + } else { + targetWidth = (targetHeight * clientWidth) / clientHeight; + } - ctx.moveTo(imgfinal[7*2]*trans_ratio.x,imgfinal[7*2+1]*trans_ratio.y); + var centerx = (maxx + minx) / 2; + var centery = (maxy + miny) / 2; + + return [ + centerx - targetWidth / 2, + centery - targetHeight / 2, + targetWidth, + targetHeight, + ]; + } + + function draw_box_on_image(ctx, box, box_corners, trans_ratio, selected) { + var imgfinal = box_corners; + + if (!selected) { + let target_color = null; + if (box.world.data.cfg.color_obj == "category") { + target_color = globalObjectCategory.get_color_by_category(box.obj_type); + } // by id + else { + let idx = box.obj_track_id + ? parseInt(box.obj_track_id) + : box.obj_local_id; + target_color = globalObjectCategory.get_color_by_id(idx); + } + + //ctx.strokeStyle = get_obj_cfg_by_type(box.obj_type).color; + + //var c = get_obj_cfg_by_type(box.obj_type).color; + var r = "0x" + (target_color.x * 256).toString(16); + var g = "0x" + (target_color.y * 256).toString(16); + var b = "0x" + (target_color.z * 256).toString(16); + + ctx.fillStyle = + "rgba(" + parseInt(r) + "," + parseInt(g) + "," + parseInt(b) + ",0.2)"; + } else { + ctx.strokeStyle = "#ff00ff"; + ctx.fillStyle = "rgba(255,0,255,0.2)"; + } - for (var i=4; i < imgfinal.length/2; i++) - { - ctx.lineTo(imgfinal[i*2+0]*trans_ratio.x, imgfinal[i*2+1]*trans_ratio.y); - } - - ctx.moveTo(imgfinal[0*2]*trans_ratio.x,imgfinal[0*2+1]*trans_ratio.y); - ctx.lineTo(imgfinal[4*2+0]*trans_ratio.x, imgfinal[4*2+1]*trans_ratio.y); - ctx.moveTo(imgfinal[1*2]*trans_ratio.x,imgfinal[1*2+1]*trans_ratio.y); - ctx.lineTo(imgfinal[5*2+0]*trans_ratio.x, imgfinal[5*2+1]*trans_ratio.y); - ctx.moveTo(imgfinal[2*2]*trans_ratio.x,imgfinal[2*2+1]*trans_ratio.y); - ctx.lineTo(imgfinal[6*2+0]*trans_ratio.x, imgfinal[6*2+1]*trans_ratio.y); - ctx.moveTo(imgfinal[3*2]*trans_ratio.x,imgfinal[3*2+1]*trans_ratio.y); - ctx.lineTo(imgfinal[7*2+0]*trans_ratio.x, imgfinal[7*2+1]*trans_ratio.y); - - - ctx.stroke(); + // front panel + ctx.beginPath(); + ctx.moveTo( + imgfinal[3 * 2] * trans_ratio.x, + imgfinal[3 * 2 + 1] * trans_ratio.y + ); + + for (var i = 0; i < imgfinal.length / 2 / 2; i++) { + ctx.lineTo( + imgfinal[i * 2 + 0] * trans_ratio.x, + imgfinal[i * 2 + 1] * trans_ratio.y + ); } -} + ctx.closePath(); + ctx.fill(); + // frame + ctx.beginPath(); -class ImageContext extends MovableView{ + ctx.moveTo( + imgfinal[3 * 2] * trans_ratio.x, + imgfinal[3 * 2 + 1] * trans_ratio.y + ); - constructor(parentUi, name, autoSwitch, cfg, on_img_click){ + for (var i = 0; i < imgfinal.length / 2 / 2; i++) { + ctx.lineTo( + imgfinal[i * 2 + 0] * trans_ratio.x, + imgfinal[i * 2 + 1] * trans_ratio.y + ); + } + //ctx.stroke(); - // create ui - let template = document.getElementById("image-wrapper-template"); - let tool = template.content.cloneNode(true); - // this.boxEditorHeaderUi.appendChild(tool); - // return this.boxEditorHeaderUi.lastElementChild; + //ctx.strokeStyle="#ff00ff"; + //ctx.beginPath(); - parentUi.appendChild(tool); - let ui = parentUi.lastElementChild; - let handle = ui.querySelector("#move-handle"); - super(handle, ui); + ctx.moveTo( + imgfinal[7 * 2] * trans_ratio.x, + imgfinal[7 * 2 + 1] * trans_ratio.y + ); - this.ui = ui; - this.cfg = cfg; - this.on_img_click = on_img_click; - this.autoSwitch = autoSwitch; - this.setImageName(name); + for (var i = 4; i < imgfinal.length / 2; i++) { + ctx.lineTo( + imgfinal[i * 2 + 0] * trans_ratio.x, + imgfinal[i * 2 + 1] * trans_ratio.y + ); } - - remove(){ - this.ui.remove(); - } - - setImageName(name) - { - this.name = name; - this.ui.querySelector("#header").innerText = (this.autoSwitch?"auto-":"")+name; - } + ctx.moveTo( + imgfinal[0 * 2] * trans_ratio.x, + imgfinal[0 * 2 + 1] * trans_ratio.y + ); + ctx.lineTo( + imgfinal[4 * 2 + 0] * trans_ratio.x, + imgfinal[4 * 2 + 1] * trans_ratio.y + ); + ctx.moveTo( + imgfinal[1 * 2] * trans_ratio.x, + imgfinal[1 * 2 + 1] * trans_ratio.y + ); + ctx.lineTo( + imgfinal[5 * 2 + 0] * trans_ratio.x, + imgfinal[5 * 2 + 1] * trans_ratio.y + ); + ctx.moveTo( + imgfinal[2 * 2] * trans_ratio.x, + imgfinal[2 * 2 + 1] * trans_ratio.y + ); + ctx.lineTo( + imgfinal[6 * 2 + 0] * trans_ratio.x, + imgfinal[6 * 2 + 1] * trans_ratio.y + ); + ctx.moveTo( + imgfinal[3 * 2] * trans_ratio.x, + imgfinal[3 * 2 + 1] * trans_ratio.y + ); + ctx.lineTo( + imgfinal[7 * 2 + 0] * trans_ratio.x, + imgfinal[7 * 2 + 1] * trans_ratio.y + ); + + ctx.stroke(); + } +} - - get_selected_box = null; +class ImageContext extends MovableView { + constructor(parentUi, name, autoSwitch, cfg, on_img_click) { + // create ui + let template = document.getElementById("image-wrapper-template"); + let tool = template.content.cloneNode(true); + // this.boxEditorHeaderUi.appendChild(tool); + // return this.boxEditorHeaderUi.lastElementChild; + parentUi.appendChild(tool); + let ui = parentUi.lastElementChild; + let handle = ui.querySelector("#move-handle"); + super(handle, ui); - init_image_op(func_get_selected_box){ - this.ui.onclick = (e)=>this.on_click(e); - this.get_selected_box = func_get_selected_box; - // var h = parentUi.querySelector("#resize-handle"); - // h.onmousedown = resize_mouse_down; - - // c.onresize = on_resize; + this.ui = ui; + this.cfg = cfg; + this.on_img_click = on_img_click; + this.autoSwitch = autoSwitch; + this.setImageName(name); + } + + remove() { + this.ui.remove(); + } + + setImageName(name) { + this.name = name; + this.ui.querySelector("#header").innerText = + (this.autoSwitch ? "auto-" : "") + name; + } + + get_selected_box = null; + + init_image_op(func_get_selected_box) { + this.ui.onclick = (e) => this.on_click(e); + this.get_selected_box = func_get_selected_box; + // var h = parentUi.querySelector("#resize-handle"); + // h.onmousedown = resize_mouse_down; + + // c.onresize = on_resize; + } + clear_main_canvas() { + var boxes = this.ui.querySelector("#svg-boxes").children; + + if (boxes.length > 0) { + for (var c = boxes.length - 1; c >= 0; c--) { + boxes[c].remove(); + } } - clear_main_canvas(){ - var boxes = this.ui.querySelector("#svg-boxes").children; - - if (boxes.length>0){ - for (var c=boxes.length-1; c >= 0; c--){ - boxes[c].remove(); - } - } + var points = this.ui.querySelector("#svg-points").children; - var points = this.ui.querySelector("#svg-points").children; - - if (points.length>0){ - for (var c=points.length-1; c >= 0; c--){ - points[c].remove(); - } - } + if (points.length > 0) { + for (var c = points.length - 1; c >= 0; c--) { + points[c].remove(); + } } + } + world = null; + img = null; + attachWorld(world) { + this.world = world; + } + hide() { + this.ui.style.display = "none"; + } + hidden() { + this.ui.style.display == "none"; + } + show() { + this.ui.style.display = ""; + } - world = null; - img = null; + drawing = false; + points = []; + polyline; - attachWorld(world){ - this.world = world; - }; + all_lines = []; - hide (){ - this.ui.style.display="none"; - }; + img_lidar_point_map = {}; - hidden(){ - this.ui.style.display=="none"; - }; - - show(){ - this.ui.style.display=""; - }; - - - - drawing = false; - points = []; - polyline; - - all_lines=[]; - - img_lidar_point_map = {}; - - point_color_by_distance(x,y) - { - // x,y are image coordinates - let p = this.img_lidar_point_map[y*this.img.width+x]; - - let distance = Math.sqrt(p[1]*p[1] + p[2]*p[2] + p[3]*p[3] ); - - if (distance > 60.0) - distance = 60.0; - else if (distance < 10.0) - distance = 10.0; - - return [(distance-10)/50.0, 1- (distance-10)/50.0, 0].map(c=>{ - let hex = Math.floor(c*255).toString(16); - if (hex.length == 1) - hex = "0"+hex; - return hex; - }).reduce((a,b)=>a+b,"#"); + point_color_by_distance(x, y) { + // x,y are image coordinates + let p = this.img_lidar_point_map[y * this.img.width + x]; - } + let distance = Math.sqrt(p[1] * p[1] + p[2] * p[2] + p[3] * p[3]); - to_polyline_attr(points){ - return points.reduce(function(x,y){ - return String(x)+","+y; - } - ) - } + if (distance > 60.0) distance = 60.0; + else if (distance < 10.0) distance = 10.0; - - to_viewbox_coord(x,y){ - var div = this.ui.querySelector("#maincanvas-svg"); - - x = Math.round(x*2048/div.clientWidth); - y = Math.round(y*1536/div.clientHeight); - return [x,y]; + return [(distance - 10) / 50.0, 1 - (distance - 10) / 50.0, 0] + .map((c) => { + let hex = Math.floor(c * 255).toString(16); + if (hex.length == 1) hex = "0" + hex; + return hex; + }) + .reduce((a, b) => a + b, "#"); + } - } + to_polyline_attr(points) { + return points.reduce(function (x, y) { + return String(x) + "," + y; + }); + } + + to_viewbox_coord(x, y) { + var div = this.ui.querySelector("#maincanvas-svg"); + + x = Math.round((x * 2048) / div.clientWidth); + y = Math.round((y * 1536) / div.clientHeight); + return [x, y]; + } + + on_click(e) { + var p = this.to_viewbox_coord(e.layerX, e.layerY); + var x = p[0]; + var y = p[1]; + + console.log("clicked", x, y); + + if (!this.drawing) { + if (e.ctrlKey) { + this.drawing = true; + var svg = this.ui.querySelector("#maincanvas-svg"); + //svg.style.position = "absolute"; + + this.polyline = document.createElementNS( + "http://www.w3.org/2000/svg", + "polyline" + ); + svg.appendChild(this.polyline); + this.points.push(x); + this.points.push(y); + + this.polyline.setAttribute("class", "maincanvas-line"); + this.polyline.setAttribute( + "points", + this.to_polyline_attr(this.points) + ); - on_click(e){ - var p= this.to_viewbox_coord(e.layerX, e.layerY); - var x=p[0]; - var y=p[1]; - - console.log("clicked",x,y); - - - if (!this.drawing){ - - if (e.ctrlKey){ - this.drawing = true; - var svg = this.ui.querySelector("#maincanvas-svg"); - //svg.style.position = "absolute"; - - this.polyline = document.createElementNS("http://www.w3.org/2000/svg", 'polyline'); - svg.appendChild(this.polyline); - this.points.push(x); - this.points.push(y); - - - this.polyline.setAttribute("class", "maincanvas-line") - this.polyline.setAttribute("points", this.to_polyline_attr(this.points)); - - var c = this.ui; - c.onmousemove = on_move; - c.ondblclick = on_dblclick; - c.onkeydown = on_key; - - } - else{ - // not drawing - //this is a test - if (false){ - let nearest_x = 100000; - let nearest_y = 100000; - let selected_pts = []; - - for (let i =x-100; i= this.img.width) - continue; - - for (let j = y-100; j= this.img.height) - continue; - - let lidarpoint = this.img_lidar_point_map[j*this.img.width+i]; - if (lidarpoint){ - //console.log(i,j, lidarpoint); - selected_pts.push(lidarpoint); //index of lidar point - - if (((i-x) * (i-x) + (j-y)*(j-y)) < ((nearest_x-x)*(nearest_x-x) + (nearest_y-y)*(nearest_y-y))){ - nearest_x = i; - nearest_y = j; - } - } - - } - } - console.log("nearest", nearest_x, nearest_y); - this.draw_point(nearest_x, nearest_y); - if (nearest_x < 100000) - { - this.on_img_click([this.img_lidar_point_map[nearest_y*this.img.width+nearest_x][0]]); - } + var c = this.ui; + c.onmousemove = on_move; + c.ondblclick = on_dblclick; + c.onkeydown = on_key; + } else { + // not drawing + //this is a test + if (false) { + let nearest_x = 100000; + let nearest_y = 100000; + let selected_pts = []; + + for (let i = x - 100; i < x + 100; i++) { + if (i < 0 || i >= this.img.width) continue; + + for (let j = y - 100; j < y + 100; j++) { + if (j < 0 || j >= this.img.height) continue; + + let lidarpoint = this.img_lidar_point_map[j * this.img.width + i]; + if (lidarpoint) { + //console.log(i,j, lidarpoint); + selected_pts.push(lidarpoint); //index of lidar point + + if ( + (i - x) * (i - x) + (j - y) * (j - y) < + (nearest_x - x) * (nearest_x - x) + + (nearest_y - y) * (nearest_y - y) + ) { + nearest_x = i; + nearest_y = j; } - + } } - - } else { - if (this.points[this.points.length-2]!=x || this.points[this.points.length-1]!=y){ - this.points.push(x); - this.points.push(y); - this.polyline.setAttribute("points", this.to_polyline_attr(this.points)); - } - + } + console.log("nearest", nearest_x, nearest_y); + this.draw_point(nearest_x, nearest_y); + if (nearest_x < 100000) { + this.on_img_click([ + this.img_lidar_point_map[ + nearest_y * this.img.width + nearest_x + ][0], + ]); + } } + } + } else { + if ( + this.points[this.points.length - 2] != x || + this.points[this.points.length - 1] != y + ) { + this.points.push(x); + this.points.push(y); + this.polyline.setAttribute( + "points", + this.to_polyline_attr(this.points) + ); + } + } + function on_move(e) { + var p = to_viewbox_coord(e.layerX, e.layerY); + var x = p[0]; + var y = p[1]; - function on_move(e){ - var p= to_viewbox_coord(e.layerX, e.layerY); - var x=p[0]; - var y=p[1]; - - console.log(x,y); - this.polyline.setAttribute("points", this.to_polyline_attr(this.points) + ',' + x + ',' + y); - } - - function on_dblclick(e){ - - this.points.push(this.points[0]); - this.points.push(this.points[1]); - - this.polyline.setAttribute("points", this.to_polyline_attr(this.points)); - console.log(this.points) - - all_lines.push(this.points); - - this.drawing = false; - this.points = []; - - var c = this.ui; - c.onmousemove = null; - c.ondblclick = null; - c.onkeypress = null; - c.blur(); - } - - function cancel(){ - - polyline.remove(); - - this.drawing = false; - this.points = []; - var c = this.ui; - c.onmousemove = null; - c.ondblclick = null; - c.onkeypress = null; - - c.blur(); - } - - function on_key(e){ - if (e.key == "Escape"){ - cancel(); - - } - } + console.log(x, y); + this.polyline.setAttribute( + "points", + this.to_polyline_attr(this.points) + "," + x + "," + y + ); } + function on_dblclick(e) { + this.points.push(this.points[0]); + this.points.push(this.points[1]); - // all boxes - + this.polyline.setAttribute("points", this.to_polyline_attr(this.points)); + console.log(this.points); + all_lines.push(this.points); - getCalib(){ - var scene_meta = this.world.sceneMeta; - - if (!scene_meta.calib.camera){ - return null; - } + this.drawing = false; + this.points = []; - //var active_camera_name = this.world.cameras.active_name; - var calib = scene_meta.calib.camera[this.name]; - - return calib; + var c = this.ui; + c.onmousemove = null; + c.ondblclick = null; + c.onkeypress = null; + c.blur(); } + function cancel() { + polyline.remove(); + this.drawing = false; + this.points = []; + var c = this.ui; + c.onmousemove = null; + c.ondblclick = null; + c.onkeypress = null; + c.blur(); + } - get_trans_ratio(){ - var img = this.world.cameras.getImageByName(this.name); - - if (!img || img.width==0){ - return null; - } - - var clientWidth, clientHeight; + function on_key(e) { + if (e.key == "Escape") { + cancel(); + } + } + } - clientWidth = 2048; - clientHeight = 1536; + // all boxes - var trans_ratio ={ - x: clientWidth/img.naturalWidth, - y: clientHeight/img.naturalHeight, - }; + getCalib() { + var scene_meta = this.world.sceneMeta; - return trans_ratio; + if (!scene_meta.calib.camera) { + return null; } - show_image(){ - var svgimage = this.ui.querySelector("#svg-image"); - - // active img is set by global, it's not set sometimes. - var img = this.world.cameras.getImageByName(this.name); - if (img){ - svgimage.setAttribute("xlink:href", img.src); - } + //var active_camera_name = this.world.cameras.active_name; + var calib = scene_meta.calib.camera[this.name]; - this.img = img; + return calib; + } + get_trans_ratio() { + var img = this.world.cameras.getImageByName(this.name); + if (!img || img.width == 0) { + return null; } + var clientWidth, clientHeight; - points_to_svg(points, trans_ratio, cssclass, radius=2){ - var ptsFinal = points.map(function(x, i){ - if (i%2==0){ - return Math.round(x * trans_ratio.x); - }else { - return Math.round(x * trans_ratio.y); - } - }); - - var svg = document.createElementNS("http://www.w3.org/2000/svg", 'g'); - - if (cssclass) - { - svg.setAttribute("class", cssclass); - } - - for (let i = 0; i < ptsFinal.length; i+=2){ - - - let x = ptsFinal[i]; - let y = ptsFinal[i+1]; - - let p = document.createElementNS("http://www.w3.org/2000/svg", 'circle'); - p.setAttribute("cx", x); - p.setAttribute("cy", y); - p.setAttribute("r", 2); - p.setAttribute("stroke-width", "1"); - - if (! cssclass){ - let image_x = points[i]; - let image_y = points[i+1]; - let color = point_color_by_distance(image_x, image_y); - color += "24"; //transparency - p.setAttribute("stroke", color); - p.setAttribute("fill", color); - } - - svg.appendChild(p); - } - - return svg; - } + clientWidth = 2048; + clientHeight = 1536; - draw_point(x,y){ - let trans_ratio = this.get_trans_ratio(); - let svg = this.ui.querySelector("#svg-points"); - let pts_svg = this.points_to_svg([x,y], trans_ratio, "radar-points"); - svg.appendChild(pts_svg); + var trans_ratio = { + x: clientWidth / img.naturalWidth, + y: clientHeight / img.naturalHeight, }; + return trans_ratio; + } + show_image() { + var svgimage = this.ui.querySelector("#svg-image"); + // active img is set by global, it's not set sometimes. + var img = this.world.cameras.getImageByName(this.name); + if (img) { + svgimage.setAttribute("xlink:href", img.src); + } - render_2d_image (){ - - - if (this.cfg.disableMainImageContext) - return; - this.clear_main_canvas(); + this.img = img; + } - this.show_image(); - this.draw_svg(); - } + points_to_svg(points, trans_ratio, cssclass, radius = 2) { + var ptsFinal = points.map(function (x, i) { + if (i % 2 == 0) { + return Math.round(x * trans_ratio.x); + } else { + return Math.round(x * trans_ratio.y); + } + }); + var svg = document.createElementNS("http://www.w3.org/2000/svg", "g"); - hide_canvas(){ - //document.getElementsByClassName("ui-wrapper")[0].style.display="none"; - this.ui.style.display="none"; + if (cssclass) { + svg.setAttribute("class", cssclass); } - show_canvas(){ - this.ui.style.display="inline"; + for (let i = 0; i < ptsFinal.length; i += 2) { + let x = ptsFinal[i]; + let y = ptsFinal[i + 1]; + + let p = document.createElementNS("http://www.w3.org/2000/svg", "circle"); + p.setAttribute("cx", x); + p.setAttribute("cy", y); + p.setAttribute("r", 2); + p.setAttribute("stroke-width", "1"); + + if (!cssclass) { + let image_x = points[i]; + let image_y = points[i + 1]; + let color = point_color_by_distance(image_x, image_y); + color += "24"; //transparency + p.setAttribute("stroke", color); + p.setAttribute("fill", color); + } + + svg.appendChild(p); } + return svg; + } - draw_svg(){ - // draw picture - var img = this.world.cameras.getImageByName(this.name); - - if (!img || img.width==0){ - this.hide_canvas(); - return; - } - - this.show_canvas(); + draw_point(x, y) { + let trans_ratio = this.get_trans_ratio(); + let svg = this.ui.querySelector("#svg-points"); + let pts_svg = this.points_to_svg([x, y], trans_ratio, "radar-points"); + svg.appendChild(pts_svg); + } - var trans_ratio = this.get_trans_ratio(); - - var calib = this.getCalib(); - if (!calib){ - return; - } + render_2d_image() { + if (this.cfg.disableMainImageContext) return; + this.clear_main_canvas(); - let svg = this.ui.querySelector("#svg-boxes"); + this.show_image(); + this.draw_svg(); + } - // draw boxes - this.world.annotation.boxes.forEach((box)=>{ - var imgfinal = box_to_2d_points(box, calib); - if (imgfinal){ - var box_svg = this.box_to_svg (box, imgfinal, trans_ratio, this.get_selected_box() == box); - svg.appendChild(box_svg); - } + hide_canvas() { + //document.getElementsByClassName("ui-wrapper")[0].style.display="none"; + this.ui.style.display = "none"; + } - }); + show_canvas() { + this.ui.style.display = "inline"; + } - svg = this.ui.querySelector("#svg-points"); + draw_svg() { + // draw picture + var img = this.world.cameras.getImageByName(this.name); - // draw radar points - if (this.cfg.projectRadarToImage) - { - this.world.radars.radarList.forEach(radar=>{ - let pts = radar.get_unoffset_radar_points(); - let ptsOnImg = points3d_to_image2d(pts, calib); + if (!img || img.width == 0) { + this.hide_canvas(); + return; + } - // there may be none after projecting - if (ptsOnImg && ptsOnImg.length>0){ - let pts_svg = this.points_to_svg(ptsOnImg, trans_ratio, radar.cssStyleSelector); - svg.appendChild(pts_svg); - } - }); - } + this.show_canvas(); + var trans_ratio = this.get_trans_ratio(); + var calib = this.getCalib(); + if (!calib) { + return; + } - // project lidar points onto camera image - if (this.cfg.projectLidarToImage){ - let pts = this.world.lidar.get_all_points(); - let ptsOnImg = points3d_to_image2d(pts, calib, true, this.img_lidar_point_map, img.width, img.height); + let svg = this.ui.querySelector("#svg-boxes"); + + // draw boxes + this.world.annotation.boxes.forEach((box) => { + var imgfinal = box_to_2d_points(box, calib); + if (imgfinal) { + var box_svg = this.box_to_svg( + box, + imgfinal, + trans_ratio, + this.get_selected_box() == box + ); + svg.appendChild(box_svg); + } + }); - // there may be none after projecting - if (ptsOnImg && ptsOnImg.length>0){ - let pts_svg = this.points_to_svg(ptsOnImg, trans_ratio); - svg.appendChild(pts_svg); - } + svg = this.ui.querySelector("#svg-points"); + + // draw radar points + if (this.cfg.projectRadarToImage) { + this.world.radars.radarList.forEach((radar) => { + let pts = radar.get_unoffset_radar_points(); + let ptsOnImg = points3d_to_image2d(pts, calib); + + // there may be none after projecting + if (ptsOnImg && ptsOnImg.length > 0) { + let pts_svg = this.points_to_svg( + ptsOnImg, + trans_ratio, + radar.cssStyleSelector + ); + svg.appendChild(pts_svg); } - + }); } - box_to_svg(box, box_corners, trans_ratio, selected){ - - - var imgfinal = box_corners.map(function(x, i){ - if (i%2==0){ - return Math.round(x * trans_ratio.x); - }else { - return Math.round(x * trans_ratio.y); - } - }) - - - var svg = document.createElementNS("http://www.w3.org/2000/svg", 'g'); - svg.setAttribute("id", "svg-box-local-"+box.obj_local_id); - - if (selected){ - svg.setAttribute("class", box.obj_type+" box-svg box-svg-selected"); - } else{ - if (box.world.data.cfg.color_obj == "id") - { - svg.setAttribute("class", "color-"+box.obj_track_id%33); - } - else // by id - { - svg.setAttribute("class", box.obj_type + " box-svg"); - } - } + // project lidar points onto camera image + if (this.cfg.projectLidarToImage) { + let pts = this.world.lidar.get_all_points(); + let ptsOnImg = points3d_to_image2d( + pts, + calib, + true, + this.img_lidar_point_map, + img.width, + img.height + ); + + // there may be none after projecting + if (ptsOnImg && ptsOnImg.length > 0) { + let pts_svg = this.points_to_svg(ptsOnImg, trans_ratio); + svg.appendChild(pts_svg); + } + } + } + + box_to_svg(box, box_corners, trans_ratio, selected) { + var imgfinal = box_corners.map(function (x, i) { + if (i % 2 == 0) { + return Math.round(x * trans_ratio.x); + } else { + return Math.round(x * trans_ratio.y); + } + }); - - var front_panel = document.createElementNS("http://www.w3.org/2000/svg", 'polygon'); - svg.appendChild(front_panel); - front_panel.setAttribute("points", - imgfinal.slice(0, 4*2).reduce(function(x,y){ - return String(x)+","+y; - }) - ) + var svg = document.createElementNS("http://www.w3.org/2000/svg", "g"); + svg.setAttribute("id", "svg-box-local-" + box.obj_local_id); + + if (selected) { + svg.setAttribute("class", box.obj_type + " box-svg box-svg-selected"); + } else { + if (box.world.data.cfg.color_obj == "id") { + svg.setAttribute("class", "color-" + (box.obj_track_id % 33)); + } // by id + else { + svg.setAttribute("class", box.obj_type + " box-svg"); + } + } - /* + var front_panel = document.createElementNS( + "http://www.w3.org/2000/svg", + "polygon" + ); + svg.appendChild(front_panel); + front_panel.setAttribute( + "points", + imgfinal.slice(0, 4 * 2).reduce(function (x, y) { + return String(x) + "," + y; + }) + ); + + /* var back_panel = document.createElementNS("http://www.w3.org/2000/svg", 'polygon'); svg.appendChild(back_panel); back_panel.setAttribute("points", @@ -703,508 +722,497 @@ class ImageContext extends MovableView{ ) */ - for (var i = 0; i<4; ++i){ - var line = document.createElementNS("http://www.w3.org/2000/svg", 'line'); - svg.appendChild(line); - line.setAttribute("x1", imgfinal[(4+i)*2]); - line.setAttribute("y1", imgfinal[(4+i)*2+1]); - line.setAttribute("x2", imgfinal[(4+(i+1)%4)*2]); - line.setAttribute("y2", imgfinal[(4+(i+1)%4)*2+1]); - } - - - for (var i = 0; i<4; ++i){ - var line = document.createElementNS("http://www.w3.org/2000/svg", 'line'); - svg.appendChild(line); - line.setAttribute("x1", imgfinal[i*2]); - line.setAttribute("y1", imgfinal[i*2+1]); - line.setAttribute("x2", imgfinal[(i+4)*2]); - line.setAttribute("y2", imgfinal[(i+4)*2+1]); - } - - return svg; + for (var i = 0; i < 4; ++i) { + var line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + svg.appendChild(line); + line.setAttribute("x1", imgfinal[(4 + i) * 2]); + line.setAttribute("y1", imgfinal[(4 + i) * 2 + 1]); + line.setAttribute("x2", imgfinal[(4 + ((i + 1) % 4)) * 2]); + line.setAttribute("y2", imgfinal[(4 + ((i + 1) % 4)) * 2 + 1]); } + for (var i = 0; i < 4; ++i) { + var line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + svg.appendChild(line); + line.setAttribute("x1", imgfinal[i * 2]); + line.setAttribute("y1", imgfinal[i * 2 + 1]); + line.setAttribute("x2", imgfinal[(i + 4) * 2]); + line.setAttribute("y2", imgfinal[(i + 4) * 2 + 1]); + } - boxes_manager = { - display_image: ()=>{ - if (!this.cfg.disableMainImageContext) - this.render_2d_image(); - }, - - add_box: (box)=>{ - var calib = this.getCalib(); - if (!calib){ - return; - } - var trans_ratio = this.get_trans_ratio(); - if (trans_ratio){ - var imgfinal = box_to_2d_points(box, calib); - if (imgfinal){ - var imgfinal = imgfinal.map(function(x, i){ - if (i%2==0){ - return Math.round(x * trans_ratio.x); - }else { - return Math.round(x * trans_ratio.y); - } - }) - - var svg_box = this.box_to_svg(box, imgfinal, trans_ratio); - var svg = this.ui.querySelector("#svg-boxes"); - svg.appendChild(svg_box); - } - } - }, - - - onBoxSelected: (box_obj_local_id, obj_type)=>{ - var b = this.ui.querySelector("#svg-box-local-"+box_obj_local_id); - if (b){ - b.setAttribute("class", "box-svg-selected"); - } - }, - - - onBoxUnselected: (box_obj_local_id, obj_type)=>{ - var b = this.ui.querySelector("#svg-box-local-"+box_obj_local_id); - - if (b) - b.setAttribute("class", obj_type); - }, - - remove_box: (box_obj_local_id)=>{ - var b = this.ui.querySelector("#svg-box-local-"+box_obj_local_id); - - if (b) - b.remove(); - }, - - update_obj_type: (box_obj_local_id, obj_type)=>{ - this.onBoxSelected(box_obj_local_id, obj_type); - }, - - update_box: (box)=>{ - var b = this.ui.querySelector("#svg-box-local-"+box.obj_local_id); - if (!b){ - return; - } - - var children = b.childNodes; - - var calib = this.getCalib(); - if (!calib){ - return; - } - - var trans_ratio = this.get_trans_ratio(); - var imgfinal = box_to_2d_points(box, calib); - - if (!imgfinal){ - //box may go out of image - return; - } - var imgfinal = imgfinal.map(function(x, i){ - if (i%2==0){ - return Math.round(x * trans_ratio.x); - }else { - return Math.round(x * trans_ratio.y); - } - }) - - if (imgfinal){ - var front_panel = children[0]; - front_panel.setAttribute("points", - imgfinal.slice(0, 4*2).reduce(function(x,y){ - return String(x)+","+y; - }) - ) - - - - for (var i = 0; i<4; ++i){ - var line = children[1+i]; - line.setAttribute("x1", imgfinal[(4+i)*2]); - line.setAttribute("y1", imgfinal[(4+i)*2+1]); - line.setAttribute("x2", imgfinal[(4+(i+1)%4)*2]); - line.setAttribute("y2", imgfinal[(4+(i+1)%4)*2+1]); - } - - - for (var i = 0; i<4; ++i){ - var line = children[5+i]; - line.setAttribute("x1", imgfinal[i*2]); - line.setAttribute("y1", imgfinal[i*2+1]); - line.setAttribute("x2", imgfinal[(i+4)*2]); - line.setAttribute("y2", imgfinal[(i+4)*2+1]); - } + return svg; + } + + boxes_manager = { + display_image: () => { + if (!this.cfg.disableMainImageContext) this.render_2d_image(); + }, + + add_box: (box) => { + var calib = this.getCalib(); + if (!calib) { + return; + } + var trans_ratio = this.get_trans_ratio(); + if (trans_ratio) { + var imgfinal = box_to_2d_points(box, calib); + if (imgfinal) { + var imgfinal = imgfinal.map(function (x, i) { + if (i % 2 == 0) { + return Math.round(x * trans_ratio.x); + } else { + return Math.round(x * trans_ratio.y); } + }); + var svg_box = this.box_to_svg(box, imgfinal, trans_ratio); + var svg = this.ui.querySelector("#svg-boxes"); + svg.appendChild(svg_box); + } + } + }, + + onBoxSelected: (box_obj_local_id, obj_type) => { + var b = this.ui.querySelector("#svg-box-local-" + box_obj_local_id); + if (b) { + b.setAttribute("class", "box-svg-selected"); + } + }, + + onBoxUnselected: (box_obj_local_id, obj_type) => { + var b = this.ui.querySelector("#svg-box-local-" + box_obj_local_id); + + if (b) b.setAttribute("class", obj_type); + }, + + remove_box: (box_obj_local_id) => { + var b = this.ui.querySelector("#svg-box-local-" + box_obj_local_id); + + if (b) b.remove(); + }, + + update_obj_type: (box_obj_local_id, obj_type) => { + this.onBoxSelected(box_obj_local_id, obj_type); + }, + + update_box: (box) => { + var b = this.ui.querySelector("#svg-box-local-" + box.obj_local_id); + if (!b) { + return; + } + + var children = b.childNodes; + + var calib = this.getCalib(); + if (!calib) { + return; + } + + var trans_ratio = this.get_trans_ratio(); + var imgfinal = box_to_2d_points(box, calib); + + if (!imgfinal) { + //box may go out of image + return; + } + var imgfinal = imgfinal.map(function (x, i) { + if (i % 2 == 0) { + return Math.round(x * trans_ratio.x); + } else { + return Math.round(x * trans_ratio.y); + } + }); + + if (imgfinal) { + var front_panel = children[0]; + front_panel.setAttribute( + "points", + imgfinal.slice(0, 4 * 2).reduce(function (x, y) { + return String(x) + "," + y; + }) + ); + + for (var i = 0; i < 4; ++i) { + var line = children[1 + i]; + line.setAttribute("x1", imgfinal[(4 + i) * 2]); + line.setAttribute("y1", imgfinal[(4 + i) * 2 + 1]); + line.setAttribute("x2", imgfinal[(4 + ((i + 1) % 4)) * 2]); + line.setAttribute("y2", imgfinal[(4 + ((i + 1) % 4)) * 2 + 1]); } - } + for (var i = 0; i < 4; ++i) { + var line = children[5 + i]; + line.setAttribute("x1", imgfinal[i * 2]); + line.setAttribute("y1", imgfinal[i * 2 + 1]); + line.setAttribute("x2", imgfinal[(i + 4) * 2]); + line.setAttribute("y2", imgfinal[(i + 4) * 2 + 1]); + } + } + }, + }; } - class ImageContextManager { - constructor(parentUi, selectorUi, cfg, on_img_click){ - this.parentUi = parentUi; - this.selectorUi = selectorUi; - this.cfg = cfg; - this.on_img_click = on_img_click; - - this.addImage("", true); - - - this.selectorUi.onmouseenter=function(event){ - if (this.timerId) - { - clearTimeout(this.timerId); - this.timerId = null; - } - - event.target.querySelector("#camera-list").style.display=""; - - }; - - - this.selectorUi.onmouseleave=function(event){ - let ui = event.target.querySelector("#camera-list"); - - this.timerId = setTimeout(()=>{ - ui.style.display="none"; - this.timerId = null; - }, - 200); - - }; - - this.selectorUi.querySelector("#camera-list").onclick = (event)=>{ - let cameraName = event.target.innerText; - - if (cameraName == "auto"){ - - let existed = this.images.find(x=>x.autoSwitch); - - if (existed) - { - this.removeImage(existed); - } - else - { - this.addImage("", true); - } - - } - else{ - let existed = this.images.find(x=>!x.autoSwitch && x.name == cameraName); - - if (existed) - { - this.removeImage(existed); - - } - else - { - this.addImage(cameraName); - } - } - }; - - } - - updateCameraList(cameras){ - - let autoCamera = '
auto
'; + constructor(parentUi, selectorUi, cfg, on_img_click) { + this.parentUi = parentUi; + this.selectorUi = selectorUi; + this.cfg = cfg; + this.on_img_click = on_img_click; - if (this.images.find(i=>i.autoSwitch)){ - autoCamera = '
auto
'; - } - - let camera_selector_str = cameras.map(c=>{ - - let existed = this.images.find(i=>i.name == c && !i.autoSwitch); - let className = existed?"camera-item camera-selected":"camera-item"; + this.addImage("", true); - return `
${c}
`; - }).reduce((x,y)=>x+y, autoCamera); + this.selectorUi.onmouseenter = function (event) { + if (this.timerId) { + clearTimeout(this.timerId); + this.timerId = null; + } - let ui = this.selectorUi.querySelector("#camera-list"); - ui.innerHTML = camera_selector_str; - ui.style.display="none"; - - this.setDefaultBestCamera(cameras[0]); - } - - setDefaultBestCamera(c){ + event.target.querySelector("#camera-list").style.display = ""; + }; - if (!this.bestCamera) - { - let existed = this.images.find(x=>x.autoSwitch); - if (existed) - { - existed.setImageName(c); - } + this.selectorUi.onmouseleave = function (event) { + let ui = event.target.querySelector("#camera-list"); - this.bestCamera = c; - } - } - - images = []; - addImage(name, autoSwitch) - { + this.timerId = setTimeout(() => { + ui.style.display = "none"; + this.timerId = null; + }, 200); + }; - if (autoSwitch && this.bestCamera && !name) - name = this.bestCamera; - - let image = new ImageContext(this.parentUi, name, autoSwitch, this.cfg, this.on_img_click); + this.selectorUi.querySelector("#camera-list").onclick = (event) => { + let cameraName = event.target.innerText; - this.images.push(image); + if (cameraName == "auto") { + let existed = this.images.find((x) => x.autoSwitch); - if (this.init_image_op_para){ - image.init_image_op(this.init_image_op_para); + if (existed) { + this.removeImage(existed); + } else { + this.addImage("", true); } + } else { + let existed = this.images.find( + (x) => !x.autoSwitch && x.name == cameraName + ); - if (this.world){ - image.attachWorld(this.world); - image.render_2d_image(); + if (existed) { + this.removeImage(existed); + } else { + this.addImage(cameraName); } + } + }; + } + updateCameraList(cameras) { + let autoCamera = + '
auto
'; - let selectorName = autoSwitch?"auto":name; - - let ui = this.selectorUi.querySelector("#camera-item-"+selectorName); - if (ui) - ui.className = "camera-item camera-selected"; - - - return image; + if (this.images.find((i) => i.autoSwitch)) { + autoCamera = + '
auto
'; } - removeImage(image){ + let camera_selector_str = cameras + .map((c) => { + let existed = this.images.find((i) => i.name == c && !i.autoSwitch); + let className = existed ? "camera-item camera-selected" : "camera-item"; - let selectorName = image.autoSwitch?"auto":image.name; - this.selectorUi.querySelector("#camera-item-"+selectorName).className = "camera-item"; - this.images = this.images.filter(x=>x!=image); - image.remove(); + return `
${c}
`; + }) + .reduce((x, y) => x + y, autoCamera); + let ui = this.selectorUi.querySelector("#camera-list"); + ui.innerHTML = camera_selector_str; + ui.style.display = "none"; - } - - setBestCamera(camera) - { - this.images.filter(i=>i.autoSwitch).forEach(i=>{ - i.setImageName(camera); - i.boxes_manager.display_image(); - }); - - this.bestCamera = camera; - } + this.setDefaultBestCamera(cameras[0]); + } - render_2d_image(){ - this.images.forEach(i=>i.render_2d_image()); - } - - attachWorld(world){ + setDefaultBestCamera(c) { + if (!this.bestCamera) { + let existed = this.images.find((x) => x.autoSwitch); + if (existed) { + existed.setImageName(c); + } - this.world = world; - this.images.forEach(i=>i.attachWorld(world)); + this.bestCamera = c; } + } - hide(){ - this.images.forEach(i=>i.hide()); - } + images = []; + addImage(name, autoSwitch) { + if (autoSwitch && this.bestCamera && !name) name = this.bestCamera; + let image = new ImageContext( + this.parentUi, + name, + autoSwitch, + this.cfg, + this.on_img_click + ); - show(){ - this.images.forEach(i=>i.show()); - } + this.images.push(image); - clear_main_canvas(){ - this.images.forEach(i=>i.clear_main_canvas()); + if (this.init_image_op_para) { + image.init_image_op(this.init_image_op_para); } - init_image_op(op){ - this.init_image_op_para = op; - this.images.forEach(i=>i.init_image_op(op)); + if (this.world) { + image.attachWorld(this.world); + image.render_2d_image(); } - hidden(){ - return false; - } - - choose_best_camera_for_point = choose_best_camera_for_point; - - self = this; - - boxes_manager = { - - display_image: ()=>{ - if (!this.cfg.disableMainImageContext) - this.render_2d_image(); - }, - - add_box: (box)=>{ - this.images.forEach(i=>i.boxes_manager.add_box(box)); - }, - - - onBoxSelected: (box_obj_local_id, obj_type)=>{ - this.images.forEach(i=>i.boxes_manager.onBoxSelected(box_obj_local_id, obj_type)); - }, - - - onBoxUnselected: (box_obj_local_id, obj_type)=>{ - this.images.forEach(i=>i.boxes_manager.onBoxUnselected(box_obj_local_id, obj_type)); - }, - - remove_box: (box_obj_local_id)=>{ - this.images.forEach(i=>i.boxes_manager.remove_box(box_obj_local_id)); - }, - - update_obj_type: (box_obj_local_id, obj_type)=>{ - this.images.forEach(i=>i.boxes_manager.update_obj_type(box_obj_local_id, obj_type)); - }, - - update_box: (box)=>{ - this.images.forEach(i=>i.boxes_manager.update_box(box)); - } - } - + let selectorName = autoSwitch ? "auto" : name; + + let ui = this.selectorUi.querySelector("#camera-item-" + selectorName); + if (ui) ui.className = "camera-item camera-selected"; + + return image; + } + + removeImage(image) { + let selectorName = image.autoSwitch ? "auto" : image.name; + this.selectorUi.querySelector("#camera-item-" + selectorName).className = + "camera-item"; + this.images = this.images.filter((x) => x != image); + image.remove(); + } + + setBestCamera(camera) { + this.images + .filter((i) => i.autoSwitch) + .forEach((i) => { + i.setImageName(camera); + i.boxes_manager.display_image(); + }); + + this.bestCamera = camera; + } + + render_2d_image() { + this.images.forEach((i) => i.render_2d_image()); + } + + attachWorld(world) { + this.world = world; + this.images.forEach((i) => i.attachWorld(world)); + } + + hide() { + this.images.forEach((i) => i.hide()); + } + + show() { + this.images.forEach((i) => i.show()); + } + + clear_main_canvas() { + this.images.forEach((i) => i.clear_main_canvas()); + } + + init_image_op(op) { + this.init_image_op_para = op; + this.images.forEach((i) => i.init_image_op(op)); + } + hidden() { + return false; + } + + choose_best_camera_for_point = choose_best_camera_for_point; + + self = this; + + boxes_manager = { + display_image: () => { + if (!this.cfg.disableMainImageContext) this.render_2d_image(); + }, + + add_box: (box) => { + this.images.forEach((i) => i.boxes_manager.add_box(box)); + }, + + onBoxSelected: (box_obj_local_id, obj_type) => { + this.images.forEach((i) => + i.boxes_manager.onBoxSelected(box_obj_local_id, obj_type) + ); + }, + + onBoxUnselected: (box_obj_local_id, obj_type) => { + this.images.forEach((i) => + i.boxes_manager.onBoxUnselected(box_obj_local_id, obj_type) + ); + }, + + remove_box: (box_obj_local_id) => { + this.images.forEach((i) => i.boxes_manager.remove_box(box_obj_local_id)); + }, + + update_obj_type: (box_obj_local_id, obj_type) => { + this.images.forEach((i) => + i.boxes_manager.update_obj_type(box_obj_local_id, obj_type) + ); + }, + + update_box: (box) => { + this.images.forEach((i) => i.boxes_manager.update_box(box)); + }, + }; } -function box_to_2d_points(box, calib){ - var scale = box.scale; - var pos = box.position; - var rotation = box.rotation; +function box_to_2d_points(box, calib) { + var scale = box.scale; + var pos = box.position; + var rotation = box.rotation; - var box3d = psr_to_xyz(pos, scale, rotation); + var box3d = psr_to_xyz(pos, scale, rotation); - //console.log(box.obj_track_id, box3d.slice(8*4)); + //console.log(box.obj_track_id, box3d.slice(8*4)); - box3d = box3d.slice(0,8*4); - return points3d_homo_to_image2d(box3d, calib); -} + box3d = box3d.slice(0, 8 * 4); + return points3d_homo_to_image2d(box3d, calib); +} // points3d is length 4 row vector, homogeneous coordinates // returns 2d row vectors -function points3d_homo_to_image2d(points3d, calib, accept_partial=false,save_map, img_dx, img_dy){ - var imgpos = matmul(calib.extrinsic, points3d, 4); - - //rect matrix shall be applied here, for kitti - if (calib.rect){ - imgpos = matmul(calib.rect, imgpos, 4); - } - - var imgpos3 = vector4to3(imgpos); - - var imgpos2; - if (calib.intrinsic.length>9) { - imgpos2 = matmul(calib.intrinsic, imgpos, 4); +function points3d_homo_to_image2d( + points3d, + calib, + accept_partial = false, + save_map, + img_dx, + img_dy +) { + var imgpos = matmul(calib.extrinsic, points3d, 4); + + //rect matrix shall be applied here, for kitti + if (calib.rect) { + imgpos = matmul(calib.rect, imgpos, 4); + } + + var imgpos3 = vector4to3(imgpos); + + var imgpos2; + if (calib.intrinsic.length > 9) { + imgpos2 = matmul(calib.intrinsic, imgpos, 4); + } else imgpos2 = matmul(calib.intrinsic, imgpos3, 3); + + let imgfinal = vector3_nomalize(imgpos2); + let imgfinal_filterd = []; + + if (accept_partial) { + let temppos = []; + let p = imgpos3; + for (var i = 0; i < p.length / 3; i++) { + if (p[i * 3 + 2] > 0) { + let x = imgfinal[i * 2]; + let y = imgfinal[i * 2 + 1]; + + x = Math.round(x); + y = Math.round(y); + if (x > 0 && x < img_dx && y > 0 && y < img_dy) { + if (save_map) { + save_map[img_dx * y + x] = [ + i, + points3d[i * 4 + 0], + points3d[i * 4 + 1], + points3d[i * 4 + 2], + ]; //save index? a little dangerous! //[points3d[i*4+0], points3d[i*4+1], points3d[i*4+2]]; + } + + imgfinal_filterd.push(x); + imgfinal_filterd.push(y); + } else { + // console.log("points outside of image",x,y); + } + } } - else - imgpos2 = matmul(calib.intrinsic, imgpos3, 3); - - let imgfinal = vector3_nomalize(imgpos2); - let imgfinal_filterd = []; - - if (accept_partial){ - let temppos=[]; - let p = imgpos3; - for (var i = 0; i0){ - let x = imgfinal[i*2]; - let y = imgfinal[i*2+1]; - - x = Math.round(x); - y = Math.round(y); - if (x > 0 && x < img_dx && y > 0 && y < img_dy){ - if (save_map){ - save_map[img_dx*y+x] = [i, points3d[i*4+0], points3d[i*4+1], points3d[i*4+2]]; //save index? a little dangerous! //[points3d[i*4+0], points3d[i*4+1], points3d[i*4+2]]; - } - - imgfinal_filterd.push(x); - imgfinal_filterd.push(y); - - } - else{ - // console.log("points outside of image",x,y); - } - - } - } - imgfinal = imgfinal_filterd; - //warning: what if calib.intrinsic.length - //todo: this function need clearance - //imgpos2 = matmul(calib.intrinsic, temppos, 3); - } - else if (!accept_partial && !all_points_in_image_range(imgpos3)){ - return null; - } + imgfinal = imgfinal_filterd; + //warning: what if calib.intrinsic.length + //todo: this function need clearance + //imgpos2 = matmul(calib.intrinsic, temppos, 3); + } else if (!accept_partial && !all_points_in_image_range(imgpos3)) { + return null; + } - return imgfinal; + return imgfinal; } -function point3d_to_homo(points){ - let homo=[]; - for (let i =0; i0){ - return valid_proj_pos[0].calib; - } + //console.log(valid_proj_pos); - return null; + if (valid_proj_pos.length > 0) { + return valid_proj_pos[0].calib; + } + return null; } - -export {ImageContextManager, BoxImageContext}; +export { ImageContextManager, BoxImageContext }; diff --git a/public/js/info_box.js b/public/js/info_box.js index 425053e..421698e 100644 --- a/public/js/info_box.js +++ b/public/js/info_box.js @@ -1,115 +1,92 @@ import { globalKeyDownManager } from "./keydown_manager.js"; import { PopupDialog } from "./popup_dialog.js"; - - -class InfoBox extends PopupDialog{ - - mouseDown = false; - mouseDwwnPos = {}; - - - constructor(ui) - { - super(ui); - - this.contentUi = this.ui.querySelector("#info-content"); - - this.buttons = { - "yes": this.ui.querySelector("#btn-yes"), - "no": this.ui.querySelector("#btn-no"), - "maximize": this.ui.querySelector("#btn-maximize"), - "restore": this.ui.querySelector("#btn-restore"), - "exit": this.ui.querySelector("#btn-exit"), - }; - - for (let btn in this.buttons) - { - this.buttons[btn].onclick = ()=>{ - this.hide(btn); - } - } - - this.ui.addEventListener("keydown", (ev)=>{ - //anykey - if ( ev.shiftKey || ev.ctrlKey || ev.altKey) - { - // - } - else - { - this.hide(); - ev.preventDefault(); - ev.stopPropagation(); - } - - }); +class InfoBox extends PopupDialog { + mouseDown = false; + mouseDwwnPos = {}; + + constructor(ui) { + super(ui); + + this.contentUi = this.ui.querySelector("#info-content"); + + this.buttons = { + yes: this.ui.querySelector("#btn-yes"), + no: this.ui.querySelector("#btn-no"), + maximize: this.ui.querySelector("#btn-maximize"), + restore: this.ui.querySelector("#btn-restore"), + exit: this.ui.querySelector("#btn-exit"), + }; + + for (let btn in this.buttons) { + this.buttons[btn].onclick = () => { + this.hide(btn); + }; } - - - showButtons(btns){ - for (let btn in this.buttons) - { - this.buttons[btn].style.display = 'none'; - } - - for (let btn in btns) - { - this.buttons[btns[btn]].style.display = ''; - } + this.ui.addEventListener("keydown", (ev) => { + //anykey + if (ev.shiftKey || ev.ctrlKey || ev.altKey) { + // + } else { + this.hide(); + ev.preventDefault(); + ev.stopPropagation(); + } + }); + } + + showButtons(btns) { + for (let btn in this.buttons) { + this.buttons[btn].style.display = "none"; } - makeVisible(pointerPosition) - { - if (!pointerPosition) - { - - //default pos - let parentRect = this.ui.getBoundingClientRect(); - let viewRect = this.viewUi.getBoundingClientRect(); - - this.viewUi.style.top = (parentRect.top+parentRect.height/3) + "px"; - this.viewUi.style.left = (parentRect.left+parentRect.width/2-viewRect.width/2) + "px"; - } - else - { - let parentRect = this.ui.getBoundingClientRect(); - let viewRect = this.viewUi.getBoundingClientRect(); - - let left = pointerPosition.x - viewRect.width/2; - if (left < parentRect.left) left = parentRect.left; - if (left + viewRect.width > parentRect.right) - left -= left + viewRect.width - parentRect.right; - - let top = pointerPosition.y - viewRect.height/2; - if (top < parentRect.top) - top = parentRect.top; - - if (top + viewRect.height > parentRect.bottom) - top -= top + viewRect.height - parentRect.bottom; - - this.viewUi.style.top = top + "px"; - this.viewUi.style.left = left + "px"; - } + for (let btn in btns) { + this.buttons[btns[btn]].style.display = ""; } + } + + makeVisible(pointerPosition) { + if (!pointerPosition) { + //default pos + let parentRect = this.ui.getBoundingClientRect(); + let viewRect = this.viewUi.getBoundingClientRect(); + + this.viewUi.style.top = parentRect.top + parentRect.height / 3 + "px"; + this.viewUi.style.left = + parentRect.left + parentRect.width / 2 - viewRect.width / 2 + "px"; + } else { + let parentRect = this.ui.getBoundingClientRect(); + let viewRect = this.viewUi.getBoundingClientRect(); + + let left = pointerPosition.x - viewRect.width / 2; + if (left < parentRect.left) left = parentRect.left; + if (left + viewRect.width > parentRect.right) + left -= left + viewRect.width - parentRect.right; + + let top = pointerPosition.y - viewRect.height / 2; + if (top < parentRect.top) top = parentRect.top; + + if (top + viewRect.height > parentRect.bottom) + top -= top + viewRect.height - parentRect.bottom; + + this.viewUi.style.top = top + "px"; + this.viewUi.style.left = left + "px"; + } + } + show(title, content, btnList, onexit, pointerPosition) { + this.showButtons(btnList); - show(title, content, btnList, onexit, pointerPosition) - { - this.showButtons(btnList); - - this.titleUi.innerText = title; - this.contentUi.innerHTML = content; - - super.show(onexit); + this.titleUi.innerText = title; + this.contentUi.innerHTML = content; - this.makeVisible(pointerPosition); + super.show(onexit); - this.ui.focus(); - } + this.makeVisible(pointerPosition); + this.ui.focus(); + } } - -export {InfoBox}; \ No newline at end of file +export { InfoBox }; diff --git a/public/js/keydown_manager.js b/public/js/keydown_manager.js index dca2829..af6a128 100644 --- a/public/js/keydown_manager.js +++ b/public/js/keydown_manager.js @@ -1,42 +1,30 @@ - - -class KeyDownManager -{ - - handlerList = []; - - // return id; - register(handler, name) - { - this.handlerList.push([name, handler]); - console.log("register keydown", name); - } - - deregister(name) - { - console.log("deregister keydown", name); - this.handlerList = this.handlerList.filter(v=>v[0]!== name); - } - - constructor() - { - document.addEventListener( 'keydown', (event)=>{ - - for (let i = this.handlerList.length-1; i >= 0; i--) - { - let ret = this.handlerList[i][1](event); - - if (!ret) - { - break; - } - } - }); - } - +class KeyDownManager { + handlerList = []; + + // return id; + register(handler, name) { + this.handlerList.push([name, handler]); + console.log("register keydown", name); + } + + deregister(name) { + console.log("deregister keydown", name); + this.handlerList = this.handlerList.filter((v) => v[0] !== name); + } + + constructor() { + document.addEventListener("keydown", (event) => { + for (let i = this.handlerList.length - 1; i >= 0; i--) { + let ret = this.handlerList[i][1](event); + + if (!ret) { + break; + } + } + }); + } } - var globalKeyDownManager = new KeyDownManager(); -export{globalKeyDownManager}; \ No newline at end of file +export { globalKeyDownManager }; diff --git a/public/js/lidar.js b/public/js/lidar.js index 85662e8..3234667 100644 --- a/public/js/lidar.js +++ b/public/js/lidar.js @@ -1,159 +1,174 @@ - -import * as THREE from './lib/three.module.js'; -import { matmul, euler_angle_to_rotate_matrix, transpose, psr_to_xyz, array_as_vector_range, array_as_vector_index_range, vector_range, euler_angle_to_rotate_matrix_3by3} from "./util.js" -import { PCDLoader } from './lib/PCDLoader.js'; -import {globalObjectCategory} from './obj_cfg.js'; - - -import {settings} from "./settings.js" - -function Lidar(sceneMeta, world, frameInfo){ - this.world = world; - this.data = world.data; - this.frameInfo = frameInfo; - this.sceneMeta = sceneMeta; - - this.points = null; - this.points_load_time = 0; - - this.remove_high_ponts = function(pcd, z){ - let position = []; - let color = []; - let normal = []; - let intensity = []; - //3, 3, 3, 1 - - for (let i = 0; i < pcd.position.length/3; i++){ - if (pcd.position[i*3+2] < z){ - position.push(pcd.position[i*3+0]); - position.push(pcd.position[i*3+1]); - position.push(pcd.position[i*3+2]); - - if (pcd.color.length>0){ - color.push(pcd.color[i*3+0]); - color.push(pcd.color[i*3+1]); - color.push(pcd.color[i*3+2]); - } - - if (pcd.normal.length>0){ - normal.push(pcd.normal[i*3+0]); - normal.push(pcd.normal[i*3+1]); - normal.push(pcd.normal[i*3+2]); - } - - if (pcd.intensity){ - intensity.push(pcd.intensity[i]); - } - } +import * as THREE from "./lib/three.module.js"; +import { + matmul, + euler_angle_to_rotate_matrix, + transpose, + psr_to_xyz, + array_as_vector_range, + array_as_vector_index_range, + vector_range, + euler_angle_to_rotate_matrix_3by3, +} from "./util.js"; +import { PCDLoader } from "./lib/PCDLoader.js"; +import { globalObjectCategory } from "./obj_cfg.js"; + +import { settings } from "./settings.js"; + +function Lidar(sceneMeta, world, frameInfo) { + this.world = world; + this.data = world.data; + this.frameInfo = frameInfo; + this.sceneMeta = sceneMeta; + + this.points = null; + this.points_load_time = 0; + + this.remove_high_ponts = function (pcd, z) { + let position = []; + let color = []; + let normal = []; + let intensity = []; + //3, 3, 3, 1 + + for (let i = 0; i < pcd.position.length / 3; i++) { + if (pcd.position[i * 3 + 2] < z) { + position.push(pcd.position[i * 3 + 0]); + position.push(pcd.position[i * 3 + 1]); + position.push(pcd.position[i * 3 + 2]); + + if (pcd.color.length > 0) { + color.push(pcd.color[i * 3 + 0]); + color.push(pcd.color[i * 3 + 1]); + color.push(pcd.color[i * 3 + 2]); } - pcd.position = position; - pcd.intensity = intensity; - pcd.color = color; - pcd.normal = normal; - - return pcd; - }; - - - - this.preload=function(on_preload_finished){ - this.on_preload_finished = on_preload_finished; - - var loader = new PCDLoader(); - - var _self = this; - loader.load( this.frameInfo.get_pcd_path(), - //ok - function ( pcd ) { - - - - - _self.points_parse_time = new Date().getTime(); - console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "parse pionts ", _self.points_parse_time - _self.create_time, "ms"); + if (pcd.normal.length > 0) { + normal.push(pcd.normal[i * 3 + 0]); + normal.push(pcd.normal[i * 3 + 1]); + normal.push(pcd.normal[i * 3 + 2]); + } - // if (_self.frameInfo.transform_matrix){ + if (pcd.intensity) { + intensity.push(pcd.intensity[i]); + } + } + } - // var arr = position; - // var num = position.length; - // var ni = 3; + pcd.position = position; + pcd.intensity = intensity; + pcd.color = color; + pcd.normal = normal; + + return pcd; + }; + + this.preload = function (on_preload_finished) { + this.on_preload_finished = on_preload_finished; + + var loader = new PCDLoader(); + + var _self = this; + loader.load( + this.frameInfo.get_pcd_path(), + //ok + function (pcd) { + _self.points_parse_time = new Date().getTime(); + console.log( + _self.points_load_time, + _self.frameInfo.scene, + _self.frameInfo.frame, + "parse pionts ", + _self.points_parse_time - _self.create_time, + "ms" + ); - // for (var i=0; i 0 ) - geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) ); + // build geometry + _self.world.data.dbg.alloc(); + var geometry = new THREE.BufferGeometry(); + if (position.length > 0) + geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute(position, 3) + ); + + let normal = pcd.normal; + // normal and colore are note used in av scenes. + if (normal.length > 0) + geometry.setAttribute( + "normal", + new THREE.Float32BufferAttribute(normal, 3) + ); + + let color = pcd.color; + if (color.length == 0) { + color = []; + + // by default we set all points to same color + for (let i = 0; i < position.length; ++i) { + color.push(_self.data.cfg.point_brightness); + } + + // if enabled intensity we color points by intensity. + if ( + _self.data.cfg.color_points == "intensity" && + pcd.intensity.length > 0 + ) { + // map intensity to color + for (var i = 0; i < pcd.intensity.length; ++i) { + let intensity = pcd.intensity[i]; + intensity *= 8; + + if (intensity > 1) intensity = 1.0; + + //color.push( 2 * Math.abs(0.5-intensity)); + + color[i * 3] = intensity; + color[i * 3 + 1] = intensity; + color[i * 3 + 2] = 1 - intensity; + } + } - let normal = pcd.normal; - // normal and colore are note used in av scenes. - if ( normal.length > 0 ) - geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normal, 3 ) ); - - let color = pcd.color; - if ( color.length == 0 ) { - color = [] - - // by default we set all points to same color - for (let i =0; i< position.length; ++i){ - color.push(_self.data.cfg.point_brightness); - } - - - // if enabled intensity we color points by intensity. - if (_self.data.cfg.color_points=="intensity" && pcd.intensity.length>0){ - // map intensity to color - for (var i =0; i< pcd.intensity.length; ++i){ - let intensity = pcd.intensity[i]; - intensity *= 8; - - if (intensity > 1) - intensity = 1.0; - - - //color.push( 2 * Math.abs(0.5-intensity)); - - color[i*3] = intensity; - color[i*3+1] = intensity; - color[i*3+2] = 1 - intensity; - } - } - - // save color, in case color needs to be restored. - pcd.color = color; - } + // save color, in case color needs to be restored. + pcd.color = color; + } - geometry.setAttribute( 'color', new THREE.Float32BufferAttribute(color, 3 ) ); + geometry.setAttribute( + "color", + new THREE.Float32BufferAttribute(color, 3) + ); - geometry.computeBoundingSphere(); - // build material + geometry.computeBoundingSphere(); + // build material - var material = new THREE.PointsMaterial( { size: _self.data.cfg.point_size, vertexColors: THREE.VertexColors } ); + var material = new THREE.PointsMaterial({ + size: _self.data.cfg.point_size, + vertexColors: THREE.VertexColors, + }); - /* + /* if ( color.length > 0 ) { material.vertexColors = color; @@ -165,256 +180,267 @@ function Lidar(sceneMeta, world, frameInfo){ } */ - //material.size = 2; - material.sizeAttenuation = false; - - // build mesh - - var mesh = new THREE.Points( geometry, material ); - mesh.name = "pcd"; - - //return mesh; - // add to parent. - _self.world.webglGroup.add(mesh); - - _self.points = mesh; - _self.pcd = pcd; - //_self.points_backup = mesh; - - _self.build_points_index(); - _self.points_load_time = new Date().getTime(); + //material.size = 2; + material.sizeAttenuation = false; - console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "loaded pionts ", _self.points_load_time - _self.create_time, "ms"); + // build mesh - _self._afterPreload(); - }, + var mesh = new THREE.Points(geometry, material); + mesh.name = "pcd"; - // on progress, - function(){ + //return mesh; + // add to parent. + _self.world.webglGroup.add(mesh); - }, + _self.points = mesh; + _self.pcd = pcd; + //_self.points_backup = mesh; - // on error - function(){ - //error - console.log("load pcd failed."); - _self._afterPreload(); - }, + _self.build_points_index(); + _self.points_load_time = new Date().getTime(); - // on file loaded - function(){ - _self.points_readfile_time = new Date().getTime(); - console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "read file ", _self.points_readfile_time - _self.create_time, "ms"); - } + console.log( + _self.points_load_time, + _self.frameInfo.scene, + _self.frameInfo.frame, + "loaded pionts ", + _self.points_load_time - _self.create_time, + "ms" ); - }; - this.deleteAll = function(){ - return this.remove_all_points(); + _self._afterPreload(); + }, + + // on progress, + function () {}, + + // on error + function () { + //error + console.log("load pcd failed."); + _self._afterPreload(); + }, + + // on file loaded + function () { + _self.points_readfile_time = new Date().getTime(); + console.log( + _self.points_load_time, + _self.frameInfo.scene, + _self.frameInfo.frame, + "read file ", + _self.points_readfile_time - _self.create_time, + "ms" + ); + } + ); + }; + + this.deleteAll = function () { + return this.remove_all_points(); + }; + + this._afterPreload = function () { + this.preloaded = true; + console.log("lidar preloaded"); + //go ahead, may load picture + if (this.on_preload_finished) { + this.on_preload_finished(); } - - this._afterPreload = function(){ - this.preloaded = true; - console.log("lidar preloaded"); - //go ahead, may load picture - if (this.on_preload_finished){ - this.on_preload_finished(); - } - if (this.go_cmd_received){ - this.go(this.webglScene, this.on_go_finished); - } - }; - - - this.loaded = false; - this.webglScene = null; - this.go_cmd_received = false; - this.on_go_finished = null; - - this.go = function(webglScene, on_go_finished){ - this.webglScene = webglScene; - - if (this.preloaded){ - - if (!this.world.data.cfg.show_background){ - this.hide_background(); - } - - - if (this.data.cfg.color_obj != "no"){ - this.color_objects(); - } - - if (on_go_finished) - on_go_finished(); - } else { - this.go_cmd_received = true; - this.on_go_finished = on_go_finished; - } - }; - - this.unload = function(){ - this.cancel_highlight(); - - if (this.points){ - // this.world.webglGroup.remove(this.points); - - // if (this.points.points_backup){ - // let backup = this.points.points_backup; - // this.points.points_backup = null; - // this.remove_all_points(); - // this.points = backup; - - // } - } - }; - - this.deleteAll = function(){ - this.remove_all_points(); + if (this.go_cmd_received) { + this.go(this.webglScene, this.on_go_finished); } + }; - this.set_point_size=function(v){ - if (this.points){ - this.points.material.size = v; + this.loaded = false; + this.webglScene = null; + this.go_cmd_received = false; + this.on_go_finished = null; - // this could happen if the points are still loading - if (this.points.points_backup){ - this.points.points_backup.material.size = v; + this.go = function (webglScene, on_go_finished) { + this.webglScene = webglScene; - if (this.points.points_backup.points_backup){ - this.points.points_backup.points_backup.material.size = v; - } - } - } - - }; - - this.color_objects = function(){ - if (this.data.cfg.color_obj != "no"){ - this.world.annotation.boxes.map((b)=>{ - if (!b.annotator) - this.set_box_points_color(b); - }) - } - }; + if (this.preloaded) { + if (!this.world.data.cfg.show_background) { + this.hide_background(); + } - // color points according to object category - this.color_points=function(){ - // color all points inside these boxes - let color = this.points.geometry.getAttribute("color").array; - - // step 1, color all points. - if (this.data.cfg.color_points=="intensity" && this.pcd.intensity.length>0){ - // by intensity - for (var i =0; i< this.pcd.intensity.length; ++i){ - let intensity = this.pcd.intensity[i]; - intensity *= 8; - - if (intensity > 1) - intensity = 1.0; - - - //color.push( 2 * Math.abs(0.5-intensity)); - - color[i*3] = intensity; - color[i*3+1] = intensity; - color[i*3+2] = 1 - intensity; - } - } - else - { - // mono color - for (let i =0; i< this.pcd.position.length; ++i){ - color[i] = this.data.cfg.point_brightness; - } - } - - // step 2 color objects + if (this.data.cfg.color_obj != "no") { this.color_objects(); - - //this.update_points_color(); - }; + } - this.transformPointsByEgoPose = function(points){ + if (on_go_finished) on_go_finished(); + } else { + this.go_cmd_received = true; + this.on_go_finished = on_go_finished; + } + }; + + this.unload = function () { + this.cancel_highlight(); + + if (this.points) { + // this.world.webglGroup.remove(this.points); + // if (this.points.points_backup){ + // let backup = this.points.points_backup; + // this.points.points_backup = null; + // this.remove_all_points(); + // this.points = backup; + // } + } + }; - if (!this.world.transLidar) - return points; + this.deleteAll = function () { + this.remove_all_points(); + }; + this.set_point_size = function (v) { + if (this.points) { + this.points.material.size = v; - let newPoints=[]; - for (let i=0; i { + if (!b.annotator) this.set_box_points_color(b); + }); + } + }; + + // color points according to object category + this.color_points = function () { + // color all points inside these boxes + let color = this.points.geometry.getAttribute("color").array; + + // step 1, color all points. + if ( + this.data.cfg.color_points == "intensity" && + this.pcd.intensity.length > 0 + ) { + // by intensity + for (var i = 0; i < this.pcd.intensity.length; ++i) { + let intensity = this.pcd.intensity[i]; + intensity *= 8; + + if (intensity > 1) intensity = 1.0; + + //color.push( 2 * Math.abs(0.5-intensity)); + + color[i * 3] = intensity; + color[i * 3 + 1] = intensity; + color[i * 3 + 2] = 1 - intensity; + } + } else { + // mono color + for (let i = 0; i < this.pcd.position.length; ++i) { + color[i] = this.data.cfg.point_brightness; + } + } - this.center = center; - } + // step 2 color objects + this.color_objects(); + + //this.update_points_color(); + }; + + this.transformPointsByEgoPose = function (points) { + if (!this.world.transLidar) return points; + + let newPoints = []; + for (let i = 0; i < points.length; i += 3) { + let p = matmul( + this.world.transLidar, + [points[i], points[i + 1], points[i + 2], 1], + 4 + ); + newPoints.push(p[0]); + newPoints.push(p[1]); + newPoints.push(p[2]); + } + return newPoints; + }; + + this.get_all_pionts = function () { + return this.points.geometry.getAttribute("position"); + }; + + this.computeCenter = function () { + if (!this.center) { + let position = this.points.geometry.getAttribute("position"); + // computer center position + let center = { x: 0, y: 0, z: 0 }; + for (let i = 0; i < position.count; i++) { + center.x += position.array[i * 3]; + center.y += position.array[i * 3 + 1]; + center.z += position.array[i * 3 + 2]; + } + + center.x /= position.count; + center.y /= position.count; + center.z /= position.count; + + this.center = center; + } - return this.center; - }; + return this.center; + }; - this.build_points_index=function(){ - var ps = this.points.geometry.getAttribute("position"); - var points_index = {}; + this.build_points_index = function () { + var ps = this.points.geometry.getAttribute("position"); + var points_index = {}; - if (ps){ // points may be empty - for (var i = 0; i 0 ) { - geometry.setAttribute( 'position', new THREE.Float32BufferAttribute(hl_point, 3 ) ); - geometry.setAttribute( 'color', new THREE.Float32BufferAttribute(hl_color, 3 ) ); - } - - - geometry.computeBoundingSphere(); + // hide all points not inside any box + this.hide_background = function () { + if (this.points.points_backup) { + //already hidden, or in highlight mode + return; + } - var material = new THREE.PointsMaterial( { size: _self.data.cfg.point_size, vertexColors: THREE.VertexColors } ); + var _self = this; + var pos = this.points.geometry.getAttribute("position"); + var color = this.points.geometry.getAttribute("color"); + + var hl_point = []; + var hl_color = []; + var highlight_point_indices = []; + this.world.annotation.boxes.forEach(function (box) { + var indices = _self._get_points_index_of_box(_self.points, box, 1); + + indices.forEach(function (i) { + hl_point.push(pos.array[i * 3]); + hl_point.push(pos.array[i * 3 + 1]); + hl_point.push(pos.array[i * 3 + 2]); + + hl_color.push(color.array[i * 3]); + hl_color.push(color.array[i * 3 + 1]); + hl_color.push(color.array[i * 3 + 2]); + }); + + highlight_point_indices = highlight_point_indices.concat(indices); + }); + + // build new geometry + this.world.data.dbg.alloc(); + var geometry = new THREE.BufferGeometry(); + + if (hl_point.length > 0) { + geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute(hl_point, 3) + ); + geometry.setAttribute( + "color", + new THREE.Float32BufferAttribute(hl_color, 3) + ); + } - material.sizeAttenuation = false; + geometry.computeBoundingSphere(); - var mesh = new THREE.Points( geometry, material ); - mesh.name = "pcd"; - mesh.points_backup = this.points; - mesh.highlight_point_indices = highlight_point_indices; + var material = new THREE.PointsMaterial({ + size: _self.data.cfg.point_size, + vertexColors: THREE.VertexColors, + }); - //swith geometry - this.world.webglGroup.remove(this.points); + material.sizeAttenuation = false; - this.points = mesh; - this.build_points_index(); - this.world.webglGroup.add(mesh); - }; + var mesh = new THREE.Points(geometry, material); + mesh.name = "pcd"; + mesh.points_backup = this.points; + mesh.highlight_point_indices = highlight_point_indices; - this.cancel_highlight=function(box){ - if (this.points && this.points.points_backup){ - - this.world.annotation.set_box_opacity(this.data.cfg.box_opacity); + //swith geometry + this.world.webglGroup.remove(this.points); - //copy colors, maybe changed. - if (this.data.cfg.color_obj != "no"){ - var highlight_point_color = this.points.geometry.getAttribute("color"); - var backup_point_color = this.points.points_backup.geometry.getAttribute("color"); - - this.points.highlight_point_indices.forEach(function(n, i){ - backup_point_color.array[n*3] = highlight_point_color.array[i*3]; - backup_point_color.array[n*3+1] = highlight_point_color.array[i*3+1]; - backup_point_color.array[n*3+2] = highlight_point_color.array[i*3+2]; - }); - } + this.points = mesh; + this.build_points_index(); + this.world.webglGroup.add(mesh); + }; + this.cancel_highlight = function (box) { + if (this.points && this.points.points_backup) { + this.world.annotation.set_box_opacity(this.data.cfg.box_opacity); - //switch - var points_backup = this.points.points_backup; - this.points.points_backup = null; - - this.world.webglGroup.remove(this.points); - this.remove_all_points(); //this.points is null now - this.points = points_backup; - - if (box){ - // in highlighted mode, the box my be moved outof the highlighted area, so - // we need to color them again. - if (this.data.cfg.color_obj != "no") - this.set_box_points_color(box); - } + //copy colors, maybe changed. + if (this.data.cfg.color_obj != "no") { + var highlight_point_color = this.points.geometry.getAttribute("color"); + var backup_point_color = + this.points.points_backup.geometry.getAttribute("color"); - if (this.data.cfg.color_obj != "no") - this.update_points_color(); - - this.world.webglGroup.add(this.points); - } - }; - - this.reset_points=function(points){ // coordinates of points - - - this.world.data.dbg.alloc(); - var geometry = new THREE.BufferGeometry(); - - - geometry.setAttribute( 'position', new THREE.Float32BufferAttribute(points, 3 ) ); - geometry.computeBoundingSphere(); - - var material = new THREE.PointsMaterial( { size: this.data.cfg.point_size} ); + this.points.highlight_point_indices.forEach(function (n, i) { + backup_point_color.array[n * 3] = highlight_point_color.array[i * 3]; + backup_point_color.array[n * 3 + 1] = + highlight_point_color.array[i * 3 + 1]; + backup_point_color.array[n * 3 + 2] = + highlight_point_color.array[i * 3 + 2]; + }); + } - material.sizeAttenuation = false; + //switch + var points_backup = this.points.points_backup; + this.points.points_backup = null; - var mesh = new THREE.Points( geometry, material ); - mesh.name = "pcd"; + this.world.webglGroup.remove(this.points); + this.remove_all_points(); //this.points is null now + this.points = points_backup; - //swith geometry - this.world.webglGroup.remove(this.points); - this.remove_all_points(); + if (box) { + // in highlighted mode, the box my be moved outof the highlighted area, so + // we need to color them again. + if (this.data.cfg.color_obj != "no") this.set_box_points_color(box); + } - this.points = mesh; - this.world.webglGroup.add(mesh); - }; + if (this.data.cfg.color_obj != "no") this.update_points_color(); - this.highlight_box_points=function(box){ - if (this.points.highlighted_box){ - //already highlighted. - return; - } + this.world.webglGroup.add(this.points); + } + }; - - // hide all other boxes - this.world.annotation.set_box_opacity(0); + this.reset_points = function (points) { + // coordinates of points - // keep myself - box.material.opacity = 1; + this.world.data.dbg.alloc(); + var geometry = new THREE.BufferGeometry(); + geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute(points, 3) + ); + geometry.computeBoundingSphere(); - var _self = this; - var pos = this.points.geometry.getAttribute("position"); - var color = this.points.geometry.getAttribute("color"); + var material = new THREE.PointsMaterial({ size: this.data.cfg.point_size }); - + material.sizeAttenuation = false; + var mesh = new THREE.Points(geometry, material); + mesh.name = "pcd"; - var hl_point=[]; - var hl_color=[]; + //swith geometry + this.world.webglGroup.remove(this.points); + this.remove_all_points(); - var highlight_point_indices= this._get_points_index_of_box(this.points, box, 3); + this.points = mesh; + this.world.webglGroup.add(mesh); + }; - highlight_point_indices.forEach(function(i){ - hl_point.push(pos.array[i*3]); - hl_point.push(pos.array[i*3+1]); - hl_point.push(pos.array[i*3+2]); + this.highlight_box_points = function (box) { + if (this.points.highlighted_box) { + //already highlighted. + return; + } - hl_color.push(color.array[i*3]); - hl_color.push(color.array[i*3+1]); - hl_color.push(color.array[i*3+2]); - }) - + // hide all other boxes + this.world.annotation.set_box_opacity(0); + + // keep myself + box.material.opacity = 1; + + var _self = this; + var pos = this.points.geometry.getAttribute("position"); + var color = this.points.geometry.getAttribute("color"); + + var hl_point = []; + var hl_color = []; + + var highlight_point_indices = this._get_points_index_of_box( + this.points, + box, + 3 + ); + + highlight_point_indices.forEach(function (i) { + hl_point.push(pos.array[i * 3]); + hl_point.push(pos.array[i * 3 + 1]); + hl_point.push(pos.array[i * 3 + 2]); + + hl_color.push(color.array[i * 3]); + hl_color.push(color.array[i * 3 + 1]); + hl_color.push(color.array[i * 3 + 2]); + }); + + // build new geometry + this.world.data.dbg.alloc(); + var geometry = new THREE.BufferGeometry(); + + if (hl_point.length > 0) { + geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute(hl_point, 3) + ); + geometry.setAttribute( + "color", + new THREE.Float32BufferAttribute(hl_color, 3) + ); + } - // build new geometry - this.world.data.dbg.alloc(); - var geometry = new THREE.BufferGeometry(); - - if (hl_point.length > 0 ) { - geometry.setAttribute( 'position', new THREE.Float32BufferAttribute(hl_point, 3 ) ); - geometry.setAttribute( 'color', new THREE.Float32BufferAttribute(hl_color, 3 ) ); - } - - - geometry.computeBoundingSphere(); + geometry.computeBoundingSphere(); - var material = new THREE.PointsMaterial( { size: _self.data.cfg.point_size, vertexColors: THREE.VertexColors } ); + var material = new THREE.PointsMaterial({ + size: _self.data.cfg.point_size, + vertexColors: THREE.VertexColors, + }); - material.sizeAttenuation = false; + material.sizeAttenuation = false; - var mesh = new THREE.Points( geometry, material ); - mesh.name = "highlighted_pcd"; + var mesh = new THREE.Points(geometry, material); + mesh.name = "highlighted_pcd"; - //swith geometry - this.world.webglGroup.remove(this.points); + //swith geometry + this.world.webglGroup.remove(this.points); - mesh.points_backup = this.points; - mesh.highlight_point_indices = highlight_point_indices; - mesh.highlighted_box = box; + mesh.points_backup = this.points; + mesh.highlight_point_indices = highlight_point_indices; + mesh.highlighted_box = box; - this.points = mesh; - this.build_points_index(); - this.world.webglGroup.add(mesh); - }; + this.points = mesh; + this.build_points_index(); + this.world.webglGroup.add(mesh); + }; - this.get_points_indices_of_box=function(box){ - return this._get_points_of_box(this.points, box, 1).index; - }; + this.get_points_indices_of_box = function (box) { + return this._get_points_of_box(this.points, box, 1).index; + }; - this.get_points_of_box_in_box_coord=function(box){ - return this._get_points_of_box(this.points, box, 1).position; - }; + this.get_points_of_box_in_box_coord = function (box) { + return this._get_points_of_box(this.points, box, 1).position; + }; - // IMPORTANT - // ground plane affects auto-adjustment - // we don't count in the ponits of lowest part to reduce the affection. - // note how the 'lower part' is defined, we count - // lowest_part_type has two options: lowest_point, or lowest_box - this.get_points_dimmension_of_box=function(box, use_box_bottom_as_limit){ - var p = this._get_points_of_box(this.points, box, 1).position; //position is relative to box coordinates + // IMPORTANT + // ground plane affects auto-adjustment + // we don't count in the ponits of lowest part to reduce the affection. + // note how the 'lower part' is defined, we count + // lowest_part_type has two options: lowest_point, or lowest_box + this.get_points_dimmension_of_box = function (box, use_box_bottom_as_limit) { + var p = this._get_points_of_box(this.points, box, 1).position; //position is relative to box coordinates - var lowest_limit = - box.scale.z/2; + var lowest_limit = -box.scale.z / 2; - if (!use_box_bottom_as_limit){ - var extreme1 = vector_range(p, 3); - lowest_limit = extreme1.min[2]; - } - - - //filter out lowest part - var p = p.filter(function(x){ - return x[2] - settings.ground_filter_height > lowest_limit; - }) - - //compute range again. - var extreme2 = vector_range(p, 3); - - return { - max:{ - x: extreme2.max[0], - y: extreme2.max[1], - z: extreme2.max[2], - }, - min:{ - x: extreme2.min[0], - y: extreme2.min[1], - z: lowest_limit, - } - } - }; - - // given points and box, calculate new box scale - this.get_dimension_of_points=function(indices, box){ - var p = this._get_points_of_box(this.points, box, 1, indices).position; - var extreme1 = vector_range(p, 3); - - //filter out lowest part, to calculate x-y size. - var p = p.filter(function(x){ - return x[2] - settings.ground_filter_height > extreme1.min[2]; - }) - - //compute range again. - var extreme2 = vector_range(p, 3); - - return { - max:{ - x: extreme2.max[0], - y: extreme2.max[1], - z: extreme1.max[2], // orignal extreme. - }, - min:{ - x: extreme2.min[0], - y: extreme2.min[1], - z: extreme1.min[2], - } - } - }; + if (!use_box_bottom_as_limit) { + var extreme1 = vector_range(p, 3); + lowest_limit = extreme1.min[2]; + } - //centered, but without rotation - this.get_points_relative_coordinates_of_box_wo_rotation=function(box, scale_ratio){ - return this._get_points_of_box(this.points, box, scale_ratio).position_wo_rotation; + //filter out lowest part + var p = p.filter(function (x) { + return x[2] - settings.ground_filter_height > lowest_limit; + }); + + //compute range again. + var extreme2 = vector_range(p, 3); + + return { + max: { + x: extreme2.max[0], + y: extreme2.max[1], + z: extreme2.max[2], + }, + min: { + x: extreme2.min[0], + y: extreme2.min[1], + z: lowest_limit, + }, }; - - - this.get_points_of_box=function(box, scale_ratio){ - return this._get_points_of_box(this.points, box, scale_ratio); - }; - - - this.get_points_relative_coordinates_of_box=function(box, scale_ratio){ - var ret = this._get_points_of_box(this.points, box, scale_ratio); - return ret.position; + }; + + // given points and box, calculate new box scale + this.get_dimension_of_points = function (indices, box) { + var p = this._get_points_of_box(this.points, box, 1, indices).position; + var extreme1 = vector_range(p, 3); + + //filter out lowest part, to calculate x-y size. + var p = p.filter(function (x) { + return x[2] - settings.ground_filter_height > extreme1.min[2]; + }); + + //compute range again. + var extreme2 = vector_range(p, 3); + + return { + max: { + x: extreme2.max[0], + y: extreme2.max[1], + z: extreme1.max[2], // orignal extreme. + }, + min: { + x: extreme2.min[0], + y: extreme2.min[1], + z: extreme1.min[2], + }, }; + }; + + //centered, but without rotation + this.get_points_relative_coordinates_of_box_wo_rotation = function ( + box, + scale_ratio + ) { + return this._get_points_of_box(this.points, box, scale_ratio) + .position_wo_rotation; + }; + + this.get_points_of_box = function (box, scale_ratio) { + return this._get_points_of_box(this.points, box, scale_ratio); + }; + + this.get_points_relative_coordinates_of_box = function (box, scale_ratio) { + var ret = this._get_points_of_box(this.points, box, scale_ratio); + return ret.position; + }; + + this._get_points_index_of_box = function (points, box, scale_ratio) { + return this._get_points_of_box(points, box, scale_ratio).index; + }; + + // this + this._get_points_of_box = function (points, box, scale_ratio, point_indices) { + if (!scale_ratio) { + scale_ratio = 1; + } + var pos_array = points.geometry.getAttribute("position").array; + + var relative_position = []; + var relative_position_wo_rotation = []; + + var r = box.rotation; + var trans = transpose( + euler_angle_to_rotate_matrix(r, { x: 0, y: 0, z: 0 }), + 4 + ); + + var indices = []; + var cand_point_indices = point_indices; + if (!point_indices) { + cand_point_indices = this.get_covering_position_indices( + points, + box.position, + box.scale, + box.rotation, + scale_ratio + ); + } + cand_point_indices.forEach(function (i) { + //for (var i = 0; i < pos.count; i++){ + var x = pos_array[i * 3]; + var y = pos_array[i * 3 + 1]; + var z = pos_array[i * 3 + 2]; - this._get_points_index_of_box=function(points, box, scale_ratio){ - return this._get_points_of_box(points, box, scale_ratio).index; - }; + var p = [x - box.position.x, y - box.position.y, z - box.position.z, 1]; - // this - this._get_points_of_box=function(points, box, scale_ratio, point_indices){ + var tp = matmul(trans, p, 4); - if (!scale_ratio){ - scale_ratio = 1; + if (!point_indices) { + // if indices is provided by caller, don't filter + if ( + Math.abs(tp[0]) > (box.scale.x / 2) * scale_ratio + 0.01 || + Math.abs(tp[1]) > (box.scale.y / 2) * scale_ratio + 0.01 || + Math.abs(tp[2]) > (box.scale.z / 2) * scale_ratio + 0.01 + ) { + return; } - var pos_array = points.geometry.getAttribute("position").array; - - - var relative_position = []; - var relative_position_wo_rotation = []; - - var r = box.rotation; - var trans = transpose(euler_angle_to_rotate_matrix(r, {x:0, y:0, z:0}), 4); - - var indices=[]; - var cand_point_indices = point_indices; - if (!point_indices) - { - cand_point_indices = this.get_covering_position_indices(points, box.position, box.scale, box.rotation, scale_ratio); - } - - cand_point_indices.forEach(function(i){ - //for (var i = 0; i < pos.count; i++){ - var x = pos_array[i*3]; - var y = pos_array[i*3+1]; - var z = pos_array[i*3+2]; - var p = [x-box.position.x, y-box.position.y, z-box.position.z, 1]; + indices.push(i); + } - var tp = matmul(trans, p, 4); + relative_position.push([tp[0], tp[1], tp[2]]); + relative_position_wo_rotation.push([p[0], p[1], p[2]]); + }); - if (!point_indices){ - // if indices is provided by caller, don't filter - if ((Math.abs(tp[0]) > box.scale.x/2 * scale_ratio+0.01) - || (Math.abs(tp[1]) > box.scale.y/2 * scale_ratio+0.01) - || (Math.abs(tp[2]) > box.scale.z/2 *scale_ratio+0.01) ){ - return; - } - - indices.push(i); - } - - relative_position.push([tp[0],tp[1],tp[2]]); - relative_position_wo_rotation.push([p[0], p[1], p[2]]) - - }); - - //console.log("found indices: " + indices.length); + //console.log("found indices: " + indices.length); - return { - index: indices, - position: relative_position, - position_wo_rotation: relative_position_wo_rotation, - } + return { + index: indices, + position: relative_position, + position_wo_rotation: relative_position_wo_rotation, }; - - this.findTop = function(box, init_scale_ratio){ - var points = this.points; - var pos_array = points.geometry.getAttribute("position").array; - - var trans = transpose(euler_angle_to_rotate_matrix(box.rotation, {x:0, y:0, z:0}), 4); - - - var cand_point_indices = this.get_covering_position_indices(points, box.position, box.scale, box.rotation, init_scale_ratio); - // all cand points are translated into box coordinates - - let translated_cand_points = cand_point_indices.map(function(i){ - let x = pos_array[i*3]; - let y = pos_array[i*3+1]; - let z = pos_array[i*3+2]; - - let p = [x-box.position.x, y-box.position.y, z-box.position.z, 1]; - let tp = matmul(trans, p, 4); - return tp; - }); - - - let maxZ = -1000; - - - translated_cand_points.forEach((tp, i)=>{ - if (Math.abs(tp[0]) < box.scale.x * init_scale_ratio.x/2 && - Math.abs(tp[1]) < box.scale.y * init_scale_ratio.y/2 && - Math.abs(tp[2]) < box.scale.z * init_scale_ratio.z/2) - { - if (tp[2] > maxZ) - maxZ = tp[2]; - } - }); - - return maxZ; + }; + + this.findTop = function (box, init_scale_ratio) { + var points = this.points; + var pos_array = points.geometry.getAttribute("position").array; + + var trans = transpose( + euler_angle_to_rotate_matrix(box.rotation, { x: 0, y: 0, z: 0 }), + 4 + ); + + var cand_point_indices = this.get_covering_position_indices( + points, + box.position, + box.scale, + box.rotation, + init_scale_ratio + ); + // all cand points are translated into box coordinates + + let translated_cand_points = cand_point_indices.map(function (i) { + let x = pos_array[i * 3]; + let y = pos_array[i * 3 + 1]; + let z = pos_array[i * 3 + 2]; + + let p = [x - box.position.x, y - box.position.y, z - box.position.z, 1]; + let tp = matmul(trans, p, 4); + return tp; + }); + + let maxZ = -1000; + + translated_cand_points.forEach((tp, i) => { + if ( + Math.abs(tp[0]) < (box.scale.x * init_scale_ratio.x) / 2 && + Math.abs(tp[1]) < (box.scale.y * init_scale_ratio.y) / 2 && + Math.abs(tp[2]) < (box.scale.z * init_scale_ratio.z) / 2 + ) { + if (tp[2] > maxZ) maxZ = tp[2]; + } + }); + + return maxZ; + }; + + // find bottom and top points, in range of init_scale_ratio + this.findBottom = function (box, init_scale_ratio) { + var points = this.points; + var pos_array = points.geometry.getAttribute("position").array; + + var trans = transpose( + euler_angle_to_rotate_matrix(box.rotation, { x: 0, y: 0, z: 0 }), + 4 + ); + + var cand_point_indices = this.get_covering_position_indices( + points, + box.position, + box.scale, + box.rotation, + init_scale_ratio + ); + // all cand points are translated into box coordinates + + let translated_cand_points = cand_point_indices.map(function (i) { + let x = pos_array[i * 3]; + let y = pos_array[i * 3 + 1]; + let z = pos_array[i * 3 + 2]; + + let p = [x - box.position.x, y - box.position.y, z - box.position.z, 1]; + let tp = matmul(trans, p, 4); + return tp; + }); + + let minZ = 1000; + + translated_cand_points.forEach((tp, i) => { + if ( + Math.abs(tp[0]) < (box.scale.x * init_scale_ratio.x) / 2 && + Math.abs(tp[1]) < (box.scale.y * init_scale_ratio.y) / 2 && + Math.abs(tp[2]) < (box.scale.z * init_scale_ratio.z) / 2 + ) { + if (tp[2] < minZ) minZ = tp[2]; + } + }); + + return minZ; + }; + + this.grow_box = function (box, min_distance, init_scale_ratio) { + console.log( + "grow box, min_distance", + min_distance, + box.scale, + init_scale_ratio + ); + let start_time = new Date().getTime(); + var points = this.points; + var pos_array = points.geometry.getAttribute("position").array; + + var trans = transpose( + euler_angle_to_rotate_matrix(box.rotation, { x: 0, y: 0, z: 0 }), + 4 + ); + + var cand_point_indices = this.get_covering_position_indices( + points, + box.position, + box.scale, + box.rotation, + init_scale_ratio + ); + + //todo: different definition. + let groundLevel = 0.3; + + if (this.data.cfg.enableDynamicGroundLevel) { + groundLevel = Math.min( + box.scale.z / 3, + Math.max(0.2, box.scale.x / 10, box.scale.y / 10) + ); + console.log("ground level", groundLevel, box.scale); } - // find bottom and top points, in range of init_scale_ratio - this.findBottom = function(box, init_scale_ratio){ - - var points = this.points; - var pos_array = points.geometry.getAttribute("position").array; - - var trans = transpose(euler_angle_to_rotate_matrix(box.rotation, {x:0, y:0, z:0}), 4); - - - var cand_point_indices = this.get_covering_position_indices(points, box.position, box.scale, box.rotation, init_scale_ratio); - // all cand points are translated into box coordinates - - let translated_cand_points = cand_point_indices.map(function(i){ - let x = pos_array[i*3]; - let y = pos_array[i*3+1]; - let z = pos_array[i*3+2]; - - let p = [x-box.position.x, y-box.position.y, z-box.position.z, 1]; - let tp = matmul(trans, p, 4); - return tp; - }); - - - let minZ = 1000; - - - translated_cand_points.forEach((tp, i)=>{ - if (Math.abs(tp[0]) < box.scale.x * init_scale_ratio.x/2 && - Math.abs(tp[1]) < box.scale.y * init_scale_ratio.y/2 && - Math.abs(tp[2]) < box.scale.z * init_scale_ratio.z/2) - { - if (tp[2] < minZ) - minZ = tp[2]; - } - }); - - return minZ; + // all cand points are translated into box coordinates + + let translated_cand_points = cand_point_indices.map(function (i) { + let x = pos_array[i * 3]; + let y = pos_array[i * 3 + 1]; + let z = pos_array[i * 3 + 2]; + + let p = [x - box.position.x, y - box.position.y, z - box.position.z, 1]; + let tp = matmul(trans, p, 4); + return tp; + }); + + let extreme = { + max: { + x: -100000, + y: -100000, + z: -100000, + }, + + min: { + x: 1000000, + y: 1000000, + z: 1000000, + }, }; - this.grow_box = function(box, min_distance, init_scale_ratio){ - console.log("grow box, min_distance", min_distance, box.scale, init_scale_ratio); - let start_time = new Date().getTime(); - var points = this.points; - var pos_array = points.geometry.getAttribute("position").array; - - var trans = transpose(euler_angle_to_rotate_matrix(box.rotation, {x:0, y:0, z:0}), 4); - - - var cand_point_indices = this.get_covering_position_indices(points, box.position, box.scale, box.rotation, init_scale_ratio); - - //todo: different definition. - let groundLevel = 0.3; - - if (this.data.cfg.enableDynamicGroundLevel) - { - groundLevel = Math.min(box.scale.z/3, Math.max(0.2, box.scale.x/10, box.scale.y/10)); - console.log('ground level', groundLevel, box.scale); + let inside_points = 0; + translated_cand_points.forEach((tp, i) => { + if ( + Math.abs(tp[0]) > box.scale.x / 2 + 0.01 || + Math.abs(tp[1]) > box.scale.y / 2 + 0.01 || + Math.abs(tp[2]) > box.scale.z / 2 + 0.01 + ) { + return; + } else { + if ( + box.scale.z < 0.6 || + (box.scale.z > 0.6 && tp[2] > -box.scale.z / 2 + groundLevel) + ) { + inside_points += 1; + + if (tp[0] > extreme.max.x) { + extreme.max.x = tp[0]; + } + + if (tp[0] < extreme.min.x) { + extreme.min.x = tp[0]; + } + + if (tp[1] > extreme.max.y) { + extreme.max.y = tp[1]; + } + + if (tp[1] < extreme.min.y) { + extreme.min.y = tp[1]; + } } - - - // all cand points are translated into box coordinates - - let translated_cand_points = cand_point_indices.map(function(i){ - let x = pos_array[i*3]; - let y = pos_array[i*3+1]; - let z = pos_array[i*3+2]; - - let p = [x-box.position.x, y-box.position.y, z-box.position.z, 1]; - let tp = matmul(trans, p, 4); - return tp; - }); - - - - let extreme= { - max: { - x:-100000, - y:-100000, - z:-100000, - }, - - min: { - x:1000000, - y:1000000, - z:1000000, - }, - }; - - let inside_points = 0; - translated_cand_points.forEach((tp, i)=>{ - if ((Math.abs(tp[0]) > box.scale.x/2+0.01) - || (Math.abs(tp[1]) > box.scale.y/2+0.01) - || (Math.abs(tp[2]) > box.scale.z/2+0.01) ){ - - - return; - } else{ - - if ((box.scale.z < 0.6) || ((box.scale.z > 0.6) && (tp[2] > -box.scale.z/2 + groundLevel))) - { - inside_points += 1; - - if (tp[0] > extreme.max.x) { - extreme.max.x = tp[0]; - } - - if (tp[0] < extreme.min.x){ - extreme.min.x = tp[0]; - } - - if (tp[1] > extreme.max.y){ - extreme.max.y = tp[1]; - } - - if (tp[1] < extreme.min.y){ - extreme.min.y = tp[1]; - } - } - - if (tp[2] > extreme.max.z){ - extreme.max.z = tp[2]; - } - - if (tp[2] < extreme.min.z){ - extreme.min.z = tp[2]; - } - - } - }); - - if (inside_points < 10) //too few points, give up. - { - return { - max:{ - x: box.scale.x/2, - y: box.scale.y/2, - z: box.scale.z/2, - }, - min:{ - x: -box.scale.x/2, - y: -box.scale.y/2, - z: -box.scale.z/2, - } - }; + if (tp[2] > extreme.max.z) { + extreme.max.z = tp[2]; } - //let translated_cand_points_with_ground = translated_cand_points; - - // filter ground points - // translated_cand_points = translated_cand_points.filter(function(tp, i){ - // return tp[2] > -box.scale.z/2 + groundLevel; - // }); - - - let extreme_adjusted = true; - let loop_count = 0; - while (extreme_adjusted){ - loop_count++; - if (loop_count > 100000) - { - console.log("deep loops in grow_box"); - break; - } - - extreme_adjusted = false; - - // x+ - let find_point = translated_cand_points.find(tp=>{ - return tp[0] > extreme.max.x && tp[0] < extreme.max.x + min_distance/2 && - tp[1] < extreme.max.y && tp[1] > extreme.min.y && - tp[2] < extreme.max.z && tp[2] > extreme.min.z + groundLevel; - }); - - if (find_point){ - extreme.max.x += min_distance/2; - extreme_adjusted = true; - } - - // x - - find_point = translated_cand_points.find(tp=>{ - return tp[0] < extreme.min.x && tp[0] > extreme.min.x - min_distance/2 && - tp[1] < extreme.max.y && tp[1] > extreme.min.y && - tp[2] < extreme.max.z && tp[2] > extreme.min.z + groundLevel; - }); - - if (find_point){ - extreme.min.x -= min_distance/2; - extreme_adjusted = true; - } - - // y+ - find_point = translated_cand_points.find(tp=>{ - return tp[1] > extreme.max.y && tp[1] < extreme.max.y + min_distance/2 && - tp[0] < extreme.max.x && tp[0] > extreme.min.x && - tp[2] < extreme.max.z && tp[2] > extreme.min.z + groundLevel; - }); - - if (find_point){ - extreme.max.y += min_distance/2; - extreme_adjusted = true; - } - - // y - - find_point = translated_cand_points.find(tp=>{ - return tp[1] < extreme.min.y && tp[1] > extreme.min.y - min_distance/2 && - tp[0] < extreme.max.x && tp[0] > extreme.min.x && - tp[2] < extreme.max.z && tp[2] > extreme.min.z + groundLevel; - }); - - if (find_point){ - extreme.min.y -= min_distance/2; - extreme_adjusted = true; - } - - - // z+ - find_point = translated_cand_points.find(tp=>{ - return tp[0] < extreme.max.x && tp[0] > extreme.min.x && - tp[1] < extreme.max.y && tp[1] > extreme.min.y && - tp[2] > extreme.max.z && tp[2] < extreme.max.z + min_distance/2; - }); - - if (find_point){ - extreme.max.z += min_distance/2; - extreme_adjusted = true; - } - - // z- - find_point = translated_cand_points.find(tp=>{ - return tp[0] < extreme.max.x && tp[0] > extreme.min.x && - tp[1] < extreme.max.y && tp[1] > extreme.min.y && - tp[2] < extreme.min.z && tp[2] > extreme.min.z - min_distance/2; - }); - - if (find_point){ - extreme.min.z -= min_distance/2; - extreme_adjusted = true; - } - + if (tp[2] < extreme.min.z) { + extreme.min.z = tp[2]; } + } + }); + + if (inside_points < 10) { + //too few points, give up. + return { + max: { + x: box.scale.x / 2, + y: box.scale.y / 2, + z: box.scale.z / 2, + }, + min: { + x: -box.scale.x / 2, + y: -box.scale.y / 2, + z: -box.scale.z / 2, + }, + }; + } + //let translated_cand_points_with_ground = translated_cand_points; + + // filter ground points + // translated_cand_points = translated_cand_points.filter(function(tp, i){ + // return tp[2] > -box.scale.z/2 + groundLevel; + // }); + + let extreme_adjusted = true; + let loop_count = 0; + while (extreme_adjusted) { + loop_count++; + if (loop_count > 100000) { + console.log("deep loops in grow_box"); + break; + } + + extreme_adjusted = false; + + // x+ + let find_point = translated_cand_points.find((tp) => { + return ( + tp[0] > extreme.max.x && + tp[0] < extreme.max.x + min_distance / 2 && + tp[1] < extreme.max.y && + tp[1] > extreme.min.y && + tp[2] < extreme.max.z && + tp[2] > extreme.min.z + groundLevel + ); + }); + + if (find_point) { + extreme.max.x += min_distance / 2; + extreme_adjusted = true; + } + + // x - + find_point = translated_cand_points.find((tp) => { + return ( + tp[0] < extreme.min.x && + tp[0] > extreme.min.x - min_distance / 2 && + tp[1] < extreme.max.y && + tp[1] > extreme.min.y && + tp[2] < extreme.max.z && + tp[2] > extreme.min.z + groundLevel + ); + }); + + if (find_point) { + extreme.min.x -= min_distance / 2; + extreme_adjusted = true; + } + + // y+ + find_point = translated_cand_points.find((tp) => { + return ( + tp[1] > extreme.max.y && + tp[1] < extreme.max.y + min_distance / 2 && + tp[0] < extreme.max.x && + tp[0] > extreme.min.x && + tp[2] < extreme.max.z && + tp[2] > extreme.min.z + groundLevel + ); + }); + + if (find_point) { + extreme.max.y += min_distance / 2; + extreme_adjusted = true; + } + + // y - + find_point = translated_cand_points.find((tp) => { + return ( + tp[1] < extreme.min.y && + tp[1] > extreme.min.y - min_distance / 2 && + tp[0] < extreme.max.x && + tp[0] > extreme.min.x && + tp[2] < extreme.max.z && + tp[2] > extreme.min.z + groundLevel + ); + }); + + if (find_point) { + extreme.min.y -= min_distance / 2; + extreme_adjusted = true; + } + + // z+ + find_point = translated_cand_points.find((tp) => { + return ( + tp[0] < extreme.max.x && + tp[0] > extreme.min.x && + tp[1] < extreme.max.y && + tp[1] > extreme.min.y && + tp[2] > extreme.max.z && + tp[2] < extreme.max.z + min_distance / 2 + ); + }); + + if (find_point) { + extreme.max.z += min_distance / 2; + extreme_adjusted = true; + } + + // z- + find_point = translated_cand_points.find((tp) => { + return ( + tp[0] < extreme.max.x && + tp[0] > extreme.min.x && + tp[1] < extreme.max.y && + tp[1] > extreme.min.y && + tp[2] < extreme.min.z && + tp[2] > extreme.min.z - min_distance / 2 + ); + }); - // refine extreme values - //1 set initial value - let refined_extreme= { - max: { - x: extreme.max.x - min_distance/2, - y: extreme.max.y - min_distance/2, - z: extreme.max.z - min_distance/2, - }, - - min: { - x: extreme.min.x + min_distance/2, - y: extreme.min.y + min_distance/2, - z: extreme.min.z + min_distance/2, - }, - }; - - - //2 find refined values. - translated_cand_points.forEach(tp=>{ - if (tp[0] > extreme.max.x || tp[0] < extreme.min.x || - tp[1] > extreme.max.y || tp[1] < extreme.min.y || - tp[2] > extreme.max.z || tp[2] < extreme.min.z) - { - - } - else{ - if (tp[0] > refined_extreme.max.x && tp[2] > extreme.min.z + groundLevel) { - refined_extreme.max.x = tp[0]; - } - - if (tp[0] < refined_extreme.min.x && tp[2] > extreme.min.z + groundLevel){ - refined_extreme.min.x = tp[0]; - } - - if (tp[1] > refined_extreme.max.y && tp[2] > extreme.min.z + groundLevel){ - refined_extreme.max.y = tp[1]; - } - - if (tp[1] < refined_extreme.min.y && tp[2] > extreme.min.z + groundLevel){ - refined_extreme.min.y = tp[1]; - } - - if (tp[2] > refined_extreme.max.z){ - refined_extreme.max.z = tp[2]; - } - - if (tp[2] < refined_extreme.min.z){ - refined_extreme.min.z = tp[2]; - } - - } - }); - - refined_extreme.min.z -= groundLevel; - console.log("refined extreme", JSON.stringify(refined_extreme)); - return refined_extreme; + if (find_point) { + extreme.min.z -= min_distance / 2; + extreme_adjusted = true; + } } - - this.get_box_points_number=function(box){ - var indices = this._get_points_index_of_box(this.points, box, 1.0); - return indices.length; + // refine extreme values + //1 set initial value + let refined_extreme = { + max: { + x: extreme.max.x - min_distance / 2, + y: extreme.max.y - min_distance / 2, + z: extreme.max.z - min_distance / 2, + }, + + min: { + x: extreme.min.x + min_distance / 2, + y: extreme.min.y + min_distance / 2, + z: extreme.min.z + min_distance / 2, + }, }; - this.reset_box_points_color = function(box){ - let color = this.points.geometry.getAttribute("color").array; - let indices = this._get_points_index_of_box(this.points, box, 1.0); - if (this.data.cfg.color_points=="intensity") - { - - indices.forEach((i)=>{ - let intensity = this.pcd.intensity[i]; - intensity *= 8; - - if (intensity > 1) - intensity = 1.0; - - color[i*3] = intensity; - color[i*3+1] = intensity; - color[i*3+2] = 1 - intensity; - }); - - } - else - { - indices.forEach((i)=>{ - color[i*3] = this.data.cfg.point_brightness; - color[i*3+1] = this.data.cfg.point_brightness; - color[i*3+2] = this.data.cfg.point_brightness; - }); + //2 find refined values. + translated_cand_points.forEach((tp) => { + if ( + tp[0] > extreme.max.x || + tp[0] < extreme.min.x || + tp[1] > extreme.max.y || + tp[1] < extreme.min.y || + tp[2] > extreme.max.z || + tp[2] < extreme.min.z + ) { + } else { + if ( + tp[0] > refined_extreme.max.x && + tp[2] > extreme.min.z + groundLevel + ) { + refined_extreme.max.x = tp[0]; } - }; - - this.set_box_points_color=function(box, target_color){ - //var pos = this.points.geometry.getAttribute("position"); - var color = this.points.geometry.getAttribute("color"); - - if (!target_color){ - if (this.data.cfg.color_obj == "category") - { - target_color = globalObjectCategory.get_color_by_category(box.obj_type); - } - else if (this.data.cfg.color_obj == "id")// by id - { - let idx = (box.obj_track_id)?parseInt(box.obj_track_id): box.obj_local_id; - target_color = globalObjectCategory.get_color_by_id(idx); - } - else // no color - { - - } + if ( + tp[0] < refined_extreme.min.x && + tp[2] > extreme.min.z + groundLevel + ) { + refined_extreme.min.x = tp[0]; } - if (target_color) - { - - var indices = this._get_points_index_of_box(this.points, box, 1.0); - indices.forEach(function(i){ - color.array[i*3] = target_color.x; - color.array[i*3+1] = target_color.y; - color.array[i*3+2] = target_color.z; - }); + if ( + tp[1] > refined_extreme.max.y && + tp[2] > extreme.min.z + groundLevel + ) { + refined_extreme.max.y = tp[1]; } - - }; - - this.set_spec_points_color=function(point_indices, target_color){ - //var pos = this.points.geometry.getAttribute("position"); - var color = this.points.geometry.getAttribute("color"); - - point_indices.forEach(function(i){ - color.array[i*3] = target_color.x; - color.array[i*3+1] = target_color.y; - color.array[i*3+2] = target_color.z; - }); - }; - // this is used when pointbrightness is updated. - this.recolor_all_points = function(){ - this.set_points_color({ - x: this.data.cfg.point_brightness, - y: this.data.cfg.point_brightness, - z: this.data.cfg.point_brightness, - }); - this.color_points(); - this.update_points_color(); - }; - - // set all points to specified color - this.set_points_color=function(target_color){ - var color = this.points.geometry.getAttribute("color"); - for (var i = 0; i extreme.min.z + groundLevel + ) { + refined_extreme.min.y = tp[1]; } - }; - - this.update_points_color=function(){ - if (this.points){ //some time points may fail to load. - this.points.geometry.getAttribute("color").needsUpdate = true; - //this.points.geometry.removeAttribute("color"); - //this.points.geometry.setAttribute("color", new THREE.Float32BufferAttribute(color.array, 3 )); + if (tp[2] > refined_extreme.max.z) { + refined_extreme.max.z = tp[2]; } - }; - - this.remove_all_points=function(){ - if (this.points){ - this.world.data.dbg.free(); - this.points.geometry.dispose(); - this.points.material.dispose(); - - - if (this.points.points_backup){ - this.world.data.dbg.free(); - this.points.points_backup.geometry.dispose(); - this.points.points_backup.material.dispose(); - - if (this.points.points_backup.points_backup){ - this.world.data.dbg.free(); - this.points.points_backup.points_backup.geometry.dispose(); - this.points.points_backup.points_backup.material.dispose(); - this.points.points_backup.points_backup = null; - } - - this.points.points_backup = null; - } - + if (tp[2] < refined_extreme.min.z) { + refined_extreme.min.z = tp[2]; + } + } + }); + + refined_extreme.min.z -= groundLevel; + console.log("refined extreme", JSON.stringify(refined_extreme)); + return refined_extreme; + }; + + this.get_box_points_number = function (box) { + var indices = this._get_points_index_of_box(this.points, box, 1.0); + return indices.length; + }; + + this.reset_box_points_color = function (box) { + let color = this.points.geometry.getAttribute("color").array; + let indices = this._get_points_index_of_box(this.points, box, 1.0); + if (this.data.cfg.color_points == "intensity") { + indices.forEach((i) => { + let intensity = this.pcd.intensity[i]; + intensity *= 8; + + if (intensity > 1) intensity = 1.0; + + color[i * 3] = intensity; + color[i * 3 + 1] = intensity; + color[i * 3 + 2] = 1 - intensity; + }); + } else { + indices.forEach((i) => { + color[i * 3] = this.data.cfg.point_brightness; + color[i * 3 + 1] = this.data.cfg.point_brightness; + color[i * 3 + 2] = this.data.cfg.point_brightness; + }); + } + }; + + this.set_box_points_color = function (box, target_color) { + //var pos = this.points.geometry.getAttribute("position"); + var color = this.points.geometry.getAttribute("color"); + + if (!target_color) { + if (this.data.cfg.color_obj == "category") { + target_color = globalObjectCategory.get_color_by_category(box.obj_type); + } else if (this.data.cfg.color_obj == "id") { + // by id + let idx = box.obj_track_id + ? parseInt(box.obj_track_id) + : box.obj_local_id; + target_color = globalObjectCategory.get_color_by_id(idx); + } // no color + else { + } + } - this.points = null; - }else { - console.error("destroy empty world!"); + if (target_color) { + var indices = this._get_points_index_of_box(this.points, box, 1.0); + indices.forEach(function (i) { + color.array[i * 3] = target_color.x; + color.array[i * 3 + 1] = target_color.y; + color.array[i * 3 + 2] = target_color.z; + }); + } + }; + + this.set_spec_points_color = function (point_indices, target_color) { + //var pos = this.points.geometry.getAttribute("position"); + var color = this.points.geometry.getAttribute("color"); + + point_indices.forEach(function (i) { + color.array[i * 3] = target_color.x; + color.array[i * 3 + 1] = target_color.y; + color.array[i * 3 + 2] = target_color.z; + }); + }; + + // this is used when pointbrightness is updated. + this.recolor_all_points = function () { + this.set_points_color({ + x: this.data.cfg.point_brightness, + y: this.data.cfg.point_brightness, + z: this.data.cfg.point_brightness, + }); + this.color_points(); + this.update_points_color(); + }; + + // set all points to specified color + this.set_points_color = function (target_color) { + var color = this.points.geometry.getAttribute("color"); + for (var i = 0; i < color.count; i++) { + color.array[i * 3] = target_color.x; + color.array[i * 3 + 1] = target_color.y; + color.array[i * 3 + 2] = target_color.z; + } + }; + + this.update_points_color = function () { + if (this.points) { + //some time points may fail to load. + this.points.geometry.getAttribute("color").needsUpdate = true; + //this.points.geometry.removeAttribute("color"); + //this.points.geometry.setAttribute("color", new THREE.Float32BufferAttribute(color.array, 3 )); + } + }; + + this.remove_all_points = function () { + if (this.points) { + this.world.data.dbg.free(); + this.points.geometry.dispose(); + this.points.material.dispose(); + + if (this.points.points_backup) { + this.world.data.dbg.free(); + this.points.points_backup.geometry.dispose(); + this.points.points_backup.material.dispose(); + + if (this.points.points_backup.points_backup) { + this.world.data.dbg.free(); + this.points.points_backup.points_backup.geometry.dispose(); + this.points.points_backup.points_backup.material.dispose(); + this.points.points_backup.points_backup = null; } - }; + this.points.points_backup = null; + } + this.points = null; + } else { + console.error("destroy empty world!"); + } + }; + + this.select_points_by_view_rect = function (x, y, w, h, camera) { + var points = this.points; + var pos_array = points.geometry.getAttribute("position").array; + + var indices = []; + var points = []; + var p = new THREE.Vector3(); + + for (var i = 0; i < pos_array.length / 3; i++) { + p.set(pos_array[i * 3], pos_array[i * 3 + 1], pos_array[i * 3 + 2]); + p = this.world.lidarPosToScene(p); + p.project(camera); + //p.x = p.x/p.z; + //p.y = p.y/p.z; + //console.log(p); + if (p.x >= x && p.x <= x + w && p.y >= y && p.y <= y + h && p.z > 0) { + indices.push(i); + points.push([ + pos_array[i * 3], + pos_array[i * 3 + 1], + pos_array[i * 3 + 2], + ]); + } + } - this.select_points_by_view_rect=function(x,y,w,h, camera){ - var points = this.points; - var pos_array = points.geometry.getAttribute("position").array; + console.log("select rect points", indices.length); - var indices = []; - var points = []; - var p = new THREE.Vector3(); - - for (var i=0; i< pos_array.length/3; i++){ - p.set(pos_array[i*3], pos_array[i*3+1], pos_array[i*3+2]); - p = this.world.lidarPosToScene(p); - p.project(camera); - //p.x = p.x/p.z; - //p.y = p.y/p.z; - //console.log(p); - if ((p.x >= x) && (p.x <= x+w) && (p.y>=y) && (p.y<=y+h) && (p.z>0)){ - indices.push(i); - points.push([pos_array[i*3], pos_array[i*3+1], pos_array[i*3+2]]); - } - } + //this.set_spec_points_color(indices, {x:1,y:0,z:0}); + //this.update_points_color(); - console.log("select rect points", indices.length); + return points; + }; - //this.set_spec_points_color(indices, {x:1,y:0,z:0}); - //this.update_points_color(); + this.get_centroid = function (point_indices) { + let points = this.points; + let pos_array = points.geometry.getAttribute("position").array; - return points; + let center = { + x: 0, + y: 0, + z: 0, }; - this.get_centroid = function(point_indices){ - let points = this.points; - let pos_array = points.geometry.getAttribute("position").array; + point_indices.forEach((i) => { + center.x += pos_array[i * 3]; + center.y += pos_array[i * 3 + 1]; + center.z += pos_array[i * 3 + 2]; + }); - let center ={ - x:0,y:0,z:0 - }; - - point_indices.forEach(i=>{ - center.x += pos_array[i*3]; - center.y += pos_array[i*3+1]; - center.z += pos_array[i*3+2]; - }); - - center.x /= point_indices.length; - center.y /= point_indices.length; - center.z /= point_indices.length; - - return center; - - }; + center.x /= point_indices.length; + center.y /= point_indices.length; + center.z /= point_indices.length; - this.create_box_by_points = function(point_indices, camera){ + return center; + }; - - let indices = point_indices; - let points = this.points; - let pos_array = points.geometry.getAttribute("position").array; + this.create_box_by_points = function (point_indices, camera) { + let indices = point_indices; + let points = this.points; + let pos_array = points.geometry.getAttribute("position").array; - // todo: copied the following code from next function. refactor it! - console.log("select rect points", indices.length); + // todo: copied the following code from next function. refactor it! + console.log("select rect points", indices.length); - //compute center, no need to tranform to box coordinates, and can't do it in this stage. - /* + //compute center, no need to tranform to box coordinates, and can't do it in this stage. + /* var extreme = array_as_vector_index_range(pos_array, 3, indices); var center = { @@ -1373,59 +1475,84 @@ function Lidar(sceneMeta, world, frameInfo){ z: (extreme.max[2]+extreme.min[2])/2, }; */ - var rotation_z = camera.rotation.z + Math.PI/2; - var trans = transpose(euler_angle_to_rotate_matrix({x:0,y:0,z:rotation_z}, {x:0, y:0, z:0}), 4); - - let center ={ - x:0,y:0,z:0 - }; - - point_indices.forEach(i=>{ - center.x += pos_array[i*3]; - center.y += pos_array[i*3+1]; - center.z += pos_array[i*3+2]; - }); - - center.x /= point_indices.length; - center.y /= point_indices.length; - center.z /= point_indices.length; - center.z = 0; - - - var relative_position = []; - indices.forEach(function(i){ - //for (var i = 0; i < pos.count; i++){ - var x = pos_array[i*3]; - var y = pos_array[i*3+1]; - var z = pos_array[i*3+2]; - var p = [x-center.x, y-center.y, z-center.z, 1]; - var tp = matmul(trans, p, 4); - relative_position.push([tp[0],tp[1],tp[2]]); - }); - - var relative_extreme = vector_range(relative_position); - var scale = { - x: relative_extreme.max[0] - relative_extreme.min[0], - y: relative_extreme.max[1] - relative_extreme.min[1], - z: relative_extreme.max[2] - relative_extreme.min[2], - }; - - // enlarge scale a little - - - // adjust center - this.world.annotation.translate_box_position(center, rotation_z, "x", relative_extreme.min[0] + scale.x/2); - this.world.annotation.translate_box_position(center, rotation_z, "y", relative_extreme.min[1] + scale.y/2); - this.world.annotation.translate_box_position(center, rotation_z, "z", relative_extreme.min[2] + scale.z/2); - - - scale.x += 0.02; - scale.y += 0.02; - scale.z += 0.02; + var rotation_z = camera.rotation.z + Math.PI / 2; + var trans = transpose( + euler_angle_to_rotate_matrix( + { x: 0, y: 0, z: rotation_z }, + { x: 0, y: 0, z: 0 } + ), + 4 + ); + + let center = { + x: 0, + y: 0, + z: 0, + }; - return this.world.annotation.add_box(center, scale, {x:0,y:0,z:rotation_z}, "Unknown", ""); + point_indices.forEach((i) => { + center.x += pos_array[i * 3]; + center.y += pos_array[i * 3 + 1]; + center.z += pos_array[i * 3 + 2]; + }); + + center.x /= point_indices.length; + center.y /= point_indices.length; + center.z /= point_indices.length; + center.z = 0; + + var relative_position = []; + indices.forEach(function (i) { + //for (var i = 0; i < pos.count; i++){ + var x = pos_array[i * 3]; + var y = pos_array[i * 3 + 1]; + var z = pos_array[i * 3 + 2]; + var p = [x - center.x, y - center.y, z - center.z, 1]; + var tp = matmul(trans, p, 4); + relative_position.push([tp[0], tp[1], tp[2]]); + }); + + var relative_extreme = vector_range(relative_position); + var scale = { + x: relative_extreme.max[0] - relative_extreme.min[0], + y: relative_extreme.max[1] - relative_extreme.min[1], + z: relative_extreme.max[2] - relative_extreme.min[2], }; + // enlarge scale a little + + // adjust center + this.world.annotation.translate_box_position( + center, + rotation_z, + "x", + relative_extreme.min[0] + scale.x / 2 + ); + this.world.annotation.translate_box_position( + center, + rotation_z, + "y", + relative_extreme.min[1] + scale.y / 2 + ); + this.world.annotation.translate_box_position( + center, + rotation_z, + "z", + relative_extreme.min[2] + scale.z / 2 + ); + + scale.x += 0.02; + scale.y += 0.02; + scale.z += 0.02; + + return this.world.annotation.add_box( + center, + scale, + { x: 0, y: 0, z: rotation_z }, + "Unknown", + "" + ); + }; } -export{Lidar} +export { Lidar }; diff --git a/public/js/log.js b/public/js/log.js index 80ab7fd..66bbfad 100644 --- a/public/js/log.js +++ b/public/js/log.js @@ -1,202 +1,213 @@ import { PopupDialog } from "./popup_dialog.js"; - - - -class LogWindow extends PopupDialog{ - - mouseDown = false; - mouseDwwnPos = {}; - - - constructor(ui, btn) - { - super(ui); - - this.btn = btn; - this.svg = btn.querySelector("#log-svg"); - - this.logsContentUi = this.ui.querySelector("#content-logs"); - this.errorsContentUi = this.ui.querySelector("#content-errors"); - this.clearBtn = this.ui.querySelector("#btn-clear"); - - this.clearBtn.onclick = ()=>{ this.logsContentUi.innerHTML = ""; }; - this.log("Welcome!"); - - this.logBtn = this.ui.querySelector("#tab-log"); - this.errorBtn = this.ui.querySelector("#tab-error"); - - this.logBtn.onclick= ()=>{ - this.logBtn.className = "tab-button tab-selected"; - this.errorBtn.className = "tab-button"; - - this.logsContentUi.style.display = 'inherit'; - this.errorsContentUi.style.display = 'none'; - } - - this.errorBtn.onclick= ()=>{ - this.errorBtn.className = "tab-button tab-selected"; - this.logBtn.className = "tab-button"; - - this.logsContentUi.style.display = 'none'; - this.errorsContentUi.style.display = 'inherit'; - } - } - - setErrorsContent(errors) - { - let summary = `${errors.length} warnings.
`; - let text = errors.map(r=>`${r.frame_id},${r.obj_id}, ${r.desc}
`).reduce((a,b)=>a+b, summary); - this.errorsContentUi.innerHTML = text; - - this.errorsContentUi.querySelectorAll(".log-object-frame-id").forEach(ele=>{ - ele.onclick = (event)=>{ - let obj = event.currentTarget.innerHTML.split(","); - console.log("click", obj); - window.editor.currentMainEditor.gotoObjectFrame(...obj); //frameid, objid - } - }); - } - - - setUi(ui) - { - - } - - show() - { - super.show(); - } - - gettime() { - let d = new Date(); - return "" + d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate() + " " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); +class LogWindow extends PopupDialog { + mouseDown = false; + mouseDwwnPos = {}; + + constructor(ui, btn) { + super(ui); + + this.btn = btn; + this.svg = btn.querySelector("#log-svg"); + + this.logsContentUi = this.ui.querySelector("#content-logs"); + this.errorsContentUi = this.ui.querySelector("#content-errors"); + this.clearBtn = this.ui.querySelector("#btn-clear"); + + this.clearBtn.onclick = () => { + this.logsContentUi.innerHTML = ""; + }; + this.log("Welcome!"); + + this.logBtn = this.ui.querySelector("#tab-log"); + this.errorBtn = this.ui.querySelector("#tab-error"); + + this.logBtn.onclick = () => { + this.logBtn.className = "tab-button tab-selected"; + this.errorBtn.className = "tab-button"; + + this.logsContentUi.style.display = "inherit"; + this.errorsContentUi.style.display = "none"; + }; + + this.errorBtn.onclick = () => { + this.errorBtn.className = "tab-button tab-selected"; + this.logBtn.className = "tab-button"; + + this.logsContentUi.style.display = "none"; + this.errorsContentUi.style.display = "inherit"; + }; + } + + setErrorsContent(errors) { + let summary = `${errors.length} warnings.
`; + let text = errors + .map( + (r) => + `${r.frame_id},${r.obj_id}, ${r.desc}
` + ) + .reduce((a, b) => a + b, summary); + this.errorsContentUi.innerHTML = text; + + this.errorsContentUi + .querySelectorAll(".log-object-frame-id") + .forEach((ele) => { + ele.onclick = (event) => { + let obj = event.currentTarget.innerHTML.split(","); + console.log("click", obj); + window.editor.currentMainEditor.gotoObjectFrame(...obj); //frameid, objid + }; + }); + } + + setUi(ui) {} + + show() { + super.show(); + } + + gettime() { + let d = new Date(); + return ( + "" + + d.getFullYear() + + "-" + + (d.getMonth() + 1) + + "-" + + d.getDate() + + " " + + d.getHours() + + ":" + + d.getMinutes() + + ":" + + d.getSeconds() + ); + } + + autoScroll = true; + updateAutoScrollFlag() { + let div = this.logsContentUi; + this.autoScroll = div.scrollHeight - 10 < div.scrollTop + div.clientHeight; + } + + autoScrollOutput() { + let div = this.logsContentUi; + if (this.autoScroll) div.scrollTop = div.scrollHeight; + } + + isInt(n) { + return n % 1 === 0; + } + + buildLogStr(args) { + let thisstr = ""; + for (let i in args) { + if (typeof args[i] == "number") { + thisstr += + " " + + (isInt(args[i]) ? args[i] : args[i].toFixed(6)) + + ""; + } else if ([".", ",", ":", ";"].find((c) => c == args[i])) { + thisstr += args[i]; + } else { + thisstr += " " + args[i]; + } } - autoScroll = true; - updateAutoScrollFlag() - { - let div = this.logsContentUi; - this.autoScroll = (div.scrollHeight-10 < div.scrollTop + div.clientHeight); - } - - autoScrollOutput(){ - let div = this.logsContentUi; - if (this.autoScroll) - div.scrollTop = div.scrollHeight; - } - - isInt(n) { - return n % 1 === 0; - } - - buildLogStr(args) { - let thisstr = ""; - for (let i in args) { - if (typeof args[i] == "number") { - thisstr += " " + (isInt(args[i]) ? args[i] : args[i].toFixed(6)) + ""; - } else if ([".", ",", ":", ";"].find((c) => c == args[i])) { - thisstr += args[i]; - } else { - thisstr += " " + args[i]; - } - } - - return thisstr; - } - - logcolor(color) { - this.updateAutoScrollFlag(); - let args = [...arguments]; - console.log(...args.slice(1)); - let old_content = this.logsContentUi.innerHTML; - - let thisstr = this.gettime() + " "; - thisstr += this.buildLogStr(args.slice(1)); - - this.logid++; - - - this.logsContentUi.innerHTML = old_content + "
" + thisstr + "
"; + return thisstr; + } - this.autoScrollOutput(); - } - - - logid = 0; - maxLogLength = 10000; // stringLength; - log() { + logcolor(color) { + this.updateAutoScrollFlag(); + let args = [...arguments]; + console.log(...args.slice(1)); + let old_content = this.logsContentUi.innerHTML; - this.svg.style.fill= this.logid %2 ? "red" : "green"; + let thisstr = this.gettime() + " "; + thisstr += this.buildLogStr(args.slice(1)); - this.updateAutoScrollFlag(); - - console.log(...arguments); - let old_content = this.logsContentUi.innerHTML; + this.logid++; - let thisstr = this.gettime() + " "; - //let thisstr = ""; - thisstr += this.buildLogStr(arguments); + this.logsContentUi.innerHTML = + old_content + + "
" + + thisstr + + "
"; - this.logid++; - - if (old_content.length > this.maxLogLength) - { - old_content = old_content.slice(old_content.length-this.maxLogLength); - let firstLogPos = old_content.search("
" + thisstr + "
"; - this.autoScrollOutput(); - } + logid = 0; + maxLogLength = 10000; // stringLength; + log() { + this.svg.style.fill = this.logid % 2 ? "red" : "green"; - logappend() { - //console.log(...arguments); - this.updateAutoScrollFlag(); - let thisstr = this.buildLogStr(arguments); - this.logsContentUi.querySelector("#log-" + this.logid).innerHTML += thisstr; - this.autoScrollOutput(); - } + this.updateAutoScrollFlag(); - logappendcolor(color) { - this.updateAutoScrollFlag(); - let args = [...arguments]; - let thisstr = this.buildLogStr(args.slice(1)); - let div = this.logsContentUi.querySelector("#log-" + this.logid); - div.className = color; - div.innerHTML += thisstr; - this.autoScrollOutput(); - } + console.log(...arguments); + let old_content = this.logsContentUi.innerHTML; - logonce() { - this.updateAutoScrollFlag(); - let old_content = this.logsContentUi.innerHTML; + let thisstr = this.gettime() + " "; + //let thisstr = ""; + thisstr += this.buildLogStr(arguments); - let thisstr = this.gettime() + " "; - - thisstr += this.buildLogStr(arguments); + this.logid++; - let laststr = this.logsContentUi.querySelector("#log-" + this.logid); - if (laststr && laststr.innerHTML && thisstr == laststr.innerHTML) - return; - - this.logid++; - this.logsContentUi.innerHTML = old_content + "
" + thisstr + "
"; - this.autoScrollOutput(); + if (old_content.length > this.maxLogLength) { + old_content = old_content.slice(old_content.length - this.maxLogLength); + let firstLogPos = old_content.search("
" + thisstr + "
"; + this.autoScrollOutput(); + } + + logappend() { + //console.log(...arguments); + this.updateAutoScrollFlag(); + let thisstr = this.buildLogStr(arguments); + this.logsContentUi.querySelector("#log-" + this.logid).innerHTML += thisstr; + this.autoScrollOutput(); + } + + logappendcolor(color) { + this.updateAutoScrollFlag(); + let args = [...arguments]; + let thisstr = this.buildLogStr(args.slice(1)); + let div = this.logsContentUi.querySelector("#log-" + this.logid); + div.className = color; + div.innerHTML += thisstr; + this.autoScrollOutput(); + } + + logonce() { + this.updateAutoScrollFlag(); + let old_content = this.logsContentUi.innerHTML; + + let thisstr = this.gettime() + " "; + + thisstr += this.buildLogStr(arguments); + + let laststr = this.logsContentUi.querySelector("#log-" + this.logid); + if (laststr && laststr.innerHTML && thisstr == laststr.innerHTML) return; + + this.logid++; + this.logsContentUi.innerHTML = + old_content + "
" + thisstr + "
"; + this.autoScrollOutput(); + } } - - - - let logger = null; -function create_logger(ui, btn){ - logger = new LogWindow(ui, btn); +function create_logger(ui, btn) { + logger = new LogWindow(ui, btn); } -export{logger, create_logger}; \ No newline at end of file +export { logger, create_logger }; diff --git a/public/js/main.js b/public/js/main.js index 544f784..d006b91 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,64 +1,57 @@ -import{Config} from "./config.js" -import{Editor} from "./editor.js" -import {Data} from './data.js' - +import { Config } from "./config.js"; +import { Editor } from "./editor.js"; +import { Data } from "./data.js"; let pointsGlobalConfig = new Config(); window.pointsGlobalConfig = pointsGlobalConfig; - pointsGlobalConfig.load(); +document.documentElement.className = "theme-" + pointsGlobalConfig.theme; -document.documentElement.className="theme-"+pointsGlobalConfig.theme; - - -document.body.addEventListener('keydown', event => { - if (event.ctrlKey && 'asdv'.indexOf(event.key) !== -1) { - event.preventDefault() - } +document.body.addEventListener("keydown", (event) => { + if (event.ctrlKey && "asdv".indexOf(event.key) !== -1) { + event.preventDefault(); + } }); -async function createMainEditor(){ - - let template = document.querySelector('#editor-template'); - let maindiv = document.querySelector("#main-editor"); +async function createMainEditor() { + let template = document.querySelector("#editor-template"); + let maindiv = document.querySelector("#main-editor"); let main_ui = template.content.cloneNode(true); maindiv.appendChild(main_ui); // input parameter is changed after `append` let editorCfg = pointsGlobalConfig; let dataCfg = pointsGlobalConfig; - + let data = new Data(dataCfg); await data.init(); - let editor = new Editor(maindiv.lastElementChild, maindiv, editorCfg, data, "main-editor") + let editor = new Editor( + maindiv.lastElementChild, + maindiv, + editorCfg, + data, + "main-editor" + ); window.editor = editor; editor.run(); return editor; -} - -async function start(){ +} - +async function start() { let mainEditor = await createMainEditor(); - - let url_string = window.location.href + let url_string = window.location.href; let url = new URL(url_string); //language let scene = url.searchParams.get("scene"); let frame = url.searchParams.get("frame"); - if (scene && frame) - { + if (scene && frame) { mainEditor.load_world(scene, frame); } } - - - start(); - diff --git a/public/js/ml.js b/public/js/ml.js index 04ab4bf..ecb3854 100644 --- a/public/js/ml.js +++ b/public/js/ml.js @@ -1,157 +1,155 @@ - - import { logger } from "./log.js"; -import {matmul, euler_angle_to_rotate_matrix_3by3, transpose, matmul2} from "./util.js" +import { + matmul, + euler_angle_to_rotate_matrix_3by3, + transpose, + matmul2, +} from "./util.js"; const annMath = { + sub: function (a, b) { + //pos, rot, scale - sub: function(a,b){ //pos, rot, scale - - let c = []; - for (let i in a) - { - c[i] = a[i] - b[i]; - } + let c = []; + for (let i in a) { + c[i] = a[i] - b[i]; + } - return this.norm(c); - }, + return this.norm(c); + }, - div: function(a, d){ // d is scalar - let c = []; - for (let i in a) - { - c[i] = a[i]/d; - } + div: function (a, d) { + // d is scalar + let c = []; + for (let i in a) { + c[i] = a[i] / d; + } - return c; - }, + return c; + }, - add: function(a, b){ - let c = []; - for (let i in a) - { - c[i] = a[i] + b[i]; - } + add: function (a, b) { + let c = []; + for (let i in a) { + c[i] = a[i] + b[i]; + } - - return this.norm(c); - }, - - mul: function(a, d) // d is scalar - { - let c = []; - for (let i in a) - { - c[i] = a[i]*d; - } + return this.norm(c); + }, - return this.norm(c); - }, + mul: function ( + a, + d // d is scalar + ) { + let c = []; + for (let i in a) { + c[i] = a[i] * d; + } - norm: function(c) - { - for (let i = 3; i< 6; i++) - { - if (c[i] > Math.PI) - { - c[i] -= Math.PI * 2; - } - else if (c[i] < - Math.PI) - { - c[i] += Math.PI * 2; - } - } + return this.norm(c); + }, - return c; - }, + norm: function (c) { + for (let i = 3; i < 6; i++) { + if (c[i] > Math.PI) { + c[i] -= Math.PI * 2; + } else if (c[i] < -Math.PI) { + c[i] += Math.PI * 2; + } + } - normAngle: function (a){ - if (a > Math.PI) - { - return a - Math.PI * 2; - } - else if (a < - Math.PI) - { - return a + Math.PI * 2; - } - - return a; - }, - - eleMul: function(a,b) //element-wise multiplication - { - let c = []; - for (let i in a) - { - c[i] = a[i] * b[i]; - } + return c; + }, - - return c; + normAngle: function (a) { + if (a > Math.PI) { + return a - Math.PI * 2; + } else if (a < -Math.PI) { + return a + Math.PI * 2; } -}; + return a; + }, + eleMul: function ( + a, + b //element-wise multiplication + ) { + let c = []; + for (let i in a) { + c[i] = a[i] * b[i]; + } + return c; + }, +}; var ml = { - backend: tf.getBackend(), - - calibrate_axes: function(points){ - console.log("backend of tensorflow:", tf.getBackend()); - console.log("number of points:", points.count); - - var center_points = {}; - for (var i = 0; i-10 && - points.array[i*3+1] < 10 && points.array[i*3+1]>-10) // x,y in [-10,10] - { - var key = (10 + Math.round(points.array[i*3]))*100 + (Math.round(points.array[i*3+1])+10); - if (center_points[key]){ - - // save only minimal index - if (points.array[i*3+2] < points.array[center_points[key]*3+2]){ - center_points[key] = i; - } - - }else { - center_points[key] = i; - } - } - } - - var center_point_indices = []; - for (var i in center_points){ - center_point_indices.push(center_points[i]); + backend: tf.getBackend(), + + calibrate_axes: function (points) { + console.log("backend of tensorflow:", tf.getBackend()); + console.log("number of points:", points.count); + + var center_points = {}; + for (var i = 0; i < points.count; i++) { + if ( + points.array[i * 3] < 10 && + points.array[i * 3] > -10 && + points.array[i * 3 + 1] < 10 && + points.array[i * 3 + 1] > -10 + ) { + // x,y in [-10,10] + var key = + (10 + Math.round(points.array[i * 3])) * 100 + + (Math.round(points.array[i * 3 + 1]) + 10); + if (center_points[key]) { + // save only minimal index + if ( + points.array[i * 3 + 2] < points.array[center_points[key] * 3 + 2] + ) { + center_points[key] = i; + } + } else { + center_points[key] = i; } + } + } - //console.log(center_point_indices); - var points_2d = center_point_indices.map(i => [points.array[i*3],points.array[i*3+1],points.array[i*3+2]]); - var points_array = points_2d.flatMap(x=> x); - - - var sum = points_2d.reduce(function(s, x){ - return [s[0] + x[0], - s[1] + x[1], - s[2] + x[2]]; - },[0,0,0]); - var count = points_2d.length; - var mean = [sum[0]/count, sum[1]/count, sum[2]/count]; - - var data_centered = points_2d.map(function(x){ - return [ - x[0] - mean[0], - x[1] - mean[1], - x[2] - mean[2], - ]; - }) - - var normal_v = this.train(data_centered); - - + var center_point_indices = []; + for (var i in center_points) { + center_point_indices.push(center_points[i]); + } - data.world.add_line(mean, [-normal_v[0]*10, -normal_v[1]*10, normal_v[2]*10]); - data.world.lidar.reset_points(points_array); - /* + //console.log(center_point_indices); + var points_2d = center_point_indices.map((i) => [ + points.array[i * 3], + points.array[i * 3 + 1], + points.array[i * 3 + 2], + ]); + var points_array = points_2d.flatMap((x) => x); + + var sum = points_2d.reduce( + function (s, x) { + return [s[0] + x[0], s[1] + x[1], s[2] + x[2]]; + }, + [0, 0, 0] + ); + var count = points_2d.length; + var mean = [sum[0] / count, sum[1] / count, sum[2] / count]; + + var data_centered = points_2d.map(function (x) { + return [x[0] - mean[0], x[1] - mean[1], x[2] - mean[2]]; + }); + + var normal_v = this.train(data_centered); + + data.world.add_line(mean, [ + -normal_v[0] * 10, + -normal_v[1] * 10, + normal_v[2] * 10, + ]); + data.world.lidar.reset_points(points_array); + /* var trans_matrix = transpose(euler_angle_to_rotate_matrix_3by3({x:Math.atan2(normal_v[1], -1), y: 0, z: 0})); @@ -163,396 +161,369 @@ var ml = { //data.world.lidar.update_points_color(); */ - - - return center_point_indices; - }, - - train: function(data_centered) // data is ?*3 array. - { - - - - var XY = data_centered.map(function(x){return x.slice(0,2);}); - var Z = data_centered.map(function(x){return x[2];}); - - - var x = tf.tensor2d(XY); - var para = tf.variable(tf.tensor2d([[Math.random(), Math.random()]])); - - const learningRate = 0.00001; - const optimizer = tf.train.sgd(learningRate); - para.print(); - for (var i=0; i<20; i++){ - optimizer.minimize(function() { - var dists = tf.matMul(para, x.transpose()); - var sqrdiff = tf.squaredDifference(dists, Z); - var loss = tf.div(tf.sum(sqrdiff), sqrdiff.shape[0]); - loss.print(); - return loss; - }); - - console.log(i); - para.print(); - } - - var pv = para.dataSync(); - console.log("train result: ", pv); - return [pv[0], pv[1], 1]; + return center_point_indices; + }, + + train: function ( + data_centered // data is ?*3 array. + ) { + var XY = data_centered.map(function (x) { + return x.slice(0, 2); + }); + var Z = data_centered.map(function (x) { + return x[2]; + }); + + var x = tf.tensor2d(XY); + var para = tf.variable(tf.tensor2d([[Math.random(), Math.random()]])); + + const learningRate = 0.00001; + const optimizer = tf.train.sgd(learningRate); + para.print(); + for (var i = 0; i < 20; i++) { + optimizer.minimize(function () { + var dists = tf.matMul(para, x.transpose()); + var sqrdiff = tf.squaredDifference(dists, Z); + var loss = tf.div(tf.sum(sqrdiff), sqrdiff.shape[0]); + loss.print(); + return loss; + }); + + console.log(i); + para.print(); } - , - - - // data is N*2 matrix, - l_shape_fit: function(data){ - - // cos, sin - // -sin, cos - var A = tf.tensor2d(data); - //A = tf.expandDims(A, [0]); - - var theta = []; - var min = 0; - var min_index = 0; - for (var i =0; i<=90; i+=1){ - var obj = cal_objetive(A, i); - - if (min==0 || min > obj){ - min_index = i; - min = obj; - } - } - console.log(min_index, min); - return min; - - //end of func - - function cal_objetive(A, theta){ - let r = theta*Math.PI/180; - let bases = tf.tensor2d([[Math.cos(r), -Math.sin(r)], - [Math.sin(r), Math.cos(r)]]); - - let proj = tf.matMul(A, bases); // n * 2 - let max = tf.max(proj, 0); // 1*2 - let min = tf.min(proj, 0); // 1*2 - var dist_to_min = tf.sum(tf.square(tf.sub(proj, min)), 0); - var dist_to_max = tf.sum(tf.square(tf.sub(max, proj)), 0); - - // axis 0 - var dist0, dist1; // dist to axis 0, axis 1 - if (dist_to_min.gather(0).dataSync() < dist_to_max.gather(0).dataSync()){ - dist0 = tf.sub(proj.gather([0], 1), min.gather(0)); - } else { - dist0 = tf.sub(max.gather(0), proj.gather([0], 1)); - } - - if (dist_to_min.gather(1).dataSync() < dist_to_max.gather(1).dataSync()){ - dist1 = tf.sub(proj.gather([1], 1), min.gather(1)); - } else { - dist1 = tf.sub(max.gather(1), proj.gather([1], 1)); - } - - // concat dist0, dist1 - var min_dist = tf.concat([dist0, dist1], 1).min(1); - return min_dist.sum().dataSync()[0]; - } + var pv = para.dataSync(); + console.log("train result: ", pv); + return [pv[0], pv[1], 1]; + }, + // data is N*2 matrix, + l_shape_fit: function (data) { + // cos, sin + // -sin, cos + var A = tf.tensor2d(data); + //A = tf.expandDims(A, [0]); + + var theta = []; + var min = 0; + var min_index = 0; + for (var i = 0; i <= 90; i += 1) { + var obj = cal_objetive(A, i); + + if (min == 0 || min > obj) { + min_index = i; + min = obj; + } + } + console.log(min_index, min); + return min; + + //end of func + + function cal_objetive(A, theta) { + let r = (theta * Math.PI) / 180; + let bases = tf.tensor2d([ + [Math.cos(r), -Math.sin(r)], + [Math.sin(r), Math.cos(r)], + ]); + + let proj = tf.matMul(A, bases); // n * 2 + let max = tf.max(proj, 0); // 1*2 + let min = tf.min(proj, 0); // 1*2 + var dist_to_min = tf.sum(tf.square(tf.sub(proj, min)), 0); + var dist_to_max = tf.sum(tf.square(tf.sub(max, proj)), 0); + + // axis 0 + var dist0, dist1; // dist to axis 0, axis 1 + if (dist_to_min.gather(0).dataSync() < dist_to_max.gather(0).dataSync()) { + dist0 = tf.sub(proj.gather([0], 1), min.gather(0)); + } else { + dist0 = tf.sub(max.gather(0), proj.gather([0], 1)); + } + + if (dist_to_min.gather(1).dataSync() < dist_to_max.gather(1).dataSync()) { + dist1 = tf.sub(proj.gather([1], 1), min.gather(1)); + } else { + dist1 = tf.sub(max.gather(1), proj.gather([1], 1)); + } + + // concat dist0, dist1 + var min_dist = tf.concat([dist0, dist1], 1).min(1); + return min_dist.sum().dataSync()[0]; } - , + }, + // predict_rotation_cb: function(data, callback){ + // var xhr = new XMLHttpRequest(); + // // we defined the xhr + + // xhr.onreadystatechange = function () { + // if (this.readyState != 4) + // return; + + // if (this.status == 200) { + // var ret = JSON.parse(this.responseText); + // console.log(ret); + // callback(ret.angle); + // } + // else{ + // console.log(this); + // } + + // }; + + // xhr.open('POST', "/predict_rotation", true); + // xhr.send(JSON.stringify({"points": data})); + // }, + + predict_rotation: function (data) { + const req = new Request("/predict_rotation"); + let init = { + method: "POST", + body: JSON.stringify({ points: data }), + }; + // we defined the xhr + console.log("start predict rotatoin.", data.length, "points"); - // predict_rotation_cb: function(data, callback){ - // var xhr = new XMLHttpRequest(); - // // we defined the xhr - - // xhr.onreadystatechange = function () { - // if (this.readyState != 4) - // return; - - // if (this.status == 200) { - // var ret = JSON.parse(this.responseText); - // console.log(ret); - // callback(ret.angle); - // } - // else{ - // console.log(this); - // } - - // }; - - // xhr.open('POST', "/predict_rotation", true); - // xhr.send(JSON.stringify({"points": data})); - // }, - - predict_rotation: function(data){ - const req = new Request("/predict_rotation"); - let init = { - method: 'POST', - body: JSON.stringify({"points": data}) - }; - // we defined the xhr - console.log("start predict rotatoin.", data.length, 'points') - - return fetch(req, init) - .then(response=>{ - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - }else{ - console.log("predict rotatoin response received.") - return response.json(); + return fetch(req, init) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } else { + console.log("predict rotatoin response received."); + return response.json(); + } + }) + .catch((reject) => { + console.log("error predicting yaw angle!"); + }); + }, + + // autoadj is async + interpolate_annotation: async function (anns, autoAdj, onFinishOneBox) { + let i = 0; + while (true) { + while (i + 1 < anns.length && !(anns[i] && !anns[i + 1])) { + i++; + } + + let start = i; + i += 2; + + while (i < anns.length && !anns[i]) { + i++; + } + + if (i < anns.length) { + let end = i; + // insert (begin, end) + let interpolate_step = annMath.div( + annMath.sub(anns[end], anns[start]), + end - start + ); + + for (let inserti = start + 1; inserti < end; inserti++) { + let tempAnn = annMath.add(anns[inserti - 1], interpolate_step); + + if (autoAdj) { + try { + let adjustedAnn = await autoAdj(inserti, tempAnn); + + let adjustedYaw = annMath.normAngle(adjustedAnn[5] - tempAnn[5]); + + if (Math.abs(adjustedYaw) > Math.PI / 2) { + console.log("adjust angle by Math.PI."); + adjustedAnn[5] = annMath.normAngle(adjustedAnn[5] + Math.PI); + } + + if (!pointsGlobalConfig.enableAutoRotateXY) { + // adjustedAnn[3] = tempAnn[3]; + // adjustedAnn[4] = tempAnn[4]; + adjustedAnn[3] = 0; + adjustedAnn[4] = 0; + } + + tempAnn = adjustedAnn; + } catch (e) { + console.log(e); } - }) - .catch(reject=>{ - console.log("error predicting yaw angle!"); - }); - - }, + // + } - // autoadj is async - interpolate_annotation: async function(anns, autoAdj, onFinishOneBox){ - - let i = 0; - while(true){ - while (i+1 < anns.length && !(anns[i] && !anns[i+1])){ - i++; - } + anns[inserti] = tempAnn; - let start = i; - i+=2; + // adjust step since we have finished annotate one more box. + interpolate_step = annMath.div( + annMath.sub(anns[end], anns[inserti]), + end - inserti + ); - while (i < anns.length && !anns[i]){ - i++; - } - - if (i < anns.length){ - let end = i; - // insert (begin, end) - let interpolate_step = annMath.div(annMath.sub(anns[end], anns[start]), (end-start)); - - for (let inserti=start+1; inserti Math.PI/2) - { - console.log("adjust angle by Math.PI."); - adjustedAnn[5] = annMath.normAngle(adjustedAnn[5] + Math.PI); - } - - if (!pointsGlobalConfig.enableAutoRotateXY) - { - // adjustedAnn[3] = tempAnn[3]; - // adjustedAnn[4] = tempAnn[4]; - adjustedAnn[3] = 0; - adjustedAnn[4] = 0; - - } - - tempAnn = adjustedAnn; - } - catch (e) - { - console.log(e); - } - // - } - - anns[inserti] = tempAnn; - - // adjust step since we have finished annotate one more box. - interpolate_step = annMath.div(annMath.sub(anns[end], anns[inserti]), (end-inserti)); - - if (onFinishOneBox) - onFinishOneBox(inserti); - } - }else{ - break; - } + if (onFinishOneBox) onFinishOneBox(inserti); } + } else { + break; + } + } - // interpolate finished - - - //forward - i = 0; - while (i < anns.length && !anns[i]) - i++; - - if (i < anns.length){ - let filter = new MaFilter(anns[i]); - i++; - - while (i < anns.length && anns[i]){ - filter.update(anns[i]); - i++; - } - - while (i < anns.length && !anns[i]){ - let tempAnn = filter.predict(); + // interpolate finished - if (autoAdj){ - try { - let adjustedAnn = await autoAdj(i, tempAnn); + //forward + i = 0; + while (i < anns.length && !anns[i]) i++; - let adjustedYaw = annMath.normAngle(adjustedAnn[5] - tempAnn[5]); + if (i < anns.length) { + let filter = new MaFilter(anns[i]); + i++; - if (Math.abs(adjustedYaw) > Math.PI/2) - { - console.log("adjust angle by Math.PI."); - adjustedAnn[5] = annMath.normAngle(adjustedAnn[5] + Math.PI); - } + while (i < anns.length && anns[i]) { + filter.update(anns[i]); + i++; + } - tempAnn = adjustedAnn; + while (i < anns.length && !anns[i]) { + let tempAnn = filter.predict(); - filter.update(tempAnn); - } catch (error) { - console.log(error); - filter.nextStep(tempAnn); - } - - } - else{ - filter.nextStep(tempAnn); - } + if (autoAdj) { + try { + let adjustedAnn = await autoAdj(i, tempAnn); - anns[i] = tempAnn; - // we should update - if (onFinishOneBox) - onFinishOneBox(i); + let adjustedYaw = annMath.normAngle(adjustedAnn[5] - tempAnn[5]); - i++; + if (Math.abs(adjustedYaw) > Math.PI / 2) { + console.log("adjust angle by Math.PI."); + adjustedAnn[5] = annMath.normAngle(adjustedAnn[5] + Math.PI); } - } - // now extrapolate - - //backward - i = anns.length-1; - while (i >= 0 && !anns[i]) - i--; - - if (i >= 0){ - let filter = new MaFilter(anns[i]); - i--; - while (i >= 0 && anns[i]){ - filter.update(anns[i]); - i--; - } + tempAnn = adjustedAnn; - while (i >= 0 && !anns[i]){ - let tempAnn = filter.predict(); - if (autoAdj){ - let adjustedAnn = await autoAdj(i, tempAnn).catch(e=>{ - logger.log(e); - return tempAnn; - }); + filter.update(tempAnn); + } catch (error) { + console.log(error); + filter.nextStep(tempAnn); + } + } else { + filter.nextStep(tempAnn); + } - let adjustedYaw = annMath.normAngle(adjustedAnn[5] - tempAnn[5]); + anns[i] = tempAnn; + // we should update + if (onFinishOneBox) onFinishOneBox(i); - if (Math.abs(adjustedYaw) > Math.PI/2) - { - console.log("adjust angle by Math.PI."); - adjustedAnn[5] = annMath.normAngle(adjustedAnn[5] + Math.PI); - } + i++; + } + } + // now extrapolate - tempAnn = adjustedAnn; + //backward + i = anns.length - 1; + while (i >= 0 && !anns[i]) i--; + if (i >= 0) { + let filter = new MaFilter(anns[i]); + i--; - filter.update(tempAnn); - } - else{ - filter.nextStep(tempAnn); - } + while (i >= 0 && anns[i]) { + filter.update(anns[i]); + i--; + } - anns[i] = tempAnn; - if (onFinishOneBox) - onFinishOneBox(i); - i--; - } - } + while (i >= 0 && !anns[i]) { + let tempAnn = filter.predict(); + if (autoAdj) { + let adjustedAnn = await autoAdj(i, tempAnn).catch((e) => { + logger.log(e); + return tempAnn; + }); - return anns; - }, - - -} + let adjustedYaw = annMath.normAngle(adjustedAnn[5] - tempAnn[5]); + if (Math.abs(adjustedYaw) > Math.PI / 2) { + console.log("adjust angle by Math.PI."); + adjustedAnn[5] = annMath.normAngle(adjustedAnn[5] + Math.PI); + } -function MaFilter_tf(initX){ // moving average filter - this.x = tf.tensor1d(initX); // pose - this.step = 0; - - this.v = tf.zeros([9]); // velocity - this.decay = tf.tensor1d([0.7, 0.7, 0.7, - 0.7, 0.7, 0.7, - 0.7, 0.7, 0.7]) + tempAnn = adjustedAnn; - this.update = function(x){ - if (this.step == 0){ - this.v = tf.sub(x, this.x); + filter.update(tempAnn); } else { - this.v = tf.add(tf.mul(tf.sub(x, this.x), this.decay), - tf.mul(this.v, tf.sub(1, this.decay))); + filter.nextStep(tempAnn); } - this.x = x; - this.step++; - }; - - this.predict = function(){ - let pred = tf.concat([tf.add(this.x, this.v).slice(0,6), this.x.slice(6)]); - return pred.dataSync(); - }; - - this.nextStep = function(x){ - this.x = x; - this.step++; - }; + anns[i] = tempAnn; + if (onFinishOneBox) onFinishOneBox(i); + i--; + } + } -} + return anns; + }, +}; +function MaFilter_tf(initX) { + // moving average filter + this.x = tf.tensor1d(initX); // pose + this.step = 0; + + this.v = tf.zeros([9]); // velocity + this.decay = tf.tensor1d([0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]); + + this.update = function (x) { + if (this.step == 0) { + this.v = tf.sub(x, this.x); + } else { + this.v = tf.add( + tf.mul(tf.sub(x, this.x), this.decay), + tf.mul(this.v, tf.sub(1, this.decay)) + ); + } + this.x = x; + this.step++; + }; -function MaFilter(initX){ // moving average filter - this.x = initX; // pose - this.step = 0; - - this.v = [0,0,0, 0,0,0, 0,0,0]; // velocity - this.ones = [1,1,1, 1,1,1, 1,1,1]; - this.decay = [0.5, 0.5, 0.5, - 0.5, 0.5, 0.5, - 0.5, 0.5, 0.5]; + this.predict = function () { + let pred = tf.concat([tf.add(this.x, this.v).slice(0, 6), this.x.slice(6)]); + return pred.dataSync(); + }; - this.update = function(x){ - if (this.step == 0){ - this.v = annMath.sub(x, this.x); - } else { - this.v = annMath.add(annMath.eleMul(annMath.sub(x, this.x), this.decay), - annMath.eleMul(this.v, annMath.sub(this.ones, this.decay))); - } + this.nextStep = function (x) { + this.x = x; + this.step++; + }; +} - this.x = x; - this.step++; - }; +function MaFilter(initX) { + // moving average filter + this.x = initX; // pose + this.step = 0; + + this.v = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // velocity + this.ones = [1, 1, 1, 1, 1, 1, 1, 1, 1]; + this.decay = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]; + + this.update = function (x) { + if (this.step == 0) { + this.v = annMath.sub(x, this.x); + } else { + this.v = annMath.add( + annMath.eleMul(annMath.sub(x, this.x), this.decay), + annMath.eleMul(this.v, annMath.sub(this.ones, this.decay)) + ); + } - this.predict = function(){ - let pred = [...annMath.add(this.x, this.v).slice(0,6), ...this.x.slice(6)]; - return pred; - }; + this.x = x; + this.step++; + }; - this.nextStep = function(x){ - this.x = x; - this.step++; - }; + this.predict = function () { + let pred = [...annMath.add(this.x, this.v).slice(0, 6), ...this.x.slice(6)]; + return pred; + }; + this.nextStep = function (x) { + this.x = x; + this.step++; + }; } -export {ml, MaFilter}; \ No newline at end of file +export { ml, MaFilter }; diff --git a/public/js/mouse.js b/public/js/mouse.js index 62b8ef0..8862169 100644 --- a/public/js/mouse.js +++ b/public/js/mouse.js @@ -1,221 +1,222 @@ - -import * as THREE from './lib/three.module.js'; - - - -function Mouse(view, op_state, mainui_container, parentUi, on_left_click, on_right_click, on_select_rect){ - this.view=view; - this.domElement = mainui_container; - this.parentUi = parentUi; - this.operation_state = op_state; - - - this.domElement.addEventListener( 'mousemove', (e)=>{this.onMouseMove(e);}, false ); - this.domElement.addEventListener( 'mousedown', (e)=>{this.onMouseDown(e);}, true ); - - - - this.raycaster = new THREE.Raycaster(); - this.onDownPosition = new THREE.Vector2(); - this.onUpPosition = new THREE.Vector2(); - - - this.handleLeftClick = on_left_click; - this.handleRightClick = on_right_click; - this.handleSelectRect = on_select_rect; - - - var in_select_mode = false; - var select_start_pos; - var select_end_pos; - - this.get_mouse_location_in_world = function(){ - this.raycaster.setFromCamera( this.onUpPosition, this.view.camera ); - var o = this.raycaster.ray.origin; - var d = this.raycaster.ray.direction; - - var alpha = - o.z/d.z; - var x = o.x + d.x*alpha; - var y = o.y + d.y*alpha; - return {x:x, y:y, z:0}; - }; - - - this.get_screen_location_in_world = function(x,y){ - var screen_pos = new THREE.Vector2(); - screen_pos.x = x; - screen_pos.y = y; - - this.raycaster.setFromCamera( screen_pos, this.view.camera ); - var o = this.raycaster.ray.origin; - var d = this.raycaster.ray.direction; - - var alpha = - o.z/d.z; - var x = o.x + d.x*alpha; - var y = o.y + d.y*alpha; - return {x:x, y:y, z:0}; - }; - - - - - this.getMousePosition = function( dom, offsetX, offsetY ) { - - - return [offsetX/dom.clientWidth * 2 - 1, - offsetY/dom.clientHeight * 2 + 1]; - - }; - - this.getIntersects = function( point, objects ) { - - // mouse is temp var - let mouse = new THREE.Vector2(); - mouse.set(point.x, point.y); - - this.raycaster.setFromCamera( mouse, this.view.camera ); - - return this.raycaster.intersectObjects( objects, false ); // 2nd argument: recursive. - - }; - - - this.onMouseDown=function( event ) { - - in_select_mode = false; - - if (event.which==3){ - this.operation_state.key_pressed = false; - } else if (event.which == 1){ - console.log("mouse left key down!"); - if (event.ctrlKey || event.shiftKey){ - event.stopPropagation(); - event.preventDefault(); - - in_select_mode = true; - - select_start_pos={ - x: event.offsetX, - y: event.offsetY, - } - } - } - - var array = this.getMousePosition(this.domElement, event.offsetX, event.offsetY ); - this.onDownPosition.fromArray( array ); - console.log("mouse down", array); - - this.domElement.addEventListener( 'mouseup', on_mouse_up, false ); - - } - - this.onMouseMove=function( event ) { +import * as THREE from "./lib/three.module.js"; + +function Mouse( + view, + op_state, + mainui_container, + parentUi, + on_left_click, + on_right_click, + on_select_rect +) { + this.view = view; + this.domElement = mainui_container; + this.parentUi = parentUi; + this.operation_state = op_state; + + this.domElement.addEventListener( + "mousemove", + (e) => { + this.onMouseMove(e); + }, + false + ); + this.domElement.addEventListener( + "mousedown", + (e) => { + this.onMouseDown(e); + }, + true + ); + + this.raycaster = new THREE.Raycaster(); + this.onDownPosition = new THREE.Vector2(); + this.onUpPosition = new THREE.Vector2(); + + this.handleLeftClick = on_left_click; + this.handleRightClick = on_right_click; + this.handleSelectRect = on_select_rect; + + var in_select_mode = false; + var select_start_pos; + var select_end_pos; + + this.get_mouse_location_in_world = function () { + this.raycaster.setFromCamera(this.onUpPosition, this.view.camera); + var o = this.raycaster.ray.origin; + var d = this.raycaster.ray.direction; + + var alpha = -o.z / d.z; + var x = o.x + d.x * alpha; + var y = o.y + d.y * alpha; + return { x: x, y: y, z: 0 }; + }; + + this.get_screen_location_in_world = function (x, y) { + var screen_pos = new THREE.Vector2(); + screen_pos.x = x; + screen_pos.y = y; + + this.raycaster.setFromCamera(screen_pos, this.view.camera); + var o = this.raycaster.ray.origin; + var d = this.raycaster.ray.direction; + + var alpha = -o.z / d.z; + var x = o.x + d.x * alpha; + var y = o.y + d.y * alpha; + return { x: x, y: y, z: 0 }; + }; + + this.getMousePosition = function (dom, offsetX, offsetY) { + return [ + (offsetX / dom.clientWidth) * 2 - 1, + (-offsetY / dom.clientHeight) * 2 + 1, + ]; + }; + + this.getIntersects = function (point, objects) { + // mouse is temp var + let mouse = new THREE.Vector2(); + mouse.set(point.x, point.y); + + this.raycaster.setFromCamera(mouse, this.view.camera); + + return this.raycaster.intersectObjects(objects, false); // 2nd argument: recursive. + }; + + this.onMouseDown = function (event) { + in_select_mode = false; + + if (event.which == 3) { + this.operation_state.key_pressed = false; + } else if (event.which == 1) { + console.log("mouse left key down!"); + if (event.ctrlKey || event.shiftKey) { + event.stopPropagation(); event.preventDefault(); - //console.log(this.getMousePosition(this.domElement, event.offsetX, event.offsetY)); + in_select_mode = true; - if (in_select_mode){ - - select_end_pos={ - x: event.offsetX, - y: event.offsetY, - }; - - - if (event.offsetX != select_start_pos.x || event.offsetY != select_end_pos.y){ - //draw select box - var sbox = this.parentUi.querySelector("#select-box"); - - sbox.style.display="inherit"; - - + select_start_pos = { + x: event.offsetX, + y: event.offsetY, + }; + } + } - if (select_start_pos.x < select_end_pos.x){ - sbox.style.left = select_start_pos.x + 'px'; - sbox.style.width = select_end_pos.x - select_start_pos.x + 'px'; - }else { - sbox.style.left = select_end_pos.x + 'px'; - sbox.style.width = -select_end_pos.x + select_start_pos.x + 'px'; - } + var array = this.getMousePosition( + this.domElement, + event.offsetX, + event.offsetY + ); + this.onDownPosition.fromArray(array); + console.log("mouse down", array); + + this.domElement.addEventListener("mouseup", on_mouse_up, false); + }; + + this.onMouseMove = function (event) { + event.preventDefault(); + + //console.log(this.getMousePosition(this.domElement, event.offsetX, event.offsetY)); + + if (in_select_mode) { + select_end_pos = { + x: event.offsetX, + y: event.offsetY, + }; + + if ( + event.offsetX != select_start_pos.x || + event.offsetY != select_end_pos.y + ) { + //draw select box + var sbox = this.parentUi.querySelector("#select-box"); + + sbox.style.display = "inherit"; + + if (select_start_pos.x < select_end_pos.x) { + sbox.style.left = select_start_pos.x + "px"; + sbox.style.width = select_end_pos.x - select_start_pos.x + "px"; + } else { + sbox.style.left = select_end_pos.x + "px"; + sbox.style.width = -select_end_pos.x + select_start_pos.x + "px"; + } - if (select_start_pos.y < select_end_pos.y){ - sbox.style.top = select_start_pos.y + 'px'; - sbox.style.height = select_end_pos.y - select_start_pos.y + 'px'; - }else { - sbox.style.top = select_end_pos.y + 'px'; - sbox.style.height = -select_end_pos.y + select_start_pos.y + 'px'; - } - } + if (select_start_pos.y < select_end_pos.y) { + sbox.style.top = select_start_pos.y + "px"; + sbox.style.height = select_end_pos.y - select_start_pos.y + "px"; + } else { + sbox.style.top = select_end_pos.y + "px"; + sbox.style.height = -select_end_pos.y + select_start_pos.y + "px"; } - + } } + }; + + var on_mouse_up = (e) => { + this.onMouseUp(e); + }; + + this.onMouseUp = function (event) { + this.domElement.removeEventListener("mouseup", on_mouse_up, false); + + var array = this.getMousePosition( + this.domElement, + event.offsetX, + event.offsetY + ); + this.onUpPosition.fromArray(array); + + console.log("mouse up", array); + + if (this.onDownPosition.distanceTo(this.onUpPosition) === 0) { + if (event.which == 3) { + //right click + // if no other key pressed, we consider this as a right click + if (!this.operation_state.key_pressed) { + console.log("right clicked."); + this.handleRightClick(event); + } + } else { + // left click + this.handleLeftClick(event); + } - var on_mouse_up = (e)=>{this.onMouseUp(e)}; - - this.onMouseUp=function( event ) { - this.domElement.removeEventListener( 'mouseup', on_mouse_up, false ); - - + in_select_mode = false; + return; + } - - var array = this.getMousePosition(this.domElement, event.offsetX, event.offsetY ); - this.onUpPosition.fromArray( array ); + if (in_select_mode) { + in_select_mode = false; - console.log("mouse up", array); + var sbox = this.parentUi.querySelector("#select-box"); + sbox.style.display = "none"; - if ( this.onDownPosition.distanceTo( this.onUpPosition ) === 0 ) { - if (event.which == 3){ - //right click - // if no other key pressed, we consider this as a right click - if (!this.operation_state.key_pressed){ - console.log("right clicked."); - this.handleRightClick(event); - } - } - else{ - // left click - this.handleLeftClick(event); - } + if (this.handleSelectRect) { + var x, y, w, h; - in_select_mode = false; - return; + if (this.onDownPosition.x < this.onUpPosition.x) { + x = this.onDownPosition.x; + w = this.onUpPosition.x - this.onDownPosition.x; + } else { + x = this.onUpPosition.x; + w = this.onDownPosition.x - this.onUpPosition.x; } - - - if (in_select_mode){ - in_select_mode = false; - - - var sbox = this.parentUi.querySelector("#select-box"); - sbox.style.display="none"; - - if (this.handleSelectRect){ - var x,y,w,h; - - if (this.onDownPosition.x < this.onUpPosition.x){ - x = this.onDownPosition.x; - w = this.onUpPosition.x - this.onDownPosition.x; - } - else{ - x = this.onUpPosition.x; - w = this.onDownPosition.x - this.onUpPosition.x; - } - - if (this.onDownPosition.y < this.onUpPosition.y){ - y = this.onDownPosition.y; - h = this.onUpPosition.y - this.onDownPosition.y; - } - else{ - y = this.onUpPosition.y; - h = this.onDownPosition.y - this.onUpPosition.y; - } - - console.log("select rect",x,y,w,h); - this.handleSelectRect(x,y,w,h, event.ctrlKey, event.shiftKey); - } + + if (this.onDownPosition.y < this.onUpPosition.y) { + y = this.onDownPosition.y; + h = this.onUpPosition.y - this.onDownPosition.y; + } else { + y = this.onUpPosition.y; + h = this.onDownPosition.y - this.onUpPosition.y; } + console.log("select rect", x, y, w, h); + this.handleSelectRect(x, y, w, h, event.ctrlKey, event.shiftKey); + } } - + }; } -export{Mouse} \ No newline at end of file +export { Mouse }; diff --git a/public/js/obj_cfg.js b/public/js/obj_cfg.js index 1b00222..a3e8f95 100644 --- a/public/js/obj_cfg.js +++ b/public/js/obj_cfg.js @@ -1,172 +1,190 @@ // size is the dimension of the object in x/y/z axis, with unit meter. -class ObjectCategory -{ - - - obj_type_map = { - Car: {color: '#86af49', size:[4.5, 1.8, 1.5], attr:["door open", "trunk open"]}, - Pedestrian: {color: '#ff0000', size:[0.4, 0.5, 1.7], attr:["umbrella", "sitting", "squating", "bending over", "luggage"]}, - Van: {color: '#00ff00', size:[4.5, 1.8, 1.5], attr:["door open", "trunk open"]}, - Bus: {color: '#ffff00', size:[13, 3, 3.5]}, - Truck: {color: '#00ffff', size:[10., 2.8, 3]}, - - ScooterRider: {color: '#ff8800', size:[1.6, 0.6, 1.6], attr:["umbrella", "1 passenger", "2 passengers", "3 passengers"]}, - Scooter: {color: '#aaaa00', size:[1.6, 0.6, 1.0]}, - - - BicycleRider: {color: '#88ff00', size:[1.6, 0.6, 1.7], attr:["umbrella", "1 passenger", "2 passengers", "3 passengers"]}, - Bicycle: {color: '#ff8800', size:[1.6, 0.6, 1.2], attr:["laying down"]}, - - - Motorcycle: {color: '#aaaa00', size:[1.6, 0.6, 1.2], attr:["umbrella"]}, - MotorcyleRider: {color: '#ff8800', size:[1.6, 0.6, 1.6], attr:["umbrella", "1 passenger", "2 passengers", "3 passengers"]}, - - - - PoliceCar: {color: '#86af49', size:[4.5, 1.8, 1.5]}, - TourCar: {color: '#86af49', size:[4.4, 1.5, 2.2]}, - - RoadWorker: {color: '#ff0000', size:[0.4, 0.5, 1.7]}, - Child: {color: '#ff0000', size:[0.4, 0.5, 1.2]}, - - //Crowd: {color: '#ff0000', size:[1.6, 0.6, 1.2]}, - - BabyCart: {color: '#ff0000', size:[0.8, 0.5, 1.0]}, - Cart: {color: '#ff0000', size:[0.8, 0.5, 1.0]}, - Cone: {color: '#ff0000', size:[0.3, 0.3, 0.6]}, - FireHydrant: {color: '#ff0000', size:[0.4, 0.4, 0.6]}, - SaftyTriangle: {color: '#ff0000', size:[0.3, 0.4, 0.4]}, - PlatformCart: {color: '#ff0000', size:[1.2, 0.8, 1.0]}, - ConstructionCart: {color: '#ff0000', size:[1.2, 0.8, 1.0]}, - RoadBarrel: {color: '#ff0000', size:[0.5, 0.5, 0.6]}, - TrafficBarrier: {color: '#ff0000', size:[1.5, 0.3, 1.2]}, - LongVehicle: {color: '#ff0000', size:[16, 3, 3]}, - - - BicycleGroup: {color: '#ff0000', size:[1.6, 0.6, 1.2]}, - - - ConcreteTruck: {color: '#00ffff', size:[10., 2.8, 3]}, - Tram: {color: '#00ffff', size:[10., 2.8, 3]}, - Excavator: {color: '#00ffff', size:[6., 3, 3]}, - - Animal: {color: '#00aaff', size:[1.6, 0.6, 1.2]}, - - TrashCan: {color: '#00aaff', size:[0.6, 0.4, 1.0]}, - - ForkLift: {color: '#00aaff', size:[5.0, 1.2, 2.0]}, - Trimotorcycle: {color: '#00aaff', size:[2.6, 1.0, 1.6]}, - FreightTricycle: {color: '#00aaff', size:[2.6, 1.0, 1.6]}, - Crane: {color: '#00aaff', size:[5.0, 1.2, 2.0]}, - RoadRoller: {color: '#00aaff', size:[2.7, 1.5, 2.0]}, - Bulldozer: {color: '#00aaff', size:[3.0, 2.0, 2.0]}, - - DontCare: {color: '#00ff88', size:[4, 4, 3]}, - Misc: {color: '#008888', size:[4.5, 1.8, 1.5]}, - Unknown: {color: '#008888', size:[4.5, 1.8, 1.5]}, - Unknown1: {color: '#008888', size:[4.5, 1.8, 1.5]}, - Unknown2: {color: '#008888', size:[4.5, 1.8, 1.5]}, - Unknown3: {color: '#008888', size:[4.5, 1.8, 1.5]}, - Unknown4: {color: '#008888', size:[4.5, 1.8, 1.5]}, - Unknown5: {color: '#008888', size:[4.5, 1.8, 1.5]}, - }; - - - constructor(){ - - } - - popularCategories = ["Car", "Pedestrian", "Van", "Bus", "Truck", "Scooter", "ScooterRider", "Bicycle", "BicycleRider"]; - - guess_obj_type_by_dimension(scale){ - - var max_score = 0; - var max_name = 0; - this.popularCategories.forEach(i=>{ - var o = this.obj_type_map[i]; - var scorex = o.size[0]/scale.x; - var scorey = o.size[1]/scale.y; - var scorez = o.size[2]/scale.z; - - if (scorex>1) scorex = 1/scorex; - if (scorey>1) scorey = 1/scorey; - if (scorez>1) scorez = 1/scorez; - - if (scorex + scorey + scorez > max_score){ - max_score = scorex + scorey + scorez; - max_name = i; - } - }); - - console.log("guess type", max_name); - return max_name; +class ObjectCategory { + obj_type_map = { + Car: { + color: "#86af49", + size: [4.5, 1.8, 1.5], + attr: ["door open", "trunk open"], + }, + Pedestrian: { + color: "#ff0000", + size: [0.4, 0.5, 1.7], + attr: ["umbrella", "sitting", "squating", "bending over", "luggage"], + }, + Van: { + color: "#00ff00", + size: [4.5, 1.8, 1.5], + attr: ["door open", "trunk open"], + }, + Bus: { color: "#ffff00", size: [13, 3, 3.5] }, + Truck: { color: "#00ffff", size: [10, 2.8, 3] }, + + ScooterRider: { + color: "#ff8800", + size: [1.6, 0.6, 1.6], + attr: ["umbrella", "1 passenger", "2 passengers", "3 passengers"], + }, + Scooter: { color: "#aaaa00", size: [1.6, 0.6, 1.0] }, + + BicycleRider: { + color: "#88ff00", + size: [1.6, 0.6, 1.7], + attr: ["umbrella", "1 passenger", "2 passengers", "3 passengers"], + }, + Bicycle: { color: "#ff8800", size: [1.6, 0.6, 1.2], attr: ["laying down"] }, + + Motorcycle: { color: "#aaaa00", size: [1.6, 0.6, 1.2], attr: ["umbrella"] }, + MotorcyleRider: { + color: "#ff8800", + size: [1.6, 0.6, 1.6], + attr: ["umbrella", "1 passenger", "2 passengers", "3 passengers"], + }, + + PoliceCar: { color: "#86af49", size: [4.5, 1.8, 1.5] }, + TourCar: { color: "#86af49", size: [4.4, 1.5, 2.2] }, + + RoadWorker: { color: "#ff0000", size: [0.4, 0.5, 1.7] }, + Child: { color: "#ff0000", size: [0.4, 0.5, 1.2] }, + + //Crowd: {color: '#ff0000', size:[1.6, 0.6, 1.2]}, + + BabyCart: { color: "#ff0000", size: [0.8, 0.5, 1.0] }, + Cart: { color: "#ff0000", size: [0.8, 0.5, 1.0] }, + Cone: { color: "#ff0000", size: [0.3, 0.3, 0.6] }, + FireHydrant: { color: "#ff0000", size: [0.4, 0.4, 0.6] }, + SaftyTriangle: { color: "#ff0000", size: [0.3, 0.4, 0.4] }, + PlatformCart: { color: "#ff0000", size: [1.2, 0.8, 1.0] }, + ConstructionCart: { color: "#ff0000", size: [1.2, 0.8, 1.0] }, + RoadBarrel: { color: "#ff0000", size: [0.5, 0.5, 0.6] }, + TrafficBarrier: { color: "#ff0000", size: [1.5, 0.3, 1.2] }, + LongVehicle: { color: "#ff0000", size: [16, 3, 3] }, + + BicycleGroup: { color: "#ff0000", size: [1.6, 0.6, 1.2] }, + + ConcreteTruck: { color: "#00ffff", size: [10, 2.8, 3] }, + Tram: { color: "#00ffff", size: [10, 2.8, 3] }, + Excavator: { color: "#00ffff", size: [6, 3, 3] }, + + Animal: { color: "#00aaff", size: [1.6, 0.6, 1.2] }, + + TrashCan: { color: "#00aaff", size: [0.6, 0.4, 1.0] }, + + ForkLift: { color: "#00aaff", size: [5.0, 1.2, 2.0] }, + Trimotorcycle: { color: "#00aaff", size: [2.6, 1.0, 1.6] }, + FreightTricycle: { color: "#00aaff", size: [2.6, 1.0, 1.6] }, + Crane: { color: "#00aaff", size: [5.0, 1.2, 2.0] }, + RoadRoller: { color: "#00aaff", size: [2.7, 1.5, 2.0] }, + Bulldozer: { color: "#00aaff", size: [3.0, 2.0, 2.0] }, + + DontCare: { color: "#00ff88", size: [4, 4, 3] }, + Misc: { color: "#008888", size: [4.5, 1.8, 1.5] }, + Unknown: { color: "#008888", size: [4.5, 1.8, 1.5] }, + Unknown1: { color: "#008888", size: [4.5, 1.8, 1.5] }, + Unknown2: { color: "#008888", size: [4.5, 1.8, 1.5] }, + Unknown3: { color: "#008888", size: [4.5, 1.8, 1.5] }, + Unknown4: { color: "#008888", size: [4.5, 1.8, 1.5] }, + Unknown5: { color: "#008888", size: [4.5, 1.8, 1.5] }, + }; + + constructor() {} + + popularCategories = [ + "Car", + "Pedestrian", + "Van", + "Bus", + "Truck", + "Scooter", + "ScooterRider", + "Bicycle", + "BicycleRider", + ]; + + guess_obj_type_by_dimension(scale) { + var max_score = 0; + var max_name = 0; + this.popularCategories.forEach((i) => { + var o = this.obj_type_map[i]; + var scorex = o.size[0] / scale.x; + var scorey = o.size[1] / scale.y; + var scorez = o.size[2] / scale.z; + + if (scorex > 1) scorex = 1 / scorex; + if (scorey > 1) scorey = 1 / scorey; + if (scorez > 1) scorez = 1 / scorez; + + if (scorex + scorey + scorez > max_score) { + max_score = scorex + scorey + scorez; + max_name = i; + } + }); + + console.log("guess type", max_name); + return max_name; + } + + global_color_idx = 0; + get_color_by_id(id) { + let idx = parseInt(id); + + if (!idx) { + idx = this.global_color_idx; + this.global_color_idx += 1; } - global_color_idx = 0; - get_color_by_id(id){ - let idx = parseInt(id); + idx %= 33; + idx = (idx * 19) % 33; - if (!idx) - { - idx = this.global_color_idx; - this.global_color_idx += 1; - } + return { + x: (idx * 8) / 256.0, + y: 1 - (idx * 8) / 256.0, + z: idx < 16 ? (idx * 2 * 8) / 256.0 : ((32 - idx) * 2 * 8) / 256.0, + }; + } - idx %= 33; - idx = idx*19 % 33; + get_color_by_category(category) { + let target_color_hex = parseInt( + "0x" + this.get_obj_cfg_by_type(category).color.slice(1) + ); - return { - x: idx*8/256.0, - y: 1- idx*8/256.0, - z: (idx<16)?(idx*2*8/256.0):((32-idx)*2*8/256.0), - }; - } - - get_color_by_category(category){ - let target_color_hex = parseInt("0x"+this.get_obj_cfg_by_type(category).color.slice(1)); - - return { - x: (target_color_hex/256/256)/255.0, - y: (target_color_hex/256 % 256)/255.0, - z: (target_color_hex % 256)/255.0, - }; - } + return { + x: target_color_hex / 256 / 256 / 255.0, + y: ((target_color_hex / 256) % 256) / 255.0, + z: (target_color_hex % 256) / 255.0, + }; + } - get_obj_cfg_by_type(name){ - if (this.obj_type_map[name]){ - return this.obj_type_map[name]; - } - else{ - return this.obj_type_map["Unknown"]; - } + get_obj_cfg_by_type(name) { + if (this.obj_type_map[name]) { + return this.obj_type_map[name]; + } else { + return this.obj_type_map["Unknown"]; } + } - // name_array = [] - - // build_name_array(){ - // for (var n in this.obj_type_map){ - // name_array.push(n); - // } - // } + // name_array = [] + // build_name_array(){ + // for (var n in this.obj_type_map){ + // name_array.push(n); + // } + // } - // get_next_obj_type_name(name){ + // get_next_obj_type_name(name){ - // if (name_array.length == 0) { - // build_name_array(); - // } + // if (name_array.length == 0) { + // build_name_array(); + // } - // var idx = name_array.findIndex(function(n){return n==name;}) - // idx+=1; - // idx %= name_array.length; - - // return name_array[idx]; - // } + // var idx = name_array.findIndex(function(n){return n==name;}) + // idx+=1; + // idx %= name_array.length; + // return name_array[idx]; + // } } - let globalObjectCategory = new ObjectCategory(); -export {globalObjectCategory}; \ No newline at end of file +export { globalObjectCategory }; diff --git a/public/js/obj_id_list.js b/public/js/obj_id_list.js index e992751..fabfeea 100644 --- a/public/js/obj_id_list.js +++ b/public/js/obj_id_list.js @@ -1,117 +1,119 @@ - - - -class ObjectIdManager -{ - maxId = 1; - objectList = []; - //todo: should use all worldlist - generateNewUniqueId(world){ - this.maxId += 1; - return this.maxId; - } - - scene = ""; - setCurrentScene(scene, done) - { - if (scene != this.scene) - { - this.scene = scene; - this.load_obj_ids_of_scene(scene, done); - } - } - - forceUpdate(done) - { - this.load_obj_ids_of_scene(this.scene, done); - } - - // should just tell editor - // don't change html elements directly. - setObjdIdListOptions() - { - let objSelOptions = this.objectList.map(function(c){ - return ""; - }).reduce(function(x,y){return x+y;}, - ""); - document.getElementById("object-selector").innerHTML = objSelOptions; - - - let objIdsOptions = this.objectList.map(function(c){ - return ""; - }).reduce(function(x,y){return x+y;}, - //""); - //"" - "" - ); - - document.getElementById("obj-ids-of-scene").innerHTML = objIdsOptions; +class ObjectIdManager { + maxId = 1; + objectList = []; + //todo: should use all worldlist + generateNewUniqueId(world) { + this.maxId += 1; + return this.maxId; + } + + scene = ""; + setCurrentScene(scene, done) { + if (scene != this.scene) { + this.scene = scene; + this.load_obj_ids_of_scene(scene, done); } - - sortObjIdList() - { - this.objectList = this.objectList.sort(function(x, y){ - return parseInt(x.id) - parseInt(y.id); - }); - } - - // called when 1) new object 2) category/id modified - addObject(obj) - { - if (! this.objectList.find(x=>x.id == obj.id && x.category == obj.category)) - { - this.objectList.push(obj); - this.sortObjIdList(); - - this.setObjdIdListOptions(); - - if (obj.id > this.maxId) - { - this.maxId = parseInt(obj.id); - } - } - } - - load_obj_ids_of_scene(scene, done){ - - var xhr = new XMLHttpRequest(); - // we defined the xhr - let self =this; - - xhr.onreadystatechange = function() { - if (this.readyState != 4) - return; - - if (this.status == 200) { - var ret = JSON.parse(this.responseText); - - self.objectList = ret; - self.sortObjIdList(); - self.maxId = Math.max(...ret.map(function(x){return x.id;})); - if (self.maxId < 0) // this is -infinity if there is no ids. - self.maxId = 0; - - self.setObjdIdListOptions(); - - if (done) - done(ret) - } - - }; - - xhr.open('GET', "/objs_of_scene?scene="+scene, true); - xhr.send(); - } - - - getObjById(id) - { - return this.objectList.find(x=>x.id == id); + } + + forceUpdate(done) { + this.load_obj_ids_of_scene(this.scene, done); + } + + // should just tell editor + // don't change html elements directly. + setObjdIdListOptions() { + let objSelOptions = this.objectList + .map(function (c) { + return ( + "" + ); + }) + .reduce(function (x, y) { + return x + y; + }, ""); + document.getElementById("object-selector").innerHTML = objSelOptions; + + let objIdsOptions = this.objectList + .map(function (c) { + return ""; + }) + .reduce( + function (x, y) { + return x + y; + }, + //""); + //"" + "" + ); + + document.getElementById("obj-ids-of-scene").innerHTML = objIdsOptions; + } + + sortObjIdList() { + this.objectList = this.objectList.sort(function (x, y) { + return parseInt(x.id) - parseInt(y.id); + }); + } + + // called when 1) new object 2) category/id modified + addObject(obj) { + if ( + !this.objectList.find((x) => x.id == obj.id && x.category == obj.category) + ) { + this.objectList.push(obj); + this.sortObjIdList(); + + this.setObjdIdListOptions(); + + if (obj.id > this.maxId) { + this.maxId = parseInt(obj.id); + } } + } + + load_obj_ids_of_scene(scene, done) { + var xhr = new XMLHttpRequest(); + // we defined the xhr + let self = this; + + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + + if (this.status == 200) { + var ret = JSON.parse(this.responseText); + + self.objectList = ret; + self.sortObjIdList(); + self.maxId = Math.max( + ...ret.map(function (x) { + return x.id; + }) + ); + if (self.maxId < 0) + // this is -infinity if there is no ids. + self.maxId = 0; + + self.setObjdIdListOptions(); + + if (done) done(ret); + } + }; + + xhr.open("GET", "/objs_of_scene?scene=" + scene, true); + xhr.send(); + } + + getObjById(id) { + return this.objectList.find((x) => x.id == id); + } } - let objIdManager = new ObjectIdManager(); - -export {objIdManager}; \ No newline at end of file +export { objIdManager }; diff --git a/public/js/pcd_viewer.js b/public/js/pcd_viewer.js index 83b901b..c84acbb 100644 --- a/public/js/pcd_viewer.js +++ b/public/js/pcd_viewer.js @@ -1,192 +1,188 @@ -import * as THREE from './lib/three.module.js'; -import { PCDLoader } from './lib/PCDLoader.js'; -import { OrbitControls } from './lib/OrbitControls.js'; -import { SelectionBox } from './lib/SelectionBox.js'; -import { SelectionHelper } from './lib/SelectionHelper.js'; - - +import * as THREE from "./lib/three.module.js"; +import { PCDLoader } from "./lib/PCDLoader.js"; +import { OrbitControls } from "./lib/OrbitControls.js"; +import { SelectionBox } from "./lib/SelectionBox.js"; +import { SelectionHelper } from "./lib/SelectionHelper.js"; var container; var camera, controls, scene, renderer; var camera; - -var url_string = window.location.href +var url_string = window.location.href; var url = new URL(url_string); //language var pcd_file = url.searchParams.get("file"); - init(); animate(); function init() { - document.body.addEventListener('keydown', event => { - if (event.ctrlKey && 'asdv'.indexOf(event.key) !== -1) { - event.preventDefault() - } - }) - + document.body.addEventListener("keydown", (event) => { + if (event.ctrlKey && "asdv".indexOf(event.key) !== -1) { + event.preventDefault(); + } + }); - scene = new THREE.Scene(); + scene = new THREE.Scene(); + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + camera = new THREE.PerspectiveCamera( + 65, + window.innerWidth / window.innerHeight, + 1, + 800 + ); + camera.position.x = 0; + camera.position.z = 50; + camera.position.y = 0; + camera.up.set(0, 0, 1); + camera.lookAt(0, 0, 0); - renderer = new THREE.WebGLRenderer( { antialias: true } ); - renderer.setPixelRatio( window.devicePixelRatio ); + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener("change", render); // call this only in static scenes (i.e., if there is no animation loop) + container = document.createElement("container"); - - camera = new THREE.PerspectiveCamera( 65, window.innerWidth / window.innerHeight, 1, 800 ); - camera.position.x = 0; - camera.position.z = 50; - camera.position.y = 0; - camera.up.set( 0, 0, 1); - camera.lookAt( 0, 0, 0 ); + document.body.appendChild(container); + container.appendChild(renderer.domElement); - - controls = new OrbitControls( camera, renderer.domElement ); - controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop) - - container = document.createElement( 'container' ); - + document.addEventListener("keydown", keydown); - document.body.appendChild( container ); - container.appendChild( renderer.domElement ); + scene.add(new THREE.AxesHelper(2)); - document.addEventListener("keydown", keydown) + onWindowResize(); + window.addEventListener("resize", onWindowResize, false); - scene.add( new THREE.AxesHelper( 2 ) ); + load_all(); - onWindowResize(); - window.addEventListener( 'resize', onWindowResize, false ); - - load_all(); - - render(); - + render(); } -function load_all(){ - load_pcd("pcd", pcd_file, 0xff0000); +function load_all() { + load_pcd("pcd", pcd_file, 0xff0000); } - -function keydown( ev ) { - - switch ( ev.key) { - case '+': - clouds["src"].material.size *= 1.2; - clouds["tgt"].material.size *= 1.2; - clouds["out"].material.size *= 1.2; - break; - case '-': - clouds["src"].material.size /= 1.2; - clouds["tgt"].material.size /= 1.2; - clouds["out"].material.size /= 1.2; - - break; - } +function keydown(ev) { + switch (ev.key) { + case "+": + clouds["src"].material.size *= 1.2; + clouds["tgt"].material.size *= 1.2; + clouds["out"].material.size *= 1.2; + break; + case "-": + clouds["src"].material.size /= 1.2; + clouds["tgt"].material.size /= 1.2; + clouds["out"].material.size /= 1.2; + + break; + } } - function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize( window.innerWidth, window.innerHeight ); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { - requestAnimationFrame( animate ); - controls.update(); - render(); - + requestAnimationFrame(animate); + controls.update(); + render(); } - -function render(){ - renderer.render( scene, camera ); +function render() { + renderer.render(scene, camera); } +function load_pcd(name, file, overall_color) { + var loader = new PCDLoader(); + + loader.load(file, function (pcd) { + var position = pcd.position; + var color = pcd.color; + var normal = pcd.normal; + // build geometry + var geometry = new THREE.BufferGeometry(); + if (position.length > 0) + geometry.addAttribute( + "position", + new THREE.Float32BufferAttribute(position, 3) + ); + if (normal.length > 0) + geometry.addAttribute( + "normal", + new THREE.Float32BufferAttribute(normal, 3) + ); + if (color.length > 0) + geometry.addAttribute( + "color", + new THREE.Float32BufferAttribute(color, 3) + ); + + geometry.computeBoundingSphere(); + // build material + + var material = new THREE.PointsMaterial({ size: 0.005 }); + + if (color.length > 0) { + material.vertexColors = VertexColors; + } else { + material.color.setHex(overall_color); + } -function load_pcd(name, file, overall_color){ - var loader = new PCDLoader(); - - loader.load( file, - function ( pcd ) { - var position = pcd.position; - var color = pcd.color; - var normal = pcd.normal; - // build geometry - var geometry = new THREE.BufferGeometry(); - if ( position.length > 0 ) geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) ); - if ( normal.length > 0 ) geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normal, 3 ) ); - if ( color.length > 0 ) geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( color, 3 ) ); - - geometry.computeBoundingSphere(); - // build material - - var material = new THREE.PointsMaterial( { size: 0.005 } ); - - if ( color.length > 0 ) { - material.vertexColors = VertexColors; - } else { - material.color.setHex(overall_color ); - } - - //material.size = 0.1; + //material.size = 0.1; - // build mesh + // build mesh - var mesh = new THREE.Points( geometry, material ); - mesh.name = "pcd"; + var mesh = new THREE.Points(geometry, material); + mesh.name = "pcd"; - //return mesh; - - scene.add(mesh); + //return mesh; - - //var center = points.geometry.boundingSphere.center; - //controls.target.set( center.x, center.y, center.z ); - //controls.update(); - }, - ); + scene.add(mesh); + //var center = points.geometry.boundingSphere.center; + //controls.target.set( center.x, center.y, center.z ); + //controls.update(); + }); } - - -var selectionBox = new SelectionBox( camera, scene ); -var helper = new SelectionHelper( selectionBox, renderer, 'selectBox' ); -document.addEventListener( 'mousedown', function ( event ) { - for ( var item of selectionBox.collection ) { - item.material.emissive.set( 0x000000 ); +var selectionBox = new SelectionBox(camera, scene); +var helper = new SelectionHelper(selectionBox, renderer, "selectBox"); +document.addEventListener("mousedown", function (event) { + for (var item of selectionBox.collection) { + item.material.emissive.set(0x000000); + } + selectionBox.startPoint.set( + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + 0.5 + ); +}); +document.addEventListener("mousemove", function (event) { + if (helper.isDown) { + for (var i = 0; i < selectionBox.collection.length; i++) { + selectionBox.collection[i].material.emissive.set(0x000000); } - selectionBox.startPoint.set( - ( event.clientX / window.innerWidth ) * 2 - 1, - - ( event.clientY / window.innerHeight ) * 2 + 1, - 0.5 ); -} ); -document.addEventListener( 'mousemove', function ( event ) { - if ( helper.isDown ) { - for ( var i = 0; i < selectionBox.collection.length; i ++ ) { - selectionBox.collection[ i ].material.emissive.set( 0x000000 ); - } - selectionBox.endPoint.set( - ( event.clientX / window.innerWidth ) * 2 - 1, - - ( event.clientY / window.innerHeight ) * 2 + 1, - 0.5 ); - var allSelected = selectionBox.select(); - for ( var i = 0; i < allSelected.length; i ++ ) { - allSelected[ i ].material.emissive.set( 0xffffff ); - } - } -} ); -document.addEventListener( 'mouseup', function ( event ) { selectionBox.endPoint.set( - ( event.clientX / window.innerWidth ) * 2 - 1, - - ( event.clientY / window.innerHeight ) * 2 + 1, - 0.5 ); + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + 0.5 + ); var allSelected = selectionBox.select(); - for ( var i = 0; i < allSelected.length; i ++ ) { - allSelected[ i ].material.emissive.set( 0xffffff ); + for (var i = 0; i < allSelected.length; i++) { + allSelected[i].material.emissive.set(0xffffff); } -} ); \ No newline at end of file + } +}); +document.addEventListener("mouseup", function (event) { + selectionBox.endPoint.set( + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + 0.5 + ); + var allSelected = selectionBox.select(); + for (var i = 0; i < allSelected.length; i++) { + allSelected[i].material.emissive.set(0xffffff); + } +}); diff --git a/public/js/play.js b/public/js/play.js index 60370b3..1a2d844 100644 --- a/public/js/play.js +++ b/public/js/play.js @@ -1,155 +1,128 @@ - - - -function PlayControl(data){ - - this.data = data; - this.stop_play_flag=true; - this.pause_play_flag=false; - - this.pause_resume_play=function(){ - this.pause_play_flag=!this.pause_play_flag; - - if (!this.pause_play_flag && !this.stop_play_flag){ - this.play(this.on_load_world_finished); - } - }; - - - this.stop_play=function(){ - this.stop_play_flag=true; - this.pause_play_flag=false; - }; - - this.on_load_world_finished = null; - this.play=function(on_load_world_finished, fps=2){ - this.on_load_world_finished = on_load_world_finished; - - if (!this.data.meta){ - console.log("no meta data! cannot play"); - return; - } - - // if (this.stop_play_flag == false && !resume){ - // return; - // } - - this.stop_play_flag = false; - this.pause_play_flag = false; - - var scene_meta = data.world.sceneMeta; - - var scope=this; - - - - let start_frame = data.world.frameInfo.frame; - - let current_frame_index = scene_meta.frames.findIndex(function(x){return x == data.world.frameInfo.frame;}) - if (current_frame_index == scene_meta.frames.length-1) - { - //this is the last frmae - // we go to first frame. - start_frame = scene_meta.frames[0]; - } - - - play_frame(scene_meta, start_frame, on_load_world_finished); - - - async function play_frame(scene_meta, frame, on_load_world_finished){ - if (!scope.stop_play_flag && !scope.pause_play_flag) - { - var world = await scope.data.getWorld(scene_meta.scene, frame) - - if (world.preloaded()) //found, data ready - { - scope.data.activate_world( - world, - function(){//on load finished - //views[0].detach_control(); - on_load_world_finished(world); - - // play next frame - let frame_index = world.frameInfo.frame_index; - if (frame_index+1 < scene_meta.frames.length) - { - var next_frame = scene_meta.frames[frame_index+1]; - setTimeout( - function(){ - play_frame(scene_meta, next_frame, on_load_world_finished); - }, - 1000/fps); - } - else{ - scope.stop_play(); - } - - }); - - } - else{ - //not ready. - console.log("wait buffer!", frame); - - setTimeout( - function(){ - play_frame(scene_meta, frame, on_load_world_finished); - }, - 10); - } - - +function PlayControl(data) { + this.data = data; + this.stop_play_flag = true; + this.pause_play_flag = false; + + this.pause_resume_play = function () { + this.pause_play_flag = !this.pause_play_flag; + + if (!this.pause_play_flag && !this.stop_play_flag) { + this.play(this.on_load_world_finished); + } + }; + + this.stop_play = function () { + this.stop_play_flag = true; + this.pause_play_flag = false; + }; + + this.on_load_world_finished = null; + this.play = function (on_load_world_finished, fps = 2) { + this.on_load_world_finished = on_load_world_finished; + + if (!this.data.meta) { + console.log("no meta data! cannot play"); + return; + } + + // if (this.stop_play_flag == false && !resume){ + // return; + // } + + this.stop_play_flag = false; + this.pause_play_flag = false; + + var scene_meta = data.world.sceneMeta; + + var scope = this; + + let start_frame = data.world.frameInfo.frame; + + let current_frame_index = scene_meta.frames.findIndex(function (x) { + return x == data.world.frameInfo.frame; + }); + if (current_frame_index == scene_meta.frames.length - 1) { + //this is the last frmae + // we go to first frame. + start_frame = scene_meta.frames[0]; + } + + play_frame(scene_meta, start_frame, on_load_world_finished); + + async function play_frame(scene_meta, frame, on_load_world_finished) { + if (!scope.stop_play_flag && !scope.pause_play_flag) { + var world = await scope.data.getWorld(scene_meta.scene, frame); + + if (world.preloaded()) { + //found, data ready + scope.data.activate_world(world, function () { + //on load finished + //views[0].detach_control(); + on_load_world_finished(world); + + // play next frame + let frame_index = world.frameInfo.frame_index; + if (frame_index + 1 < scene_meta.frames.length) { + var next_frame = scene_meta.frames[frame_index + 1]; + setTimeout(function () { + play_frame(scene_meta, next_frame, on_load_world_finished); + }, 1000 / fps); + } else { + scope.stop_play(); } - }; - }; - - - -// function play_current_scene_without_buffer(){ - -// if (!data.meta){ -// console.log("no meta data! cannot play"); -// return; -// } - -// if (stop_play_flag== false){ -// return; -// } - -// stop_play_flag = false; - -// var scene_meta = data.get_current_world_scene_meta(); -// var sceneName= scene_meta.scene; - -// play_frame(scene_meta, data.world.frameInfo.frame); - - -// function play_frame(scene_meta, frame){ -// load_world(sceneName, frame); - - -// if (!stop_play_flag) -// { -// var frame_index = scene_meta.frames.findIndex(function(x){return x == frame;}); -// if (frame_index+1 < scene_meta.frames.length) -// { -// next_frame = scene_meta.frames[frame_index+1]; -// setTimeout( -// function(){ -// play_frame(scene_meta, next_frame); -// }, -// 100); -// } -// else{ -// stop_play_flag = true; -// } - -// } -// }; -// } - + }); + } else { + //not ready. + console.log("wait buffer!", frame); + + setTimeout(function () { + play_frame(scene_meta, frame, on_load_world_finished); + }, 10); + } + } + } + }; + + // function play_current_scene_without_buffer(){ + + // if (!data.meta){ + // console.log("no meta data! cannot play"); + // return; + // } + + // if (stop_play_flag== false){ + // return; + // } + + // stop_play_flag = false; + + // var scene_meta = data.get_current_world_scene_meta(); + // var sceneName= scene_meta.scene; + + // play_frame(scene_meta, data.world.frameInfo.frame); + + // function play_frame(scene_meta, frame){ + // load_world(sceneName, frame); + + // if (!stop_play_flag) + // { + // var frame_index = scene_meta.frames.findIndex(function(x){return x == frame;}); + // if (frame_index+1 < scene_meta.frames.length) + // { + // next_frame = scene_meta.frames[frame_index+1]; + // setTimeout( + // function(){ + // play_frame(scene_meta, next_frame); + // }, + // 100); + // } + // else{ + // stop_play_flag = true; + // } + + // } + // }; + // } } - -export {PlayControl}; \ No newline at end of file +export { PlayControl }; diff --git a/public/js/popup_dialog.js b/public/js/popup_dialog.js index 96bc5ef..bc362a5 100644 --- a/public/js/popup_dialog.js +++ b/public/js/popup_dialog.js @@ -1,185 +1,155 @@ +class MovableView { + mouseDown = false; + mouseDownPos = null; + + // move starts in dragableUi, + // movable in movableUi, + // the pos of posUi is set. + constructor(dragableUi, posUi, funcOnMove) { + let movableUi = document.getElementById("move-handle-wrapper"); + + dragableUi.addEventListener("mousedown", (event) => { + if (event.which == 1 && event.currentTarget == event.target) { + this.mouseDown = true; + this.mouseDownPos = { x: event.clientX, y: event.clientY }; + + movableUi.style.display = "inherit"; + } + }); + + movableUi.addEventListener("mouseup", (event) => { + if (this.mouseDown) { + dragableUi.style.cursor = ""; + event.stopPropagation(); + event.preventDefault(); + this.mouseDown = false; + + movableUi.style.display = "none"; + } + }); + + movableUi.addEventListener("mousemove", (event) => { + if (this.mouseDown) { + let posDelta = { + x: event.clientX - this.mouseDownPos.x, + y: event.clientY - this.mouseDownPos.y, + }; + dragableUi.style.cursor = "move"; -class MovableView -{ - mouseDown = false; - mouseDownPos = null; - - // move starts in dragableUi, - // movable in movableUi, - // the pos of posUi is set. - constructor(dragableUi, posUi, funcOnMove) - { - - let movableUi = document.getElementById("move-handle-wrapper"); - - dragableUi.addEventListener("mousedown", (event)=>{ - if (event.which == 1 && event.currentTarget == event.target) - { - - this.mouseDown = true; - this.mouseDownPos = {x: event.clientX, y:event.clientY}; - - movableUi.style.display="inherit"; - } - }); - - movableUi.addEventListener("mouseup", (event)=>{ - if (this.mouseDown){ - dragableUi.style.cursor = ""; - event.stopPropagation(); - event.preventDefault(); - this.mouseDown = false; - - movableUi.style.display="none"; - } - }); - - movableUi.addEventListener("mousemove", (event)=>{ - - if (this.mouseDown){ - let posDelta = { - x: event.clientX - this.mouseDownPos.x, - y: event.clientY - this.mouseDownPos.y - }; - - dragableUi.style.cursor = "move"; - - this.mouseDownPos = {x: event.clientX, y:event.clientY}; - - let left = posUi.offsetLeft; - let top = posUi.offsetTop; - - posUi.style.left = Math.max(0, (left + posDelta.x)) + 'px'; - posUi.style.top = Math.max(0, (top + posDelta.y)) + 'px'; - - if (funcOnMove) - funcOnMove(); - } - - }); - } -} + this.mouseDownPos = { x: event.clientX, y: event.clientY }; + let left = posUi.offsetLeft; + let top = posUi.offsetTop; -class PopupDialog extends MovableView -{ - constructor(ui) - { - super(ui.querySelector("#header"), ui.querySelector("#view")); + posUi.style.left = Math.max(0, left + posDelta.x) + "px"; + posUi.style.top = Math.max(0, top + posDelta.y) + "px"; - this.ui = ui; //wrapper - this.viewUi = this.ui.querySelector("#view"); - this.headerUi = this.ui.querySelector("#header"); - this.titleUi = this.ui.querySelector("#title"); - - this.ui.onclick = ()=>{ - this.hide(); - }; - - this.ui.addEventListener("keydown", (event)=>{ - - if (event.key == 'Escape'){ - this.hide(); - - event.preventDefault(); - } - event.stopPropagation(); - }); - - this.viewUi.onclick = function(event){ - //event.preventDefault(); - event.stopPropagation(); - }; - - this.titleUi.onclick = function(event){ - //event.preventDefault(); - event.stopPropagation(); - }; - - this.titleUi.addEventListener("mousedown", (event)=>{ - //event.preventDefault(); - event.stopPropagation(); - }); - - this.titleUi.addEventListener("contextmenu", (event)=>{ - event.stopPropagation(); - }) - - // this.viewUi.addEventListener("contextmenu", (e)=>{ - // e.stopPropagation(); - // e.preventDefault(); - // }); - - - - - - - // this.ui.querySelector("#info-view").onclick = function(event){ - // event.preventDefault(); - // event.stopPropagation(); - // }; - - - this.ui.querySelector("#btn-exit").onclick = (event)=>{ - this.hide(); - } - - this.maximizeButton = this.ui.querySelector("#btn-maximize") - - if (this.maximizeButton) - { - this.maximizeButton.onclick = (event)=>{ - let v = this.viewUi; - v.style.top = "0%"; - v.style.left = "0%"; - v.style.width = "100%"; - v.style.height = "100%"; - v.style["z-index"] = 5; - - event.currentTarget.style.display = 'none'; - this.ui.querySelector("#btn-restore").style.display = "inherit"; - }; - } - - this.restoreButton = this.ui.querySelector("#btn-restore"); - - if (this.restoreButton) { - this.restoreButton.onclick = (event)=>{ - let v = this.viewUi; - v.style.top = "20%"; - v.style.left = "20%"; - v.style.width = "60%"; - v.style.height = "60%"; - event.currentTarget.style.display = 'none'; - this.ui.querySelector("#btn-maximize").style.display = "inherit"; - }; - } + if (funcOnMove) funcOnMove(); + } + }); + } +} +class PopupDialog extends MovableView { + constructor(ui) { + super(ui.querySelector("#header"), ui.querySelector("#view")); + + this.ui = ui; //wrapper + this.viewUi = this.ui.querySelector("#view"); + this.headerUi = this.ui.querySelector("#header"); + this.titleUi = this.ui.querySelector("#title"); + + this.ui.onclick = () => { + this.hide(); + }; + + this.ui.addEventListener("keydown", (event) => { + if (event.key == "Escape") { + this.hide(); + + event.preventDefault(); + } + event.stopPropagation(); + }); + + this.viewUi.onclick = function (event) { + //event.preventDefault(); + event.stopPropagation(); + }; + + this.titleUi.onclick = function (event) { + //event.preventDefault(); + event.stopPropagation(); + }; + + this.titleUi.addEventListener("mousedown", (event) => { + //event.preventDefault(); + event.stopPropagation(); + }); + + this.titleUi.addEventListener("contextmenu", (event) => { + event.stopPropagation(); + }); + + // this.viewUi.addEventListener("contextmenu", (e)=>{ + // e.stopPropagation(); + // e.preventDefault(); + // }); + + // this.ui.querySelector("#info-view").onclick = function(event){ + // event.preventDefault(); + // event.stopPropagation(); + // }; + + this.ui.querySelector("#btn-exit").onclick = (event) => { + this.hide(); + }; + + this.maximizeButton = this.ui.querySelector("#btn-maximize"); + + if (this.maximizeButton) { + this.maximizeButton.onclick = (event) => { + let v = this.viewUi; + v.style.top = "0%"; + v.style.left = "0%"; + v.style.width = "100%"; + v.style.height = "100%"; + v.style["z-index"] = 5; + + event.currentTarget.style.display = "none"; + this.ui.querySelector("#btn-restore").style.display = "inherit"; + }; } + this.restoreButton = this.ui.querySelector("#btn-restore"); + + if (this.restoreButton) { + this.restoreButton.onclick = (event) => { + let v = this.viewUi; + v.style.top = "20%"; + v.style.left = "20%"; + v.style.width = "60%"; + v.style.height = "60%"; + event.currentTarget.style.display = "none"; + this.ui.querySelector("#btn-maximize").style.display = "inherit"; + }; + } + } + hide(msg) { + this.ui.style.display = "none"; - hide(msg) - { - this.ui.style.display = 'none'; - - if (this.onExit) - { - this.onExit(msg); - } - } - - show(onexit) - { - this.ui.style.display = 'inherit'; - this.onExit = onexit; - //this.ui.focus(); + if (this.onExit) { + this.onExit(msg); } + } - + show(onexit) { + this.ui.style.display = "inherit"; + this.onExit = onexit; + //this.ui.focus(); + } } - -export {PopupDialog, MovableView} \ No newline at end of file +export { PopupDialog, MovableView }; diff --git a/public/js/radar.js b/public/js/radar.js index 6c88ed6..32013aa 100644 --- a/public/js/radar.js +++ b/public/js/radar.js @@ -1,418 +1,423 @@ -import * as THREE from './lib/three.module.js'; -import { PCDLoader } from './lib/PCDLoader.js'; -import { matmul, euler_angle_to_rotate_matrix_3by3} from "./util.js" - -function Radar(sceneMeta, world, frameInfo, radarName){ - this.world = world; - this.frameInfo = frameInfo; - this.name = radarName; - this.sceneMeta = sceneMeta; - this.coordinatesOffset = world.coordinatesOffset; - - this.showPointsOnly = false; - this.showRadarBoxFlag = false; - this.cssStyleSelector = this.sceneMeta.calib.radar[this.name].cssstyleselector; - this.color = this.sceneMeta.calib.radar[this.name].color; - this.velocityScale = 0.3; +import * as THREE from "./lib/three.module.js"; +import { PCDLoader } from "./lib/PCDLoader.js"; +import { matmul, euler_angle_to_rotate_matrix_3by3 } from "./util.js"; + +function Radar(sceneMeta, world, frameInfo, radarName) { + this.world = world; + this.frameInfo = frameInfo; + this.name = radarName; + this.sceneMeta = sceneMeta; + this.coordinatesOffset = world.coordinatesOffset; + + this.showPointsOnly = false; + this.showRadarBoxFlag = false; + this.cssStyleSelector = + this.sceneMeta.calib.radar[this.name].cssstyleselector; + this.color = this.sceneMeta.calib.radar[this.name].color; + this.velocityScale = 0.3; + + if (!this.color) { + this.color = [1.0, 0.0, 0.0]; + } + + this._radar_points_raw = null; // read from file, centered at 0 + this.elements = null; // geometry points + + this.preloaded = false; + this.loaded = false; + + this.go_cmd_received = false; + this.webglScene = null; + this.on_go_finished = null; + this.go = function (webglScene, on_go_finished) { + this.webglScene = webglScene; + + if (this.preloaded) { + if (this.elements) { + this.webglScene.add(this.elements.points); + + if (!this.showPointsOnly) + this.elements.arrows.forEach((a) => this.webglScene.add(a)); + + if (this.showRadarBoxFlag) this.webglScene.add(this.radar_box); + } + + this.loaded = true; + if (on_go_finished) on_go_finished(); + } + + //anyway we save go cmd + { + this.go_cmd_received = true; + this.on_go_finished = on_go_finished; + } + }; - if (!this.color){ - this.color = [1.0, 0.0, 0.0]; + this.showRadarBox = function () { + this.showRadarBoxFlag = true; + this.webglScene.add(this.radar_box); + }; + + this.hideRadarBox = function () { + this.showRadarBoxFlag = false; + this.webglScene.remove(this.radar_box); + }; + + this.get_unoffset_radar_points = function () { + if (this.elements) { + let pts = this.elements.points.geometry.getAttribute("position").array; + return pts.map((p, i) => p - this.world.coordinatesOffset[i % 3]); + } else { + return []; } + }; - this._radar_points_raw = null; // read from file, centered at 0 - this.elements = null; // geometry points + // todo: what if it's not preloaded yet + this.unload = function (keep_box) { + if (this.elements) { + this.webglScene.remove(this.elements.points); + if (!this.showPointsOnly) + this.elements.arrows.forEach((a) => this.webglScene.remove(a)); - this.preloaded = false; + if (!keep_box) this.webglScene.remove(this.radar_box); + } this.loaded = false; + }; + // todo: its possible to remove points before preloading, + this.deleteAll = function (keep_box) { + if (this.loaded) { + this.unload(); + } - this.go_cmd_received = false; - this.webglScene = null; - this.on_go_finished = null; - this.go = function(webglScene, on_go_finished){ - this.webglScene = webglScene; + if (this.elements) { + //this.scene.remove(this.points); + this.world.data.dbg.free(); - if (this.preloaded){ - if (this.elements){ - this.webglScene.add(this.elements.points); + if (this.elements.points) { + this.elements.points.geometry.dispose(); + this.elements.points.material.dispose(); + } - if (!this.showPointsOnly) - this.elements.arrows.forEach(a=>this.webglScene.add(a)); + if (this.elements.arrows) { + this.elements.arrows.forEach((a) => { + this.world.data.dbg.free(); + a.geometry.dispose(); + a.material.dispose(); + }); + } - if (this.showRadarBoxFlag) - this.webglScene.add(this.radar_box); - } + this.elements = null; + } - this.loaded = true; - if (on_go_finished) - on_go_finished(); - } - - //anyway we save go cmd - { - this.go_cmd_received = true; - this.on_go_finished = on_go_finished; - } - }; + if (!keep_box && this.radar_box) { + this.world.data.dbg.free(); + this.radar_box.geometry.dispose(); + this.radar_box.material.dispose(); + this.radar_box = null; + } + }; + + this.preload = function (on_preload_finished) { + var loader = new PCDLoader(); + + var _self = this; + loader.load( + this.frameInfo.get_radar_path(this.name), + //ok + function (pcd) { + var position = pcd.position; + //var velocity = pcd.velocity; + // velocity is a vector anchored at position, + // we translate them into position of the vector head + var velocity = position.map( + (p, i) => pcd.velocity[i] + pcd.position[i] + ); - this.showRadarBox = function(){ - this.showRadarBoxFlag = true; - this.webglScene.add(this.radar_box); - }; + // scale velocity + // velocity = velocity.map(v=>v*_self.velocityScale); - this.hideRadarBox = function(){ - this.showRadarBoxFlag = false; - this.webglScene.remove(this.radar_box); - }; + //_self.points_parse_time = new Date().getTime(); + //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "parse pionts ", _self.points_parse_time - _self.create_time, "ms"); + _self._radar_points_raw = position; + _self._radar_velocity_raw = velocity; - this.get_unoffset_radar_points = function(){ - if (this.elements){ - let pts = this.elements.points.geometry.getAttribute("position").array; - return pts.map((p,i)=>p-this.world.coordinatesOffset[i %3]); - } - else{ - return []; - } - }; + // add one box to calibrate radar with lidar + _self.radar_box = _self.createRadarBox(); - // todo: what if it's not preloaded yet - this.unload = function(keep_box){ - if (this.elements){ - this.webglScene.remove(this.elements.points); - if (!this.showPointsOnly) - this.elements.arrows.forEach(a=>this.webglScene.remove(a)); - - if (!keep_box) - this.webglScene.remove(this.radar_box); - } - this.loaded = false; - }; + // install callback for box changing + _self.radar_box.on_box_changed = () => { + _self.move_radar(_self.radar_box); + }; - // todo: its possible to remove points before preloading, - this.deleteAll = function(keep_box){ - if (this.loaded){ - this.unload(); - } - - if (this.elements){ - //this.scene.remove(this.points); - this.world.data.dbg.free(); - - if (this.elements.points) - { - this.elements.points.geometry.dispose(); - this.elements.points.material.dispose(); - } - - if (this.elements.arrows) - { - this.elements.arrows.forEach(a=>{ - this.world.data.dbg.free(); - a.geometry.dispose(); - a.material.dispose(); - }) - } - - this.elements = null; - } - - if (!keep_box && this.radar_box){ - this.world.data.dbg.free(); - this.radar_box.geometry.dispose(); - this.radar_box.material.dispose(); - this.radar_box = null; - } - }; + //position = _self.transformPointsByOffset(position); + position = _self.move_radar_points(_self.radar_box); + velocity = _self.move_radar_velocity(_self.radar_box); + let elements = _self.buildRadarGeometry(position, velocity); + _self.elements = elements; + //_self.points_backup = mesh; - this.preload = function(on_preload_finished){ - var loader = new PCDLoader(); - - var _self = this; - loader.load( this.frameInfo.get_radar_path(this.name), - //ok - function ( pcd ) { - var position = pcd.position; - //var velocity = pcd.velocity; - // velocity is a vector anchored at position, - // we translate them into position of the vector head - var velocity = position.map((p,i)=>pcd.velocity[i]+pcd.position[i]); - - // scale velocity - // velocity = velocity.map(v=>v*_self.velocityScale); - - //_self.points_parse_time = new Date().getTime(); - //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "parse pionts ", _self.points_parse_time - _self.create_time, "ms"); - _self._radar_points_raw = position; - _self._radar_velocity_raw = velocity; - - // add one box to calibrate radar with lidar - _self.radar_box = _self.createRadarBox(); - - // install callback for box changing - _self.radar_box.on_box_changed = ()=>{ - _self.move_radar(_self.radar_box); - }; - - //position = _self.transformPointsByOffset(position); - position = _self.move_radar_points(_self.radar_box); - velocity = _self.move_radar_velocity(_self.radar_box); - let elements = _self.buildRadarGeometry(position, velocity); - - _self.elements = elements; - //_self.points_backup = mesh; - - _self._afterPreload(); - - }, - - // on progress, - function(){}, - - // on error - function(){ - //error - console.log("load radar failed."); - _self._afterPreload(); - }, - - // on file loaded - function(){ - //_self.points_readfile_time = new Date().getTime(); - //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "read file ", _self.points_readfile_time - _self.create_time, "ms"); - } - ); - }; + _self._afterPreload(); + }, + + // on progress, + function () {}, + + // on error + function () { + //error + console.log("load radar failed."); + _self._afterPreload(); + }, + + // on file loaded + function () { + //_self.points_readfile_time = new Date().getTime(); + //console.log(_self.points_load_time, _self.frameInfo.scene, _self.frameInfo.frame, "read file ", _self.points_readfile_time - _self.create_time, "ms"); + } + ); + }; + + // internal funcs below + this._afterPreload = function () { + this.preloaded = true; + console.log(`radar ${this.radarname} preloaded`); + if (this.on_preload_finished) { + this.on_preload_finished(); + } + if (this.go_cmd_received) { + this.go(this.webglScene, this.on_go_finished); + } + }; - // internal funcs below - this._afterPreload = function(){ - this.preloaded = true; - console.log(`radar ${this.radarname} preloaded`); - if (this.on_preload_finished){ - this.on_preload_finished(); - } - if (this.go_cmd_received){ - this.go(this.webglScene, this.on_go_finished); - } - }; + this.createRadarBox = function () { + if (this.sceneMeta.calib.radar && this.sceneMeta.calib.radar[this.name]) { + return this.world.annotation.createCuboid( + { + x: + this.sceneMeta.calib.radar[this.name].translation[0] + + this.coordinatesOffset[0], + y: + this.sceneMeta.calib.radar[this.name].translation[1] + + this.coordinatesOffset[1], + z: + this.sceneMeta.calib.radar[this.name].translation[2] + + this.coordinatesOffset[2], + }, + { x: 1, y: 1, z: 1 }, + { + x: this.sceneMeta.calib.radar[this.name].rotation[0], + y: this.sceneMeta.calib.radar[this.name].rotation[1], + z: this.sceneMeta.calib.radar[this.name].rotation[2], + }, + "radar", + this.name + ); + } else { + return this.world.annotation.createCuboid( + { + x: this.coordinatesOffset[0], + y: this.coordinatesOffset[1], + z: this.coordinatesOffset[2], + }, + { x: 1, y: 1, z: 1 }, + { x: 0, y: 0, z: 0 }, + "radar", + this.name + ); + } + }; + + this.buildPoints = function (position) { + // build geometry + this.world.data.dbg.alloc(); + let geometry = new THREE.BufferGeometry(); + if (position.length > 0) + geometry.addAttribute( + "position", + new THREE.Float32BufferAttribute(position, 3) + ); + + let pointColor = this.color; + let color = []; + for (var i = 0; i < position.length; i += 3) { + color.push(pointColor[0]); + color.push(pointColor[1]); + color.push(pointColor[2]); + } - this.createRadarBox = function(){ - if (this.sceneMeta.calib.radar && this.sceneMeta.calib.radar[this.name]){ - return this.world.annotation.createCuboid( - { - x: this.sceneMeta.calib.radar[this.name].translation[0] + this.coordinatesOffset[0], - y: this.sceneMeta.calib.radar[this.name].translation[1] + this.coordinatesOffset[1], - z: this.sceneMeta.calib.radar[this.name].translation[2] + this.coordinatesOffset[2], - }, - {x:1,y:1, z:1}, - { - x: this.sceneMeta.calib.radar[this.name].rotation[0], - y: this.sceneMeta.calib.radar[this.name].rotation[1], - z: this.sceneMeta.calib.radar[this.name].rotation[2], - }, - "radar", - this.name); - - }else { - return this.world.annotation.createCuboid( - {x: this.coordinatesOffset[0], - y: this.coordinatesOffset[1], - z: this.coordinatesOffset[2]}, - {x:1,y:1, z:1}, - {x:0,y:0,z:0}, - "radar", - this.name); - } - }; + geometry.addAttribute("color", new THREE.Float32BufferAttribute(color, 3)); - this.buildPoints = function(position){ - // build geometry - this.world.data.dbg.alloc(); - let geometry = new THREE.BufferGeometry(); - if ( position.length > 0 ) - geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) ); - - - let pointColor = this.color; - let color=[]; - for (var i =0; i< position.length; i+=3){ - - color.push(pointColor[0]); - color.push(pointColor[1]); - color.push(pointColor[2]); - } - - geometry.addAttribute( 'color', new THREE.Float32BufferAttribute(color, 3 ) ); - - geometry.computeBoundingSphere(); - - // build material - let pointSize = this.sceneMeta.calib.radar[this.name].point_size; - if (!pointSize) - pointSize = 2; - - let material = new THREE.PointsMaterial( { size: pointSize, vertexColors: THREE.VertexColors } ); - //material.size = 2; - material.sizeAttenuation = false; - - // build mesh - let mesh = new THREE.Points( geometry, material ); - mesh.name = "radar"; - - return mesh; - }; + geometry.computeBoundingSphere(); - this.buildArrow = function(position, velocity){ - var h = 0.5; - - let p=position; - let v=velocity; + // build material + let pointSize = this.sceneMeta.calib.radar[this.name].point_size; + if (!pointSize) pointSize = 2; - var body = [ - p[0],p[1],p[2], - v[0],v[1],v[2], - ]; - + let material = new THREE.PointsMaterial({ + size: pointSize, + vertexColors: THREE.VertexColors, + }); + //material.size = 2; + material.sizeAttenuation = false; - this.world.data.dbg.alloc(); - var geo = new THREE.BufferGeometry(); - geo.addAttribute( 'position', new THREE.Float32BufferAttribute(body, 3 ) ); - + // build mesh + let mesh = new THREE.Points(geometry, material); + mesh.name = "radar"; - let color = this.color.map(c=>Math.round(c*255)).reduce((a,b)=>a*256+b, 0); + return mesh; + }; - var material = new THREE.LineBasicMaterial( { color: color, linewidth: 1, opacity: 1, transparent: true } ); - var arrow = new THREE.LineSegments( geo, material ); - return arrow; - } + this.buildArrow = function (position, velocity) { + var h = 0.5; - this.buildRadarGeometry = function(position, velocity){ - let points = this.buildPoints(position); + let p = position; + let v = velocity; - let arrows = []; - - if (!this.showPointsOnly) - { - for (let i = 0; i{ - return p + translation[i % 3]; - }); - return translated_points; - }; + this.world.data.dbg.alloc(); + var geo = new THREE.BufferGeometry(); + geo.addAttribute("position", new THREE.Float32BufferAttribute(body, 3)); - this.move_radar_points = function(box){ - return this.move_points(this._radar_points_raw, box); - }; + let color = this.color + .map((c) => Math.round(c * 255)) + .reduce((a, b) => a * 256 + b, 0); - this.move_radar_velocity = function(box){ - return this.move_points(this._radar_velocity_raw, box); - } + var material = new THREE.LineBasicMaterial({ + color: color, + linewidth: 1, + opacity: 1, + transparent: true, + }); + var arrow = new THREE.LineSegments(geo, material); + return arrow; + }; - this.move_radar= function(box){ + this.buildRadarGeometry = function (position, velocity) { + let points = this.buildPoints(position); - let translated_points = this.move_radar_points(box); - let translated_velocity = this.move_radar_velocity(box); + let arrows = []; - let elements = this.buildRadarGeometry(translated_points, translated_velocity); - - // remove old points - this.unload(true); - this.deleteAll(true); + if (!this.showPointsOnly) { + for (let i = 0; i < position.length / 3; i++) { + let arr = this.buildArrow( + position.slice(i * 3, i * 3 + 3), + velocity.slice(i * 3, i * 3 + 3) + ); + arrows.push(arr); + } + } - this.elements = elements; - //_self.points_backup = mesh; - if (this.go_cmd_received) // this should be always true - { - this.webglScene.add(this.elements.points); - if (!this.showPointsOnly) - this.elements.arrows.forEach(a=>this.webglScene.add(a)); - } + return { + points: points, + arrows: arrows, }; + }; + + this.move_points = function (points, box) { + let trans = euler_angle_to_rotate_matrix_3by3(box.rotation); + let rotated_points = matmul(trans, points, 3); + let translation = [box.position.x, box.position.y, box.position.z]; + let translated_points = rotated_points.map((p, i) => { + return p + translation[i % 3]; + }); + return translated_points; + }; + + this.move_radar_points = function (box) { + return this.move_points(this._radar_points_raw, box); + }; + + this.move_radar_velocity = function (box) { + return this.move_points(this._radar_velocity_raw, box); + }; + + this.move_radar = function (box) { + let translated_points = this.move_radar_points(box); + let translated_velocity = this.move_radar_velocity(box); + + let elements = this.buildRadarGeometry( + translated_points, + translated_velocity + ); + + // remove old points + this.unload(true); + this.deleteAll(true); + + this.elements = elements; + //_self.points_backup = mesh; + if (this.go_cmd_received) { + // this should be always true + this.webglScene.add(this.elements.points); + if (!this.showPointsOnly) + this.elements.arrows.forEach((a) => this.webglScene.add(a)); + } + }; } -function RadarManager(sceneMeta, world, frameInfo){ - this.radarList = []; +function RadarManager(sceneMeta, world, frameInfo) { + this.radarList = []; - if (world.data.cfg.enableRadar && sceneMeta.radar){ - let radars = []; - - for (let r in sceneMeta.calib.radar){ - if (!sceneMeta.calib.radar[r].disable) - radars.push(r); - } + if (world.data.cfg.enableRadar && sceneMeta.radar) { + let radars = []; - this.radarList = radars.map(name=>{ - return new Radar(sceneMeta, world, frameInfo, name); - }); + for (let r in sceneMeta.calib.radar) { + if (!sceneMeta.calib.radar[r].disable) radars.push(r); } - this.getAllBoxes = function() - { - if (this.showRadarBoxFlag) - { - return this.radarList.map(r=>r.radar_box); - } - else - { - return []; - } - }; + this.radarList = radars.map((name) => { + return new Radar(sceneMeta, world, frameInfo, name); + }); + } - this.preloaded = function(){ - for (let r in this.radarList){ - if (!this.radarList[r].preloaded) - return false; - } - return true; - }; + this.getAllBoxes = function () { + if (this.showRadarBoxFlag) { + return this.radarList.map((r) => r.radar_box); + } else { + return []; + } + }; - this.go = function(webglScene, on_go_finished){ - this.radarList.forEach(r=>r.go(webglScene, on_go_finished)); - }; + this.preloaded = function () { + for (let r in this.radarList) { + if (!this.radarList[r].preloaded) return false; + } + return true; + }; - this.preload = function(on_preload_finished){ - this.radarList.forEach(r=>r.preload(on_preload_finished)); - }; + this.go = function (webglScene, on_go_finished) { + this.radarList.forEach((r) => r.go(webglScene, on_go_finished)); + }; - this.unload = function(){ - this.radarList.forEach(r=>r.unload()); - }; + this.preload = function (on_preload_finished) { + this.radarList.forEach((r) => r.preload(on_preload_finished)); + }; - this.deleteAll = function(){ - this.radarList.forEach(r=>r.deleteAll()); - }; + this.unload = function () { + this.radarList.forEach((r) => r.unload()); + }; - this.getOperableObjects = function(){ - return this.radarList.flatMap(r=>r.getOperableObjects()); - }; + this.deleteAll = function () { + this.radarList.forEach((r) => r.deleteAll()); + }; - this.showRadarBoxFlag = false; - this.showRadarBox = function(){ - this.showRadarBoxFlag = true; - this.radarList.forEach(r=>r.showRadarBox()); - }; + this.getOperableObjects = function () { + return this.radarList.flatMap((r) => r.getOperableObjects()); + }; - this.hideRadarBox = function(){ - this.showRadarBoxFlag = false; - this.radarList.forEach(r=>r.hideRadarBox()); - } -}; + this.showRadarBoxFlag = false; + this.showRadarBox = function () { + this.showRadarBoxFlag = true; + this.radarList.forEach((r) => r.showRadarBox()); + }; + this.hideRadarBox = function () { + this.showRadarBoxFlag = false; + this.radarList.forEach((r) => r.hideRadarBox()); + }; +} -export {RadarManager} \ No newline at end of file +export { RadarManager }; diff --git a/public/js/reg_demo.js b/public/js/reg_demo.js index 4a4aa92..ef22b1d 100644 --- a/public/js/reg_demo.js +++ b/public/js/reg_demo.js @@ -1,234 +1,226 @@ -import * as THREE from './lib/three.module.js'; -import { PCDLoader } from './lib/PCDLoader.js'; -import { OrbitControls } from './lib/OrbitControls.js'; -import { GUI } from './lib/dat.gui.module.js'; -import {TextFileLoader} from "./text_file_loader.js"; +import * as THREE from "./lib/three.module.js"; +import { PCDLoader } from "./lib/PCDLoader.js"; +import { OrbitControls } from "./lib/OrbitControls.js"; +import { GUI } from "./lib/dat.gui.module.js"; +import { TextFileLoader } from "./text_file_loader.js"; var container, stats; var camera, controls, scene, renderer; var camera; - var params = { - src: true, - tgt: true, - out: true, - reload: load_all, + src: true, + tgt: true, + out: true, + reload: load_all, }; var last_cloud_ind = { - src: true, - tgt: true, - out: true, -} + src: true, + tgt: true, + out: true, +}; -var clouds ={}; +var clouds = {}; init(); animate(); function init() { - document.body.addEventListener('keydown', event => { - if (event.ctrlKey && 'asdv'.indexOf(event.key) !== -1) { - event.preventDefault() - } - }) - + document.body.addEventListener("keydown", (event) => { + if (event.ctrlKey && "asdv".indexOf(event.key) !== -1) { + event.preventDefault(); + } + }); - scene = new THREE.Scene(); + scene = new THREE.Scene(); + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + camera = new THREE.PerspectiveCamera( + 65, + window.innerWidth / window.innerHeight, + 1, + 800 + ); + camera.position.x = 0; + camera.position.z = 50; + camera.position.y = 0; + camera.up.set(0, 0, 1); + camera.lookAt(0, 0, 0); - renderer = new THREE.WebGLRenderer( { antialias: true } ); - renderer.setPixelRatio( window.devicePixelRatio ); + controls = new OrbitControls(camera, renderer.domElement); + controls.addEventListener("change", render); // call this only in static scenes (i.e., if there is no animation loop) + container = document.createElement("container"); - - camera = new THREE.PerspectiveCamera( 65, window.innerWidth / window.innerHeight, 1, 800 ); - camera.position.x = 0; - camera.position.z = 50; - camera.position.y = 0; - camera.up.set( 0, 0, 1); - camera.lookAt( 0, 0, 0 ); + document.body.appendChild(container); + container.appendChild(renderer.domElement); - - controls = new OrbitControls( camera, renderer.domElement ); - controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop) - - container = document.createElement( 'container' ); - + document.addEventListener("keydown", keydown); - document.body.appendChild( container ); - container.appendChild( renderer.domElement ); + init_gui(); + //scene.add( new THREE.AxesHelper( 2 ) ); - document.addEventListener("keydown", keydown) + onWindowResize(); + window.addEventListener("resize", onWindowResize, false); - init_gui(); - //scene.add( new THREE.AxesHelper( 2 ) ); + load_all(); - onWindowResize(); - window.addEventListener( 'resize', onWindowResize, false ); - - load_all(); - - render(); - + render(); } -function load_all(){ - - clearAll(); - load_pcd("src", "/temp/src.pcd", 0xff0000); - load_pcd("tgt", "/temp/tgt.pcd", 0x00ff00); - load_pcd("out", "/temp/out.pcd", 0xffff00); - load_transform_matrix(); +function load_all() { + clearAll(); + load_pcd("src", "/temp/src.pcd", 0xff0000); + load_pcd("tgt", "/temp/tgt.pcd", 0x00ff00); + load_pcd("out", "/temp/out.pcd", 0xffff00); + load_transform_matrix(); } -function clearAll(){ - - remove(clouds["src"]); - remove(clouds["tgt"]); - remove(clouds["out"]); - - clouds["src"] = null; - clouds["tgt"] = null; - clouds["our"] = null; - - function remove(p){ - if (p){ - scene.remove(p); - p.geometry.dispose(); - p.material.dispose(); - } - } -} +function clearAll() { + remove(clouds["src"]); + remove(clouds["tgt"]); + remove(clouds["out"]); + clouds["src"] = null; + clouds["tgt"] = null; + clouds["our"] = null; -function keydown( ev ) { - - switch ( ev.key) { - case '+': - clouds["src"].material.size *= 1.2; - clouds["tgt"].material.size *= 1.2; - clouds["out"].material.size *= 1.2; - break; - case '-': - clouds["src"].material.size /= 1.2; - clouds["tgt"].material.size /= 1.2; - clouds["out"].material.size /= 1.2; - - break; + function remove(p) { + if (p) { + scene.remove(p); + p.geometry.dispose(); + p.material.dispose(); } + } } +function keydown(ev) { + switch (ev.key) { + case "+": + clouds["src"].material.size *= 1.2; + clouds["tgt"].material.size *= 1.2; + clouds["out"].material.size *= 1.2; + break; + case "-": + clouds["src"].material.size /= 1.2; + clouds["tgt"].material.size /= 1.2; + clouds["out"].material.size /= 1.2; + + break; + } +} function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize( window.innerWidth, window.innerHeight ); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { - requestAnimationFrame( animate ); - controls.update(); - render(); - switch_cloud("src"); - switch_cloud("tgt"); - switch_cloud("out"); - - function switch_cloud(name){ - if (params[name] != last_cloud_ind[name]){ - last_cloud_ind[name] = params[name]; - if (last_cloud_ind[name]){ - scene.add(clouds[name]); - }else { - scene.remove(clouds[name]); - } - } + requestAnimationFrame(animate); + controls.update(); + render(); + switch_cloud("src"); + switch_cloud("tgt"); + switch_cloud("out"); + + function switch_cloud(name) { + if (params[name] != last_cloud_ind[name]) { + last_cloud_ind[name] = params[name]; + if (last_cloud_ind[name]) { + scene.add(clouds[name]); + } else { + scene.remove(clouds[name]); + } } + } } - -function render(){ - renderer.render( scene, camera ); +function render() { + renderer.render(scene, camera); } -function load_transform_matrix(){ - var loader = new TextFileLoader(); - loader.load( "/temp/trans.json", - function(json){ - console.log(json); - var mat = JSON.parse(json); - - var trans_html = ""; - for (var i = 0; i<4; i++){ - - trans_html += ""; - for (var j =0; j<4; j++) - trans_html += ""; - trans_html += "" - } - trans_html += "
"+ mat[i*4+j] + "
"; - document.getElementById("info").innerHTML = trans_html; - } - ) +function load_transform_matrix() { + var loader = new TextFileLoader(); + loader.load("/temp/trans.json", function (json) { + console.log(json); + var mat = JSON.parse(json); + + var trans_html = ""; + for (var i = 0; i < 4; i++) { + trans_html += ""; + for (var j = 0; j < 4; j++) + trans_html += ""; + trans_html += ""; + } + trans_html += "
" + mat[i * 4 + j] + "
"; + document.getElementById("info").innerHTML = trans_html; + }); } -function load_pcd(name, file, overall_color){ - var loader = new PCDLoader(); - - loader.load( file, - function ( pcd ) { - var position = pcd.position; - var color = pcd.color; - var normal = pcd.normal; - // build geometry - var geometry = new THREE.BufferGeometry(); - if ( position.length > 0 ) geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( position, 3 ) ); - if ( normal.length > 0 ) geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normal, 3 ) ); - if ( color.length > 0 ) geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( color, 3 ) ); - - geometry.computeBoundingSphere(); - // build material - - var material = new THREE.PointsMaterial( { size: 0.005 } ); - - if ( color.length > 0 ) { - material.vertexColors = VertexColors; - } else { - material.color.setHex(overall_color ); - } - - //material.size = 0.1; +function load_pcd(name, file, overall_color) { + var loader = new PCDLoader(); + + loader.load(file, function (pcd) { + var position = pcd.position; + var color = pcd.color; + var normal = pcd.normal; + // build geometry + var geometry = new THREE.BufferGeometry(); + if (position.length > 0) + geometry.addAttribute( + "position", + new THREE.Float32BufferAttribute(position, 3) + ); + if (normal.length > 0) + geometry.addAttribute( + "normal", + new THREE.Float32BufferAttribute(normal, 3) + ); + if (color.length > 0) + geometry.addAttribute( + "color", + new THREE.Float32BufferAttribute(color, 3) + ); + + geometry.computeBoundingSphere(); + // build material + + var material = new THREE.PointsMaterial({ size: 0.005 }); + + if (color.length > 0) { + material.vertexColors = VertexColors; + } else { + material.color.setHex(overall_color); + } - // build mesh + //material.size = 0.1; - var mesh = new THREE.Points( geometry, material ); - mesh.name = "pcd"; + // build mesh - //return mesh; - if (params[name]) - scene.add(mesh); + var mesh = new THREE.Points(geometry, material); + mesh.name = "pcd"; - clouds[name] = mesh; - //var center = points.geometry.boundingSphere.center; - //controls.target.set( center.x, center.y, center.z ); - //controls.update(); - }, - ); + //return mesh; + if (params[name]) scene.add(mesh); + clouds[name] = mesh; + //var center = points.geometry.boundingSphere.center; + //controls.target.set( center.x, center.y, center.z ); + //controls.update(); + }); } +function init_gui() { + var gui = new GUI(); + var cfgFolder = gui.addFolder("View"); -function init_gui(){ - var gui = new GUI(); - - var cfgFolder = gui.addFolder( 'View' ); - - - cfgFolder.add( params, "src"); - cfgFolder.add( params, "tgt"); - cfgFolder.add( params, "out"); - cfgFolder.add( params, "reload"); - cfgFolder.open(); - gui.open(); -} \ No newline at end of file + cfgFolder.add(params, "src"); + cfgFolder.add(params, "tgt"); + cfgFolder.add(params, "out"); + cfgFolder.add(params, "reload"); + cfgFolder.open(); + gui.open(); +} diff --git a/public/js/save.js b/public/js/save.js index 89da395..d063965 100644 --- a/public/js/save.js +++ b/public/js/save.js @@ -1,148 +1,134 @@ +import { Editor } from "./editor.js"; +import { checkScene } from "./error_check.js"; +import { logger } from "./log.js"; +function reloadWorldList(worldList, done) { + var xhr = new XMLHttpRequest(); + // we defined the xhr + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + if (this.status == 200) { + let anns = JSON.parse(this.responseText); -import { Editor } from "./editor.js"; -import { checkScene } from "./error_check.js"; -import {logger} from "./log.js" - - - - -function reloadWorldList(worldList, done){ - var xhr = new XMLHttpRequest(); - // we defined the xhr - xhr.onreadystatechange = function () { - if (this.readyState != 4) return; - - if (this.status == 200) { - let anns = JSON.parse(this.responseText); - - // load annotations - anns.forEach(a=>{ - let world = worldList.find(w=>{ - return (w.frameInfo.scene == a.scene && - w.frameInfo.frame == a.frame); - }); - if (world){ - world.annotation.reapplyAnnotation(a.annotation); - } - else{ - console.error("bug?"); - } - - }); - - if (done) - done(); + // load annotations + anns.forEach((a) => { + let world = worldList.find((w) => { + return w.frameInfo.scene == a.scene && w.frameInfo.frame == a.frame; + }); + if (world) { + world.annotation.reapplyAnnotation(a.annotation); + } else { + console.error("bug?"); } + }); + + if (done) done(); + } + }; + + xhr.open("POST", "/loadworldlist", true); + + let para = worldList.map((w) => { + return { + //todo: we could add an id, so as to associate world easily + scene: w.frameInfo.scene, + frame: w.frameInfo.frame, }; - - xhr.open('POST', "/loadworldlist", true); - - let para = worldList.map(w=>{ - return { - //todo: we could add an id, so as to associate world easily - scene: w.frameInfo.scene, - frame: w.frameInfo.frame, - }; - }); - - xhr.send(JSON.stringify(para)); -} + }); + xhr.send(JSON.stringify(para)); +} var saveDelayTimer = null; var pendingSaveList = []; -function saveWorldList(worldList){ +function saveWorldList(worldList) { + //pendingSaveList = pendingSaveList.concat(worldList); - //pendingSaveList = pendingSaveList.concat(worldList); + worldList.forEach((w) => { + if (!pendingSaveList.includes(w)) pendingSaveList.push(w); + }); - worldList.forEach(w=>{ - if (!pendingSaveList.includes(w)) - pendingSaveList.push(w); - }); + if (saveDelayTimer) { + clearTimeout(saveDelayTimer); + } - if (saveDelayTimer) - { - clearTimeout(saveDelayTimer); - } - - saveDelayTimer = setTimeout(()=>{ - - logger.log("save delay expired."); + saveDelayTimer = setTimeout( + () => { + logger.log("save delay expired."); - //pandingSaveList will be cleared soon. - let scene = pendingSaveList[0].frameInfo.scene; - + //pandingSaveList will be cleared soon. + let scene = pendingSaveList[0].frameInfo.scene; - doSaveWorldList(pendingSaveList, ()=>{ - editor.header.updateModifiedStatus(); + doSaveWorldList(pendingSaveList, () => { + editor.header.updateModifiedStatus(); - checkScene(scene); - }); + checkScene(scene); + }); - //reset + //reset - saveDelayTimer = null; - pendingSaveList = []; + saveDelayTimer = null; + pendingSaveList = []; + }, - - }, - - 500); + 500 + ); } - -function doSaveWorldList(worldList, done) -{ - if (worldList.length>0){ - if (worldList[0].data.cfg.disableLabels){ - console.log("labels not loaded, save action is prohibitted.") - return; - } +function doSaveWorldList(worldList, done) { + if (worldList.length > 0) { + if (worldList[0].data.cfg.disableLabels) { + console.log("labels not loaded, save action is prohibitted."); + return; } - - - console.log(worldList.length, "frames"); - let ann = worldList.map(w=>{ - return { - scene: w.frameInfo.scene, - frame: w.frameInfo.frame, - annotation: w.annotation.toBoxAnnotations(), - }; - }) - - var xhr = new XMLHttpRequest(); - xhr.open("POST", "/saveworldlist", true); - xhr.setRequestHeader('Content-Type', 'application/json'); - - xhr.onreadystatechange = function () { - if (this.readyState != 4) return; - - if (this.status == 200) { - - worldList.forEach(w=>{ - w.annotation.resetModified(); - }) - - logger.log(`saved: ${worldList[0].frameInfo.scene}: ${worldList.reduce((a,b)=>a+" "+b.frameInfo.frame, "")}`); - - if(done){ - done(); - } - } - else{ - window.editor.infoBox.show("Error", `save failed, status : ${this.status}`); - } - - - // end of state change: it can be after some time (async) + } + + console.log(worldList.length, "frames"); + let ann = worldList.map((w) => { + return { + scene: w.frameInfo.scene, + frame: w.frameInfo.frame, + annotation: w.annotation.toBoxAnnotations(), }; + }); + + var xhr = new XMLHttpRequest(); + xhr.open("POST", "/saveworldlist", true); + xhr.setRequestHeader("Content-Type", "application/json"); + + xhr.onreadystatechange = function () { + if (this.readyState != 4) return; + + if (this.status == 200) { + worldList.forEach((w) => { + w.annotation.resetModified(); + }); + + logger.log( + `saved: ${worldList[0].frameInfo.scene}: ${worldList.reduce( + (a, b) => a + " " + b.frameInfo.frame, + "" + )}` + ); + + if (done) { + done(); + } + } else { + window.editor.infoBox.show( + "Error", + `save failed, status : ${this.status}` + ); + } + + // end of state change: it can be after some time (async) + }; - var b = JSON.stringify(ann); - //console.log(b); - xhr.send(b); + var b = JSON.stringify(ann); + //console.log(b); + xhr.send(b); } // function saveWorld(world, done){ @@ -160,7 +146,7 @@ function doSaveWorldList(worldList, done) // xhr.onreadystatechange = function () { // if (this.readyState != 4) return; - + // if (this.status == 200) { // logger.log(`saved: ${world}`); // world.annotation.resetModified(); @@ -173,10 +159,8 @@ function doSaveWorldList(worldList, done) // done(); // } - - // } - + // // end of state change: it can be after some time (async) // }; @@ -185,5 +169,4 @@ function doSaveWorldList(worldList, done) // xhr.send(b); // } - -export {saveWorldList, reloadWorldList} \ No newline at end of file +export { saveWorldList, reloadWorldList }; diff --git a/public/js/settings.js b/public/js/settings.js index 2ac7983..026568a 100644 --- a/public/js/settings.js +++ b/public/js/settings.js @@ -1,7 +1,6 @@ -var settings= { - ground_filter_height: 0.2, - initial_z_position: -0.9, +var settings = { + ground_filter_height: 0.2, + initial_z_position: -0.9, +}; -} - -export {settings} \ No newline at end of file +export { settings }; diff --git a/public/js/side_view_op.js b/public/js/side_view_op.js index c825034..b9f46c5 100644 --- a/public/js/side_view_op.js +++ b/public/js/side_view_op.js @@ -1,243 +1,264 @@ +import { matmul2 } from "./util.js"; + +import { Quaternion, Vector3 } from "./lib/three.module.js"; + +class ProjectiveView { + constructor( + ui, + cfg, + on_edge_changed, + on_direction_changed, + on_auto_shrink, + on_moved, + on_scale, + on_wheel, + on_fit_size, + on_auto_rotate, + on_reset_rotate, + on_focus, + on_box_remove, + fn_isActive + ) { + this.ui = ui; + this.cfg = cfg; + this.on_edge_changed = on_edge_changed; + this.on_direction_changed = on_direction_changed; + this.on_auto_shrink = on_auto_shrink; + this.on_moved = on_moved; + this.on_scale = on_scale; + this.on_wheel = on_wheel; + this.on_fit_size = on_fit_size; + this.on_auto_rotate = on_auto_rotate; + this.on_reset_rotate = on_reset_rotate; + this.on_focus = on_focus; + this.on_box_remove = on_box_remove; + this.isActive = fn_isActive; + + this.lines = { + top: ui.querySelector("#line-top"), + bottom: ui.querySelector("#line-bottom"), + left: ui.querySelector("#line-left"), + right: ui.querySelector("#line-right"), + direction: ui.querySelector("#line-direction"), + }; -import {matmul2} from "./util.js" - -import { - Quaternion, - Vector3 -} from "./lib/three.module.js"; - - -class ProjectiveView{ - constructor(ui, - cfg, - on_edge_changed, - on_direction_changed, - on_auto_shrink, - on_moved, - on_scale, - on_wheel, - on_fit_size, - on_auto_rotate, - on_reset_rotate, - on_focus, - on_box_remove, - fn_isActive) - { - - this.ui = ui; - this.cfg = cfg; - this.on_edge_changed = on_edge_changed; - this.on_direction_changed = on_direction_changed; - this.on_auto_shrink = on_auto_shrink; - this.on_moved=on_moved; - this.on_scale=on_scale; - this.on_wheel=on_wheel; - this.on_fit_size=on_fit_size; - this.on_auto_rotate=on_auto_rotate; - this.on_reset_rotate=on_reset_rotate; - this.on_focus = on_focus; - this.on_box_remove = on_box_remove; - this.isActive = fn_isActive; - - this.lines = { - top: ui.querySelector("#line-top"), - bottom: ui.querySelector("#line-bottom"), - left: ui.querySelector("#line-left"), - right: ui.querySelector("#line-right"), - direction: ui.querySelector("#line-direction"), - }; - - - this.orgPointInd = ui.querySelector("#origin-point-indicator"); - - this.svg = ui.querySelector("#view-svg"); - - - this.handles = { - - top: ui.querySelector("#line-top-handle"), - bottom: ui.querySelector("#line-bottom-handle"), - left: ui.querySelector("#line-left-handle"), - right: ui.querySelector("#line-right-handle"), - direction: ui.querySelector("#line-direction-handle"), - - topleft: ui.querySelector("#top-left-handle"), - topright: ui.querySelector("#top-right-handle"), - bottomleft: ui.querySelector("#bottom-left-handle"), - bottomright: ui.querySelector("#bottom-right-handle"), - - move: ui.querySelector("#move-handle"), - }; - - this.buttons = { - fit_position: ui.querySelector("#v-fit-position"), - fit_size: ui.querySelector("#v-fit-size"), - fit_rotation: ui.querySelector("#v-fit-rotation"), - fit_all: ui.querySelector("#v-fit-all"), - reset_rotation: ui.querySelector("#v-reset-rotation"), - fit_moving_direction: ui.querySelector("#v-fit-moving-direction"), - }; - - - ui.onkeydown = this.on_key_down.bind(this); - ui.onmouseenter = (event)=>{ + this.orgPointInd = ui.querySelector("#origin-point-indicator"); - if (this.isActive()) - { - ui.focus(); + this.svg = ui.querySelector("#view-svg"); - ui.querySelector("#v-buttons").style.display="inherit"; + this.handles = { + top: ui.querySelector("#line-top-handle"), + bottom: ui.querySelector("#line-bottom-handle"), + left: ui.querySelector("#line-left-handle"), + right: ui.querySelector("#line-right-handle"), + direction: ui.querySelector("#line-direction-handle"), - if (this.on_focus) - this.on_focus(); - } - }; - ui.onmouseleave = (event)=>{ - if (this.showButtonsTimer) - clearTimeout(this.showButtonsTimer); - - this.hide_buttons(); + topleft: ui.querySelector("#top-left-handle"), + topright: ui.querySelector("#top-right-handle"), + bottomleft: ui.querySelector("#bottom-left-handle"), + bottomright: ui.querySelector("#bottom-right-handle"), - ui.blur(); - }; + move: ui.querySelector("#move-handle"), + }; - ui.onwheel = event=>{ - event.stopPropagation(); - event.preventDefault(); - this.on_wheel(event.deltaY); - }; + this.buttons = { + fit_position: ui.querySelector("#v-fit-position"), + fit_size: ui.querySelector("#v-fit-size"), + fit_rotation: ui.querySelector("#v-fit-rotation"), + fit_all: ui.querySelector("#v-fit-all"), + reset_rotation: ui.querySelector("#v-reset-rotation"), + fit_moving_direction: ui.querySelector("#v-fit-moving-direction"), + }; - this.install_edge_hanler('left', this.handles.left, this.lines, {x:-1,y:0}); - this.install_edge_hanler('right', this.handles.right, this.lines, {x:1, y:0}); - this.install_edge_hanler('top', this.handles.top, this.lines, {x:0, y:1}); - this.install_edge_hanler('bottom', this.handles.bottom, this.lines, {x:0, y:-1}); - this.install_edge_hanler('top,left', this.handles.topleft, this.lines, {x:-1, y:1}); - this.install_edge_hanler('top,right', this.handles.topright, this.lines, {x:1, y:1}); - this.install_edge_hanler('bottom,left', this.handles.bottomleft, this.lines, {x:-1, y:-1}); - this.install_edge_hanler('bottom,right', this.handles.bottomright, this.lines, {x:1, y:-1}); - this.install_edge_hanler('left,right,top,bottom', this.handles.move, this.lines, null); - - if (this.on_direction_changed){ - this.install_direction_handler("line-direction"); - } + ui.onkeydown = this.on_key_down.bind(this); + ui.onmouseenter = (event) => { + if (this.isActive()) { + ui.focus(); - this.install_buttons(); + ui.querySelector("#v-buttons").style.display = "inherit"; - } + if (this.on_focus) this.on_focus(); + } + }; + ui.onmouseleave = (event) => { + if (this.showButtonsTimer) clearTimeout(this.showButtonsTimer); + this.hide_buttons(); - mouse_start_pos = null; + ui.blur(); + }; - view_handle_dimension = { //dimension of the enclosed box - x: 0, //width - y: 0, //height - } - - view_center = { - x: 0, - y: 0, + ui.onwheel = (event) => { + event.stopPropagation(); + event.preventDefault(); + this.on_wheel(event.deltaY); }; - line(name){ - return this.lines[name]; + this.install_edge_hanler("left", this.handles.left, this.lines, { + x: -1, + y: 0, + }); + this.install_edge_hanler("right", this.handles.right, this.lines, { + x: 1, + y: 0, + }); + this.install_edge_hanler("top", this.handles.top, this.lines, { + x: 0, + y: 1, + }); + this.install_edge_hanler("bottom", this.handles.bottom, this.lines, { + x: 0, + y: -1, + }); + this.install_edge_hanler("top,left", this.handles.topleft, this.lines, { + x: -1, + y: 1, + }); + this.install_edge_hanler("top,right", this.handles.topright, this.lines, { + x: 1, + y: 1, + }); + this.install_edge_hanler( + "bottom,left", + this.handles.bottomleft, + this.lines, + { x: -1, y: -1 } + ); + this.install_edge_hanler( + "bottom,right", + this.handles.bottomright, + this.lines, + { x: 1, y: -1 } + ); + this.install_edge_hanler( + "left,right,top,bottom", + this.handles.move, + this.lines, + null + ); + + if (this.on_direction_changed) { + this.install_direction_handler("line-direction"); } - show_lines(){ - let theme = document.documentElement.className; + this.install_buttons(); + } - let lineColor = "yellow"; - if (theme == "theme-light") - lineColor = "red"; + mouse_start_pos = null; - for (var l in this.lines){ - this.lines[l].style.stroke=lineColor; - }; - } - hide_lines(){ - for (var l in this.lines){ - this.lines[l].style.stroke="#00000000"; - } - }; + view_handle_dimension = { + //dimension of the enclosed box + x: 0, //width + y: 0, //height + }; - hightlight_line(line) - { - let theme = document.documentElement.className; + view_center = { + x: 0, + y: 0, + }; - let lineColor = "red"; - if (theme == "theme-light") - lineColor = "blue"; + line(name) { + return this.lines[name]; + } - line.style.stroke=lineColor; - } + show_lines() { + let theme = document.documentElement.className; + let lineColor = "yellow"; + if (theme == "theme-light") lineColor = "red"; - disable_handle_except(exclude){ - for (var h in this.handles){ - if (this.handles[h] != exclude) - this.handles[h].style.display='none'; - } + for (var l in this.lines) { + this.lines[l].style.stroke = lineColor; } - - enable_handles(){ - for (var h in this.handles){ - this.handles[h].style.display='inherit'; - } + } + hide_lines() { + for (var l in this.lines) { + this.lines[l].style.stroke = "#00000000"; } + } - move_lines(delta, direction){ - - var x1 = this.view_center.x-this.view_handle_dimension.x/2; - var y1 = this.view_center.y-this.view_handle_dimension.y/2; - var x2 = this.view_center.x+this.view_handle_dimension.x/2; - var y2 = this.view_center.y+this.view_handle_dimension.y/2; - - if (direction){ - if (direction.x == 1){ //right - x2 += delta.x; - } else if (direction.x == -1){ //left - x1 += delta.x; - } - - if (direction.y == -1){ //bottom - y2 += delta.y; - } else if (direction.y == 1){ //top - y1 += delta.y; - } - } - else { - x1 += delta.x; - y1 += delta.y; - x2 += delta.x; - y2 += delta.y; - } + hightlight_line(line) { + let theme = document.documentElement.className; + + let lineColor = "red"; + if (theme == "theme-light") lineColor = "blue"; + + line.style.stroke = lineColor; + } - this.set_line_pos(Math.ceil(x1),Math.ceil(x2),Math.ceil(y1),Math.ceil(y2)); + disable_handle_except(exclude) { + for (var h in this.handles) { + if (this.handles[h] != exclude) this.handles[h].style.display = "none"; } + } - set_line_pos(x1,x2,y1,y2){ - this.lines.top.setAttribute("x1", "0%"); - this.lines.top.setAttribute("y1", y1); - this.lines.top.setAttribute("x2", "100%"); - this.lines.top.setAttribute("y2", y1); - - this.lines.bottom.setAttribute("x1", "0%"); - this.lines.bottom.setAttribute("y1", y2); - this.lines.bottom.setAttribute("x2", "100%"); - this.lines.bottom.setAttribute("y2", y2); - - this.lines.left.setAttribute("x1", x1); - this.lines.left.setAttribute("y1", "0%"); - this.lines.left.setAttribute("x2", x1); - this.lines.left.setAttribute("y2", "100%"); - - this.lines.right.setAttribute("x1", x2); - this.lines.right.setAttribute("y1", "0%"); - this.lines.right.setAttribute("x2", x2); - this.lines.right.setAttribute("y2", "100%"); + enable_handles() { + for (var h in this.handles) { + this.handles[h].style.display = "inherit"; + } + } + + move_lines(delta, direction) { + var x1 = this.view_center.x - this.view_handle_dimension.x / 2; + var y1 = this.view_center.y - this.view_handle_dimension.y / 2; + var x2 = this.view_center.x + this.view_handle_dimension.x / 2; + var y2 = this.view_center.y + this.view_handle_dimension.y / 2; + + if (direction) { + if (direction.x == 1) { + //right + x2 += delta.x; + } else if (direction.x == -1) { + //left + x1 += delta.x; + } + + if (direction.y == -1) { + //bottom + y2 += delta.y; + } else if (direction.y == 1) { + //top + y1 += delta.y; + } + } else { + x1 += delta.x; + y1 += delta.y; + x2 += delta.x; + y2 += delta.y; } - set_org_point_ind_pos(viewWidth, viewHeight, objPos, objRot){ - /* + this.set_line_pos( + Math.ceil(x1), + Math.ceil(x2), + Math.ceil(y1), + Math.ceil(y2) + ); + } + + set_line_pos(x1, x2, y1, y2) { + this.lines.top.setAttribute("x1", "0%"); + this.lines.top.setAttribute("y1", y1); + this.lines.top.setAttribute("x2", "100%"); + this.lines.top.setAttribute("y2", y1); + + this.lines.bottom.setAttribute("x1", "0%"); + this.lines.bottom.setAttribute("y1", y2); + this.lines.bottom.setAttribute("x2", "100%"); + this.lines.bottom.setAttribute("y2", y2); + + this.lines.left.setAttribute("x1", x1); + this.lines.left.setAttribute("y1", "0%"); + this.lines.left.setAttribute("x2", x1); + this.lines.left.setAttribute("y2", "100%"); + + this.lines.right.setAttribute("x1", x2); + this.lines.right.setAttribute("y1", "0%"); + this.lines.right.setAttribute("x2", x2); + this.lines.right.setAttribute("y2", "100%"); + } + + set_org_point_ind_pos(viewWidth, viewHeight, objPos, objRot) { + /* cos -sin sin cos * @@ -245,1263 +266,1293 @@ class ProjectiveView{ objPos.y */ - let c = Math.cos(objRot); // for topview, x goes upward, so we add pi/2 - let s = Math.sin(objRot); - - let relx = c*(-objPos.x) + s*(-objPos.y); - let rely = -s*(-objPos.x) + c*(-objPos.y); + let c = Math.cos(objRot); // for topview, x goes upward, so we add pi/2 + let s = Math.sin(objRot); - let radius = Math.sqrt(viewWidth*viewWidth/4 + viewHeight*viewHeight/4); - let distToRog = Math.sqrt(relx*relx + rely*rely) + let relx = c * -objPos.x + s * -objPos.y; + let rely = -s * -objPos.x + c * -objPos.y; - let indPosX3d = relx*radius/distToRog; - let indPosY3d = rely*radius/distToRog; + let radius = Math.sqrt( + (viewWidth * viewWidth) / 4 + (viewHeight * viewHeight) / 4 + ); + let distToRog = Math.sqrt(relx * relx + rely * rely); - let indPosX = -indPosY3d; - let indPosY = -indPosX3d; - - let dotRelPos = 0.8; - // now its pixel coordinates, x goes right, y goes down - if (indPosX > viewWidth/2*dotRelPos){ - let shrinkRatio = viewWidth/2*dotRelPos/indPosX; + let indPosX3d = (relx * radius) / distToRog; + let indPosY3d = (rely * radius) / distToRog; - indPosX = viewWidth/2*dotRelPos; - indPosY = indPosY*shrinkRatio; - } + let indPosX = -indPosY3d; + let indPosY = -indPosX3d; - if (indPosX < -viewWidth/2*dotRelPos){ - let shrinkRatio = -viewWidth/2*dotRelPos/indPosX; + let dotRelPos = 0.8; + // now its pixel coordinates, x goes right, y goes down + if (indPosX > (viewWidth / 2) * dotRelPos) { + let shrinkRatio = ((viewWidth / 2) * dotRelPos) / indPosX; - indPosX = -viewWidth/2*dotRelPos; - indPosY = indPosY*shrinkRatio; - } + indPosX = (viewWidth / 2) * dotRelPos; + indPosY = indPosY * shrinkRatio; + } - if (indPosY > viewHeight/2*dotRelPos){ - let shrinkRatio = viewHeight/2*dotRelPos/indPosY; + if (indPosX < (-viewWidth / 2) * dotRelPos) { + let shrinkRatio = ((-viewWidth / 2) * dotRelPos) / indPosX; - indPosY = viewHeight/2*dotRelPos; - indPosX = indPosX*shrinkRatio; - } + indPosX = (-viewWidth / 2) * dotRelPos; + indPosY = indPosY * shrinkRatio; + } - if (indPosY < -viewHeight/2*dotRelPos){ - let shrinkRatio = -viewHeight/2*dotRelPos/indPosY; + if (indPosY > (viewHeight / 2) * dotRelPos) { + let shrinkRatio = ((viewHeight / 2) * dotRelPos) / indPosY; - indPosY = -viewHeight/2*dotRelPos; - indPosX = indPosX*shrinkRatio; - } + indPosY = (viewHeight / 2) * dotRelPos; + indPosX = indPosX * shrinkRatio; + } + if (indPosY < (-viewHeight / 2) * dotRelPos) { + let shrinkRatio = ((-viewHeight / 2) * dotRelPos) / indPosY; - this.orgPointInd.setAttribute("cx", viewWidth/2+indPosX); - this.orgPointInd.setAttribute("cy", viewHeight/2+indPosY); + indPosY = (-viewHeight / 2) * dotRelPos; + indPosX = indPosX * shrinkRatio; } - // when direction handler is draging - rotate_lines(theta){ - - console.log(theta); - theta = -theta-Math.PI/2; - console.log(theta); - // we use rotation matrix - var trans_matrix =[ - Math.cos(theta), Math.sin(theta), this.view_center.x, - -Math.sin(theta), Math.cos(theta), this.view_center.y, - 0, 0, 1, - ] - - var points ;/*= `[ + this.orgPointInd.setAttribute("cx", viewWidth / 2 + indPosX); + this.orgPointInd.setAttribute("cy", viewHeight / 2 + indPosY); + } + + // when direction handler is draging + rotate_lines(theta) { + console.log(theta); + theta = -theta - Math.PI / 2; + console.log(theta); + // we use rotation matrix + var trans_matrix = [ + Math.cos(theta), + Math.sin(theta), + this.view_center.x, + -Math.sin(theta), + Math.cos(theta), + this.view_center.y, + 0, + 0, + 1, + ]; + + var points; /*= `[ -view_handle_dimension.x/2, view_handle_dimension.x/2,view_handle_dimension.x/2,-view_handle_dimension.x/2, 0, -view_handle_dimension.y/2, -view_handle_dimension.y/2,view_handle_dimension.y/2, view_handle_dimension.y/2, -this.view_center.y, 1,1,1,1,1 ]; */ - var trans_points ;//= matmul2(trans_matrix, points, 3); - - //console.log(points); - //var trans_points ;//= matmul2(trans_matrix, points, 3); - //console.log(trans_points); - - points =[ - 0, - -this.view_center.y, - 1 - ]; - trans_points = matmul2(trans_matrix, points, 3); - this.lines.direction.setAttribute("x2", Math.ceil(trans_points[0])); - this.lines.direction.setAttribute("y2", Math.ceil(trans_points[1])); - - points =[ - -this.view_center.x, this.view_center.x,//-view_handle_dimension.x/2, view_handle_dimension.x/2, - -this.view_handle_dimension.y/2, -this.view_handle_dimension.y/2, - 1,1, - ]; - var trans_points = matmul2(trans_matrix, points, 3); - - this.lines.top.setAttribute("x1", Math.ceil(trans_points[0])); - this.lines.top.setAttribute("y1", Math.ceil(trans_points[0+2])); - this.lines.top.setAttribute("x2", Math.ceil(trans_points[1])); - this.lines.top.setAttribute("y2", Math.ceil(trans_points[1+2])); - - - points =[ - -this.view_handle_dimension.x/2, -this.view_handle_dimension.x/2, - -this.view_center.y, this.view_center.y, - 1,1, - ]; - trans_points = matmul2(trans_matrix, points, 3); - - this.lines.left.setAttribute("x1", Math.ceil(trans_points[0])); - this.lines.left.setAttribute("y1", Math.ceil(trans_points[0+2])); - this.lines.left.setAttribute("x2", Math.ceil(trans_points[1])); - this.lines.left.setAttribute("y2", Math.ceil(trans_points[1+2])); - - - points =[ - this.view_center.x,-this.view_center.x, - this.view_handle_dimension.y/2, this.view_handle_dimension.y/2, - 1,1 - ]; - trans_points = matmul2(trans_matrix, points, 3); - this.lines.bottom.setAttribute("x1", Math.ceil(trans_points[1])); - this.lines.bottom.setAttribute("y1", Math.ceil(trans_points[1+2])); - this.lines.bottom.setAttribute("x2", Math.ceil(trans_points[0])); - this.lines.bottom.setAttribute("y2", Math.ceil(trans_points[0+2])); - - points =[ - this.view_handle_dimension.x/2,this.view_handle_dimension.x/2, - -this.view_center.y,this.view_center.y, - 1,1 - ]; - trans_points = matmul2(trans_matrix, points, 3); - - this.lines.right.setAttribute("x1", Math.ceil(trans_points[0])); - this.lines.right.setAttribute("y1", Math.ceil(trans_points[0+2])); - this.lines.right.setAttribute("x2", Math.ceil(trans_points[1])); - this.lines.right.setAttribute("y2", Math.ceil(trans_points[1+2])); - + var trans_points; //= matmul2(trans_matrix, points, 3); + + //console.log(points); + //var trans_points ;//= matmul2(trans_matrix, points, 3); + //console.log(trans_points); + + points = [0, -this.view_center.y, 1]; + trans_points = matmul2(trans_matrix, points, 3); + this.lines.direction.setAttribute("x2", Math.ceil(trans_points[0])); + this.lines.direction.setAttribute("y2", Math.ceil(trans_points[1])); + + points = [ + -this.view_center.x, + this.view_center.x, //-view_handle_dimension.x/2, view_handle_dimension.x/2, + -this.view_handle_dimension.y / 2, + -this.view_handle_dimension.y / 2, + 1, + 1, + ]; + var trans_points = matmul2(trans_matrix, points, 3); + + this.lines.top.setAttribute("x1", Math.ceil(trans_points[0])); + this.lines.top.setAttribute("y1", Math.ceil(trans_points[0 + 2])); + this.lines.top.setAttribute("x2", Math.ceil(trans_points[1])); + this.lines.top.setAttribute("y2", Math.ceil(trans_points[1 + 2])); + + points = [ + -this.view_handle_dimension.x / 2, + -this.view_handle_dimension.x / 2, + -this.view_center.y, + this.view_center.y, + 1, + 1, + ]; + trans_points = matmul2(trans_matrix, points, 3); + + this.lines.left.setAttribute("x1", Math.ceil(trans_points[0])); + this.lines.left.setAttribute("y1", Math.ceil(trans_points[0 + 2])); + this.lines.left.setAttribute("x2", Math.ceil(trans_points[1])); + this.lines.left.setAttribute("y2", Math.ceil(trans_points[1 + 2])); + + points = [ + this.view_center.x, + -this.view_center.x, + this.view_handle_dimension.y / 2, + this.view_handle_dimension.y / 2, + 1, + 1, + ]; + trans_points = matmul2(trans_matrix, points, 3); + this.lines.bottom.setAttribute("x1", Math.ceil(trans_points[1])); + this.lines.bottom.setAttribute("y1", Math.ceil(trans_points[1 + 2])); + this.lines.bottom.setAttribute("x2", Math.ceil(trans_points[0])); + this.lines.bottom.setAttribute("y2", Math.ceil(trans_points[0 + 2])); + + points = [ + this.view_handle_dimension.x / 2, + this.view_handle_dimension.x / 2, + -this.view_center.y, + this.view_center.y, + 1, + 1, + ]; + trans_points = matmul2(trans_matrix, points, 3); + + this.lines.right.setAttribute("x1", Math.ceil(trans_points[0])); + this.lines.right.setAttribute("y1", Math.ceil(trans_points[0 + 2])); + this.lines.right.setAttribute("x2", Math.ceil(trans_points[1])); + this.lines.right.setAttribute("y2", Math.ceil(trans_points[1 + 2])); + } + + update_view_handle(viewport, obj_dimension, obj_pos, obj_rot) { + var viewport_ratio = viewport.width / viewport.height; + var box_ratio = obj_dimension.x / obj_dimension.y; + + var width = 0; + var height = 0; + + if (box_ratio > viewport_ratio) { + //handle width is viewport.width*2/3 + width = (viewport.width * (2 / 3)) / viewport.zoom_ratio; + height = width / box_ratio; + } else { + //handle height is viewport.height*2/3 + height = (viewport.height * 2) / 3 / viewport.zoom_ratio; + width = height * box_ratio; } - - update_view_handle(viewport, obj_dimension, obj_pos, obj_rot){ - var viewport_ratio = viewport.width/viewport.height; - var box_ratio = obj_dimension.x/obj_dimension.y; - - var width=0; - var height=0; - - if (box_ratio > viewport_ratio){ - //handle width is viewport.width*2/3 - width = viewport.width*(2/3)/viewport.zoom_ratio; - height = width/box_ratio; - } - else{ - //handle height is viewport.height*2/3 - height = viewport.height*2/3/viewport.zoom_ratio; - width = height*box_ratio; - } - - this.view_handle_dimension.x = width; - this.view_handle_dimension.y = height; - - // viewport width/height is position-irrelavent - // so x and y is relative value. - var x = viewport.width/2;//viewport.left + viewport.width/2; - var y = viewport.height/2//viewport.bottom - viewport.height/2; - - var left = x-width/2; - var right = x+width/2; - var top = y-height/2; - var bottom = y+height/2; - - this.view_center.x = x; - this.view_center.y = y; - - this.set_line_pos(left, right, top, bottom); - - if (obj_pos && obj_rot){ - this.set_org_point_ind_pos(viewport.width, viewport.height, obj_pos, obj_rot); - } - - // note when the object is too thin, the height/width value may be negative, - // this causes error reporting, but we just let it be. - var de = this.handles.left; - de.setAttribute('x', Math.ceil(left-10)); - de.setAttribute('y', "0%"); //Math.ceil(top+10)); - de.setAttribute('height', "100%");//Math.ceil(bottom-top-20)); - de.setAttribute('width', 20); - - - de = this.handles.right; - de.setAttribute('x', Math.ceil(right-10)); - de.setAttribute('y', "0%");//Math.ceil(top+10)); - de.setAttribute('height', "100%");//Math.ceil(bottom-top-20)); - de.setAttribute('width', 20); - - de = this.handles.top; - de.setAttribute('x', "0%");//Math.ceil(left+10)); - de.setAttribute('y', Math.ceil(top-10)); - de.setAttribute('width', "100%");//Math.ceil(right-left-20)); - de.setAttribute('height', 20); - - de = this.handles.bottom; - de.setAttribute('x', "0%");//Math.ceil(left+10)); - de.setAttribute('y', Math.ceil(bottom-10)); - de.setAttribute('width', "100%");//Math.ceil(right-left-20)); - de.setAttribute('height', 20); - - - de = this.handles.topleft; - de.setAttribute('x', Math.ceil(left-10)); - de.setAttribute('y', Math.ceil(top-10)); - - - de = this.handles.topright; - de.setAttribute('x', Math.ceil(right-10)); - de.setAttribute('y', Math.ceil(top-10)); - - - de = this.handles.bottomleft; - de.setAttribute('x', Math.ceil(left-10)); - de.setAttribute('y', Math.ceil(bottom-10)); - - de = this.handles.bottomright; - de.setAttribute('x', Math.ceil(right-10)); - de.setAttribute('y', Math.ceil(bottom-10)); - - //direction - if (this.on_direction_changed){ - de = this.lines.direction; - de.setAttribute('x1', Math.ceil((left+right)/2)); - de.setAttribute('y1', Math.ceil((top+bottom)/2)); - de.setAttribute('x2', Math.ceil((left+right)/2)); - de.setAttribute('y2', Math.ceil(0)); - - de = this.handles.direction; - de.setAttribute('x', Math.ceil((left+right)/2-10)); - de.setAttribute('y', 0);//Math.ceil(top+10)); - de.setAttribute('height', Math.ceil((bottom-top)/2-10+top)); - } - else{ - de = this.lines.direction; - de.style.display = "none"; - - de = this.handles.direction; - de.style.display = "none"; - } - - - // move handle - de = this.ui.querySelector("#move-handle"); - de.setAttribute('x', Math.ceil((left+right)/2-10)); - de.setAttribute('y', Math.ceil((top+bottom)/2-10)); - } - - - showButtonsTimer = null; - hide_buttons(delay){ - this.ui.querySelector("#v-buttons").style.display="none"; - - if (delay) - { - if (this.showButtonsTimer){ - clearTimeout(this.showButtonsTimer); - } - - this.showButtonsTimer = setTimeout(() => { - this.ui.querySelector("#v-buttons").style.display="inherit"; - }, 200); - } - } - - hide(){ - this.hide_lines(this.lines); - }; - //install_move_handler(); - - install_edge_hanler(name, handle, lines, direction) - { - - handle.onmouseenter = ()=>{ - if (this.isActive()){ - this.show_lines(); - - if (name) - name.split(",").forEach(n=> this.hightlight_line(lines[n])); - } + this.view_handle_dimension.x = width; + this.view_handle_dimension.y = height; - }; - handle.onmouseleave = ()=>this.hide(); + // viewport width/height is position-irrelavent + // so x and y is relative value. + var x = viewport.width / 2; //viewport.left + viewport.width/2; + var y = viewport.height / 2; //viewport.bottom - viewport.height/2; + var left = x - width / 2; + var right = x + width / 2; + var top = y - height / 2; + var bottom = y + height / 2; - // handle.onmouseup = event=>{ - // if (event.which!=1) - // return; + this.view_center.x = x; + this.view_center.y = y; - // //line.style["stroke-dasharray"]="none"; - // //hide(); - // handle.onmouseleave = hide; - // }; + this.set_line_pos(left, right, top, bottom); - handle.ondblclick= (event)=>{ - if (event.which!=1) - return; - event.stopPropagation(); - event.preventDefault(); - this.on_auto_shrink(direction); //if double click on 'move' handler, the directoin is null - - }; - - handle.onmousedown = (event)=>{ - if (event.which!=1) - return; - - var svg = this.svg; - - // - event.stopPropagation(); - event.preventDefault(); - - - this.disable_handle_except(handle); - this.hide_buttons(); - - handle.onmouseleave = null; - - this.mouse_start_pos={x: event.layerX,y:event.layerY,}; - let mouse_cur_pos = {x: this.mouse_start_pos.x, y: this.mouse_start_pos.y}; - - console.log(this.mouse_start_pos); - - svg.onmouseup = (event)=>{ - svg.onmousemove = null; - svg.onmouseup=null; - this.enable_handles(); - // restore color - //hide(); - handle.onmouseleave = this.hide.bind(this); - - this.ui.querySelector("#v-buttons").style.display="inherit"; - - var handle_delta = { - x: mouse_cur_pos.x - this.mouse_start_pos.x, - y: -(mouse_cur_pos.y - this.mouse_start_pos.y), //reverse since it'll be used by 3d-coord system - }; - - console.log("delta", handle_delta); - if (handle_delta.x == 0 && handle_delta.y==0 && !event.ctrlKey && !event.shiftKey){ - return; - } - - var ratio_delta = { - x: handle_delta.x/this.view_handle_dimension.x, - y: handle_delta.y/this.view_handle_dimension.y - }; - - - if (direction){ - this.on_edge_changed(ratio_delta, direction, event.ctrlKey, event.shiftKey); - - // if (event.ctrlKey){ - // this.on_auto_shrink(direction); - // } - } - else{ - // when intall handler for mover, the direcion is left null - this.on_moved(ratio_delta); - } - } - - svg.onmousemove = (event)=>{ - - if (event.which!=1) - return; - - mouse_cur_pos={x: event.layerX,y:event.layerY,}; - - var handle_delta = { - x: mouse_cur_pos.x - this.mouse_start_pos.x, - y: mouse_cur_pos.y - this.mouse_start_pos.y, // don't reverse direction - }; - - this.move_lines(handle_delta, direction); - } - }; + if (obj_pos && obj_rot) { + this.set_org_point_ind_pos( + viewport.width, + viewport.height, + obj_pos, + obj_rot + ); } - install_direction_handler(linename){ - var handle = this.ui.querySelector("#"+linename+"-handle"); - var line = this.ui.querySelector("#"+linename); - var svg = this.svg; - - handle.onmouseenter = (event)=>{ - if (this.isActive()){ - this.show_lines(); - this.hightlight_line(line); - } - }; - - - handle.onmouseleave = ()=>this.hide(); - - handle.ondblclick= (event)=>{ - event.stopPropagation(); - event.preventDefault(); - //transform_bbox(this_axis+"_rotate_reverse"); - this.on_direction_changed(Math.PI); - }; - - - // function hide(event){ - // line.style.stroke="#00000000"; - // }; - - // handle.onmouseup = event=>{ - // if (event.which!=1) - // return; - // //line.style["stroke-dasharray"]="none"; - // //line.style.stroke="#00000000"; - // handle.onmouseleave = hide; - // }; - - - handle.onmousedown = (event)=>{ - - if (event.which!=1) - return; - - event.stopPropagation(); - event.preventDefault(); - - //line.style.stroke="yellow"; - handle.onmouseleave = null; - //show_lines(lines); - - this.disable_handle_except(handle); - - this.hide_buttons(); + // note when the object is too thin, the height/width value may be negative, + // this causes error reporting, but we just let it be. + var de = this.handles.left; + de.setAttribute("x", Math.ceil(left - 10)); + de.setAttribute("y", "0%"); //Math.ceil(top+10)); + de.setAttribute("height", "100%"); //Math.ceil(bottom-top-20)); + de.setAttribute("width", 20); + + de = this.handles.right; + de.setAttribute("x", Math.ceil(right - 10)); + de.setAttribute("y", "0%"); //Math.ceil(top+10)); + de.setAttribute("height", "100%"); //Math.ceil(bottom-top-20)); + de.setAttribute("width", 20); + + de = this.handles.top; + de.setAttribute("x", "0%"); //Math.ceil(left+10)); + de.setAttribute("y", Math.ceil(top - 10)); + de.setAttribute("width", "100%"); //Math.ceil(right-left-20)); + de.setAttribute("height", 20); + + de = this.handles.bottom; + de.setAttribute("x", "0%"); //Math.ceil(left+10)); + de.setAttribute("y", Math.ceil(bottom - 10)); + de.setAttribute("width", "100%"); //Math.ceil(right-left-20)); + de.setAttribute("height", 20); + + de = this.handles.topleft; + de.setAttribute("x", Math.ceil(left - 10)); + de.setAttribute("y", Math.ceil(top - 10)); + + de = this.handles.topright; + de.setAttribute("x", Math.ceil(right - 10)); + de.setAttribute("y", Math.ceil(top - 10)); + + de = this.handles.bottomleft; + de.setAttribute("x", Math.ceil(left - 10)); + de.setAttribute("y", Math.ceil(bottom - 10)); + + de = this.handles.bottomright; + de.setAttribute("x", Math.ceil(right - 10)); + de.setAttribute("y", Math.ceil(bottom - 10)); + + //direction + if (this.on_direction_changed) { + de = this.lines.direction; + de.setAttribute("x1", Math.ceil((left + right) / 2)); + de.setAttribute("y1", Math.ceil((top + bottom) / 2)); + de.setAttribute("x2", Math.ceil((left + right) / 2)); + de.setAttribute("y2", Math.ceil(0)); + + de = this.handles.direction; + de.setAttribute("x", Math.ceil((left + right) / 2 - 10)); + de.setAttribute("y", 0); //Math.ceil(top+10)); + de.setAttribute("height", Math.ceil((bottom - top) / 2 - 10 + top)); + } else { + de = this.lines.direction; + de.style.display = "none"; + + de = this.handles.direction; + de.style.display = "none"; + } - let handle_center={ - x: parseInt(line.getAttribute('x1')), - } + // move handle + de = this.ui.querySelector("#move-handle"); + de.setAttribute("x", Math.ceil((left + right) / 2 - 10)); + de.setAttribute("y", Math.ceil((top + bottom) / 2 - 10)); + } - this.mouse_start_pos={ - x: event.layerX, - y:event.layerY, + showButtonsTimer = null; + hide_buttons(delay) { + this.ui.querySelector("#v-buttons").style.display = "none"; - handle_offset_x: handle_center.x - event.layerX, - }; + if (delay) { + if (this.showButtonsTimer) { + clearTimeout(this.showButtonsTimer); + } + this.showButtonsTimer = setTimeout(() => { + this.ui.querySelector("#v-buttons").style.display = "inherit"; + }, 200); + } + } - let mouse_cur_pos = {x: this.mouse_start_pos.x, y: this.mouse_start_pos.y}; + hide() { + this.hide_lines(this.lines); + } + //install_move_handler(); - console.log(this.mouse_start_pos); + install_edge_hanler(name, handle, lines, direction) { + handle.onmouseenter = () => { + if (this.isActive()) { + this.show_lines(); - let theta = 0; + if (name) + name.split(",").forEach((n) => this.hightlight_line(lines[n])); + } + }; + handle.onmouseleave = () => this.hide(); + + // handle.onmouseup = event=>{ + // if (event.which!=1) + // return; + + // //line.style["stroke-dasharray"]="none"; + // //hide(); + // handle.onmouseleave = hide; + // }; + + handle.ondblclick = (event) => { + if (event.which != 1) return; + event.stopPropagation(); + event.preventDefault(); + this.on_auto_shrink(direction); //if double click on 'move' handler, the directoin is null + }; - svg.onmousemove = (event)=>{ - - mouse_cur_pos={x: event.layerX,y:event.layerY,}; - - let handle_center_cur_pos = { - x: mouse_cur_pos.x + this.mouse_start_pos.handle_offset_x, - y: mouse_cur_pos.y, - }; + handle.onmousedown = (event) => { + if (event.which != 1) return; - + var svg = this.svg; - theta = Math.atan2( - handle_center_cur_pos.y-this.view_center.y, - handle_center_cur_pos.x-this.view_center.x); - console.log(theta); + // + event.stopPropagation(); + event.preventDefault(); - this.rotate_lines(theta); - }; + this.disable_handle_except(handle); + this.hide_buttons(); - svg.onmouseup = event=>{ - svg.onmousemove = null; - svg.onmouseup=null; + handle.onmouseleave = null; - // restore color - //line.style.stroke="#00000000"; - this.enable_handles(); - handle.onmouseleave = this.hide.bind(this); + this.mouse_start_pos = { x: event.layerX, y: event.layerY }; + let mouse_cur_pos = { + x: this.mouse_start_pos.x, + y: this.mouse_start_pos.y, + }; - this.ui.querySelector("#v-buttons").style.display="inherit"; + console.log(this.mouse_start_pos); - if (theta == 0){ - return; - } + svg.onmouseup = (event) => { + svg.onmousemove = null; + svg.onmouseup = null; + this.enable_handles(); + // restore color + //hide(); + handle.onmouseleave = this.hide.bind(this); - this.on_direction_changed(-theta-Math.PI/2, event.ctrlKey); - - }; + this.ui.querySelector("#v-buttons").style.display = "inherit"; - + var handle_delta = { + x: mouse_cur_pos.x - this.mouse_start_pos.x, + y: -(mouse_cur_pos.y - this.mouse_start_pos.y), //reverse since it'll be used by 3d-coord system }; - } - on_key_down(event){ - - switch(event.key){ - case 'e': - event.preventDefault(); - event.stopPropagation(); - this.on_direction_changed(-this.cfg.rotateStep, event.ctrlKey); - this.hide_buttons(true); - return true; - case 'q': - event.preventDefault(); - event.stopPropagation(); - this.on_direction_changed(this.cfg.rotateStep, event.ctrlKey); - this.hide_buttons(true); - break; - case 'f': - event.preventDefault(); - event.stopPropagation(); - this.on_direction_changed(-this.cfg.rotateStep, true); - this.hide_buttons(true); - break; - case 'r': - event.preventDefault(); - event.stopPropagation(); - this.on_direction_changed(this.cfg.rotateStep, true); - this.hide_buttons(true); - break; - case 'g': - event.preventDefault(); - event.stopPropagation(); - this.on_direction_changed(Math.PI, false); - break; - case 'w': - case 'ArrowUp': - event.preventDefault(); - event.stopPropagation(); - this.on_moved({x:0, y: this.cfg.moveStep}); - this.hide_buttons(true); - break; - case 's': - if (!event.ctrlKey){ - event.preventDefault(); - event.stopPropagation(); - this.on_moved({x:0, y:-this.cfg.moveStep}); - this.hide_buttons(true); - break; - } else{ - console.log("ctrl+s"); - } - break; - case 'ArrowDown': - event.preventDefault(); - event.stopPropagation(); - this.on_moved({x:0, y:-this.cfg.moveStep}); - this.hide_buttons(true); - break; - case 'a': - if (event.ctrlKey) - { - break; - } - // no break; - case 'ArrowLeft': - event.preventDefault(); - event.stopPropagation(); - this.on_moved({x:-this.cfg.moveStep, y:0}); - this.hide_buttons(true); - break; - case 'd': - if (event.ctrlKey){ - console.log("ctrl+d"); - this.on_box_remove(); - break; - } - case 'ArrowRight': - event.preventDefault(); - event.stopPropagation(); - this.on_moved({x:this.cfg.moveStep, y:0}); - this.hide_buttons(true); - break; - case 'Delete': - this.on_box_remove(); - break; + console.log("delta", handle_delta); + if ( + handle_delta.x == 0 && + handle_delta.y == 0 && + !event.ctrlKey && + !event.shiftKey + ) { + return; } - } - - install_buttons() - { - let buttons = this.buttons; - let ignore_left_mouse_down = (event)=>{ - if (event.which == 1){ - event.stopPropagation(); - } + var ratio_delta = { + x: handle_delta.x / this.view_handle_dimension.x, + y: handle_delta.y / this.view_handle_dimension.y, }; - if (buttons.fit_rotation){ - buttons.fit_rotation.onmousedown = ignore_left_mouse_down; - buttons.fit_rotation.onclick = event=>{ - this.on_auto_rotate("noscaling") - }; + if (direction) { + this.on_edge_changed( + ratio_delta, + direction, + event.ctrlKey, + event.shiftKey + ); + + // if (event.ctrlKey){ + // this.on_auto_shrink(direction); + // } + } else { + // when intall handler for mover, the direcion is left null + this.on_moved(ratio_delta); } + }; - if (buttons.fit_position && this.on_fit_size){ - buttons.fit_position.onmousedown = ignore_left_mouse_down; - buttons.fit_position.onclick = event=>{ - this.on_fit_size("noscaling"); - }; - } + svg.onmousemove = (event) => { + if (event.which != 1) return; - if (buttons.fit_size && this.on_fit_size){ + mouse_cur_pos = { x: event.layerX, y: event.layerY }; - buttons.fit_size.onmousedown = ignore_left_mouse_down; - buttons.fit_size.onclick = event=>{ - this.on_fit_size(); - }; - } - - buttons.fit_all.onmousedown = ignore_left_mouse_down; - buttons.fit_all.onclick = event=>{ - //console.log("auto rotate button clicked."); - this.on_auto_rotate(); - //event.currentTarget.blur(); // this bluring will disable focus on sideview also, which is not expected. - } + var handle_delta = { + x: mouse_cur_pos.x - this.mouse_start_pos.x, + y: mouse_cur_pos.y - this.mouse_start_pos.y, // don't reverse direction + }; + this.move_lines(handle_delta, direction); + }; + }; + } + + install_direction_handler(linename) { + var handle = this.ui.querySelector("#" + linename + "-handle"); + var line = this.ui.querySelector("#" + linename); + var svg = this.svg; + + handle.onmouseenter = (event) => { + if (this.isActive()) { + this.show_lines(); + this.hightlight_line(line); + } + }; - if (buttons.reset_rotation){ + handle.onmouseleave = () => this.hide(); - buttons.reset_rotation.onmousedown = ignore_left_mouse_down; + handle.ondblclick = (event) => { + event.stopPropagation(); + event.preventDefault(); + //transform_bbox(this_axis+"_rotate_reverse"); + this.on_direction_changed(Math.PI); + }; - buttons.reset_rotation.onclick = event=>{ - //console.log("auto rotate button clicked."); - this.on_reset_rotate(); - //event.currentTarget.blur(); // this bluring will disable focus on sideview also, which is not expected. - } - } + // function hide(event){ + // line.style.stroke="#00000000"; + // }; - if (buttons.fit_moving_direction){ - buttons.fit_moving_direction.onmousedown = ignore_left_mouse_down; - buttons.fit_moving_direction.onclick = event=>{ - //console.log("auto rotate button clicked."); - this.on_auto_rotate("noscaling", "moving-direction"); - //event.currentTarget.blur(); // this bluring will disable focus on sideview also, which is not expected. - } - } - } + // handle.onmouseup = event=>{ + // if (event.which!=1) + // return; + // //line.style["stroke-dasharray"]="none"; + // //line.style.stroke="#00000000"; + // handle.onmouseleave = hide; + // }; -} + handle.onmousedown = (event) => { + if (event.which != 1) return; + event.stopPropagation(); + event.preventDefault(); -class ProjectiveViewOps{ - constructor(ui, editorCfg, boxEditor, views, boxOp, func_on_box_changed,func_on_box_remove){ + //line.style.stroke="yellow"; + handle.onmouseleave = null; + //show_lines(lines); - this.ui = ui; - this.cfg = editorCfg; - this.on_box_changed = func_on_box_changed; - this.views = views; - this.boxOp = boxOp; - this.boxEditor = boxEditor; - //internals - var scope = this; + this.disable_handle_except(handle); - function default_on_del(){ - if (scope.box){ - func_on_box_remove(scope.box); - } - } - - function default_on_focus(){ - // this is a long chain! - if (scope.box && scope.box.boxEditor.boxEditorManager) - scope.box.boxEditor.boxEditorManager.globalHeader.update_box_info(scope.box); - } + this.hide_buttons(); + let handle_center = { + x: parseInt(line.getAttribute("x1")), + }; - // direction: 1, -1 - // axis: x,y,z + this.mouse_start_pos = { + x: event.layerX, + y: event.layerY, - function auto_shrink(extreme, direction){ + handle_offset_x: handle_center.x - event.layerX, + }; - for (var axis in direction){ + let mouse_cur_pos = { + x: this.mouse_start_pos.x, + y: this.mouse_start_pos.y, + }; - if (direction[axis] !=0){ + console.log(this.mouse_start_pos); - var end = "max"; - if (direction[axis] === -1){ - end = "min"; - } - - var delta = direction[axis]*extreme[end][axis] - scope.box.scale[axis]/2; + let theta = 0; - console.log(extreme, delta); - scope.boxOp.translate_box(scope.box, axis, direction[axis]* delta/2 ); - scope.box.scale[axis] += delta; - } - } - } + svg.onmousemove = (event) => { + mouse_cur_pos = { x: event.layerX, y: event.layerY }; + let handle_center_cur_pos = { + x: mouse_cur_pos.x + this.mouse_start_pos.handle_offset_x, + y: mouse_cur_pos.y, + }; + theta = Math.atan2( + handle_center_cur_pos.y - this.view_center.y, + handle_center_cur_pos.x - this.view_center.x + ); + console.log(theta); - //direction is in 3d - function auto_stick(delta, direction, use_box_bottom_as_limit){ - //let old_dim = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, true); - //let old_scale = scope.box.scale; + this.rotate_lines(theta); + }; - let virtbox = { - position: { - x: scope.box.position.x, - y: scope.box.position.y, - z: scope.box.position.z, - }, - scale: { - x: scope.box.scale.x, - y: scope.box.scale.y, - z: scope.box.scale.z,}, - rotation: { - x: scope.box.rotation.x, - y: scope.box.rotation.y, - z: scope.box.rotation.z,} - }; + svg.onmouseup = (event) => { + svg.onmousemove = null; + svg.onmouseup = null; - scope.boxOp.translate_box(virtbox, 'x', delta.x/2 * direction.x); - scope.boxOp.translate_box(virtbox, 'y', delta.y/2 * direction.y); - scope.boxOp.translate_box(virtbox, 'z', delta.z/2 * direction.z); + // restore color + //line.style.stroke="#00000000"; + this.enable_handles(); + handle.onmouseleave = this.hide.bind(this); - virtbox.scale.x += delta.x; - virtbox.scale.y += delta.y; - virtbox.scale.z += delta.z; + this.ui.querySelector("#v-buttons").style.display = "inherit"; + if (theta == 0) { + return; + } - // note dim is the relative value - let new_dim = scope.box.world.lidar.get_points_dimmension_of_box(virtbox, use_box_bottom_as_limit); + this.on_direction_changed(-theta - Math.PI / 2, event.ctrlKey); + }; + }; + } + + on_key_down(event) { + switch (event.key) { + case "e": + event.preventDefault(); + event.stopPropagation(); + this.on_direction_changed(-this.cfg.rotateStep, event.ctrlKey); + this.hide_buttons(true); + return true; + case "q": + event.preventDefault(); + event.stopPropagation(); + this.on_direction_changed(this.cfg.rotateStep, event.ctrlKey); + this.hide_buttons(true); + break; + case "f": + event.preventDefault(); + event.stopPropagation(); + this.on_direction_changed(-this.cfg.rotateStep, true); + this.hide_buttons(true); + break; + case "r": + event.preventDefault(); + event.stopPropagation(); + this.on_direction_changed(this.cfg.rotateStep, true); + this.hide_buttons(true); + break; + case "g": + event.preventDefault(); + event.stopPropagation(); + this.on_direction_changed(Math.PI, false); + break; + case "w": + case "ArrowUp": + event.preventDefault(); + event.stopPropagation(); + this.on_moved({ x: 0, y: this.cfg.moveStep }); + this.hide_buttons(true); + break; + case "s": + if (!event.ctrlKey) { + event.preventDefault(); + event.stopPropagation(); + this.on_moved({ x: 0, y: -this.cfg.moveStep }); + this.hide_buttons(true); + break; + } else { + console.log("ctrl+s"); + } + break; + case "ArrowDown": + event.preventDefault(); + event.stopPropagation(); + this.on_moved({ x: 0, y: -this.cfg.moveStep }); + this.hide_buttons(true); + break; + case "a": + if (event.ctrlKey) { + break; + } + // no break; + case "ArrowLeft": + event.preventDefault(); + event.stopPropagation(); + this.on_moved({ x: -this.cfg.moveStep, y: 0 }); + this.hide_buttons(true); + break; + case "d": + if (event.ctrlKey) { + console.log("ctrl+d"); + this.on_box_remove(); + break; + } + case "ArrowRight": + event.preventDefault(); + event.stopPropagation(); + this.on_moved({ x: this.cfg.moveStep, y: 0 }); + this.hide_buttons(true); + break; + case "Delete": + this.on_box_remove(); + break; + } + } + + install_buttons() { + let buttons = this.buttons; + let ignore_left_mouse_down = (event) => { + if (event.which == 1) { + event.stopPropagation(); + } + }; + if (buttons.fit_rotation) { + buttons.fit_rotation.onmousedown = ignore_left_mouse_down; + buttons.fit_rotation.onclick = (event) => { + this.on_auto_rotate("noscaling"); + }; + } - for (var axis in direction){ + if (buttons.fit_position && this.on_fit_size) { + buttons.fit_position.onmousedown = ignore_left_mouse_down; + buttons.fit_position.onclick = (event) => { + this.on_fit_size("noscaling"); + }; + } - if (direction[axis] !=0){ + if (buttons.fit_size && this.on_fit_size) { + buttons.fit_size.onmousedown = ignore_left_mouse_down; + buttons.fit_size.onclick = (event) => { + this.on_fit_size(); + }; + } - var end = "max"; - if (direction[axis] === -1){ - end = "min"; - } + buttons.fit_all.onmousedown = ignore_left_mouse_down; + buttons.fit_all.onclick = (event) => { + //console.log("auto rotate button clicked."); + this.on_auto_rotate(); + //event.currentTarget.blur(); // this bluring will disable focus on sideview also, which is not expected. + }; - //scope.box.scale[axis]/2 - direction[axis]*extreme[end][axis]; - var truedelta = delta[axis]/2 + direction[axis]*new_dim[end][axis] - scope.box.scale[axis]/2; + if (buttons.reset_rotation) { + buttons.reset_rotation.onmousedown = ignore_left_mouse_down; - console.log(new_dim, delta); - scope.boxOp.translate_box(scope.box, axis, direction[axis]* truedelta ); - //scope.box.scale[axis] -= delta; - } - } + buttons.reset_rotation.onclick = (event) => { + //console.log("auto rotate button clicked."); + this.on_reset_rotate(); + //event.currentTarget.blur(); // this bluring will disable focus on sideview also, which is not expected. + }; + } - scope.on_box_changed(scope.box); - } + if (buttons.fit_moving_direction) { + buttons.fit_moving_direction.onmousedown = ignore_left_mouse_down; + buttons.fit_moving_direction.onclick = (event) => { + //console.log("auto rotate button clicked."); + this.on_auto_rotate("noscaling", "moving-direction"); + //event.currentTarget.blur(); // this bluring will disable focus on sideview also, which is not expected. + }; + } + } +} - function on_edge_changed(delta, direction){ - console.log(delta); +class ProjectiveViewOps { + constructor( + ui, + editorCfg, + boxEditor, + views, + boxOp, + func_on_box_changed, + func_on_box_remove + ) { + this.ui = ui; + this.cfg = editorCfg; + this.on_box_changed = func_on_box_changed; + this.views = views; + this.boxOp = boxOp; + this.boxEditor = boxEditor; + //internals + var scope = this; + + function default_on_del() { + if (scope.box) { + func_on_box_remove(scope.box); + } + } - scope.boxOp.translate_box(scope.box, 'x', delta.x/2 * direction.x); - scope.boxOp.translate_box(scope.box, 'y', delta.y/2 * direction.y); - scope.boxOp.translate_box(scope.box, 'z', delta.z/2 * direction.z); + function default_on_focus() { + // this is a long chain! + if (scope.box && scope.box.boxEditor.boxEditorManager) + scope.box.boxEditor.boxEditorManager.globalHeader.update_box_info( + scope.box + ); + } - scope.box.scale.x += delta.x; - scope.box.scale.y += delta.y; - scope.box.scale.z += delta.z; - scope.on_box_changed(scope.box); + // direction: 1, -1 + // axis: x,y,z + + function auto_shrink(extreme, direction) { + for (var axis in direction) { + if (direction[axis] != 0) { + var end = "max"; + if (direction[axis] === -1) { + end = "min"; + } + + var delta = + direction[axis] * extreme[end][axis] - scope.box.scale[axis] / 2; + + console.log(extreme, delta); + scope.boxOp.translate_box( + scope.box, + axis, + (direction[axis] * delta) / 2 + ); + scope.box.scale[axis] += delta; } + } + } - - function get_wheel_multiplier(wheel_direction){ - var multiplier = 1.0; - if (wheel_direction > 0){ - multiplier = 1.1; - } else { - multiplier = 0.9; - } - return multiplier; + //direction is in 3d + function auto_stick(delta, direction, use_box_bottom_as_limit) { + //let old_dim = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, true); + //let old_scale = scope.box.scale; + + let virtbox = { + position: { + x: scope.box.position.x, + y: scope.box.position.y, + z: scope.box.position.z, + }, + scale: { + x: scope.box.scale.x, + y: scope.box.scale.y, + z: scope.box.scale.z, + }, + rotation: { + x: scope.box.rotation.x, + y: scope.box.rotation.y, + z: scope.box.rotation.z, + }, + }; + + scope.boxOp.translate_box(virtbox, "x", (delta.x / 2) * direction.x); + scope.boxOp.translate_box(virtbox, "y", (delta.y / 2) * direction.y); + scope.boxOp.translate_box(virtbox, "z", (delta.z / 2) * direction.z); + + virtbox.scale.x += delta.x; + virtbox.scale.y += delta.y; + virtbox.scale.z += delta.z; + + // note dim is the relative value + let new_dim = scope.box.world.lidar.get_points_dimmension_of_box( + virtbox, + use_box_bottom_as_limit + ); + + for (var axis in direction) { + if (direction[axis] != 0) { + var end = "max"; + if (direction[axis] === -1) { + end = "min"; + } + + //scope.box.scale[axis]/2 - direction[axis]*extreme[end][axis]; + var truedelta = + delta[axis] / 2 + + direction[axis] * new_dim[end][axis] - + scope.box.scale[axis] / 2; + + console.log(new_dim, delta); + scope.boxOp.translate_box( + scope.box, + axis, + direction[axis] * truedelta + ); + //scope.box.scale[axis] -= delta; } + } + scope.on_box_changed(scope.box); + } - /////////////////////////////////////////////////////////////////////////////////// - // direction is null if triggered by dbclick on 'move' handler - function on_z_auto_shrink(direction){ - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, true); - - if (!direction){ - ['x','y'].forEach(function(axis){ - - scope.boxOp.translate_box(scope.box, axis, (extreme.max[axis] + extreme.min[axis])/2); - scope.box.scale[axis] = extreme.max[axis] - extreme.min[axis]; - - }) - } else{ - direction = { - x: direction.y, - y: -direction.x, - z: 0, - } - - auto_shrink(extreme, direction) - } - - scope.on_box_changed(scope.box); - } - - - function on_z_edge_changed(ratio, direction2d, autoShrink, lockScale){ - - var delta = { - x: scope.box.scale.x * ratio.y * direction2d.y, - y: scope.box.scale.y * ratio.x * direction2d.x, - z: 0, - }; - - let direction3d ={ - x: direction2d.y, - y: -direction2d.x, - z: 0, - }; - - if (!autoShrink && !lockScale){ - on_edge_changed(delta, direction3d); - } else if (autoShrink){ - on_edge_changed(delta, direction3d); - on_z_auto_shrink(direction2d); - } else if (lockScale){ - auto_stick(delta, direction3d, true); - } - } + function on_edge_changed(delta, direction) { + console.log(delta); - function on_z_direction_changed(theta, sticky){ - // points indices shall be obtained before rotation. - let box = scope.box; - scope.boxOp.rotate_z(box, theta, sticky) - scope.on_box_changed(box); - } + scope.boxOp.translate_box(scope.box, "x", (delta.x / 2) * direction.x); + scope.boxOp.translate_box(scope.box, "y", (delta.y / 2) * direction.y); + scope.boxOp.translate_box(scope.box, "z", (delta.z / 2) * direction.z); + scope.box.scale.x += delta.x; + scope.box.scale.y += delta.y; + scope.box.scale.z += delta.z; + scope.on_box_changed(scope.box); + } - //ratio.y vertical - //ratio.x horizental - // box.x vertical - // box.y horizental + function get_wheel_multiplier(wheel_direction) { + var multiplier = 1.0; + if (wheel_direction > 0) { + multiplier = 1.1; + } else { + multiplier = 0.9; + } + return multiplier; + } - function limit_move_step(v, min_abs_v) - { - if (v < 0) - return Math.min(v, -min_abs_v) - else if (v > 0) - return Math.max(v, min_abs_v) - else - return v; - } + /////////////////////////////////////////////////////////////////////////////////// + // direction is null if triggered by dbclick on 'move' handler + function on_z_auto_shrink(direction) { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + true + ); + + if (!direction) { + ["x", "y"].forEach(function (axis) { + scope.boxOp.translate_box( + scope.box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + scope.box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } else { + direction = { + x: direction.y, + y: -direction.x, + z: 0, + }; - function on_z_moved(ratio){ - let delta = { - x: scope.box.scale.x*ratio.y, - y: -scope.box.scale.y*ratio.x, - }; - - delta.x = limit_move_step(delta.x, 0.02); - delta.y = limit_move_step(delta.y, 0.02); - - // scope.boxOp.translate_box(scope.box, "x", delta.x); - // scope.boxOp.translate_box(scope.box, "y", delta.y); - - // scope.on_box_changed(scope.box); - scope.boxEditor.onOpCmd({ - op: "translate", - params:{ - delta - } - }); - } + auto_shrink(extreme, direction); + } + scope.on_box_changed(scope.box); + } - function on_z_scaled(ratio){ - - ratio = { - x: ratio.y, - y: ratio.x, - z: 0, - }; - - for (var axis in ratio){ - if (ratio[axis] != 0){ - scope.box.scale[axis] *= 1+ratio[axis]; - } - } - - scope.on_box_changed(scope.box); - } + function on_z_edge_changed(ratio, direction2d, autoShrink, lockScale) { + var delta = { + x: scope.box.scale.x * ratio.y * direction2d.y, + y: scope.box.scale.y * ratio.x * direction2d.x, + z: 0, + }; + + let direction3d = { + x: direction2d.y, + y: -direction2d.x, + z: 0, + }; + + if (!autoShrink && !lockScale) { + on_edge_changed(delta, direction3d); + } else if (autoShrink) { + on_edge_changed(delta, direction3d); + on_z_auto_shrink(direction2d); + } else if (lockScale) { + auto_stick(delta, direction3d, true); + } + } - function on_z_wheel(wheel_direction){ - let multiplier = get_wheel_multiplier(wheel_direction); - let newRatio = scope.views[0].zoom_ratio *= multiplier; - scope.boxEditor.updateViewZoomRatio(0, newRatio); - //z_view_handle.update_view_handle(scope.views[0].getViewPort(), {x: scope.box.scale.y, y:scope.box.scale.x}); - } + function on_z_direction_changed(theta, sticky) { + // points indices shall be obtained before rotation. + let box = scope.box; + scope.boxOp.rotate_z(box, theta, sticky); + scope.on_box_changed(box); + } - function on_z_fit_size(noscaling){ - if (noscaling) - { - // fit position only - scope.boxOp.auto_rotate_xyz(scope.box, null, - {x:true, y:true, z:false}, - scope.on_box_changed, noscaling, "dontrotate"); - } - else - { - scope.boxOp.fit_size(scope.box, ['x','y']); - scope.on_box_changed(scope.box); - } - - } + //ratio.y vertical + //ratio.x horizental + // box.x vertical + // box.y horizental - function on_z_auto_rotate(noscaling, rotate_method){ - - if (rotate_method == "moving-direction") - { - let estimatedRot = scope.boxOp.estimate_rotation_by_moving_direciton(scope.box); - - if (estimatedRot) - { - scope.box.rotation.z = estimatedRot.z; - scope.on_box_changed(scope.box); - } - } - else{ - scope.boxOp.auto_rotate_xyz(scope.box, null, - noscaling?null:{x:false, y:false, z:true}, - scope.on_box_changed, noscaling); - } - - } + function limit_move_step(v, min_abs_v) { + if (v < 0) return Math.min(v, -min_abs_v); + else if (v > 0) return Math.max(v, min_abs_v); + else return v; + } - function on_z_reset_rotate(){ - scope.box.rotation.z = 0; - scope.on_box_changed(scope.box); - } + function on_z_moved(ratio) { + let delta = { + x: scope.box.scale.x * ratio.y, + y: -scope.box.scale.y * ratio.x, + }; + + delta.x = limit_move_step(delta.x, 0.02); + delta.y = limit_move_step(delta.y, 0.02); + + // scope.boxOp.translate_box(scope.box, "x", delta.x); + // scope.boxOp.translate_box(scope.box, "y", delta.y); + + // scope.on_box_changed(scope.box); + scope.boxEditor.onOpCmd({ + op: "translate", + params: { + delta, + }, + }); + } - this.z_view_handle = new ProjectiveView(scope.ui.querySelector("#z-view-manipulator"), - editorCfg, - on_z_edge_changed, - on_z_direction_changed, - on_z_auto_shrink, - on_z_moved, - on_z_scaled, - on_z_wheel, - on_z_fit_size, - on_z_auto_rotate, - on_z_reset_rotate, - default_on_focus, - default_on_del, - this.isActive.bind(this)); - - - /////////////////////////////////////////////////////////////////////////////////// - - function on_y_edge_changed(ratio, direction2d, autoShrink, lockScale){ - - var delta = { - x: scope.box.scale.x * ratio.x * direction2d.x, - z: scope.box.scale.z * ratio.y * direction2d.y, - y: 0, - }; - - let direction3d ={ - x: direction2d.x, - z: direction2d.y, - y: 0, - }; - - if (!autoShrink && !lockScale){ - on_edge_changed(delta, direction3d); - } else if (autoShrink){ - on_edge_changed(delta, direction3d); - on_y_auto_shrink(direction2d); - } else if (lockScale){ - auto_stick(delta, direction3d, direction2d.y===0); - } - } + function on_z_scaled(ratio) { + ratio = { + x: ratio.y, + y: ratio.x, + z: 0, + }; - function on_y_auto_shrink(direction){ - - - if (!direction){ - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, false); - ['x','z'].forEach(function(axis){ - - scope.boxOp.translate_box(scope.box, axis, (extreme.max[axis] + extreme.min[axis])/2); - scope.box.scale[axis] = extreme.max[axis]-extreme.min[axis]; - - }) - - - } else{ - direction = { - x: direction.x, - y: 0, - z: direction.y, - } - - if (direction.z != 0){ - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, false); - auto_shrink(extreme, direction) - }else { - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, true); - auto_shrink(extreme, direction) - } - - } - - scope.on_box_changed(scope.box); + for (var axis in ratio) { + if (ratio[axis] != 0) { + scope.box.scale[axis] *= 1 + ratio[axis]; } + } + scope.on_box_changed(scope.box); + } - function on_y_moved(ratio){ - var delta = { - x: limit_move_step(scope.box.scale.x*ratio.x, 0.02), - z: limit_move_step(scope.box.scale.z*ratio.y,0.02), - }; + function on_z_wheel(wheel_direction) { + let multiplier = get_wheel_multiplier(wheel_direction); + let newRatio = (scope.views[0].zoom_ratio *= multiplier); + scope.boxEditor.updateViewZoomRatio(0, newRatio); + //z_view_handle.update_view_handle(scope.views[0].getViewPort(), {x: scope.box.scale.y, y:scope.box.scale.x}); + } - - // scope.boxOp.translate_box(scope.box, "x", delta.x); - // scope.boxOp.translate_box(scope.box, "z", delta.z); + function on_z_fit_size(noscaling) { + if (noscaling) { + // fit position only + scope.boxOp.auto_rotate_xyz( + scope.box, + null, + { x: true, y: true, z: false }, + scope.on_box_changed, + noscaling, + "dontrotate" + ); + } else { + scope.boxOp.fit_size(scope.box, ["x", "y"]); + scope.on_box_changed(scope.box); + } + } - // scope.on_box_changed(scope.box); - scope.boxEditor.onOpCmd({ - op: "translate", - params:{ - delta - } - }); - } + function on_z_auto_rotate(noscaling, rotate_method) { + if (rotate_method == "moving-direction") { + let estimatedRot = scope.boxOp.estimate_rotation_by_moving_direciton( + scope.box + ); - function on_y_direction_changed(theta, sticky){ - scope.boxOp.change_rotation_y(scope.box, theta, sticky, scope.on_box_changed) + if (estimatedRot) { + scope.box.rotation.z = estimatedRot.z; + scope.on_box_changed(scope.box); } + } else { + scope.boxOp.auto_rotate_xyz( + scope.box, + null, + noscaling ? null : { x: false, y: false, z: true }, + scope.on_box_changed, + noscaling + ); + } + } + function on_z_reset_rotate() { + scope.box.rotation.z = 0; + scope.on_box_changed(scope.box); + } - function on_y_scaled(ratio){ - - ratio = { - x: ratio.x, - y: 0, - z: ratio.y, - }; - - for (var axis in ratio){ - if (ratio[axis] != 0){ - scope.box.scale[axis] *= 1+ratio[axis]; - } - } - - scope.on_box_changed(scope.box); - } + this.z_view_handle = new ProjectiveView( + scope.ui.querySelector("#z-view-manipulator"), + editorCfg, + on_z_edge_changed, + on_z_direction_changed, + on_z_auto_shrink, + on_z_moved, + on_z_scaled, + on_z_wheel, + on_z_fit_size, + on_z_auto_rotate, + on_z_reset_rotate, + default_on_focus, + default_on_del, + this.isActive.bind(this) + ); + + /////////////////////////////////////////////////////////////////////////////////// + + function on_y_edge_changed(ratio, direction2d, autoShrink, lockScale) { + var delta = { + x: scope.box.scale.x * ratio.x * direction2d.x, + z: scope.box.scale.z * ratio.y * direction2d.y, + y: 0, + }; - function on_y_wheel(wheel_direction){ - let multiplier = get_wheel_multiplier(wheel_direction); - let newRatio = scope.views[1].zoom_ratio *= multiplier; - scope.boxEditor.updateViewZoomRatio(1, newRatio); - } + let direction3d = { + x: direction2d.x, + z: direction2d.y, + y: 0, + }; + + if (!autoShrink && !lockScale) { + on_edge_changed(delta, direction3d); + } else if (autoShrink) { + on_edge_changed(delta, direction3d); + on_y_auto_shrink(direction2d); + } else if (lockScale) { + auto_stick(delta, direction3d, direction2d.y === 0); + } + } - function on_y_reset_rotate(){ - scope.box.rotation.y = 0; - scope.on_box_changed(scope.box); - } + function on_y_auto_shrink(direction) { + if (!direction) { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + false + ); + ["x", "z"].forEach(function (axis) { + scope.boxOp.translate_box( + scope.box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + scope.box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } else { + direction = { + x: direction.x, + y: 0, + z: direction.y, + }; - function on_y_auto_rotate(){ - scope.boxOp.auto_rotate_y(scope.box, scope.on_box_changed); + if (direction.z != 0) { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + false + ); + auto_shrink(extreme, direction); + } else { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + true + ); + auto_shrink(extreme, direction); } + } - this.y_view_handle = new ProjectiveView(scope.ui.querySelector("#y-view-manipulator"), - editorCfg, - on_y_edge_changed, - on_y_direction_changed, - on_y_auto_shrink, - on_y_moved, - on_y_scaled, - on_y_wheel, - null, - on_y_auto_rotate, - on_y_reset_rotate, - default_on_focus, - default_on_del, - this.isActive.bind(this)); - - - /////////////////////////////////////////////////////////////////////////////////// - - function on_x_edge_changed(ratio, direction2d, autoShrink, lockScale){ - - var delta = { - y: scope.box.scale.y * ratio.x * direction2d.x, - z: scope.box.scale.z * ratio.y * direction2d.y, - x: 0, - }; - - let direction3d ={ - y: -direction2d.x, - z: direction2d.y, - x: 0, - }; - - if (!autoShrink && !lockScale){ - on_edge_changed(delta, direction3d); - } else if (autoShrink){ - on_edge_changed(delta, direction3d); - on_x_auto_shrink(direction2d); - } else if (lockScale){ - auto_stick(delta, direction3d, direction2d.y===0); - } - } + scope.on_box_changed(scope.box); + } + function on_y_moved(ratio) { + var delta = { + x: limit_move_step(scope.box.scale.x * ratio.x, 0.02), + z: limit_move_step(scope.box.scale.z * ratio.y, 0.02), + }; + + // scope.boxOp.translate_box(scope.box, "x", delta.x); + // scope.boxOp.translate_box(scope.box, "z", delta.z); + + // scope.on_box_changed(scope.box); + scope.boxEditor.onOpCmd({ + op: "translate", + params: { + delta, + }, + }); + } - function on_x_auto_shrink(direction){ - if (!direction){ - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, false); - - ['y','z'].forEach(function(axis){ - - scope.boxOp.translate_box(scope.box, axis, (extreme.max[axis] + extreme.min[axis])/2); - scope.box.scale[axis] = extreme.max[axis]-extreme.min[axis]; - - }) - - - } else{ - direction = { - x: 0, - y: -direction.x, - z: direction.y, - } - - if (direction.z != 0){ - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, false); - auto_shrink(extreme, direction) - } else { - var extreme = scope.box.world.lidar.get_points_dimmension_of_box(scope.box, true); - auto_shrink(extreme, direction) - } - } - - scope.on_box_changed(scope.box); - } + function on_y_direction_changed(theta, sticky) { + scope.boxOp.change_rotation_y( + scope.box, + theta, + sticky, + scope.on_box_changed + ); + } + function on_y_scaled(ratio) { + ratio = { + x: ratio.x, + y: 0, + z: ratio.y, + }; - function on_x_moved(ratio){ - var delta = { - y: limit_move_step(scope.box.scale.y*(-ratio.x), 0.02), - z: limit_move_step(scope.box.scale.z*ratio.y, 0.02), - }; + for (var axis in ratio) { + if (ratio[axis] != 0) { + scope.box.scale[axis] *= 1 + ratio[axis]; + } + } - - // scope.boxOp.translate_box(scope.box, "y", delta.y); - // scope.boxOp.translate_box(scope.box, "z", delta.z); + scope.on_box_changed(scope.box); + } - // scope.on_box_changed(scope.box); + function on_y_wheel(wheel_direction) { + let multiplier = get_wheel_multiplier(wheel_direction); + let newRatio = (scope.views[1].zoom_ratio *= multiplier); + scope.boxEditor.updateViewZoomRatio(1, newRatio); + } - scope.boxEditor.onOpCmd({ - op: "translate", - params:{ - delta - } - }); - } + function on_y_reset_rotate() { + scope.box.rotation.y = 0; + scope.on_box_changed(scope.box); + } - function on_x_direction_changed(theta, sticky){ - scope.boxOp.change_rotation_x(scope.box, -theta, sticky, scope.on_box_changed) - } + function on_y_auto_rotate() { + scope.boxOp.auto_rotate_y(scope.box, scope.on_box_changed); + } - function on_x_scaled(ratio){ - - ratio = { - y: ratio.x, - z: ratio.y, - }; - - for (var axis in ratio){ - if (ratio[axis] != 0){ - scope.box.scale[axis] *= 1+ratio[axis]; - } - } - - scope.on_box_changed(scope.box); - } + this.y_view_handle = new ProjectiveView( + scope.ui.querySelector("#y-view-manipulator"), + editorCfg, + on_y_edge_changed, + on_y_direction_changed, + on_y_auto_shrink, + on_y_moved, + on_y_scaled, + on_y_wheel, + null, + on_y_auto_rotate, + on_y_reset_rotate, + default_on_focus, + default_on_del, + this.isActive.bind(this) + ); + + /////////////////////////////////////////////////////////////////////////////////// + + function on_x_edge_changed(ratio, direction2d, autoShrink, lockScale) { + var delta = { + y: scope.box.scale.y * ratio.x * direction2d.x, + z: scope.box.scale.z * ratio.y * direction2d.y, + x: 0, + }; - function on_x_wheel(wheel_direction){ - let multiplier = get_wheel_multiplier(wheel_direction); - let newRatio = scope.views[2].zoom_ratio *= multiplier; - scope.boxEditor.updateViewZoomRatio(2, newRatio); - } + let direction3d = { + y: -direction2d.x, + z: direction2d.y, + x: 0, + }; + + if (!autoShrink && !lockScale) { + on_edge_changed(delta, direction3d); + } else if (autoShrink) { + on_edge_changed(delta, direction3d); + on_x_auto_shrink(direction2d); + } else if (lockScale) { + auto_stick(delta, direction3d, direction2d.y === 0); + } + } + function on_x_auto_shrink(direction) { + if (!direction) { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + false + ); + + ["y", "z"].forEach(function (axis) { + scope.boxOp.translate_box( + scope.box, + axis, + (extreme.max[axis] + extreme.min[axis]) / 2 + ); + scope.box.scale[axis] = extreme.max[axis] - extreme.min[axis]; + }); + } else { + direction = { + x: 0, + y: -direction.x, + z: direction.y, + }; - function on_x_reset_rotate(){ - scope.box.rotation.x = 0; - scope.on_box_changed(scope.box); + if (direction.z != 0) { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + false + ); + auto_shrink(extreme, direction); + } else { + var extreme = scope.box.world.lidar.get_points_dimmension_of_box( + scope.box, + true + ); + auto_shrink(extreme, direction); } + } - function on_x_auto_rotate(){ - scope.boxOp.auto_rotate_x(scope.box, scope.on_box_changed); - } + scope.on_box_changed(scope.box); + } - this.x_view_handle = new ProjectiveView(scope.ui.querySelector("#x-view-manipulator"), - editorCfg, - on_x_edge_changed, - on_x_direction_changed, - on_x_auto_shrink, - on_x_moved, - on_x_scaled, - on_x_wheel, - null, - on_x_auto_rotate, - on_x_reset_rotate, - default_on_focus, - default_on_del, - this.isActive.bind(this)); + function on_x_moved(ratio) { + var delta = { + y: limit_move_step(scope.box.scale.y * -ratio.x, 0.02), + z: limit_move_step(scope.box.scale.z * ratio.y, 0.02), + }; - } // end of constructor + // scope.boxOp.translate_box(scope.box, "y", delta.y); + // scope.boxOp.translate_box(scope.box, "z", delta.z); - // exports + // scope.on_box_changed(scope.box); + scope.boxEditor.onOpCmd({ + op: "translate", + params: { + delta, + }, + }); + } + function on_x_direction_changed(theta, sticky) { + scope.boxOp.change_rotation_x( + scope.box, + -theta, + sticky, + scope.on_box_changed + ); + } - hideAllHandlers(){ - this.ui.querySelectorAll(".subview-svg").forEach(ui=>ui.style.display="none"); - //this.ui.querySelectorAll(".v-buttons-wrapper").forEach(ui=>ui.style.display="none"); - }; + function on_x_scaled(ratio) { + ratio = { + y: ratio.x, + z: ratio.y, + }; - showAllHandlers(){ - this.ui.querySelectorAll(".subview-svg").forEach(ui=>ui.style.display=""); - //this.ui.querySelectorAll(".v-buttons-wrapper").forEach(ui=>ui.style.display=""); - }; + for (var axis in ratio) { + if (ratio[axis] != 0) { + scope.box.scale[axis] *= 1 + ratio[axis]; + } + } - isActive() - { - return !!this.box; + scope.on_box_changed(scope.box); } - //////////////////////////////////////////////////////////////////////////////////////// - // public interface - - box = undefined; - attachBox(box){ - this.box = box; - //this.show(); - this.showAllHandlers(); - this.update_view_handle(box); - }; - detach(box){ - this.box = null; - this.hideAllHandlers(); - }; + function on_x_wheel(wheel_direction) { + let multiplier = get_wheel_multiplier(wheel_direction); + let newRatio = (scope.views[2].zoom_ratio *= multiplier); + scope.boxEditor.updateViewZoomRatio(2, newRatio); + } - update_view_handle(){ - if (this.box){ - let boxPos = this.box.position; + function on_x_reset_rotate() { + scope.box.rotation.x = 0; + scope.on_box_changed(scope.box); + } - this.z_view_handle.update_view_handle(this.views[0].getViewPort(), {x: this.box.scale.y, y:this.box.scale.x}, {x: boxPos.x, y: boxPos.y}, this.box.rotation.z); - this.y_view_handle.update_view_handle(this.views[1].getViewPort(), {x: this.box.scale.x, y:this.box.scale.z}); - this.x_view_handle.update_view_handle(this.views[2].getViewPort(), {x: this.box.scale.y, y:this.box.scale.z}); - } - }; + function on_x_auto_rotate() { + scope.boxOp.auto_rotate_x(scope.box, scope.on_box_changed); + } -}; + this.x_view_handle = new ProjectiveView( + scope.ui.querySelector("#x-view-manipulator"), + editorCfg, + on_x_edge_changed, + on_x_direction_changed, + on_x_auto_shrink, + on_x_moved, + on_x_scaled, + on_x_wheel, + null, + on_x_auto_rotate, + on_x_reset_rotate, + default_on_focus, + default_on_del, + this.isActive.bind(this) + ); + } // end of constructor + + // exports + + hideAllHandlers() { + this.ui + .querySelectorAll(".subview-svg") + .forEach((ui) => (ui.style.display = "none")); + //this.ui.querySelectorAll(".v-buttons-wrapper").forEach(ui=>ui.style.display="none"); + } + + showAllHandlers() { + this.ui + .querySelectorAll(".subview-svg") + .forEach((ui) => (ui.style.display = "")); + //this.ui.querySelectorAll(".v-buttons-wrapper").forEach(ui=>ui.style.display=""); + } + + isActive() { + return !!this.box; + } + + //////////////////////////////////////////////////////////////////////////////////////// + // public interface + + box = undefined; + attachBox(box) { + this.box = box; + //this.show(); + this.showAllHandlers(); + this.update_view_handle(box); + } + detach(box) { + this.box = null; + this.hideAllHandlers(); + } + + update_view_handle() { + if (this.box) { + let boxPos = this.box.position; + + this.z_view_handle.update_view_handle( + this.views[0].getViewPort(), + { x: this.box.scale.y, y: this.box.scale.x }, + { x: boxPos.x, y: boxPos.y }, + this.box.rotation.z + ); + this.y_view_handle.update_view_handle(this.views[1].getViewPort(), { + x: this.box.scale.x, + y: this.box.scale.z, + }); + this.x_view_handle.update_view_handle(this.views[2].getViewPort(), { + x: this.box.scale.y, + y: this.box.scale.z, + }); + } + } +} -export {ProjectiveViewOps} +export { ProjectiveViewOps }; diff --git a/public/js/test.js b/public/js/test.js index cc33d77..45c5001 100644 --- a/public/js/test.js +++ b/public/js/test.js @@ -1,80 +1,84 @@ - - -import * as THREE from './lib/three.module.js'; +import * as THREE from "./lib/three.module.js"; //import Stats from './lib/stats.module.js'; -import { OrthographicTrackballControls } from './lib/OrthographicTrackballControls.js'; +import { OrthographicTrackballControls } from "./lib/OrthographicTrackballControls.js"; var camera, controls, scene, renderer, stats; init(); animate(); function init() { - camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 2000 ); - camera.position.z = 1000; - // world - scene = new THREE.Scene(); - scene.background = new THREE.Color( 0xcccccc ); - scene.fog = new THREE.FogExp2( 0xcccccc, 0.001 ); - var geometry = new THREE.CylinderBufferGeometry( 0, 10, 30, 4, 1 ); - var material = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } ); - for ( var i = 0; i < 50; i ++ ) { - var mesh = new THREE.Mesh( geometry, material ); - mesh.position.x = ( Math.random() - 0.5 ) * 1000; - mesh.position.y = ( Math.random() - 0.5 ) * 1000; - mesh.position.z = ( Math.random() - 0.5 ) * 1000; - mesh.updateMatrix(); - mesh.matrixAutoUpdate = false; - scene.add( mesh ); - } - // lights - var light = new THREE.DirectionalLight( 0xffffff ); - light.position.set( 1, 1, 1 ); - scene.add( light ); - var light = new THREE.DirectionalLight( 0x002288 ); - light.position.set( - 1, - 1, - 1 ); - scene.add( light ); - var light = new THREE.AmbientLight( 0x222222 ); - scene.add( light ); - // renderer - renderer = new THREE.WebGLRenderer( { antialias: true } ); - renderer.setPixelRatio( window.devicePixelRatio ); - renderer.setSize( window.innerWidth, window.innerHeight ); - document.body.appendChild( renderer.domElement ); - - + camera = new THREE.OrthographicCamera( + window.innerWidth / -2, + window.innerWidth / 2, + window.innerHeight / 2, + window.innerHeight / -2, + 1, + 2000 + ); + camera.position.z = 1000; + // world + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xcccccc); + scene.fog = new THREE.FogExp2(0xcccccc, 0.001); + var geometry = new THREE.CylinderBufferGeometry(0, 10, 30, 4, 1); + var material = new THREE.MeshPhongMaterial({ + color: 0xffffff, + flatShading: true, + }); + for (var i = 0; i < 50; i++) { + var mesh = new THREE.Mesh(geometry, material); + mesh.position.x = (Math.random() - 0.5) * 1000; + mesh.position.y = (Math.random() - 0.5) * 1000; + mesh.position.z = (Math.random() - 0.5) * 1000; + mesh.updateMatrix(); + mesh.matrixAutoUpdate = false; + scene.add(mesh); + } + // lights + var light = new THREE.DirectionalLight(0xffffff); + light.position.set(1, 1, 1); + scene.add(light); + var light = new THREE.DirectionalLight(0x002288); + light.position.set(-1, -1, -1); + scene.add(light); + var light = new THREE.AmbientLight(0x222222); + scene.add(light); + // renderer + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(renderer.domElement); - controls = new OrthographicTrackballControls( camera, renderer.domElement ); - controls.rotateSpeed = 1.0; - controls.zoomSpeed = 1.2; - controls.noZoom = false; - controls.noPan = false; - controls.staticMoving = true; - controls.dynamicDampingFactor = 0.3; - controls.keys = [ 65, 83, 68 ]; - controls.addEventListener( 'change', render ); + controls = new OrthographicTrackballControls(camera, renderer.domElement); + controls.rotateSpeed = 1.0; + controls.zoomSpeed = 1.2; + controls.noZoom = false; + controls.noPan = false; + controls.staticMoving = true; + controls.dynamicDampingFactor = 0.3; + controls.keys = [65, 83, 68]; + controls.addEventListener("change", render); - - - //stats = new Stats(); - //document.body.appendChild( stats.dom ); - // - window.addEventListener( 'resize', onWindowResize, false ); - // - render(); + //stats = new Stats(); + //document.body.appendChild( stats.dom ); + // + window.addEventListener("resize", onWindowResize, false); + // + render(); } function onWindowResize() { - camera.left = window.innerWidth / - 2; - camera.right = window.innerWidth / 2; - camera.top = window.innerHeight / 2; - camera.bottom = window.innerHeight / - 2; - camera.updateProjectionMatrix(); - renderer.setSize( window.innerWidth, window.innerHeight ); - controls.handleResize(); - render(); + camera.left = window.innerWidth / -2; + camera.right = window.innerWidth / 2; + camera.top = window.innerHeight / 2; + camera.bottom = window.innerHeight / -2; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + controls.handleResize(); + render(); } function animate() { - requestAnimationFrame( animate ); - controls.update(); - //stats.update(); + requestAnimationFrame(animate); + controls.update(); + //stats.update(); } function render() { - renderer.render( scene, camera ); -} \ No newline at end of file + renderer.render(scene, camera); +} diff --git a/public/js/text_file_loader.js b/public/js/text_file_loader.js index 176e540..de3717a 100644 --- a/public/js/text_file_loader.js +++ b/public/js/text_file_loader.js @@ -1,57 +1,46 @@ - import { - DefaultLoadingManager, - FileLoader, - LoaderUtils, + DefaultLoadingManager, + FileLoader, + LoaderUtils, } from "./lib/three.module.js"; -var TextFileLoader = function ( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.littleEndian = true; +var TextFileLoader = function (manager) { + this.manager = manager !== undefined ? manager : DefaultLoadingManager; + this.littleEndian = true; }; - TextFileLoader.prototype = { - - constructor: TextFileLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setResponseType( 'arraybuffer' ); - loader.load( url, function ( data ) { - - try { - var textData = LoaderUtils.decodeText( new Uint8Array( data ) ); - onLoad(textData, url); - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - throw e; - - } - - } - - }, onProgress, onError ); - - }, - - setPath: function ( value ) { - - this.path = value; - return this; - - }, + constructor: TextFileLoader, + + load: function (url, onLoad, onProgress, onError) { + var scope = this; + + var loader = new FileLoader(scope.manager); + loader.setPath(scope.path); + loader.setResponseType("arraybuffer"); + loader.load( + url, + function (data) { + try { + var textData = LoaderUtils.decodeText(new Uint8Array(data)); + onLoad(textData, url); + } catch (e) { + if (onError) { + onError(e); + } else { + throw e; + } + } + }, + onProgress, + onError + ); + }, + + setPath: function (value) { + this.path = value; + return this; + }, }; export { TextFileLoader }; diff --git a/public/js/trajectory.js b/public/js/trajectory.js index 9519bd1..6c6239c 100644 --- a/public/js/trajectory.js +++ b/public/js/trajectory.js @@ -1,173 +1,157 @@ 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 +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, }; - this.funcOnExit = funcOnExit; - //console.log(objType, objId, tracks); - this.calculateCoordinateTransform(this.object.tracks); - this.redrawAll(); + let clientLength = Math.min( + this.svgUi.clientWidth, + this.svgUi.clientHeight + ); - this.ui.focus(); - } + this.posTrans.x += (delta.x / clientLength) * 1000; + this.posTrans.y += (delta.y / clientLength) * 1000; - redrawAll() - { - this.show(); - this.clear(); - this.updateObjectScale(); - - this.drawTracks(this.object); + this.startPosForMovingCanvas = { x: event.pageX, y: event.pageY }; - this.drawScaler(); + 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; - 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(); - } - } + 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] @@ -180,242 +164,225 @@ class Trajectory extends PopupDialog{ 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 - ]; + 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"); } - 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); + 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; + } } - 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); - } - - + 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}; \ No newline at end of file +export { Trajectory }; diff --git a/public/js/util.js b/public/js/util.js index 2f11810..03c68c8 100644 --- a/public/js/util.js +++ b/public/js/util.js @@ -1,59 +1,59 @@ -import * as THREE from './lib/three.module.js'; +import * as THREE from "./lib/three.module.js"; -function dotproduct(a, b){ - var ret = 0; - for (let i = 0; i v[i][j]){ - min[j] = v[i][j]; - } + var min, max; + min = [...v[0]]; + max = [...v[0]]; - if (max[j] < v[i][j]){ - max[j] = v[i][j]; - } - } - } + for (var i = 1; i < v.length; ++i) { + for (var j = 0; j < min.length; ++j) { + if (min[j] > v[i][j]) { + min[j] = v[i][j]; + } - return { - min: min, - max: max, + if (max[j] < v[i][j]) { + max[j] = v[i][j]; + } } + } + return { + min: min, + max: max, + }; } // v is array of vector, vl is vector length -function array_as_vector_range(v, vl){ - - var n = v.length/vl; - - var min, max; - if (n === 0){ - return null; - } else{ - min = v.slice(0, vl); - max = v.slice(0, vl); +function array_as_vector_range(v, vl) { + var n = v.length / vl; + + var min, max; + if (n === 0) { + return null; + } else { + min = v.slice(0, vl); + max = v.slice(0, vl); + } + + for (var i = 1; i < n; ++i) { + for (var j = 0; j < vl; ++j) { + if (min[j] > v[i * vl + j]) { + min[j] = v[i * vl + j]; + } + + if (max[j] < v[i * vl + j]) { + max[j] = v[i * vl + j]; + } } + } - for (var i=1; i v[i*vl+j]){ - min[j] = v[i*vl+j]; - } - - if (max[j] < v[i*vl+j]){ - max[j] = v[i*vl+j]; - } - } - } - - return { - min: min, - max: max, - } + return { + min: min, + max: max, + }; } // v is 1-d array of vector, vl is vector length, p is index into v. -function array_as_vector_index_range(v, vl, p){ - - var n = p.length; - - var min, max; - if (n === 0){ - return null; - } else{ - min = v.slice(p[0]*vl, (p[0]+1)*vl); - max = v.slice(p[0]*vl, (p[0]+1)*vl); - } - - for (var i=1; i v[p[i]*vl+j]){ - min[j] = v[p[i]*vl+j]; - } - - if (max[j] < v[p[i]*vl+j]){ - max[j] = v[p[i]*vl+j]; - } - } +function array_as_vector_index_range(v, vl, p) { + var n = p.length; + + var min, max; + if (n === 0) { + return null; + } else { + min = v.slice(p[0] * vl, (p[0] + 1) * vl); + max = v.slice(p[0] * vl, (p[0] + 1) * vl); + } + + for (var i = 1; i < n; ++i) { + for (var j = 0; j < vl; ++j) { + if (min[j] > v[p[i] * vl + j]) { + min[j] = v[p[i] * vl + j]; + } + + if (max[j] < v[p[i] * vl + j]) { + max[j] = v[p[i] * vl + j]; + } } + } - return { - min: min, - max: max, - } + return { + min: min, + max: max, + }; } +function vector3_nomalize(m) { + var ret = []; + for (var i = 0; i < m.length / 3; i++) { + ret.push(m[i * 3 + 0] / m[i * 3 + 2]); + ret.push(m[i * 3 + 1] / m[i * 3 + 2]); + } - -function vector3_nomalize(m){ - var ret=[]; - for (var i=0; iScalar(0))) { - if(res[0] > Scalar(0)) { - res[0] -= Scalar(Math.PI); - } - else { - res[0] += Scalar(Math.PI); - } - res[1] = atan2(-coeff(i,k), -c2); - } - else - res[1] = atan2(-coeff(i,k), c2); - var s1 = sin(res[0]); - var c1 = cos(res[0]); - res[2] = atan2(s1*coeff(k,i)-c1*coeff(j,i), c1*coeff(j,j) - s1 * coeff(k,j)); - - if (!odd) - res = res.map(function(x){return -x;}) - - return { - x: res[0], - y: res[1], - z: res[2], - } -} - - - -var linalg_std = { - euler_angle_to_rotation_matrix: function(euler){ - var theta = [euler.x, euler.y, euler.z]; - // Calculate rotation about x axis - var R_x = new THREE.Matrix4(); - R_x.set( - 1, 0, 0, 0, - 0, Math.cos(theta[0]), -Math.sin(theta[0]), 0, - 0, Math.sin(theta[0]), Math.cos(theta[0]) , 0, - 0, 0, 0, 1, - ); - - // Calculate rotation about y axis - var R_y = new THREE.Matrix4(); - R_y.set( - Math.cos(theta[1]), 0, Math.sin(theta[1]), 0, - 0, 1, 0, 0, - -Math.sin(theta[1]), 0, Math.cos(theta[1]), 0, - 0, 0, 0, 1, - ); - - // Calculate rotation about z axis - var R_z = new THREE.Matrix4(); - R_z.set( - Math.cos(theta[2]), -Math.sin(theta[2]), 0, 0, - Math.sin(theta[2]), Math.cos(theta[2]), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - ); - - R_z.multiply(R_y); - R_z.multiply(R_x); - - return R_z; - }, - - euler_angle_from_rotation_matrix: function(m){ - var euler = new THREE.Euler(); - euler.setFromRotationMatrix(m); - return euler; - }, - - // {x:, y:, z:} - euler_angle_composite: function(current, delta){ - var current_matrix = this.euler_angle_to_rotation_matrix(current); - var delta_matrix = this.euler_angle_to_rotation_matrix(delta); - var composite_matrix = new THREE.Matrix4(); - composite_matrix.multiplyMatrices(delta_matrix, current_matrix); - - return this.euler_angle_from_rotation_matrix(composite_matrix); + var odd = false; + var res = [0, 0, 0]; + var i = 0, + j = 1, + k = 2; + + if (!msize) { + msize = 4; + } + + function coeff(x, y) { + return mat(m, msize, x, y); + } + + function atan2(x, y) { + return Math.atan2(x, y); + } + var sin = Math.sin; + var cos = Math.cos; + function Scalar(x) { + return x; + } + + res[0] = atan2(coeff(j, k), coeff(k, k)); + //var c2 = Vector2(coeff(i,i), coeff(i,j)).norm(); + var c2 = Math.sqrt(coeff(i, i) * coeff(i, i) + coeff(i, j) * coeff(i, j)); + if ((odd && res[0] < Scalar(0)) || (!odd && res[0] > Scalar(0))) { + if (res[0] > Scalar(0)) { + res[0] -= Scalar(Math.PI); + } else { + res[0] += Scalar(Math.PI); } + res[1] = atan2(-coeff(i, k), -c2); + } else res[1] = atan2(-coeff(i, k), c2); + var s1 = sin(res[0]); + var c1 = cos(res[0]); + res[2] = atan2( + s1 * coeff(k, i) - c1 * coeff(j, i), + c1 * coeff(j, j) - s1 * coeff(k, j) + ); + + if (!odd) + res = res.map(function (x) { + return -x; + }); + + return { + x: res[0], + y: res[1], + z: res[2], + }; } +var linalg_std = { + euler_angle_to_rotation_matrix: function (euler) { + var theta = [euler.x, euler.y, euler.z]; + // Calculate rotation about x axis + var R_x = new THREE.Matrix4(); + R_x.set( + 1, + 0, + 0, + 0, + 0, + Math.cos(theta[0]), + -Math.sin(theta[0]), + 0, + 0, + Math.sin(theta[0]), + Math.cos(theta[0]), + 0, + 0, + 0, + 0, + 1 + ); -function normalizeAngle(a) -{ - while (true) - { - if ( a > Math.PI) - a -= Math.PI *2; - else if (a < -Math.PI) - a += Math.PI * 2; - else - return a; - } -} + // Calculate rotation about y axis + var R_y = new THREE.Matrix4(); + R_y.set( + Math.cos(theta[1]), + 0, + Math.sin(theta[1]), + 0, + 0, + 1, + 0, + 0, + -Math.sin(theta[1]), + 0, + Math.cos(theta[1]), + 0, + 0, + 0, + 0, + 1 + ); + // Calculate rotation about z axis + var R_z = new THREE.Matrix4(); + R_z.set( + Math.cos(theta[2]), + -Math.sin(theta[2]), + 0, + 0, + Math.sin(theta[2]), + Math.cos(theta[2]), + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ); + R_z.multiply(R_y); + R_z.multiply(R_x); + + return R_z; + }, + + euler_angle_from_rotation_matrix: function (m) { + var euler = new THREE.Euler(); + euler.setFromRotationMatrix(m); + return euler; + }, + + // {x:, y:, z:} + euler_angle_composite: function (current, delta) { + var current_matrix = this.euler_angle_to_rotation_matrix(current); + var delta_matrix = this.euler_angle_to_rotation_matrix(delta); + var composite_matrix = new THREE.Matrix4(); + composite_matrix.multiplyMatrices(delta_matrix, current_matrix); + + return this.euler_angle_from_rotation_matrix(composite_matrix); + }, +}; + +function normalizeAngle(a) { + while (true) { + if (a > Math.PI) a -= Math.PI * 2; + else if (a < -Math.PI) a += Math.PI * 2; + else return a; + } +} // box(position, scale, rotation) to box corner corrdinates. // return 8 points, represented as (x,y,z,1) // note the vertices order cannot be changed, draw-box-on-image assumes // the first 4 vertex is the front plane, so it knows box direction. -function psr_to_xyz_face_points(p,s,r, minGrid){ - /* +function psr_to_xyz_face_points(p, s, r, minGrid) { + /* var trans_matrix=[ Math.cos(r.z), -Math.sin(r.z), 0, p.x, Math.sin(r.z), Math.cos(r.z), 0, p.y, @@ -505,131 +625,130 @@ function psr_to_xyz_face_points(p,s,r, minGrid){ 0, 0, 0, 1, ]; */ - var trans_matrix = euler_angle_to_rotate_matrix(r, p); - - var x=s.x/2; - var y=s.y/2; - var z=s.z/2; - -// var local_coord = [ -// [x, y, -z], [x, -y, -z], //front-left-bottom, front-right-bottom -// [x, -y, z], [x, y, z], //front-right-top, front-left-top - -// [-x, y, -z], [-x, -y, -z], //rear-left-bottom, rear-right-bottom -// [-x, -y, z], [-x, y, z], //rear-right-top, rear-left-top -// ]; + var trans_matrix = euler_angle_to_rotate_matrix(r, p); + var x = s.x / 2; + var y = s.y / 2; + var z = s.z / 2; - + // var local_coord = [ + // [x, y, -z], [x, -y, -z], //front-left-bottom, front-right-bottom + // [x, -y, z], [x, y, z], //front-right-top, front-left-top - let xs = []; - for (let i = -x ; i <=x; i+=minGrid) - { - xs.push(i); - } + // [-x, y, -z], [-x, -y, -z], //rear-left-bottom, rear-right-bottom + // [-x, -y, z], [-x, y, z], //rear-right-top, rear-left-top + // ]; - let ys = []; - for (let i = -y ; i <=y; i+=minGrid) - { - ys.push(i); - } + let xs = []; + for (let i = -x; i <= x; i += minGrid) { + xs.push(i); + } - let zs = []; - for (let i = -z ; i <=z; i+=minGrid) - { - zs.push(i); - } + let ys = []; + for (let i = -y; i <= y; i += minGrid) { + ys.push(i); + } + let zs = []; + for (let i = -z; i <= z; i += minGrid) { + zs.push(i); + } - let points = []; + let points = []; - points = points.concat(ys.map(i=>[x, i, -z, 1])); - points = points.concat(ys.map(i=>[x, i, z, 1])); - points = points.concat(ys.map(i=>[-x, i, -z, 1])); - points = points.concat(ys.map(i=>[-x, i, z, 1])); - - points = points.concat(xs.map(i=>[i, y, -z, 1])); - points = points.concat(xs.map(i=>[i, y, z, 1])); - points = points.concat(xs.map(i=>[i, -y, -z, 1])); - points = points.concat(xs.map(i=>[i, -y, z, 1])); - - points = points.concat(zs.map(i=>[x, y, i, 1])); - points = points.concat(zs.map(i=>[x, -y, i, 1])); - points = points.concat(zs.map(i=>[-x, -y, i, 1])); - points = points.concat(zs.map(i=>[-x, y, i, 1])); + points = points.concat(ys.map((i) => [x, i, -z, 1])); + points = points.concat(ys.map((i) => [x, i, z, 1])); + points = points.concat(ys.map((i) => [-x, i, -z, 1])); + points = points.concat(ys.map((i) => [-x, i, z, 1])); + points = points.concat(xs.map((i) => [i, y, -z, 1])); + points = points.concat(xs.map((i) => [i, y, z, 1])); + points = points.concat(xs.map((i) => [i, -y, -z, 1])); + points = points.concat(xs.map((i) => [i, -y, z, 1])); - points = points.reduce((a,b)=>a.concat(b)) + points = points.concat(zs.map((i) => [x, y, i, 1])); + points = points.concat(zs.map((i) => [x, -y, i, 1])); + points = points.concat(zs.map((i) => [-x, -y, i, 1])); + points = points.concat(zs.map((i) => [-x, y, i, 1])); - let world_coord = matmul(trans_matrix, points, 4); + points = points.reduce((a, b) => a.concat(b)); + let world_coord = matmul(trans_matrix, points, 4); - - return vector4to3(world_coord); + return vector4to3(world_coord); } - - -function cornersAinB(boxA,boxB){ - - - let minGrid = Math.min( - boxA.scale.x, boxA.scale.y, boxA.scale.z, - boxB.scale.x, boxB.scale.y, boxB.scale.z, - ); - - minGrid = minGrid / 2; - - - // in world coord, offset by b pos - let boxAPosInB = {x: boxA.position.x - boxB.position.x, - y: boxA.position.y - boxB.position.y, - z: boxA.position.z - boxB.position.z}; - - let cornersA = psr_to_xyz_face_points(boxAPosInB, boxA.scale, boxA.rotation, minGrid); // in world coordinates - - cornersA.push(boxAPosInB.x, boxAPosInB.y, boxAPosInB.z); //center point - - - // in box b coord - let matrixB = euler_angle_to_rotate_matrix_3by3(boxB.rotation); - matrixB = transpose(matrixB,3) - let cornersAInB = matmul(matrixB, cornersA, 3); - - - for (let i =0; i < cornersAInB.length; i+=3){ - let [x,y,z] = cornersAInB.slice(i, i+3) - - if ( Math.abs(x) < boxB.scale.x/2 && - Math.abs(y) < boxB.scale.y/2 && - Math.abs(z) < boxB.scale.z/2) - { - return true; - } - +function cornersAinB(boxA, boxB) { + let minGrid = Math.min( + boxA.scale.x, + boxA.scale.y, + boxA.scale.z, + boxB.scale.x, + boxB.scale.y, + boxB.scale.z + ); + + minGrid = minGrid / 2; + + // in world coord, offset by b pos + let boxAPosInB = { + x: boxA.position.x - boxB.position.x, + y: boxA.position.y - boxB.position.y, + z: boxA.position.z - boxB.position.z, + }; + + let cornersA = psr_to_xyz_face_points( + boxAPosInB, + boxA.scale, + boxA.rotation, + minGrid + ); // in world coordinates + + cornersA.push(boxAPosInB.x, boxAPosInB.y, boxAPosInB.z); //center point + + // in box b coord + let matrixB = euler_angle_to_rotate_matrix_3by3(boxB.rotation); + matrixB = transpose(matrixB, 3); + let cornersAInB = matmul(matrixB, cornersA, 3); + + for (let i = 0; i < cornersAInB.length; i += 3) { + let [x, y, z] = cornersAInB.slice(i, i + 3); + + if ( + Math.abs(x) < boxB.scale.x / 2 && + Math.abs(y) < boxB.scale.y / 2 && + Math.abs(z) < boxB.scale.z / 2 + ) { + return true; } + } - return false; + return false; } // check if 2 boxes has non-empty intersection - // the idea is to check if any corner of one box is inside the other one - // when boxA contains B entirely, we shoudl test the opposite way. -function intersect(boxA, boxB){ - - return cornersAinB(boxA, boxB) || cornersAinB(boxB, boxA); - - - - }; - +// the idea is to check if any corner of one box is inside the other one +// when boxA contains B entirely, we shoudl test the opposite way. +function intersect(boxA, boxB) { + return cornersAinB(boxA, boxB) || cornersAinB(boxB, boxA); +} -export {dotproduct, vector_range, array_as_vector_range, array_as_vector_index_range, vector4to3, vector3_nomalize, psr_to_xyz, matmul, - matmul2, - euler_angle_to_rotate_matrix_3by3, euler_angle_to_rotate_matrix, rotation_matrix_to_euler_angle, - linalg_std, - transpose, - mat, - normalizeAngle, - intersect -} \ No newline at end of file +export { + dotproduct, + vector_range, + array_as_vector_range, + array_as_vector_index_range, + vector4to3, + vector3_nomalize, + psr_to_xyz, + matmul, + matmul2, + euler_angle_to_rotate_matrix_3by3, + euler_angle_to_rotate_matrix, + rotation_matrix_to_euler_angle, + linalg_std, + transpose, + mat, + normalizeAngle, + intersect, +}; diff --git a/public/js/view.js b/public/js/view.js index e963b3e..8f7faa9 100644 --- a/public/js/view.js +++ b/public/js/view.js @@ -1,792 +1,805 @@ -import * as THREE from './lib/three.module.js'; -import { OrbitControls } from './lib/OrbitControls.js'; +import * as THREE from "./lib/three.module.js"; +import { OrbitControls } from "./lib/OrbitControls.js"; //import { OrthographicTrackballControls } from './lib/OrthographicTrackballControls.js'; -import { TransformControls } from './lib/TransformControls.js'; -import {matmul2, euler_angle_to_rotate_matrix} from "./util.js" - - -function ViewManager(mainViewContainer, webglScene, webglMainScene, renderer, globalRenderFunc, on_box_changed, cfg){ - - this.mainViewContainer = mainViewContainer; - this.globalRenderFunc = globalRenderFunc; - this.webglScene = webglScene; - this.webglMainScene = webglMainScene; - this.renderer = renderer; - - - this.mainView = cfg.disableMainView?null:create_main_view(webglMainScene, renderer, this.globalRenderFunc, this.mainViewContainer, on_box_changed); - - this.boxViewList = []; - - this.addBoxView = function(subviewsUi){ - let boxview = new BoxView(subviewsUi, this.mainViewContainer, this.webglScene, this.renderer, this); - this.boxViewList.push(boxview); - return boxview; +import { TransformControls } from "./lib/TransformControls.js"; +import { matmul2, euler_angle_to_rotate_matrix } from "./util.js"; + +function ViewManager( + mainViewContainer, + webglScene, + webglMainScene, + renderer, + globalRenderFunc, + on_box_changed, + cfg +) { + this.mainViewContainer = mainViewContainer; + this.globalRenderFunc = globalRenderFunc; + this.webglScene = webglScene; + this.webglMainScene = webglMainScene; + this.renderer = renderer; + + this.mainView = cfg.disableMainView + ? null + : create_main_view( + webglMainScene, + renderer, + this.globalRenderFunc, + this.mainViewContainer, + on_box_changed + ); + + this.boxViewList = []; + + this.addBoxView = function (subviewsUi) { + let boxview = new BoxView( + subviewsUi, + this.mainViewContainer, + this.webglScene, + this.renderer, + this + ); + this.boxViewList.push(boxview); + return boxview; + }; + + this.onWindowResize = function () { + if (this.mainView) this.mainView.onWindowResize(); + }; + + this.render = function () { + console.log("render verything"); + if (this.mainView) this.mainView.renderAll(); + + this.boxViewList.forEach((v) => { + //if (v.ui.style.display != 'none') //we have pseudo box now. render as commanded. + v.render(); + }); + }; + + this.setColorScheme = function () { + let scheme = document.documentElement.className; + if (scheme == "theme-dark") { + this.mainView.backgroundColor = new THREE.Color(0.0, 0.0, 0.0); + this.boxViewList.forEach((v) => { + v.views[0].backgroundColor = new THREE.Color(0.1, 0.05, 0.05); + v.views[1].backgroundColor = new THREE.Color(0.05, 0.1, 0.05); + v.views[2].backgroundColor = new THREE.Color(0.05, 0.05, 0.1); + }); + } else { + this.mainView.backgroundColor = new THREE.Color(1.0, 1.0, 1.0); + this.boxViewList.forEach((v) => { + v.views[0].backgroundColor = new THREE.Color(0.95, 0.9, 0.9); + v.views[1].backgroundColor = new THREE.Color(0.9, 0.95, 0.9); + v.views[2].backgroundColor = new THREE.Color(0.9, 0.9, 0.95); + }); } - - this.onWindowResize = function(){ - if (this.mainView) - this.mainView.onWindowResize(); + }; + + //this.setColorScheme(); + + // no public funcs below + function create_main_view( + scene, + renderer, + globalRenderFunc, + container, + on_box_changed + ) { + var view = {}; + + view.backgroundColor = + document.documentElement.className == "theme-dark" + ? new THREE.Color(0.0, 0.0, 0.0) + : new THREE.Color(1.0, 1.0, 1.0); + view.zoom_ratio = 1.0; //useless for mainview + + let camera = new THREE.PerspectiveCamera( + 65, + container.clientWidth / container.clientHeight, + 1, + 500 + ); + camera.position.x = 0; + camera.position.z = 50; + camera.position.y = 0; + camera.up.set(0, 0, 1); + camera.lookAt(0, 0, 0); + camera.name = "main view camera"; + view.camera_perspective = camera; + view.camera = camera; + + // make a blind camera to clean background when batch editing is enabled. + camera = new THREE.PerspectiveCamera( + 65, + container.clientWidth / container.clientHeight, + 1, + 500 + ); + camera.position.x = -1000; + camera.position.z = -1000; + camera.position.y = -1000; + camera.up.set(0, 0, 1); + camera.lookAt(0, 0, 0); + view.blind_camera = camera; + + view.container = container; + view.renderer = renderer; + view.scene = scene; + + view.active = true; + + view.disable = function () { + this.active = false; + this.renderWithCamera(this.blind_camera); }; - - this.render = function(){ - console.log("render verything"); - if (this.mainView) - this.mainView.renderAll(); - - this.boxViewList.forEach(v=>{ - //if (v.ui.style.display != 'none') //we have pseudo box now. render as commanded. - v.render() - }); - + + view.dumpPose = function () { + console.log(this.camera.position, this.camera.rotation); }; - - this.setColorScheme = function(){ - let scheme = document.documentElement.className; - if (scheme == "theme-dark") - { - this.mainView.backgroundColor = new THREE.Color( 0.0, 0.0, 0.0 ); - this.boxViewList.forEach(v=>{ - v.views[0].backgroundColor = new THREE.Color( 0.1, 0.05, 0.05 ); - v.views[1].backgroundColor = new THREE.Color( 0.05, 0.1, 0.05 ); - v.views[2].backgroundColor = new THREE.Color( 0.05, 0.05, 0.1 ); - }); - } - else{ - this.mainView.backgroundColor = new THREE.Color( 1.0, 1.0, 1.0 ); - this.boxViewList.forEach(v=>{ - v.views[0].backgroundColor = new THREE.Color( 0.95, 0.9, 0.9 ); - v.views[1].backgroundColor = new THREE.Color( 0.9, 0.95, 0.9 ); - v.views[2].backgroundColor = new THREE.Color( 0.9, 0.9, 0.95 ); - }) - } + view.enable = function () { + this.active = true; + this.render(); }; - //this.setColorScheme(); - - // no public funcs below - function create_main_view(scene, renderer, globalRenderFunc, container, on_box_changed){ - var view ={}; - - view.backgroundColor= (document.documentElement.className == "theme-dark") ? new THREE.Color( 0.0, 0.0, 0.0 ) : new THREE.Color( 1.0, 1.0, 1.0 ); - view.zoom_ratio = 1.0; //useless for mainview - - let camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 500 ); - camera.position.x = 0; - camera.position.z = 50; - camera.position.y = 0; - camera.up.set( 0, 0, 1); - camera.lookAt( 0, 0, 0 ); - camera.name = "main view camera"; - view.camera_perspective = camera; - view.camera = camera; - - // make a blind camera to clean background when batch editing is enabled. - camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 500 ); - camera.position.x = -1000; - camera.position.z = -1000; - camera.position.y = -1000; - camera.up.set( 0, 0, 1); - camera.lookAt( 0, 0, 0 ); - view.blind_camera = camera; - - - view.container = container; - view.renderer = renderer; - view.scene = scene; - - view.active = true; - - view.disable = function(){ - this.active = false; - this.renderWithCamera(this.blind_camera); - }; - - view.dumpPose = function() - { - console.log(this.camera.position, this.camera.rotation); - }; - - view.enable = function(){ - this.active = true; - this.render(); - }; - - //var cameraOrthoHelper = new THREE.CameraHelper( camera ); - //cameraOrthoHelper.visible=true; - //scene.add( cameraOrthoHelper ); - - view.render=function(){ - console.log("render mainview."); - if (this.active){ - //this.switch_camera(false); - this.renderWithCamera(this.camera); - } - // else - // { - // this.renderWithCamera(this.blind_camera); - // } - }; - - view.renderAll = function(){ - console.log("render mainview."); - if (this.active){ - //this.switch_camera(false); - this.renderWithCamera(this.camera); - } - else - { - this.renderWithCamera(this.blind_camera); - } - } - - view.clearView = function(){ - this.renderWithCamera(this.blind_camera); - }; - - view.renderWithCamera = function(camera){ - var left = 0; - var bottom = 0; - var width = this.container.scrollWidth; - var height = this.container.scrollHeight; - - // update viewport, so the operating lines over these views - // will be updated in time. - - - //console.log(left,bottom, width, height); - - this.renderer.setViewport( left, bottom, width, height ); - this.renderer.setScissor( left, bottom, width, height ); - this.renderer.setClearColor(view.backgroundColor ); - this.renderer.setScissorTest( true ); - - this.renderer.render( this.scene, camera ); - }; - - - var orbit_perspective = new OrbitControls( view.camera_perspective, view.container ); - orbit_perspective.update(); - orbit_perspective.addEventListener( 'change', globalRenderFunc ); - //orbit_perspective.enabled = true; - view.orbit_perspective = orbit_perspective; - - - var transform_control = new TransformControls(view.camera_perspective , view.container ); - transform_control.setSpace("local"); - transform_control.addEventListener( 'change', globalRenderFunc ); - transform_control.addEventListener( 'objectChange', function(e){on_box_changed(e.target.object);}); - - transform_control.addEventListener( 'dragging-changed', function ( event ) { - view.orbit_perspective.enabled = ! event.value; - } ); - transform_control.visible = false; - //transform_control.enabled = false; - scene.add( transform_control ); - view.transform_control_perspective = transform_control; - - - - - var width = container.clientWidth; - var height = container.clientHeight; - var asp = width/height; - - //camera = new THREE.OrthographicCamera(-800*asp, 800*asp, 800, -800, -800, 800); - // camera.position.x = 0; - // camera.position.z = 0; - // camera.position.y = 0; - // camera.up.set( 1, 0, 0); - // camera.lookAt( 0, 0, -3 ); - - //camera = new THREE.OrthographicCamera( container.clientWidth / - 2, container.clientWidth / 2, container.clientHeight / 2, container.clientHeight / - 2, -400, 400 ); - - // camera = new THREE.OrthographicCamera(-asp*200, asp*200, 200, -200, -200, 200 ); - // camera.position.z = 50; - - - // var cameraOrthoHelper = new THREE.CameraHelper( camera ); - // cameraOrthoHelper.visible=true; - // scene.add( cameraOrthoHelper ); - - - //view.camera_orth = camera; - - // var orbit_orth = new OrbitControls( view.camera_orth, view.container ); - // orbit_orth.update(); - // orbit_orth.addEventListener( 'change', render ); - // orbit_orth.enabled = false; - // view.orbit_orth = orbit_orth; - - // var orbit_orth = new OrthographicTrackballControls( view.camera_orth, view.container ); - // orbit_orth.rotateSpeed = 1.0; - // orbit_orth.zoomSpeed = 1.2; - // orbit_orth.noZoom = false; - // orbit_orth.noPan = false; - // orbit_orth.noRotate = false; - // orbit_orth.staticMoving = true; - - // orbit_orth.dynamicDampingFactor = 0.3; - // orbit_orth.keys = [ 65, 83, 68 ]; - // orbit_orth.addEventListener( 'change', globalRenderFunc ); - // orbit_orth.enabled=true; - // view.orbit_orth = orbit_orth; - - // transform_control = new TransformControls(view.camera_orth, view.container ); - // transform_control.setSpace("local"); - // transform_control.addEventListener( 'change', globalRenderFunc ); - // transform_control.addEventListener( 'objectChange', function(e){on_box_changed(e.target.object);} ); - - - // transform_control.addEventListener( 'dragging-changed', function ( event ) { - // view.orbit_orth.enabled = ! event.value; - // } ); - - - // transform_control.visible = false; - // //transform_control.enabled = true; - // //scene.add( transform_control ); - - // view.transform_control_orth = transform_control; - - - - view.camera = view.camera_perspective; - view.orbit = view.orbit_perspective; - view.transform_control = view.transform_control_perspective; - - - view.switch_camera = function(birdseye) - { - - if (!birdseye && (this.camera === this.camera_orth)){ - this.camera = this.camera_perspective; - this.orbit_orth.enabled=false; - this.orbit_perspective.enabled=true; - this.orbit = this.orbit_perspective; - - - this.transform_control_perspective.detach(); - this.transform_control_orth.detach(); - - this.transform_control_orth.enabled=false; - this.transform_control_perspective.enabled=true; - //this.transform_control_perspective.visible = false; - //this.transform_control_orth.visible = false; - this.transform_control = this.transform_control_perspective; - } - else if (birdseye && (this.camera === this.camera_perspective)) - { - this.camera = this.camera_orth; - this.orbit_orth.enabled=true; - this.orbit_perspective.enabled=false; - this.orbit = this.orbit_orth; - - this.transform_control_perspective.detach(); - this.transform_control_orth.detach(); - this.transform_control_orth.enabled=true; - this.transform_control_perspective.enabled=false; - this.transform_control = this.transform_control_orth; - } - - this.camera.updateProjectionMatrix(); - }; - - view.reset_camera = function(){ - var camera = this.camera_perspective; - camera.position.x = 0; - camera.position.z = 50; - camera.position.y = 0; - camera.up.set( 0, 0, 1); - camera.lookAt( 0, 0, 0 ); - camera.updateProjectionMatrix(); - - this.orbit_perspective.reset(); // this func will call render() - }; - - view.look_at = function(p){ - if (this.orbit === this.orbit_perspective){ - this.orbit.target.x=p.x; - this.orbit.target.y=p.y; - this.orbit.target.z=p.z; - this.orbit.update(); - } - }; - - view.onWindowResize = function(){ - - - var asp = container.clientWidth/container.clientHeight; - // this.camera_orth.left = -asp*200; - // this.camera_orth.right = asp*200; - // this.camera_orth.top = 200; - // this.camera_orth.bottom = -200 - // this.camera_orth.updateProjectionMatrix(); - - // this.orbit_orth.handleResize(); - // this.orbit_orth.update(); - - this.camera_perspective.aspect = container.clientWidth / container.clientHeight; - this.camera_perspective.updateProjectionMatrix(); - - }; - - view.reset_birdseye = function(){ - //this.orbit_orth.reset(); // - }; - view.rotate_birdseye = function(){ - //this.camera_orth.up.set( 1, 0, 0); - //this.orbit_orth.update(); - } - view.detach_control = function(){ - this.transform_control.detach(); - } - - view.target0 = view.orbit.target.clone(); - view.position0 = view.camera.position.clone(); - view.zoom0 = view.camera.zoom; - view.scale0 = null; - - view.save_orbit_state = function(highlight_obj_scale){ - this.target0.copy( this.orbit.target ); - this.position0.copy( this.camera.position ); - this.zoom0 = this.camera.zoom; - this.scale0 = {x: highlight_obj_scale.x, y: highlight_obj_scale.y, z: highlight_obj_scale.z}; - } - - view.restore_relative_orbit_state = function(highlight_obj_scale){ - - if (view.scale0){ - // restore last viewpoint - - var obj_size = Math.sqrt(view.scale0.x*view.scale0.x + view.scale0.y*view.scale0.y + view.scale0.z*view.scale0.z); - var target_obj_size = Math.sqrt(highlight_obj_scale.x*highlight_obj_scale.x + highlight_obj_scale.y*highlight_obj_scale.y + highlight_obj_scale.z*highlight_obj_scale.z); - var ratio = target_obj_size/obj_size; - - - this.camera.position.x = this.orbit.target.x + (this.position0.x - this.target0.x)*ratio; - this.camera.position.y = this.orbit.target.y + (this.position0.y - this.target0.y)*ratio; - this.camera.position.z = this.orbit.target.z + (this.position0.z - this.target0.z)*ratio; - - this.camera.zoom = this.zoom0; - } else { - // not saved yet, set default viewpoint - this.camera.position.set( - this.orbit.target.x + highlight_obj_scale.x*3, - this.orbit.target.y + highlight_obj_scale.y*3, - this.orbit.target.z + highlight_obj_scale.z*3); - } - // target is set - } - - - return view; - } -} + //var cameraOrthoHelper = new THREE.CameraHelper( camera ); + //cameraOrthoHelper.visible=true; + //scene.add( cameraOrthoHelper ); + + view.render = function () { + console.log("render mainview."); + if (this.active) { + //this.switch_camera(false); + this.renderWithCamera(this.camera); + } + // else + // { + // this.renderWithCamera(this.blind_camera); + // } + }; -function BoxView(ui, mainViewContainer, scene, renderer, viewManager){ - - - this.viewManager = viewManager; - this.mainViewContainer = mainViewContainer; - this.ui = ui; //sub-views - this.baseOffset = function(){ - // ui offset - return { - top: this.ui.offsetTop, - left: this.ui.offsetLeft - } + view.renderAll = function () { + console.log("render mainview."); + if (this.active) { + //this.switch_camera(false); + this.renderWithCamera(this.camera); + } else { + this.renderWithCamera(this.blind_camera); + } }; - - this.defaultBox = { - position: {x: -100, y: -100, z: 0}, - rotation: {x: 0, y: 0, z: 0}, - scale: {x: 5, y: 5, z: 5}, + view.clearView = function () { + this.renderWithCamera(this.blind_camera); }; - this.box = this.defaultBox; - - this.attachBox = function(box){ - - this.box = box; + view.renderWithCamera = function (camera) { + var left = 0; + var bottom = 0; + var width = this.container.scrollWidth; + var height = this.container.scrollHeight; - this.views.forEach(v=>{ - //this.box.world.webglGroup.add(v.camera); - //this.box.world.webglGroup.add(v.cameraHelper); + // update viewport, so the operating lines over these views + // will be updated in time. - this.box.world.webglGroup.add(v.cameraContainer); - this.box.world.webglGroup.add(v.cameraHelper); //seems camerahelp shold be added to top-most scene only. - }); + //console.log(left,bottom, width, height); - this.onBoxChanged(); + this.renderer.setViewport(left, bottom, width, height); + this.renderer.setScissor(left, bottom, width, height); + this.renderer.setClearColor(view.backgroundColor); + this.renderer.setScissorTest(true); + + this.renderer.render(this.scene, camera); }; - this.detach = function(){ - this.box = this.defaultBox; - this.onBoxChanged(); + + var orbit_perspective = new OrbitControls( + view.camera_perspective, + view.container + ); + orbit_perspective.update(); + orbit_perspective.addEventListener("change", globalRenderFunc); + //orbit_perspective.enabled = true; + view.orbit_perspective = orbit_perspective; + + var transform_control = new TransformControls( + view.camera_perspective, + view.container + ); + transform_control.setSpace("local"); + transform_control.addEventListener("change", globalRenderFunc); + transform_control.addEventListener("objectChange", function (e) { + on_box_changed(e.target.object); + }); + + transform_control.addEventListener("dragging-changed", function (event) { + view.orbit_perspective.enabled = !event.value; + }); + transform_control.visible = false; + //transform_control.enabled = false; + scene.add(transform_control); + view.transform_control_perspective = transform_control; + + var width = container.clientWidth; + var height = container.clientHeight; + var asp = width / height; + + //camera = new THREE.OrthographicCamera(-800*asp, 800*asp, 800, -800, -800, 800); + // camera.position.x = 0; + // camera.position.z = 0; + // camera.position.y = 0; + // camera.up.set( 1, 0, 0); + // camera.lookAt( 0, 0, -3 ); + + //camera = new THREE.OrthographicCamera( container.clientWidth / - 2, container.clientWidth / 2, container.clientHeight / 2, container.clientHeight / - 2, -400, 400 ); + + // camera = new THREE.OrthographicCamera(-asp*200, asp*200, 200, -200, -200, 200 ); + // camera.position.z = 50; + + // var cameraOrthoHelper = new THREE.CameraHelper( camera ); + // cameraOrthoHelper.visible=true; + // scene.add( cameraOrthoHelper ); + + //view.camera_orth = camera; + + // var orbit_orth = new OrbitControls( view.camera_orth, view.container ); + // orbit_orth.update(); + // orbit_orth.addEventListener( 'change', render ); + // orbit_orth.enabled = false; + // view.orbit_orth = orbit_orth; + + // var orbit_orth = new OrthographicTrackballControls( view.camera_orth, view.container ); + // orbit_orth.rotateSpeed = 1.0; + // orbit_orth.zoomSpeed = 1.2; + // orbit_orth.noZoom = false; + // orbit_orth.noPan = false; + // orbit_orth.noRotate = false; + // orbit_orth.staticMoving = true; + + // orbit_orth.dynamicDampingFactor = 0.3; + // orbit_orth.keys = [ 65, 83, 68 ]; + // orbit_orth.addEventListener( 'change', globalRenderFunc ); + // orbit_orth.enabled=true; + // view.orbit_orth = orbit_orth; + + // transform_control = new TransformControls(view.camera_orth, view.container ); + // transform_control.setSpace("local"); + // transform_control.addEventListener( 'change', globalRenderFunc ); + // transform_control.addEventListener( 'objectChange', function(e){on_box_changed(e.target.object);} ); + + // transform_control.addEventListener( 'dragging-changed', function ( event ) { + // view.orbit_orth.enabled = ! event.value; + // } ); + + // transform_control.visible = false; + // //transform_control.enabled = true; + // //scene.add( transform_control ); + + // view.transform_control_orth = transform_control; + + view.camera = view.camera_perspective; + view.orbit = view.orbit_perspective; + view.transform_control = view.transform_control_perspective; + + view.switch_camera = function (birdseye) { + if (!birdseye && this.camera === this.camera_orth) { + this.camera = this.camera_perspective; + this.orbit_orth.enabled = false; + this.orbit_perspective.enabled = true; + this.orbit = this.orbit_perspective; + + this.transform_control_perspective.detach(); + this.transform_control_orth.detach(); + + this.transform_control_orth.enabled = false; + this.transform_control_perspective.enabled = true; + //this.transform_control_perspective.visible = false; + //this.transform_control_orth.visible = false; + this.transform_control = this.transform_control_perspective; + } else if (birdseye && this.camera === this.camera_perspective) { + this.camera = this.camera_orth; + this.orbit_orth.enabled = true; + this.orbit_perspective.enabled = false; + this.orbit = this.orbit_orth; + + this.transform_control_perspective.detach(); + this.transform_control_orth.detach(); + this.transform_control_orth.enabled = true; + this.transform_control_perspective.enabled = false; + this.transform_control = this.transform_control_orth; + } + + this.camera.updateProjectionMatrix(); }; - this.onBoxChanged=function(dontRender){ - this.updateCameraPose(this.box); - this.updateCameraRange(this.box); + view.reset_camera = function () { + var camera = this.camera_perspective; + camera.position.x = 0; + camera.position.z = 50; + camera.position.y = 0; + camera.up.set(0, 0, 1); + camera.lookAt(0, 0, 0); + camera.updateProjectionMatrix(); - if (!dontRender) - this.render(); + this.orbit_perspective.reset(); // this func will call render() }; - this.updateCameraPose = function(box){ - this.views.forEach((v)=>v.updateCameraPose(box)); + view.look_at = function (p) { + if (this.orbit === this.orbit_perspective) { + this.orbit.target.x = p.x; + this.orbit.target.y = p.y; + this.orbit.target.z = p.z; + this.orbit.update(); + } }; - this.updateCameraRange = function(box){ - this.views.forEach((v)=>v.updateCameraRange(box)); + view.onWindowResize = function () { + var asp = container.clientWidth / container.clientHeight; + // this.camera_orth.left = -asp*200; + // this.camera_orth.right = asp*200; + // this.camera_orth.top = 200; + // this.camera_orth.bottom = -200 + // this.camera_orth.updateProjectionMatrix(); + + // this.orbit_orth.handleResize(); + // this.orbit_orth.update(); + + this.camera_perspective.aspect = + container.clientWidth / container.clientHeight; + this.camera_perspective.updateProjectionMatrix(); }; - this.hidden = function(){ - return this.ui.style.display == 'none'; + view.reset_birdseye = function () { + //this.orbit_orth.reset(); // + }; + view.rotate_birdseye = function () { + //this.camera_orth.up.set( 1, 0, 0); + //this.orbit_orth.update(); + }; + view.detach_control = function () { + this.transform_control.detach(); }; - this.render = function(){ - if (!this.hidden()) - this.views.forEach((v)=>v.render()); - } + view.target0 = view.orbit.target.clone(); + view.position0 = view.camera.position.clone(); + view.zoom0 = view.camera.zoom; + view.scale0 = null; + + view.save_orbit_state = function (highlight_obj_scale) { + this.target0.copy(this.orbit.target); + this.position0.copy(this.camera.position); + this.zoom0 = this.camera.zoom; + this.scale0 = { + x: highlight_obj_scale.x, + y: highlight_obj_scale.y, + z: highlight_obj_scale.z, + }; + }; - var scope = this; - - - scope.projViewProto = { - render(){ - let vp = this.getViewPort(); - - this.renderer.setViewport( vp.left, vp.bottom, vp.width, vp.height ); - this.renderer.setScissor( vp.left, vp.bottom, vp.width, vp.height ); - this.renderer.setClearColor(this.backgroundColor ); - this.renderer.setScissorTest( true ); - this.renderer.render( this.scene, this.camera ); - }, - - getViewPort(){ - return { - left : this.placeHolderUi.offsetLeft + scope.baseOffset().left, - bottom : this.container.scrollHeight - (scope.baseOffset().top + this.placeHolderUi.offsetTop + this.placeHolderUi.clientHeight), - width : this.placeHolderUi.clientWidth, - height : this.placeHolderUi.clientHeight, - zoom_ratio: this.zoom_ratio, - } - }, - - + view.restore_relative_orbit_state = function (highlight_obj_scale) { + if (view.scale0) { + // restore last viewpoint + + var obj_size = Math.sqrt( + view.scale0.x * view.scale0.x + + view.scale0.y * view.scale0.y + + view.scale0.z * view.scale0.z + ); + var target_obj_size = Math.sqrt( + highlight_obj_scale.x * highlight_obj_scale.x + + highlight_obj_scale.y * highlight_obj_scale.y + + highlight_obj_scale.z * highlight_obj_scale.z + ); + var ratio = target_obj_size / obj_size; + + this.camera.position.x = + this.orbit.target.x + (this.position0.x - this.target0.x) * ratio; + this.camera.position.y = + this.orbit.target.y + (this.position0.y - this.target0.y) * ratio; + this.camera.position.z = + this.orbit.target.z + (this.position0.z - this.target0.z) * ratio; + + this.camera.zoom = this.zoom0; + } else { + // not saved yet, set default viewpoint + this.camera.position.set( + this.orbit.target.x + highlight_obj_scale.x * 3, + this.orbit.target.y + highlight_obj_scale.y * 3, + this.orbit.target.z + highlight_obj_scale.z * 3 + ); + } + // target is set }; - this.views = [ - createTopView(scene, renderer, mainViewContainer), - createSideView(scene, renderer, mainViewContainer), - createBackView(scene, renderer, mainViewContainer), - ]; - - function createTopView(scene, renderer, container){ - let view = Object.create(scope.projViewProto); - view.name="topview"; - view.zoom_ratio = 1.0; - - view.backgroundColor =  (document.documentElement.className == "theme-dark") ? new THREE.Color( 0.1, 0.05, 0.05 ) : new THREE.Color( 0.95, 0.9, 0.9 ); - view.container = container; - view.scene = scene; - view.renderer = renderer; - view.placeHolderUi = ui.querySelector("#z-view-manipulator"); - - //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); - var width = container.clientWidth; - var height = container.clientHeight; - var asp = width/height; - - var camera = new THREE.OrthographicCamera( -3*asp, 3*asp, 3, -3, -3, 3 ); - - var cameraOrthoHelper = new THREE.CameraHelper( camera ); - cameraOrthoHelper.visible=false; - //scene.add( cameraOrthoHelper ); - view.cameraHelper = cameraOrthoHelper; - - camera.position.set(0,0,0); - camera.up.set( 1, 0, 0); - camera.lookAt( 0, 0, -3 ); - - view.camera = camera; - view.cameraContainer = new THREE.Group(); - view.cameraContainer.name = "topview-camera"; - view.cameraContainer.add(camera); - - - view.updateCameraPose=function(box){ - var p = box.position; - var r = box.rotation; - //console.log(r); - // - this.cameraContainer.position.set(p.x, p.y, p.z); - this.cameraContainer.rotation.set(r.x, r.y, r.z); - }; - - view.updateCameraRange=function(box){ - - var exp_camera_width, exp_camera_height, exp_camera_clip; - - //view.width = 0.2;//params["side view width"]; - - var view_width = view.placeHolderUi.clientWidth; - var view_height = view.placeHolderUi.clientHeight; - - exp_camera_height = box.scale.x*1.5*view.zoom_ratio; - exp_camera_width = box.scale.y*1.5*view.zoom_ratio; - exp_camera_clip = box.scale.z + 0.8; - - if (exp_camera_width/exp_camera_height > view_width/view_height){ - //increase height - exp_camera_height = exp_camera_width * view_height/view_width; - } - else - { - exp_camera_width = exp_camera_height * view_width/view_height; - } - - this.camera.top = exp_camera_height/2; - this.camera.bottom = exp_camera_height/-2; - this.camera.right = exp_camera_width/2; - this.camera.left = exp_camera_width/-2; - - this.camera.near = exp_camera_clip/-2; - this.camera.far = exp_camera_clip/2; - - // this.camera.scale.x = box.scale.x; - // this.camera.scale.y = box.scale.y; - // this.camera.scale.z = box.scale.z; - //camera.aspect = view_width / view_height; - this.camera.updateProjectionMatrix(); - this.cameraHelper.update(); - }; - - return view; - } + return view; + } +} +function BoxView(ui, mainViewContainer, scene, renderer, viewManager) { + this.viewManager = viewManager; + this.mainViewContainer = mainViewContainer; + this.ui = ui; //sub-views + this.baseOffset = function () { + // ui offset + return { + top: this.ui.offsetTop, + left: this.ui.offsetLeft, + }; + }; - function createSideView(scene, renderer, container){ - let view = Object.create(scope.projViewProto); - view.name="sideview"; - view.zoom_ratio = 1.0; - //view.backgroundColor=new THREE.Color( 0.1, 0.2, 0.1 ); - view.backgroundColor=(document.documentElement.className == "theme-dark") ? new THREE.Color( 0.05, 0.1, 0.05 ) : new THREE.Color( 0.9, 0.95, 0.9 ); - view.container = container; - view.scene = scene; - view.renderer = renderer; - view.placeHolderUi = ui.querySelector("#y-view-manipulator"); - - //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); - var width = container.clientWidth; - var height = container.clientHeight; - var asp = width/height; - - var camera = new THREE.OrthographicCamera( -3*asp, 3*asp, 3, -3, -3, 3 ); - - var cameraOrthoHelper = new THREE.CameraHelper( camera ); - cameraOrthoHelper.visible=false; - //scene.add( cameraOrthoHelper ); - view["cameraHelper"] = cameraOrthoHelper; - - view.cameraContainer = new THREE.Group(); - view.cameraContainer.name = "sideview-camera"; - - view.cameraContainer.position.x = 0; - view.cameraContainer.position.z = 0; - view.cameraContainer.position.y = 0; - - view.cameraContainer.rotation.x=0; //Math.PI/2; - view.cameraContainer.rotation.y=0; - view.cameraContainer.rotation.z=0; - - //view.cameraContainer.updateProjectionMatrix(); - - view.cameraContainer.add(camera); - - camera.position.set( 0, 0, 0); - camera.up.set(0,0,1); - camera.lookAt( 0, 3, 0 ); - - //camera.up.set( 0, 1, 0); - //camera.lookAt( 0, 0, -3 ); - - - // camera should not be changed again? - view.camera = camera; - - view.updateCameraPose=function(box){ - var p = box.position; - var r = box.rotation; - - view.cameraContainer.position.x = p.x; - view.cameraContainer.position.y = p.y; - view.cameraContainer.position.z = p.z; - - - view.cameraContainer.rotation.x=r.x; - view.cameraContainer.rotation.y=r.y; - view.cameraContainer.rotation.z=r.z; - //view.cameraContainer.updateProjectionMatrix(); - - // var trans_matrix = euler_angle_to_rotate_matrix(r, p); - - - // this.camera.position.x= p.x; - // this.camera.position.y= p.y; - // this.camera.position.z= p.z; - - - - - // var up = matmul2(trans_matrix, [0, 0, 1, 0], 4); - // this.camera.up.set( up[0], up[1], up[2]); - // var at = matmul2(trans_matrix, [0, 1, 0, 1], 4); - // this.camera.lookAt( at[0], at[1], at[2] ); - - - // this.camera.updateProjectionMatrix(); - // this.cameraHelper.update(); - }; - - view.updateCameraRange=function(box){ - - var exp_camera_width, exp_camera_height, exp_camera_clip; - - //view.width = 0.2;//params["side view width"]; - - var view_width = view.placeHolderUi.clientWidth; - var view_height = view.placeHolderUi.clientHeight; - - exp_camera_width = box.scale.x*1.5*view.zoom_ratio; - exp_camera_height = box.scale.z*1.5*view.zoom_ratio; - - exp_camera_clip = box.scale.y*1.2; - - if (exp_camera_width/exp_camera_height > view_width/view_height){ - //increase height - exp_camera_height = exp_camera_width * view_height/view_width; - } - else - { - exp_camera_width = exp_camera_height * view_width/view_height; - } - - this.camera.top = exp_camera_height/2; - this.camera.bottom = exp_camera_height/-2; - this.camera.right = exp_camera_width/2; - this.camera.left = exp_camera_width/-2; - - this.camera.near = exp_camera_clip/-2; - this.camera.far = exp_camera_clip/2; - - //camera.aspect = view_width / view_height; - this.camera.updateProjectionMatrix(); - this.cameraHelper.update(); - }; - - return view; - } + this.defaultBox = { + position: { x: -100, y: -100, z: 0 }, + rotation: { x: 0, y: 0, z: 0 }, + scale: { x: 5, y: 5, z: 5 }, + }; - function createBackView(scene, renderer, container){ - let view = Object.create(scope.projViewProto); - view.name="backview"; - view.zoom_ratio = 1.0; - //view.backgroundColor=new THREE.Color( 0.2, 0.1, 0.1 ); - view.backgroundColor= (document.documentElement.className == "theme-dark") ? new THREE.Color( 0.05, 0.05, 0.1 ) : new THREE.Color( 0.9, 0.9, 0.95 ); - view.container = container; - view.scene = scene; - view.renderer = renderer; - view.placeHolderUi = ui.querySelector("#x-view-manipulator"); - - //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); - var width = container.clientWidth; - var height = container.clientHeight; - var asp = width/height; - - var camera = new THREE.OrthographicCamera( -3*asp, 3*asp, 3, -3, -3, 3 ); - - var cameraOrthoHelper = new THREE.CameraHelper( camera ); - cameraOrthoHelper.visible=false; - //scene.add( cameraOrthoHelper ); - view["cameraHelper"] = cameraOrthoHelper; - - camera.position.set(0,0,0); - camera.up.set(0,0,1); - camera.lookAt(3,0,0); - - view.camera = camera; - view.cameraContainer = new THREE.Group(); - view.cameraContainer.name = "backview-camera"; - view.cameraContainer.position.set(0,0,0); - view.cameraContainer.rotation.set(0,0,0); - - view.cameraContainer.add(camera); - - view.updateCameraPose=function(box){ - - let p = box.position; - let r = box.rotation; - - // let trans_matrix = euler_angle_to_rotate_matrix(r, p); - - // this.camera.position.x= p.x; - // this.camera.position.y= p.y; - // this.camera.position.z= p.z; - - // var up3 = matmul2(trans_matrix, [0, 0, 1, 0], 4); - // this.camera.up.set( up3[0], up3[1], up3[2]); - // var at3 = matmul2(trans_matrix, [1, 0, 0, 1], 4); - // this.camera.lookAt( at3[0], at3[1], at3[2] ); - - - // this.camera.updateProjectionMatrix(); - // this.cameraHelper.update(); - - this.cameraContainer.position.set(p.x, p.y, p.z); - this.cameraContainer.rotation.set(r.x, r.y, r.z); - }; - - view.updateCameraRange=function(box){ - - var exp_camera_width, exp_camera_height, exp_camera_clip; - - //view.width = 0.2;//params["side view width"]; - - var view_width = view.placeHolderUi.clientWidth; - var view_height = view.placeHolderUi.clientHeight; - - exp_camera_width = box.scale.y*1.5*view.zoom_ratio; - exp_camera_height = box.scale.z*1.5*view.zoom_ratio; - exp_camera_clip = box.scale.x*1.2; - - if (exp_camera_width/exp_camera_height > view_width/view_height){ - //increase height - exp_camera_height = exp_camera_width * view_height/view_width; - } - else - { - exp_camera_width = exp_camera_height * view_width/view_height; - } - - this.camera.top = exp_camera_height/2; - this.camera.bottom = exp_camera_height/-2; - this.camera.right = exp_camera_width/2; - this.camera.left = exp_camera_width/-2; - - this.camera.near = exp_camera_clip/-2; - this.camera.far = exp_camera_clip/2; - - //camera.aspect = view_width / view_height; - this.camera.updateProjectionMatrix(); - this.cameraHelper.update(); - }; - - return view; - } + this.box = this.defaultBox; + + this.attachBox = function (box) { + this.box = box; + + this.views.forEach((v) => { + //this.box.world.webglGroup.add(v.camera); + //this.box.world.webglGroup.add(v.cameraHelper); + + this.box.world.webglGroup.add(v.cameraContainer); + this.box.world.webglGroup.add(v.cameraHelper); //seems camerahelp shold be added to top-most scene only. + }); + + this.onBoxChanged(); + }; + this.detach = function () { + this.box = this.defaultBox; + this.onBoxChanged(); + }; + + this.onBoxChanged = function (dontRender) { + this.updateCameraPose(this.box); + this.updateCameraRange(this.box); + + if (!dontRender) this.render(); + }; + + this.updateCameraPose = function (box) { + this.views.forEach((v) => v.updateCameraPose(box)); + }; + + this.updateCameraRange = function (box) { + this.views.forEach((v) => v.updateCameraRange(box)); + }; + + this.hidden = function () { + return this.ui.style.display == "none"; + }; + + this.render = function () { + if (!this.hidden()) this.views.forEach((v) => v.render()); + }; + + var scope = this; + + scope.projViewProto = { + render() { + let vp = this.getViewPort(); + + this.renderer.setViewport(vp.left, vp.bottom, vp.width, vp.height); + this.renderer.setScissor(vp.left, vp.bottom, vp.width, vp.height); + this.renderer.setClearColor(this.backgroundColor); + this.renderer.setScissorTest(true); + this.renderer.render(this.scene, this.camera); + }, + + getViewPort() { + return { + left: this.placeHolderUi.offsetLeft + scope.baseOffset().left, + bottom: + this.container.scrollHeight - + (scope.baseOffset().top + + this.placeHolderUi.offsetTop + + this.placeHolderUi.clientHeight), + width: this.placeHolderUi.clientWidth, + height: this.placeHolderUi.clientHeight, + zoom_ratio: this.zoom_ratio, + }; + }, + }; + + this.views = [ + createTopView(scene, renderer, mainViewContainer), + createSideView(scene, renderer, mainViewContainer), + createBackView(scene, renderer, mainViewContainer), + ]; + + function createTopView(scene, renderer, container) { + let view = Object.create(scope.projViewProto); + view.name = "topview"; + view.zoom_ratio = 1.0; + + view.backgroundColor = + document.documentElement.className == "theme-dark" + ? new THREE.Color(0.1, 0.05, 0.05) + : new THREE.Color(0.95, 0.9, 0.9); + view.container = container; + view.scene = scene; + view.renderer = renderer; + view.placeHolderUi = ui.querySelector("#z-view-manipulator"); + + //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); + var width = container.clientWidth; + var height = container.clientHeight; + var asp = width / height; + + var camera = new THREE.OrthographicCamera(-3 * asp, 3 * asp, 3, -3, -3, 3); + + var cameraOrthoHelper = new THREE.CameraHelper(camera); + cameraOrthoHelper.visible = false; + //scene.add( cameraOrthoHelper ); + view.cameraHelper = cameraOrthoHelper; + + camera.position.set(0, 0, 0); + camera.up.set(1, 0, 0); + camera.lookAt(0, 0, -3); + + view.camera = camera; + view.cameraContainer = new THREE.Group(); + view.cameraContainer.name = "topview-camera"; + view.cameraContainer.add(camera); + + view.updateCameraPose = function (box) { + var p = box.position; + var r = box.rotation; + //console.log(r); + // + this.cameraContainer.position.set(p.x, p.y, p.z); + this.cameraContainer.rotation.set(r.x, r.y, r.z); + }; + + view.updateCameraRange = function (box) { + var exp_camera_width, exp_camera_height, exp_camera_clip; + + //view.width = 0.2;//params["side view width"]; + + var view_width = view.placeHolderUi.clientWidth; + var view_height = view.placeHolderUi.clientHeight; + + exp_camera_height = box.scale.x * 1.5 * view.zoom_ratio; + exp_camera_width = box.scale.y * 1.5 * view.zoom_ratio; + exp_camera_clip = box.scale.z + 0.8; + + if (exp_camera_width / exp_camera_height > view_width / view_height) { + //increase height + exp_camera_height = (exp_camera_width * view_height) / view_width; + } else { + exp_camera_width = (exp_camera_height * view_width) / view_height; + } + + this.camera.top = exp_camera_height / 2; + this.camera.bottom = exp_camera_height / -2; + this.camera.right = exp_camera_width / 2; + this.camera.left = exp_camera_width / -2; + + this.camera.near = exp_camera_clip / -2; + this.camera.far = exp_camera_clip / 2; + + // this.camera.scale.x = box.scale.x; + // this.camera.scale.y = box.scale.y; + // this.camera.scale.z = box.scale.z; + //camera.aspect = view_width / view_height; + this.camera.updateProjectionMatrix(); + this.cameraHelper.update(); + }; + + return view; + } + + function createSideView(scene, renderer, container) { + let view = Object.create(scope.projViewProto); + view.name = "sideview"; + view.zoom_ratio = 1.0; + //view.backgroundColor=new THREE.Color( 0.1, 0.2, 0.1 ); + view.backgroundColor = + document.documentElement.className == "theme-dark" + ? new THREE.Color(0.05, 0.1, 0.05) + : new THREE.Color(0.9, 0.95, 0.9); + view.container = container; + view.scene = scene; + view.renderer = renderer; + view.placeHolderUi = ui.querySelector("#y-view-manipulator"); + + //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); + var width = container.clientWidth; + var height = container.clientHeight; + var asp = width / height; + + var camera = new THREE.OrthographicCamera(-3 * asp, 3 * asp, 3, -3, -3, 3); + + var cameraOrthoHelper = new THREE.CameraHelper(camera); + cameraOrthoHelper.visible = false; + //scene.add( cameraOrthoHelper ); + view["cameraHelper"] = cameraOrthoHelper; + + view.cameraContainer = new THREE.Group(); + view.cameraContainer.name = "sideview-camera"; + + view.cameraContainer.position.x = 0; + view.cameraContainer.position.z = 0; + view.cameraContainer.position.y = 0; + + view.cameraContainer.rotation.x = 0; //Math.PI/2; + view.cameraContainer.rotation.y = 0; + view.cameraContainer.rotation.z = 0; + + //view.cameraContainer.updateProjectionMatrix(); + + view.cameraContainer.add(camera); + + camera.position.set(0, 0, 0); + camera.up.set(0, 0, 1); + camera.lookAt(0, 3, 0); + + //camera.up.set( 0, 1, 0); + //camera.lookAt( 0, 0, -3 ); + + // camera should not be changed again? + view.camera = camera; + + view.updateCameraPose = function (box) { + var p = box.position; + var r = box.rotation; + + view.cameraContainer.position.x = p.x; + view.cameraContainer.position.y = p.y; + view.cameraContainer.position.z = p.z; + + view.cameraContainer.rotation.x = r.x; + view.cameraContainer.rotation.y = r.y; + view.cameraContainer.rotation.z = r.z; + //view.cameraContainer.updateProjectionMatrix(); + + // var trans_matrix = euler_angle_to_rotate_matrix(r, p); + + // this.camera.position.x= p.x; + // this.camera.position.y= p.y; + // this.camera.position.z= p.z; + + // var up = matmul2(trans_matrix, [0, 0, 1, 0], 4); + // this.camera.up.set( up[0], up[1], up[2]); + // var at = matmul2(trans_matrix, [0, 1, 0, 1], 4); + // this.camera.lookAt( at[0], at[1], at[2] ); + + // this.camera.updateProjectionMatrix(); + // this.cameraHelper.update(); + }; + + view.updateCameraRange = function (box) { + var exp_camera_width, exp_camera_height, exp_camera_clip; + + //view.width = 0.2;//params["side view width"]; + + var view_width = view.placeHolderUi.clientWidth; + var view_height = view.placeHolderUi.clientHeight; + + exp_camera_width = box.scale.x * 1.5 * view.zoom_ratio; + exp_camera_height = box.scale.z * 1.5 * view.zoom_ratio; + + exp_camera_clip = box.scale.y * 1.2; + + if (exp_camera_width / exp_camera_height > view_width / view_height) { + //increase height + exp_camera_height = (exp_camera_width * view_height) / view_width; + } else { + exp_camera_width = (exp_camera_height * view_width) / view_height; + } + + this.camera.top = exp_camera_height / 2; + this.camera.bottom = exp_camera_height / -2; + this.camera.right = exp_camera_width / 2; + this.camera.left = exp_camera_width / -2; + + this.camera.near = exp_camera_clip / -2; + this.camera.far = exp_camera_clip / 2; + + //camera.aspect = view_width / view_height; + this.camera.updateProjectionMatrix(); + this.cameraHelper.update(); + }; + + return view; + } + + function createBackView(scene, renderer, container) { + let view = Object.create(scope.projViewProto); + view.name = "backview"; + view.zoom_ratio = 1.0; + //view.backgroundColor=new THREE.Color( 0.2, 0.1, 0.1 ); + view.backgroundColor = + document.documentElement.className == "theme-dark" + ? new THREE.Color(0.05, 0.05, 0.1) + : new THREE.Color(0.9, 0.9, 0.95); + view.container = container; + view.scene = scene; + view.renderer = renderer; + view.placeHolderUi = ui.querySelector("#x-view-manipulator"); + + //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); + var width = container.clientWidth; + var height = container.clientHeight; + var asp = width / height; + + var camera = new THREE.OrthographicCamera(-3 * asp, 3 * asp, 3, -3, -3, 3); + + var cameraOrthoHelper = new THREE.CameraHelper(camera); + cameraOrthoHelper.visible = false; + //scene.add( cameraOrthoHelper ); + view["cameraHelper"] = cameraOrthoHelper; + + camera.position.set(0, 0, 0); + camera.up.set(0, 0, 1); + camera.lookAt(3, 0, 0); + + view.camera = camera; + view.cameraContainer = new THREE.Group(); + view.cameraContainer.name = "backview-camera"; + view.cameraContainer.position.set(0, 0, 0); + view.cameraContainer.rotation.set(0, 0, 0); + + view.cameraContainer.add(camera); + + view.updateCameraPose = function (box) { + let p = box.position; + let r = box.rotation; + + // let trans_matrix = euler_angle_to_rotate_matrix(r, p); + + // this.camera.position.x= p.x; + // this.camera.position.y= p.y; + // this.camera.position.z= p.z; + + // var up3 = matmul2(trans_matrix, [0, 0, 1, 0], 4); + // this.camera.up.set( up3[0], up3[1], up3[2]); + // var at3 = matmul2(trans_matrix, [1, 0, 0, 1], 4); + // this.camera.lookAt( at3[0], at3[1], at3[2] ); + + // this.camera.updateProjectionMatrix(); + // this.cameraHelper.update(); + + this.cameraContainer.position.set(p.x, p.y, p.z); + this.cameraContainer.rotation.set(r.x, r.y, r.z); + }; + + view.updateCameraRange = function (box) { + var exp_camera_width, exp_camera_height, exp_camera_clip; + + //view.width = 0.2;//params["side view width"]; + + var view_width = view.placeHolderUi.clientWidth; + var view_height = view.placeHolderUi.clientHeight; + + exp_camera_width = box.scale.y * 1.5 * view.zoom_ratio; + exp_camera_height = box.scale.z * 1.5 * view.zoom_ratio; + exp_camera_clip = box.scale.x * 1.2; + + if (exp_camera_width / exp_camera_height > view_width / view_height) { + //increase height + exp_camera_height = (exp_camera_width * view_height) / view_width; + } else { + exp_camera_width = (exp_camera_height * view_width) / view_height; + } + + this.camera.top = exp_camera_height / 2; + this.camera.bottom = exp_camera_height / -2; + this.camera.right = exp_camera_width / 2; + this.camera.left = exp_camera_width / -2; + + this.camera.near = exp_camera_clip / -2; + this.camera.far = exp_camera_clip / 2; + + //camera.aspect = view_width / view_height; + this.camera.updateProjectionMatrix(); + this.cameraHelper.update(); + }; + + return view; + } } -export {ViewManager} \ No newline at end of file +export { ViewManager }; diff --git a/public/js/world.js b/public/js/world.js index 9ba9ddd..e28482e 100644 --- a/public/js/world.js +++ b/public/js/world.js @@ -1,28 +1,31 @@ -import * as THREE from './lib/three.module.js'; - - -import {RadarManager} from "./radar.js" -import {AuxLidarManager} from "./aux_lidar.js" -import {Lidar} from "./lidar.js" -import {Annotation} from "./annotation.js" -import {EgoPose} from "./ego_pose.js" -import {logger} from "./log.js" -import { euler_angle_to_rotate_matrix, euler_angle_to_rotate_matrix_3by3, matmul, matmul2 , mat} from './util.js'; - -function FrameInfo(data, sceneMeta, sceneName, frame){ - - this.data = data; - this.sceneMeta = sceneMeta; - this.dir = ""; - this.scene = sceneName; - this.frame = frame; - this.pcd_ext = ""; - this.frame_index = this.sceneMeta.frames.findIndex(function(x){return x==frame;}), - this.transform_matrix = this.sceneMeta.point_transform_matrix, - this.annotation_format = this.sceneMeta.boxtype, //xyz(24 number), csr(center, scale, rotation, 9 number) - - - +import * as THREE from "./lib/three.module.js"; + +import { RadarManager } from "./radar.js"; +import { AuxLidarManager } from "./aux_lidar.js"; +import { Lidar } from "./lidar.js"; +import { Annotation } from "./annotation.js"; +import { EgoPose } from "./ego_pose.js"; +import { logger } from "./log.js"; +import { + euler_angle_to_rotate_matrix, + euler_angle_to_rotate_matrix_3by3, + matmul, + matmul2, + mat, +} from "./util.js"; + +function FrameInfo(data, sceneMeta, sceneName, frame) { + this.data = data; + this.sceneMeta = sceneMeta; + this.dir = ""; + this.scene = sceneName; + this.frame = frame; + this.pcd_ext = ""; + (this.frame_index = this.sceneMeta.frames.findIndex(function (x) { + return x == frame; + })), + (this.transform_matrix = this.sceneMeta.point_transform_matrix), + (this.annotation_format = this.sceneMeta.boxtype), //xyz(24 number), csr(center, scale, rotation, 9 number) // this.set = function(scene, frame_index, frame, transform_matrix, annotation_format){ // this.scene = scene; // this.frame = frame; @@ -31,579 +34,608 @@ function FrameInfo(data, sceneMeta, sceneName, frame){ // this.annotation_format = annotation_format; // }; - - this.get_pcd_path = function(){ - return 'data/'+ this.scene + "/lidar/" + this.frame + this.sceneMeta.lidar_ext; - }; - this.get_radar_path = function(name){ - return `data/${this.scene}/radar/${name}/${this.frame}${this.sceneMeta.radar_ext}`; - }; - this.get_aux_lidar_path = function(name){ - return `data/${this.scene}/aux_lidar/${name}/${this.frame}${this.sceneMeta.radar_ext}`; + (this.get_pcd_path = function () { + return ( + "data/" + this.scene + "/lidar/" + this.frame + this.sceneMeta.lidar_ext + ); + }); + this.get_radar_path = function (name) { + return `data/${this.scene}/radar/${name}/${this.frame}${this.sceneMeta.radar_ext}`; + }; + this.get_aux_lidar_path = function (name) { + return `data/${this.scene}/aux_lidar/${name}/${this.frame}${this.sceneMeta.radar_ext}`; + }; + + this.get_anno_path = function () { + if (this.annotation_format == "psr") { + return "data/" + this.scene + "/label/" + this.frame + ".json"; + } else { + return "data/" + this.scene + "/bbox.xyz/" + this.frame + ".bbox.txt"; } - - this.get_anno_path = function(){ - if (this.annotation_format=="psr"){ - return 'data/'+this.scene + "/label/" + this.frame + ".json"; - } - else{ - return 'data/'+this.scene + "/bbox.xyz/" + this.frame + ".bbox.txt"; - } - - }; - - this.anno_to_boxes = function(text){ - var _self = this; - if (this.annotation_format == "psr"){ - - var boxes = JSON.parse(text); - - - return boxes; - } - else - return this.python_xyz_to_psr(text); - - }; - this.transform_point = function(m, x,y, z){ - var rx = x*m[0]+y*m[1]+z*m[2]; - var ry = x*m[3]+y*m[4]+z*m[5]; - var rz = x*m[6]+y*m[7]+z*m[8]; - - return [rx, ry, rz]; - }; - - /* + }; + + this.anno_to_boxes = function (text) { + var _self = this; + if (this.annotation_format == "psr") { + var boxes = JSON.parse(text); + + return boxes; + } else return this.python_xyz_to_psr(text); + }; + this.transform_point = function (m, x, y, z) { + var rx = x * m[0] + y * m[1] + z * m[2]; + var ry = x * m[3] + y * m[4] + z * m[5]; + var rz = x * m[6] + y * m[7] + z * m[8]; + + return [rx, ry, rz]; + }; + + /* input is coordinates of 8 vertices bottom-left-front, bottom-right-front, bottom-right-back, bottom-left-back top-left-front, top-right-front, top-right-back, top-left-back this format is what SECOND/PointRcnn save their results. */ - this.python_xyz_to_psr = function(text){ - var _self = this; - - var points_array = text.split('\n').filter(function(x){return x;}).map(function(x){return x.split(' ').map(function(x){return parseFloat(x);})}) - - - var boxes = points_array.map(function(ps){ - for (var i=0; i<8; i++){ - var p = _self.transform_point(_self.transform_matrix, ps[3*i+0],ps[3*i+1],ps[3*i+2]); - ps[i*3+0] = p[0]; - ps[i*3+1] = p[1]; - ps[i*3+2] = p[2]; - } - return ps; - }); - - var boxes_ann = boxes.map(this.xyz_to_psr); - - return boxes_ann; //, boxes]; - }; + this.python_xyz_to_psr = function (text) { + var _self = this; + + var points_array = text + .split("\n") + .filter(function (x) { + return x; + }) + .map(function (x) { + return x.split(" ").map(function (x) { + return parseFloat(x); + }); + }); + + var boxes = points_array.map(function (ps) { + for (var i = 0; i < 8; i++) { + var p = _self.transform_point( + _self.transform_matrix, + ps[3 * i + 0], + ps[3 * i + 1], + ps[3 * i + 2] + ); + ps[i * 3 + 0] = p[0]; + ps[i * 3 + 1] = p[1]; + ps[i * 3 + 2] = p[2]; + } + return ps; + }); + + var boxes_ann = boxes.map(this.xyz_to_psr); + + return boxes_ann; //, boxes]; + }; + + this.xyz_to_psr = function (ann_input) { + var ann = []; + if (ann_input.length == 24) ann = ann_input; + else + for (var i = 0; i < ann_input.length; i++) { + if ((i + 1) % 4 != 0) { + ann.push(ann_input[i]); + } + } - this.xyz_to_psr = function(ann_input){ - var ann = []; - if (ann_input.length==24) - ann = ann_input; - else - for (var i = 0; i 0) - // this.active_name = active_name; - // else if (this.names && this.names.length>0) - // this.active_name = this.names[0]; - - var _self = this; - - if (this.names){ - this.names.forEach(function(cam){ - _self.content[cam] = new Image(); - _self.content[cam].onload= function(){ - _self.loaded_flag[cam] = true; - _self.on_image_loaded(); - }; - _self.content[cam].onerror=function(){ - _self.loaded_flag[cam] = true; - _self.on_image_loaded(); - }; - - _self.content[cam].src = 'data/'+sceneName+'/camera/' + cam + '/'+ frame + sceneMeta.camera_ext; - console.log("image set") - }); - } - }, - - this.on_image_loaded = function(){ - if (this.loaded()){ - this.on_all_loaded(); - } - } + }; } - - -function World(data, sceneName, frame, coordinatesOffset, on_preload_finished){ - this.data = data; - this.sceneMeta = this.data.getMetaBySceneName(sceneName); - this.frameInfo = new FrameInfo(this.data, this.sceneMeta, sceneName, frame); - - - this.coordinatesOffset = coordinatesOffset; - - - this.toString = function(){ - return this.frameInfo.scene + "," + this.frameInfo.frame; +function Images(sceneMeta, sceneName, frame) { + this.loaded = function () { + for (var n in this.names) { + if (!this.loaded_flag[this.names[n]]) return false; } - //points_backup: null, //for restore from highlight - - this.cameras = new Images(this.sceneMeta, sceneName, frame); - this.radars = new RadarManager(this.sceneMeta, this, this.frameInfo); - this.lidar = new Lidar(this.sceneMeta, this, this.frameInfo); - this.annotation = new Annotation(this.sceneMeta, this, this.frameInfo); - this.aux_lidars = new AuxLidarManager(this.sceneMeta, this, this.frameInfo); - this.egoPose = new EgoPose(this.sceneMeta, this, this.FrameInfo); - - // todo: state of world could be put in a variable - // but still need mulitple flags. - - this.points_loaded = false, - - - - this.preloaded=function(){ - return this.lidar.preloaded && - this.annotation.preloaded && - //this.cameras.loaded() && - this.aux_lidars.preloaded() && - this.radars.preloaded()&& - this.egoPose.preloaded; - }; - - this.create_time = 0; - this.finish_time = 0; - this.on_preload_finished = null; - - this.on_subitem_preload_finished = function(on_preload_finished){ - if (this.preloaded()){ - - logger.log(`finished preloading ${this.frameInfo.scene} ${this.frameInfo.frame}`); - - this.calcTransformMatrix(); - - - if (this.on_preload_finished){ - this.on_preload_finished(this); - } - - if (this.active){ - this.go(); - } - } - }; + return true; + }; + + this.names = sceneMeta.camera; //["image","left","right"], + this.loaded_flag = {}; + // this.active_name = ""; + // this.active_image = function(){ + // return this.content[this.active_name]; + // }; + this.getImageByName = function (name) { + return this.content[name]; + }; + + // this.activate = function(name){ + // this.active_name = name; + // }; + + this.content = {}; + this.on_all_loaded = null; + + (this.load = function (on_all_loaded, active_name) { + this.on_all_loaded = on_all_loaded; + + // if global camera not set, use first camera as default. + // if (active_name.length > 0) + // this.active_name = active_name; + // else if (this.names && this.names.length>0) + // this.active_name = this.names[0]; + + var _self = this; + + if (this.names) { + this.names.forEach(function (cam) { + _self.content[cam] = new Image(); + _self.content[cam].onload = function () { + _self.loaded_flag[cam] = true; + _self.on_image_loaded(); + }; + _self.content[cam].onerror = function () { + _self.loaded_flag[cam] = true; + _self.on_image_loaded(); + }; - this.calcTransformMatrix = function() - { - if (this.egoPose.egoPose){ - - let thisPose = this.egoPose.egoPose; - let refPose = this.data.getRefEgoPose(this.frameInfo.scene, thisPose); - - - let thisRot = { - x: thisPose.pitch * Math.PI/180.0, - y: thisPose.roll * Math.PI/180.0, - z: - thisPose.azimuth * Math.PI/180.0 - }; - - let posDelta = { - x: thisPose.x - refPose.x, - y: thisPose.y - refPose.y, - z: thisPose.z - refPose.z, - }; - - - - //console.log("pose", thisPose, refPose, delta); - - //let theta = delta.rotation.z*Math.PI/180.0; - - // https://docs.novatel.com/OEM7/Content/SPAN_Operation/SPAN_Translations_Rotations.htm - //let trans_utm_ego = euler_angle_to_rotate_matrix_3by3({x: refPose.pitch*Math.PI/180.0, y: refPose.roll*Math.PI/180.0, z: refPose.azimuth*Math.PI/180.0}, "ZXY"); - - // this should be a calib matrix - //let trans_lidar_ego = euler_angle_to_rotate_matrix({x: 0, y: 0, z: Math.PI}, {x:0, y:0, z:0.4}); - - let trans_lidar_ego = new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0,0,Math.PI, "ZYX")) - .setPosition(0, 0, 0.4); - - - //let trans_ego_utm = euler_angle_to_rotate_matrix(thisRot, posDelta, "ZXY"); - let trans_ego_utm = new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(thisRot.x, thisRot.y, thisRot.z, "ZXY")) - .setPosition(posDelta.x, posDelta.y, posDelta.z); - - let trans_utm_scene = new THREE.Matrix4().identity().setPosition(this.coordinatesOffset[0], this.coordinatesOffset[1], this.coordinatesOffset[2]); - // let offset_ego = matmul(trans_utm_ego, [delta.position.x, delta.position.y, delta.position.z], 3); - // let offset_lidar = matmul(trans_ego_lidar, offset_ego, 3); - - // let trans_lidar = euler_angle_to_rotate_matrix({x: - delta.rotation.x*Math.PI/180.0, y: -delta.rotation.y*Math.PI/180.0, z: - delta.rotation.z*Math.PI/180.0}, - // {x:offset_lidar[0], y:offset_lidar[1], z:offset_lidar[2]}, - // "ZXY"); - - // let R = matmul2(trans_ego_utm, trans_lidar_ego, 4); - // let inv = [ - // mat(R,4,0,0), mat(R,4,1,0), mat(R,4,2,0), -mat(R,4,0,3), - // mat(R,4,0,1), mat(R,4,1,1), mat(R,4,2,1), -mat(R,4,1,3), - // mat(R,4,0,2), mat(R,4,1,2), mat(R,4,2,2), -mat(R,4,2,3), - // 0, 0, 0, 1, - // ]; - - this.trans_lidar_utm = new THREE.Matrix4().multiplyMatrices(trans_ego_utm, trans_lidar_ego); - - if (this.data.cfg.coordinateSystem == "utm") - this.trans_lidar_scene = new THREE.Matrix4().multiplyMatrices(trans_utm_scene, this.trans_lidar_utm); - else - this.trans_lidar_scene = trans_utm_scene; //only offset. - - this.trans_utm_lidar = new THREE.Matrix4().copy(this.trans_lidar_utm).invert(); - this.trans_scene_lidar = new THREE.Matrix4().copy(this.trans_lidar_scene).invert(); - - - } - else - { - let trans_utm_scene = new THREE.Matrix4().identity().setPosition(this.coordinatesOffset[0], this.coordinatesOffset[1], this.coordinatesOffset[2]); - let id = new THREE.Matrix4().identity(); - - this.trans_lidar_utm = id; - this.trans_lidar_scene = trans_utm_scene; - - this.trans_utm_lidar = new THREE.Matrix4().copy(this.trans_lidar_utm).invert(); - this.trans_scene_lidar = new THREE.Matrix4().copy(this.trans_lidar_scene).invert(); - } - - - - this.webglGroup.matrix.copy(this.trans_lidar_scene); - this.webglGroup.matrixAutoUpdate = false; - }; - - // global scene - this.scenePosToLidar = function(pos) - { - let tp = new THREE.Vector4(pos.x, pos.y, pos.z, 1).applyMatrix4(this.trans_scene_lidar); - - return tp; + _self.content[cam].src = + "data/" + + sceneName + + "/camera/" + + cam + + "/" + + frame + + sceneMeta.camera_ext; + console.log("image set"); + }); } + }), + (this.on_image_loaded = function () { + if (this.loaded()) { + this.on_all_loaded(); + } + }); +} - // global scene - this.lidarPosToScene = function(pos) - { - let tp = new THREE.Vector3(pos.x, pos.y, pos.z).applyMatrix4(this.trans_lidar_scene); - - return tp; +function World(data, sceneName, frame, coordinatesOffset, on_preload_finished) { + this.data = data; + this.sceneMeta = this.data.getMetaBySceneName(sceneName); + this.frameInfo = new FrameInfo(this.data, this.sceneMeta, sceneName, frame); + + this.coordinatesOffset = coordinatesOffset; + + this.toString = function () { + return this.frameInfo.scene + "," + this.frameInfo.frame; + }; + //points_backup: null, //for restore from highlight + + this.cameras = new Images(this.sceneMeta, sceneName, frame); + this.radars = new RadarManager(this.sceneMeta, this, this.frameInfo); + this.lidar = new Lidar(this.sceneMeta, this, this.frameInfo); + this.annotation = new Annotation(this.sceneMeta, this, this.frameInfo); + this.aux_lidars = new AuxLidarManager(this.sceneMeta, this, this.frameInfo); + this.egoPose = new EgoPose(this.sceneMeta, this, this.FrameInfo); + + // todo: state of world could be put in a variable + // but still need mulitple flags. + + (this.points_loaded = false), + (this.preloaded = function () { + return ( + this.lidar.preloaded && + this.annotation.preloaded && + //this.cameras.loaded() && + this.aux_lidars.preloaded() && + this.radars.preloaded() && + this.egoPose.preloaded + ); + }); + + this.create_time = 0; + this.finish_time = 0; + this.on_preload_finished = null; + + this.on_subitem_preload_finished = function (on_preload_finished) { + if (this.preloaded()) { + logger.log( + `finished preloading ${this.frameInfo.scene} ${this.frameInfo.frame}` + ); + + this.calcTransformMatrix(); + + if (this.on_preload_finished) { + this.on_preload_finished(this); + } + + if (this.active) { + this.go(); + } } - - // global scene - this.lidarPosToUtm = function(pos) - { - let tp = new THREE.Vector3(pos.x, pos.y, pos.z).applyMatrix4(this.trans_lidar_utm); - - return tp; - } - - - - this.sceneRotToLidar = function(rotEuler) - { - if (!rotEuler.isEuler) - { - rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); - } - - let rotG = new THREE.Quaternion().setFromEuler(rotEuler); - let GlobalToLocalRot = new THREE.Quaternion().setFromRotationMatrix(this.trans_scene_lidar); - - let retQ = rotG.multiply(GlobalToLocalRot); - - let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); - - return retEuler; + }; + + this.calcTransformMatrix = function () { + if (this.egoPose.egoPose) { + let thisPose = this.egoPose.egoPose; + let refPose = this.data.getRefEgoPose(this.frameInfo.scene, thisPose); + + let thisRot = { + x: (thisPose.pitch * Math.PI) / 180.0, + y: (thisPose.roll * Math.PI) / 180.0, + z: (-thisPose.azimuth * Math.PI) / 180.0, + }; + + let posDelta = { + x: thisPose.x - refPose.x, + y: thisPose.y - refPose.y, + z: thisPose.z - refPose.z, + }; + + //console.log("pose", thisPose, refPose, delta); + + //let theta = delta.rotation.z*Math.PI/180.0; + + // https://docs.novatel.com/OEM7/Content/SPAN_Operation/SPAN_Translations_Rotations.htm + //let trans_utm_ego = euler_angle_to_rotate_matrix_3by3({x: refPose.pitch*Math.PI/180.0, y: refPose.roll*Math.PI/180.0, z: refPose.azimuth*Math.PI/180.0}, "ZXY"); + + // this should be a calib matrix + //let trans_lidar_ego = euler_angle_to_rotate_matrix({x: 0, y: 0, z: Math.PI}, {x:0, y:0, z:0.4}); + + let trans_lidar_ego = new THREE.Matrix4() + .makeRotationFromEuler(new THREE.Euler(0, 0, Math.PI, "ZYX")) + .setPosition(0, 0, 0.4); + + //let trans_ego_utm = euler_angle_to_rotate_matrix(thisRot, posDelta, "ZXY"); + let trans_ego_utm = new THREE.Matrix4() + .makeRotationFromEuler( + new THREE.Euler(thisRot.x, thisRot.y, thisRot.z, "ZXY") + ) + .setPosition(posDelta.x, posDelta.y, posDelta.z); + + let trans_utm_scene = new THREE.Matrix4() + .identity() + .setPosition( + this.coordinatesOffset[0], + this.coordinatesOffset[1], + this.coordinatesOffset[2] + ); + // let offset_ego = matmul(trans_utm_ego, [delta.position.x, delta.position.y, delta.position.z], 3); + // let offset_lidar = matmul(trans_ego_lidar, offset_ego, 3); + + // let trans_lidar = euler_angle_to_rotate_matrix({x: - delta.rotation.x*Math.PI/180.0, y: -delta.rotation.y*Math.PI/180.0, z: - delta.rotation.z*Math.PI/180.0}, + // {x:offset_lidar[0], y:offset_lidar[1], z:offset_lidar[2]}, + // "ZXY"); + + // let R = matmul2(trans_ego_utm, trans_lidar_ego, 4); + // let inv = [ + // mat(R,4,0,0), mat(R,4,1,0), mat(R,4,2,0), -mat(R,4,0,3), + // mat(R,4,0,1), mat(R,4,1,1), mat(R,4,2,1), -mat(R,4,1,3), + // mat(R,4,0,2), mat(R,4,1,2), mat(R,4,2,2), -mat(R,4,2,3), + // 0, 0, 0, 1, + // ]; + + this.trans_lidar_utm = new THREE.Matrix4().multiplyMatrices( + trans_ego_utm, + trans_lidar_ego + ); + + if (this.data.cfg.coordinateSystem == "utm") + this.trans_lidar_scene = new THREE.Matrix4().multiplyMatrices( + trans_utm_scene, + this.trans_lidar_utm + ); + else this.trans_lidar_scene = trans_utm_scene; //only offset. + + this.trans_utm_lidar = new THREE.Matrix4() + .copy(this.trans_lidar_utm) + .invert(); + this.trans_scene_lidar = new THREE.Matrix4() + .copy(this.trans_lidar_scene) + .invert(); + } else { + let trans_utm_scene = new THREE.Matrix4() + .identity() + .setPosition( + this.coordinatesOffset[0], + this.coordinatesOffset[1], + this.coordinatesOffset[2] + ); + let id = new THREE.Matrix4().identity(); + + this.trans_lidar_utm = id; + this.trans_lidar_scene = trans_utm_scene; + + this.trans_utm_lidar = new THREE.Matrix4() + .copy(this.trans_lidar_utm) + .invert(); + this.trans_scene_lidar = new THREE.Matrix4() + .copy(this.trans_lidar_scene) + .invert(); } - this.lidarRotToScene = function(rotEuler) - { - if (!rotEuler.isEuler) - { - rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); - } - - let rotL = new THREE.Quaternion().setFromEuler(rotEuler); - let localToGlobalRot = new THREE.Quaternion().setFromRotationMatrix(this.trans_lidar_scene) + this.webglGroup.matrix.copy(this.trans_lidar_scene); + this.webglGroup.matrixAutoUpdate = false; + }; - let retQ = rotL.multiply(localToGlobalRot); + // global scene + this.scenePosToLidar = function (pos) { + let tp = new THREE.Vector4(pos.x, pos.y, pos.z, 1).applyMatrix4( + this.trans_scene_lidar + ); - let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); + return tp; + }; - return retEuler; - } + // global scene + this.lidarPosToScene = function (pos) { + let tp = new THREE.Vector3(pos.x, pos.y, pos.z).applyMatrix4( + this.trans_lidar_scene + ); - this.lidarRotToUtm = function(rotEuler) - { - if (!rotEuler.isEuler) - { - rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); - } + return tp; + }; - let rotL = new THREE.Quaternion().setFromEuler(rotEuler); - let localToGlobalRot = new THREE.Quaternion().setFromRotationMatrix(this.trans_lidar_utm) + // global scene + this.lidarPosToUtm = function (pos) { + let tp = new THREE.Vector3(pos.x, pos.y, pos.z).applyMatrix4( + this.trans_lidar_utm + ); - let retQ = rotL.multiply(localToGlobalRot); + return tp; + }; - let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); - - return retEuler; + this.sceneRotToLidar = function (rotEuler) { + if (!rotEuler.isEuler) { + rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); } - this.utmRotToLidar = function(rotEuler) - { - if (!rotEuler.isEuler) - { - rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); - } + let rotG = new THREE.Quaternion().setFromEuler(rotEuler); + let GlobalToLocalRot = new THREE.Quaternion().setFromRotationMatrix( + this.trans_scene_lidar + ); - let rot = new THREE.Quaternion().setFromEuler(rotEuler); - let trans = new THREE.Quaternion().setFromRotationMatrix(this.trans_utm_lidar); + let retQ = rotG.multiply(GlobalToLocalRot); - let retQ = rot.multiply(trans); + let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); - let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); + return retEuler; + }; - return retEuler; + this.lidarRotToScene = function (rotEuler) { + if (!rotEuler.isEuler) { + rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); } + let rotL = new THREE.Quaternion().setFromEuler(rotEuler); + let localToGlobalRot = new THREE.Quaternion().setFromRotationMatrix( + this.trans_lidar_scene + ); - this.preload=function(on_preload_finished){ - this.create_time = new Date().getTime(); - console.log(this.create_time, sceneName, frame, "start"); - - this.webglGroup = new THREE.Group(); - this.webglGroup.name = "world"; - - - let _preload_cb = ()=>this.on_subitem_preload_finished(on_preload_finished); + let retQ = rotL.multiply(localToGlobalRot); - this.lidar.preload(_preload_cb); - this.annotation.preload(_preload_cb) - this.radars.preload(_preload_cb); - this.cameras.load(_preload_cb, this.data.active_camera_name); - this.aux_lidars.preload(_preload_cb); - this.egoPose.preload(_preload_cb); - }; - - this.scene = null, - this.destroy_old_world = null, //todo, this can be a boolean - this.on_finished = null, - this.activate=function(scene, destroy_old_world, on_finished){ - this.scene = scene; - this.active = true; - this.destroy_old_world = destroy_old_world; - this.on_finished = on_finished; - if (this.preloaded()){ - this.go(); - } - }; - - this.active = false, - this.everythingDone = false; - - this.go=function(){ - - if (this.everythingDone){ - //console.error("re-activate world?"); - - //however we still call on_finished - if (this.on_finished){ - this.on_finished(); - } - return; - } + let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); - if (this.preloaded()){ - - //this.points.material.size = data.cfg.point_size; - - if (this.destroy_old_world){ - this.destroy_old_world(); - } - - if (this.destroyed){ - console.log("go after destroyed."); - this.unload(); - return; - } - - this.scene.add(this.webglGroup); - - this.lidar.go(this.scene); - this.annotation.go(this.scene); - this.radars.go(this.scene); - this.aux_lidars.go(this.scene); + return retEuler; + }; + this.lidarRotToUtm = function (rotEuler) { + if (!rotEuler.isEuler) { + rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); + } - this.finish_time = new Date().getTime(); - console.log(this.finish_time, sceneName, frame, "loaded in ", this.finish_time - this.create_time, "ms"); - + let rotL = new THREE.Quaternion().setFromEuler(rotEuler); + let localToGlobalRot = new THREE.Quaternion().setFromRotationMatrix( + this.trans_lidar_utm + ); - // render is called in on_finished() callback - if (this.on_finished){ - this.on_finished(); - } + let retQ = rotL.multiply(localToGlobalRot); - this.everythingDone = true; - } - }; + let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); + return retEuler; + }; - this.add_line=function(start, end, color){ - var line = this.new_line(start, end, color); - this.scene.add(line); - }; + this.utmRotToLidar = function (rotEuler) { + if (!rotEuler.isEuler) { + rotEuler = new THREE.Euler(rotEuler.x, rotEuler.y, rotEuler.z, "XYZ"); + } + let rot = new THREE.Quaternion().setFromEuler(rotEuler); + let trans = new THREE.Quaternion().setFromRotationMatrix( + this.trans_utm_lidar + ); + + let retQ = rot.multiply(trans); + + let retEuler = new THREE.Euler().setFromQuaternion(retQ, rotEuler.order); + + return retEuler; + }; + + this.preload = function (on_preload_finished) { + this.create_time = new Date().getTime(); + console.log(this.create_time, sceneName, frame, "start"); + + this.webglGroup = new THREE.Group(); + this.webglGroup.name = "world"; + + let _preload_cb = () => + this.on_subitem_preload_finished(on_preload_finished); + + this.lidar.preload(_preload_cb); + this.annotation.preload(_preload_cb); + this.radars.preload(_preload_cb); + this.cameras.load(_preload_cb, this.data.active_camera_name); + this.aux_lidars.preload(_preload_cb); + this.egoPose.preload(_preload_cb); + }; + + (this.scene = null), + (this.destroy_old_world = null), //todo, this can be a boolean + (this.on_finished = null), + (this.activate = function (scene, destroy_old_world, on_finished) { + this.scene = scene; + this.active = true; + this.destroy_old_world = destroy_old_world; + this.on_finished = on_finished; + if (this.preloaded()) { + this.go(); + } + }); + + (this.active = false), (this.everythingDone = false); + + this.go = function () { + if (this.everythingDone) { + //console.error("re-activate world?"); + + //however we still call on_finished + if (this.on_finished) { + this.on_finished(); + } + return; + } + if (this.preloaded()) { + //this.points.material.size = data.cfg.point_size; + + if (this.destroy_old_world) { + this.destroy_old_world(); + } + + if (this.destroyed) { + console.log("go after destroyed."); + this.unload(); + return; + } + + this.scene.add(this.webglGroup); + + this.lidar.go(this.scene); + this.annotation.go(this.scene); + this.radars.go(this.scene); + this.aux_lidars.go(this.scene); + + this.finish_time = new Date().getTime(); + console.log( + this.finish_time, + sceneName, + frame, + "loaded in ", + this.finish_time - this.create_time, + "ms" + ); + + // render is called in on_finished() callback + if (this.on_finished) { + this.on_finished(); + } + + this.everythingDone = true; + } + }; - this.new_line=function(start, end, color){ + this.add_line = function (start, end, color) { + var line = this.new_line(start, end, color); + this.scene.add(line); + }; - var vertex = start.concat(end); - this.world.data.dbg.alloc(); - var line = new THREE.BufferGeometry(); - line.addAttribute( 'position', new THREE.Float32BufferAttribute(vertex, 3 ) ); - - if (!color){ - color = 0x00ff00; - } - - var material = new THREE.LineBasicMaterial( { color: color, linewidth: 1, opacity: this.data.cfg.box_opacity, transparent: true } ); - return new THREE.LineSegments( line, material ); - }; + this.new_line = function (start, end, color) { + var vertex = start.concat(end); + this.world.data.dbg.alloc(); + var line = new THREE.BufferGeometry(); + line.addAttribute("position", new THREE.Float32BufferAttribute(vertex, 3)); + if (!color) { + color = 0x00ff00; + } + var material = new THREE.LineBasicMaterial({ + color: color, + linewidth: 1, + opacity: this.data.cfg.box_opacity, + transparent: true, + }); + return new THREE.LineSegments(line, material); + }; - this.destroyed = false; + this.destroyed = false; - // todo, Image resource to be released? + // todo, Image resource to be released? - this.unload = function(){ - if (this.everythingDone){ - //unload all from scene, but don't destroy elements - this.lidar.unload(); - this.radars.unload(); - this.aux_lidars.unload(); - this.annotation.unload(); + this.unload = function () { + if (this.everythingDone) { + //unload all from scene, but don't destroy elements + this.lidar.unload(); + this.radars.unload(); + this.aux_lidars.unload(); + this.annotation.unload(); - this.scene.remove(this.webglGroup); - - this.active = false; - this.everythingDone = false; - } - }; + this.scene.remove(this.webglGroup); + this.active = false; + this.everythingDone = false; + } + }; + this.deleteAll = function () { + var _self = this; - this.deleteAll = function(){ - var _self= this; + logger.log(`delete world ${this.frameInfo.scene},${this.frameInfo.frame}`); - logger.log(`delete world ${this.frameInfo.scene},${this.frameInfo.frame}`); + if (this.everythingDone) { + this.unload(); + } - if (this.everythingDone){ - this.unload(); - } - - // todo, check if all objects are removed from webgl scene. - if (this.destroyed){ - console.log("destroy destroyed world!"); - } + // todo, check if all objects are removed from webgl scene. + if (this.destroyed) { + console.log("destroy destroyed world!"); + } - this.lidar.deleteAll(); - this.radars.deleteAll(); - this.aux_lidars.deleteAll(); - this.annotation.deleteAll(); + this.lidar.deleteAll(); + this.radars.deleteAll(); + this.aux_lidars.deleteAll(); + this.annotation.deleteAll(); - this.destroyed = true; - console.log(this.frameInfo.scene, this.frameInfo.frame, "destroyed"); - // remove me from buffer - }; + this.destroyed = true; + console.log(this.frameInfo.scene, this.frameInfo.frame, "destroyed"); + // remove me from buffer + }; - this.preload(on_preload_finished); + this.preload(on_preload_finished); } -export {World}; - +export { World };