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

1508 lines
48 KiB
JavaScript

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}