|
|
|
@ -189,6 +189,14 @@ function bvh_undelete_stroke(state, stroke) {
@@ -189,6 +189,14 @@ function bvh_undelete_stroke(state, stroke) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function bvh_copy_fullnode(quad, node, result_buffer) { |
|
|
|
|
if (quad_fully_inside(quad, node.bbox)) { |
|
|
|
|
tv_append(result_buffer, tv_data(node.stroke_indices)); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function bvh_intersect_quad(state, bvh, quad, result_buffer) { |
|
|
|
|
if (bvh.root === null) { |
|
|
|
|
return; |
|
|
|
@ -206,8 +214,8 @@ function bvh_intersect_quad(state, bvh, quad, result_buffer) {
@@ -206,8 +214,8 @@ function bvh_intersect_quad(state, bvh, quad, result_buffer) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (node.is_fullnode) { |
|
|
|
|
if (quad_fully_inside(quad, node.bbox)) { |
|
|
|
|
tv_append(result_buffer, tv_data(node.stroke_indices)); |
|
|
|
|
const fully_inside = bvh_copy_fullnode(quad, node, result_buffer); |
|
|
|
|
if (fully_inside) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -292,11 +300,40 @@ function bvh_point(state, p) {
@@ -292,11 +300,40 @@ function bvh_point(state, p) {
|
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function bvh_construct_rec(state, bvh, vertical, strokes, depth) { |
|
|
|
|
function bvh_construct_rec(state, bvh, strokes, depth) { |
|
|
|
|
if (strokes.length > 1) { |
|
|
|
|
// internal
|
|
|
|
|
let sorted_strokes; |
|
|
|
|
|
|
|
|
|
let min_x = strokes[0].bbox.cx; |
|
|
|
|
let min_y = strokes[0].bbox.cy; |
|
|
|
|
let max_x = strokes[0].bbox.cx; |
|
|
|
|
let max_y = strokes[0].bbox.cy; |
|
|
|
|
|
|
|
|
|
for (let i = 0; i < strokes.length; ++i) { |
|
|
|
|
const stroke = strokes[i]; |
|
|
|
|
const cx = stroke.bbox.cx; |
|
|
|
|
const cy = stroke.bbox.cy; |
|
|
|
|
|
|
|
|
|
if (cx < min_x) { |
|
|
|
|
min_x = cx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (cy < min_y) { |
|
|
|
|
min_y = cx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (cx > max_x) { |
|
|
|
|
max_x = cx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (cy > max_y) { |
|
|
|
|
max_y = cy; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const vertical = (max_y - min_y) > (max_x - min_x); |
|
|
|
|
|
|
|
|
|
if (vertical) { |
|
|
|
|
sorted_strokes = strokes.toSorted((a, b) => a.bbox.cy - b.bbox.cy); |
|
|
|
|
} else { |
|
|
|
@ -306,8 +343,8 @@ function bvh_construct_rec(state, bvh, vertical, strokes, depth) {
@@ -306,8 +343,8 @@ function bvh_construct_rec(state, bvh, vertical, strokes, depth) {
|
|
|
|
|
const node_index = bvh_make_internal(bvh); |
|
|
|
|
const left_of_split_count = Math.floor(strokes.length / 2); |
|
|
|
|
|
|
|
|
|
const child1 = bvh_construct_rec(state, bvh, !vertical, sorted_strokes.slice(0, left_of_split_count), depth + 1); |
|
|
|
|
const child2 = bvh_construct_rec(state, bvh, !vertical, sorted_strokes.slice(left_of_split_count, sorted_strokes.length), depth + 1); |
|
|
|
|
const child1 = bvh_construct_rec(state, bvh, sorted_strokes.slice(0, left_of_split_count), depth + 1); |
|
|
|
|
const child2 = bvh_construct_rec(state, bvh, sorted_strokes.slice(left_of_split_count, sorted_strokes.length), depth + 1); |
|
|
|
|
|
|
|
|
|
bvh.nodes[child1].parent_index = node_index; |
|
|
|
|
bvh.nodes[child2].parent_index = node_index; |
|
|
|
@ -339,6 +376,52 @@ function bvh_construct_rec(state, bvh, vertical, strokes, depth) {
@@ -339,6 +376,52 @@ function bvh_construct_rec(state, bvh, vertical, strokes, depth) {
|
|
|
|
|
function bvh_construct(state) { |
|
|
|
|
const strokes = state.events.filter(e => e.type === EVENT.STROKE && e.deleted !== true); |
|
|
|
|
if (strokes.length > 0) { |
|
|
|
|
state.bvh.root = bvh_construct_rec(state, state.bvh, true, strokes, 0); |
|
|
|
|
state.bvh.root = bvh_construct_rec(state, state.bvh, strokes, 0); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function bvh_get_fullnodes_debug(state, context) { |
|
|
|
|
const bvh = state.bvh; |
|
|
|
|
const result = []; |
|
|
|
|
const stack = []; |
|
|
|
|
|
|
|
|
|
const screen_topleft = screen_to_canvas(state, {'x': 0, 'y': 0}); |
|
|
|
|
const screen_bottomright = screen_to_canvas(state, {'x': context.canvas.width, 'y': context.canvas.height}); |
|
|
|
|
const screen_topright = { 'x': screen_bottomright.x, 'y': screen_topleft.y }; |
|
|
|
|
const screen_bottomleft = { 'x': screen_topleft.x, 'y': screen_bottomright.y }; |
|
|
|
|
|
|
|
|
|
const quad = { |
|
|
|
|
'x1': screen_topleft.x, |
|
|
|
|
'y1': screen_topleft.y, |
|
|
|
|
'x2': screen_bottomright.x, |
|
|
|
|
'y2': screen_bottomright.y |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if (bvh.root === null) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
stack.push({'depth': 0, 'node_index': bvh.root}); |
|
|
|
|
|
|
|
|
|
while (stack.length > 0) { |
|
|
|
|
const entry = stack.pop(); |
|
|
|
|
const node = bvh.nodes[entry.node_index]; |
|
|
|
|
|
|
|
|
|
if (!quads_intersect(node.bbox, quad)) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (node.is_fullnode) { |
|
|
|
|
result.push({...node.bbox}); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!node.is_leaf && entry.depth < config.bvh_fullnode_depth) { |
|
|
|
|
stack.push({'depth': entry.depth + 1, 'node_index': node.child1}); |
|
|
|
|
stack.push({'depth': entry.depth + 1, 'node_index': node.child2}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|