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)=>{ if (this.isActive()) { ui.focus(); 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(); ui.blur(); }; ui.onwheel = event=>{ event.stopPropagation(); event.preventDefault(); this.on_wheel(event.deltaY); }; 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"); } this.install_buttons(); } mouse_start_pos = null; view_handle_dimension = { //dimension of the enclosed box x: 0, //width y: 0, //height } view_center = { x: 0, y: 0, }; line(name){ return this.lines[name]; } show_lines(){ let theme = document.documentElement.className; let lineColor = "yellow"; if (theme == "theme-light") lineColor = "red"; 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"; } }; hightlight_line(line) { let theme = document.documentElement.className; let lineColor = "red"; if (theme == "theme-light") lineColor = "blue"; line.style.stroke=lineColor; } disable_handle_except(exclude){ for (var h in this.handles){ if (this.handles[h] != exclude) this.handles[h].style.display='none'; } } 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; } 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 * objPos.x 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 radius = Math.sqrt(viewWidth*viewWidth/4 + viewHeight*viewHeight/4); let distToRog = Math.sqrt(relx*relx + rely*rely) let indPosX3d = relx*radius/distToRog; let indPosY3d = rely*radius/distToRog; 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; indPosX = viewWidth/2*dotRelPos; indPosY = indPosY*shrinkRatio; } if (indPosX < -viewWidth/2*dotRelPos){ let shrinkRatio = -viewWidth/2*dotRelPos/indPosX; indPosX = -viewWidth/2*dotRelPos; indPosY = indPosY*shrinkRatio; } if (indPosY > viewHeight/2*dotRelPos){ let shrinkRatio = viewHeight/2*dotRelPos/indPosY; indPosY = viewHeight/2*dotRelPos; indPosX = indPosX*shrinkRatio; } if (indPosY < -viewHeight/2*dotRelPos){ let shrinkRatio = -viewHeight/2*dotRelPos/indPosY; indPosY = -viewHeight/2*dotRelPos; indPosX = indPosX*shrinkRatio; } 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])); } 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])); } }; 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 }; 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); } }; } 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(); let handle_center={ x: parseInt(line.getAttribute('x1')), } this.mouse_start_pos={ x: event.layerX, y:event.layerY, handle_offset_x: handle_center.x - event.layerX, }; let mouse_cur_pos = {x: this.mouse_start_pos.x, y: this.mouse_start_pos.y}; console.log(this.mouse_start_pos); let theta = 0; 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); this.rotate_lines(theta); }; svg.onmouseup = event=>{ svg.onmousemove = null; svg.onmouseup=null; // restore color //line.style.stroke="#00000000"; this.enable_handles(); handle.onmouseleave = this.hide.bind(this); this.ui.querySelector("#v-buttons").style.display="inherit"; if (theta == 0){ return; } 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") }; } 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 (buttons.fit_size && this.on_fit_size){ 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. } if (buttons.reset_rotation){ buttons.reset_rotation.onmousedown = ignore_left_mouse_down; 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. } } 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. } } } } 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); } } 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); } // 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; } } } //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); } function on_edge_changed(delta, direction){ console.log(delta); 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); } 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 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_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); } //ratio.y vertical //ratio.x horizental // box.x vertical // box.y horizental 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_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 } }); } 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_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_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); } } 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 on_z_reset_rotate(){ scope.box.rotation.z = 0; 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, }; 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_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); } 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_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, }; 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_wheel(wheel_direction){ let multiplier = get_wheel_multiplier(wheel_direction); let newRatio = scope.views[1].zoom_ratio *= multiplier; scope.boxEditor.updateViewZoomRatio(1, newRatio); } function on_y_reset_rotate(){ scope.box.rotation.y = 0; scope.on_box_changed(scope.box); } function on_y_auto_rotate(){ scope.boxOp.auto_rotate_y(scope.box, scope.on_box_changed); } 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); } } 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_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), }; // 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.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) } 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); } 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); } function on_x_reset_rotate(){ scope.box.rotation.x = 0; scope.on_box_changed(scope.box); } 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}