https://github.com/thu-vis/MutualDetector
Tip revision: 2b019233d6851facadec8e9215cc805eef47932c authored by Changjian Chen on 20 May 2024, 01:52:04 UTC
update readme
update readme
Tip revision: 2b01923
render_image_card.js
import * as Global from "../plugins/global";
import * as d3 from "d3";
const ImageCards = function(parent) {
let that = this;
that.parent = parent;
that.server_url = that.parent.server_url;
that.set_group = that.parent.set_group;
that.grid_group = that.parent.grid_group;
that.drag_grid_group = that.parent.drag_grid_group;
that.label_group = that.parent.label_group;
that.nav_group = that.parent.nav_group;
that.nav_gray = "#8f8f8f";
that.nav_offset_x = 800;
that.nav_offset_y = 18;
that.nav_margin = 50;
that.nav_group.attr(
"transform",
"translate(" + that.nav_offset_x + ", " + that.nav_offset_y + ")"
);
that.nav_group
.append("rect")
.attr("class", "nav-rect")
.attr("width", 5)
.attr("height", 0)
.attr("x", -2.5)
.attr("fill", Global.GrayColor);
// animation
that.create_ani = that.parent.create_ani;
that.update_ani = that.parent.update_ani;
that.remove_ani = that.parent.remove_ani;
// let match_color = "#D3D3E5";
// let mismatch_color = "#ED2939";
let match_color = "#D3D3E5";
let mismatch_color = "#E05246";
that.image_margin_percent = 0.9;
that.selected_image_idx = null;
//
that.boundingbox_width = 12;
// let labels = Array(); // Label layout
let img_width = 80;
let margin_size = 20;
// let margin_top_size = 100;
let plot_width = 800;
let plot_height = 800;
var offset_x = 0; // position of the left grid when the mode is "juxtaposition"
var offset_y = 100;
let grid_margin = 0.5 * 2;
this.focus_grid_for_edit = null;
this.target_grid_image = null;
let mouse_pressed = false;
let mouse_pos = {
x: -1,
y: -1,
};
that.relative_sampling_area = {
x: 0,
y: 0,
w: 1,
h: 1,
};
let plot_x,
plot_y = 1;
that.click_ids = [];
that.click_count = 0;
that.labels = [];
that.use_label_layout = false;
this.get_set_layout_from_parent = function() {
// set
that.layout_height = that.parent.layout_height;
that.set_height = that.parent.set_height;
that.set_left = that.parent.set_left;
that.set_width = that.parent.set_width;
that.set_margin = that.parent.set_margin;
that.image_height = that.parent.image_height;
that.image_margin = that.parent.image_margin;
that.text_height = that.parent.text_height;
that.top_image_margin = that.parent.top_image_margin;
};
this.get_set_layout_from_parent();
(this.set_focus_image = function(image) {
that.parent.set_focus_image(image);
}),
(this.set_expand_set_id = function(id) {
that.parent.set_expand_set_id(id);
});
this.set_grid_layout_data = function(data) {
that.parent.set_grid_layout_data(data);
};
that.set_animation_time = function() {
// animation
that.create_ani = that.parent.create_ani;
that.update_ani = that.parent.update_ani;
that.remove_ani = that.parent.remove_ani;
};
this.get_expand_set_id = function() {
return that.parent.expand_set_id;
};
this.get_grid_data = function() {
return that.parent.grid_data;
};
this.get_grid_image_info = function() {
return that.parent.grid_image_info;
};
this.get_nav_id = function() {
return that.parent.nav_id;
};
this.visible = function(d) {
if (that.get_expand_set_id() === -1) {
if (d.collapse === 1) {
return 0;
} else {
return 1;
}
} else {
return 0;
}
};
this.fetch_grid_layout = function(query) {
return that.parent.fetch_grid_layout(query);
};
this.sub_component_update = function(
sets,
vis_image_per_cluster,
grids,
grids_pos
) {
// update layout config
that.get_set_layout_from_parent();
// update state
that.vis_image_per_cluster = vis_image_per_cluster;
sets.forEach((d) => {
that.vis_image_per_cluster[d.id].forEach((n) => {
n.collapse = d.collapse;
});
});
that.grids = grids;
offset_x = grids_pos.offset_x;
offset_y = grids_pos.offset_y;
plot_width = grids_pos.side_length;
plot_height = grids_pos.side_length;
Object.values(that.vis_image_per_cluster).forEach((d) => {
// let x = that.image_margin;
d.forEach((n, i) => {
n.vis_h = that.image_height;
n.vis_w = that.image_height;
n.x = that.parent.x_position(i) + that.image_margin;
// n.inner_margin = that.image_height * that.image_margin_percent / 2;
// x = x + n.vis_w + that.image_margin;
});
});
that.labels = that.label_layout(
Global.deepCopy(that.grids),
plot_width,
that.labels
);
// let grid_image_info = that.get_grid_image_info();
// that.labels.forEach(d => {
// let info = grid_image_info.find(info => info.idx === d.img_id);
// d.d = info.d;
// })
that.use_label_layout = that.labels.length ? 1 : 0;
if (that.get_expand_set_id() < 0) {
that.click_ids = [];
} else {
that.click_ids.push({
id: that.get_nav_id(),
layout: Global.deepCopy(grids),
});
that.click_ids.forEach((d, i) => {
// d.x = i * that.nav_margin;
// d.y = 0;
d.x = 0;
d.y = i * that.nav_margin;
d.last_one = i === that.click_ids.length - 1 ? true : false;
});
}
console.log("image card sub component update", sets, grids);
// update view
that.e_sets = that.set_group.selectAll(".set").data(sets, (d) => d.id);
that.e_grids = that.grid_group
.selectAll(".grid")
.data(grids, (d) => d.img_id);
that.e_labels = that.label_group
.selectAll(".label")
.data(that.labels, (d) => d.img_id);
that.e_navs = that.nav_group
.selectAll(".nav-circle")
.data(that.click_ids, (d) => d.id);
that.remove();
that.update();
that.create();
that.set_animation_time();
};
this.create = function() {
that.set_create();
that.grid_create();
that.label_create();
that.nav_create();
};
this.set_create = function() {
// set
let set_groups = that.e_sets
.enter()
.append("g")
.attr("class", "set")
.attr("id", (d) => "set-" + d.id)
.attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")");
set_groups
.style("opacity", 0)
.on("mouseover", function(ev) {
if (that.get_expand_set_id() === -1) that.box_highlight(ev);
})
.on("mouseout", function() {
that.box_dehighlight();
})
.transition()
.duration(that.create_ani)
.delay(that.update_ani + that.remove_ani)
.style("opacity", 1);
// expand icon
set_groups
.append("rect")
.attr("class", "expand-rect")
.attr("x", -11)
.attr("y", 0)
.attr("width", 10)
.attr("height", 10)
.style("rx", 3)
.style("ry", 3)
.style("fill", "white")
.style("stroke", "gray")
.style("stroke-width", 1)
.on("click", (_, d) => {
if (d.id === that.get_expand_set_id()) {
that.set_expand_set_id(-1);
} else {
if (that.parent.selected_node["node_ids"].length == 0) {
alert(
"To check the grid layout, you should select one label in the label hierarchy."
);
return;
}
that.set_expand_set_id(d.id);
}
});
set_groups
.append("path")
.attr("class", "expand-path")
.style("stroke", "none")
.style("fill", "gray")
.attr("d", Global.plus_path_d(-11, 0, 10, 10, 2));
// set_groups
// .append("rect")
// .attr("class", "collapse-rect")
// .attr("x", -11)
// .attr("y", 20)
// .attr("width", 10)
// .attr("height", 10)
// .style("rx", 3)
// .style("ry", 3)
// .style("fill", "white")
// .style("stroke", "gray")
// .on("click", (_, d) => {
// console.log("collapse click");
// d.collapse = d.collapse === 1 ? 0 : 1;
// that.parent.update_data();
// that.parent.update_view(); // TODO: watch-function way
// });
set_groups
.append("rect")
.attr("class", "background")
.style("fill", "white")
.style("stroke", "#d0d0d0")
.style("stroke-width", 1.5)
.attr("width", (d) => d.width)
.attr("height", (d) => d.height);
set_groups
.append("path")
.attr("class", "collapse-path")
.style("stroke", "gray")
.style("stroke-width", "2")
.style("fill", "white")
.style("cursor", "hand")
.attr("d", d => d.collapse ? Global.collapse_icon(11, d.height / 2, 0)
: Global.collapse_icon(-11, d.height / 2 - 10, 1))
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", (d) => (that.visible(d) ? "auto" : "none"))
.on("click", (_, d) => {
console.log("collapse click");
d.collapse = d.collapse === 1 ? 0 : 1;
that.parent.update_data();
that.parent.update_view(); // TODO: watch-function way
});
that.image_groups = set_groups
.selectAll("g.detection-result")
.data((d) => that.vis_image_per_cluster[d.id]);
let g_image_groups = that.image_groups
.enter()
.append("g")
.attr("class", "detection-result")
.attr(
"transform",
(d) => "translate(" + d.x + ", " + that.top_image_margin + ")"
);
g_image_groups
.append("rect")
.attr("class", "image-shadow")
.attr("id", (d) => "image-shadow-" + d.idx)
.attr("x", 0)
.attr("y", 0)
.attr("width", (d) => d.vis_w)
.attr("height", (d) => d.vis_h)
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", "none")
.style("fill", "white")
.style("stroke", "white")
.style("stroke-width", 5);
g_image_groups
.append("image")
.attr("id", (d) => "set-image-" + d.idx)
.attr("x", 0)
.attr("y", 0)
.attr("width", (d) => d.vis_w)
.attr("height", (d) => d.vis_h)
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", (d) => (that.visible(d) ? 1 : "none"))
.attr(
"href",
(d) => that.server_url + `/image/image?filename=${d.idx}.jpg`
)
.on("mouseover", function(ev) {
if (that.get_expand_set_id() === -1) that.box_highlight(ev);
that.image_highlight(ev);
})
.on("mouseout", function() {
that.box_dehighlight();
that.image_dehighlight();
})
.on("click", (ev, d) => {
console.log("click image", d);
ev.stopPropagation();
that.selected_image_idx = d.idx;
let self = d3
.select(ev.target.parentElement)
.selectAll(".image-shadow");
self.style("stroke", "rgb(237,129,55)");
// that.set_focus_image(d);
that.parent.fetch_single_image_detection_for_focus_text({
image_id: d.idx,
});
});
that.box_groups = that.set_group
.selectAll(".set")
.selectAll(".detection-result")
.selectAll("rect.box")
.data((d) => {
// that.box_groups = g_image_groups.selectAll("rect.box").data((d) => {
let dets = d.d;
let res = [];
for (let i = 0; i < dets.length; i++) {
let x = d.vis_w * dets[i][0];
let width = d.vis_w * (dets[i][2] - dets[i][0]);
let y = d.vis_h * dets[i][1];
let height = d.vis_h * (dets[i][3] - dets[i][1]);
let collapse = d.collapse;
res.push({ x, y, width, height, collapse });
}
return res;
});
that.box_groups
.exit()
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.remove();
that.box_groups
.enter()
.append("rect")
.attr("class", "box")
.attr("x", (d) => d.x)
.attr("y", (d) => d.y)
.attr("width", (d) => d.width)
.attr("height", (d) => d.height)
.style("fill", "none")
.style("stroke", Global.BoxRed)
.style("stroke-width", 1)
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", (d) => (that.visible(d) ? 1 : "none"));
that.box_groups
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("x", (d) => d.x)
.attr("y", (d) => d.y)
.attr("width", (d) => d.width)
.attr("height", (d) => d.height)
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", (d) => (that.visible(d) ? "auto" : "none"));
};
this.grid_create = function() {
let set_id = that.get_expand_set_id();
let selected_set = that.parent.sets[set_id];
let images = that.vis_image_per_cluster[set_id];
let dragstarted = function() {
let tag = d3.select(this);
console.log("drag start", tag);
that.timer = setTimeout(function() {
_dragstarted(tag);
}, 200);
};
let _dragstarted = function(tag) {
that.focus_grid_for_edit = tag.data()[0];
that.drag_grid_group
.append("rect")
.attr("id", "drag-background")
.attr("x", that.parent.set_left)
.attr("y", 0)
.attr("width", that.parent.set_width)
.attr("height", that.parent.layout_height)
.style("fill", "gray")
.style("opacity", 0.1);
let drag_grid_group = that.drag_grid_group
.selectAll("g")
.data(images)
.enter()
.append("g")
.attr("class", "drag-image")
.attr(
"transform",
(d) =>
"translate(" +
(selected_set.x + d.x) +
", " +
that.top_image_margin +
")"
);
drag_grid_group
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", (d) => d.vis_w)
.attr("height", (d) => d.vis_h)
.style("pointer-events", "none")
.style("fill", "white")
.style("stroke", "white")
.style("stroke-width", 5);
drag_grid_group
.append("image")
.attr("x", 0)
.attr("y", 0)
.attr("width", (d) => d.vis_w)
.attr("height", (d) => d.vis_h)
.attr(
"href",
(d) => that.server_url + `/image/image?filename=${d.idx}.jpg`
)
.on("mouseover", function(ev, d) {
console.log("drag image mouseover");
that.target_grid_image = d;
let element = ev.target;
let self = d3.select(element.parentElement).selectAll(".rect");
self.style("stroke", "rgb(237,129,55)");
})
.on("mouseout", function() {
console.log("drag image mouseout");
that.target_grid_image = null;
that.drag_grid_group
.selectAll(".drag-image")
.selectAll("rect")
.style("stroke", "white");
});
that.drag_grid_group
.append("rect")
.attr("id", "drag-grid")
.attr("x", tag.data()[0].x)
.attr("y", tag.data()[0].y)
.attr("width", tag.data()[0].width - 2 * grid_margin)
.attr("height", tag.data()[0].width - 2 * grid_margin)
.style("pointer-events", "none")
.style("stroke", "black")
.style("fill", "green")
.style("opacity", 0.5);
that.grid_group.style("opacity", 0.1);
that.label_group.style("opacity", 0.1);
};
let dragged = function(event) {
that.drag_grid_group
.select("#drag-grid")
.attr("x", event.x)
.attr("y", event.y);
};
let dragended = function() {
clearTimeout(that.timer);
console.log("drag end");
that.drag_grid_group.select("#drag-grid").remove();
if (that.target_grid_image !== null) {
let set_id = that.get_expand_set_id();
let images = that.vis_image_per_cluster[set_id];
let images_ids = images.map((d) => d.idx);
let idx = images_ids.indexOf(that.target_grid_image.idx);
let removed_image = that.vis_image_per_cluster[set_id][idx];
removed_image.d = that.focus_grid_for_edit.d;
removed_image.idx = that.focus_grid_for_edit.img_id;
console.log("image", images);
that.drag_grid_group
.selectAll("g")
.data(images)
.selectAll("image")
.attr(
"href",
(d) => that.server_url + `/image/image?filename=${d.idx}.jpg`
);
}
that.drag_grid_group.select("#drag-background").remove();
setTimeout(function() {
that.drag_grid_group
.selectAll(".drag-image")
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.remove();
that.grid_group
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.style("opacity", 1);
that.label_group
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.style("opacity", 1);
}, 200);
};
let drag = d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
let grid_groups = that.e_grids
.enter()
.append("g")
.attr("class", "grid")
.attr("id", (d) => "grid-id-" + d.img_id + "-" + d.id)
.attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")")
.call(drag)
.on("mouseover", function() {
d3.select(this)
.select("rect")
.style("stroke-width", 2.0);
})
.on("mouseout", function() {
d3.select(this)
.select("rect")
.style("stroke-width", 0.0);
})
.on("click", (_, d) => {
console.log("click image", d);
// that.set_focus_image(d);
that.parent.fetch_single_image_detection_for_focus_text({
image_id: d.img_id,
});
});
// TOOD: mouseover, mouseout
grid_groups
.style("opacity", 0)
.transition()
.duration(that.create_ani)
.delay(that.update_ani + that.remove_ani)
.style("opacity", 1);
grid_groups
.append("rect")
.attr("class", "boundingbox")
.attr("x", 0 + grid_margin)
.attr("y", 0 + grid_margin)
.attr("width", (d) => d.width - 2 * grid_margin)
.attr("height", (d) => d.width - 2 * grid_margin)
.style("fill", (d) => (d.mismatch > 0 ? mismatch_color : match_color))
.style("stroke", "black")
.style("stroke-width", 0)
.style("stroke-opacity", 1);
grid_groups
.append("image")
.attr("class", "display")
.attr("x", 0.5 * that.boundingbox_width + grid_margin)
.attr("y", 0.5 * that.boundingbox_width + grid_margin)
.attr("width", (d) => d.width - that.boundingbox_width - 2 * grid_margin)
.attr("height", (d) => d.width - that.boundingbox_width - 2 * grid_margin)
.attr("xlink:href", (d) =>
that.use_label_layout
? null
: that.server_url + `/image/image?filename=${d.img_id}.jpg`
)
.style("pointer-events", "none");
let box_groups = grid_groups.selectAll("rect.box").data((d) => {
let dets = d.d;
let res = [];
for (let i = 0; i < dets.length; i++) {
let tmp = dets[i].slice(0, 4);
tmp.width = d.width;
res.push(tmp);
}
return res;
});
box_groups
.enter()
.append("rect")
.attr("class", "box")
.attr(
"x",
(d) =>
0.5 * that.boundingbox_width +
(d.width - that.boundingbox_width) * d[0]
)
.attr(
"y",
(d) =>
0.5 * that.boundingbox_width +
(d.width - that.boundingbox_width) * d[1]
)
.attr("width", (d) => (d.width - that.boundingbox_width) * (d[2] - d[0]))
.attr("height", (d) => (d.width - that.boundingbox_width) * (d[3] - d[1]))
.style("fill", "none")
.style("stroke", Global.BoxRed)
.style("stroke-width", 1)
.style(
"opacity",
!that.use_label_layout && that.get_expand_set_id() > -1 ? 1 : 0
)
.style(
"pointer-events",
!that.use_label_layout && that.get_expand_set_id() > -1 ? 1 : "none"
);
};
this.label_create = function() {
let label_group = that.e_labels
.enter()
.append("g")
.attr("class", "label")
.attr("id", (d) => "label-id-" + d.img_id);
label_group
.style("opacity", 0)
.transition()
.duration(that.create_ani)
.delay(that.update_ani + that.remove_ani)
.style("opacity", 1);
// label_group
// .append("rect")
// .attr("x", d => d.grid.x + offset_x + 1)
// .attr("y", d => d.grid.y + offset_y + 1)
// .attr("width", d => d.grid.w - 2)
// .attr("height", d => d.grid.h - 2)
// .style("fill", "black")
// .style("stroke", "none")
// .style("opacity", 0.25);
label_group
.append("rect")
.attr("x", (d) => d.grid.x + offset_x)
.attr("y", (d) => d.grid.y + offset_y)
.attr("width", (d) => d.grid.w)
.attr("height", (d) => d.grid.h)
.style("fill", "none")
.style("stroke", "#fdfe6c")
.style("stroke-width", 4);
label_group
.append("image")
.attr("x", (d) => d.label.x + offset_x)
.attr("y", (d) => d.label.y + offset_y)
.attr("width", (d) => d.label.w)
.attr("height", (d) => d.label.h)
.attr(
"xlink:href",
(d) => that.server_url + `/image/image?filename=${d.img_id}.jpg`
)
.on("click", (_, d) => {
console.log("click image", d);
// that.set_focus_image(d);
that.parent.fetch_single_image_detection_for_focus_text({
image_id: d.img_id,
});
});
let box_groups = label_group.selectAll("rect.box").data((d) => {
let dets = d.d;
let res = [];
for (let i = 0; i < dets.length; i++) {
let x = d.label.x + offset_x + d.label.w * dets[i][0];
// let width = d.grid.w * (dets[i][2] - dets[i][0]);
let width = d.label.w * (dets[i][2] - dets[i][0]);
let y = d.label.y + offset_y + d.label.h * dets[i][1];
let height = d.label.h * (dets[i][3] - dets[i][1]);
res.push({ x, y, width, height });
}
return res;
});
box_groups
.enter()
.append("rect")
.attr("class", "box")
.attr("x", (d) => d.x)
.attr("y", (d) => d.y)
.attr("width", (d) => d.width)
.attr("height", (d) => d.height)
.style("fill", "none")
.style("stroke", Global.BoxRed)
.style("stroke-width", 1)
.style("opacity", that.get_expand_set_id() > -1 ? 1 : 0)
.style("pointer-events", that.get_expand_set_id() > -1 ? 1 : "none");
};
this.nav_create = function() {
let nav_group = that.e_navs
.enter()
.append("circle")
.attr("class", "nav-circle")
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
.attr("r", 10)
.style("fill", (d) => (d.last_one ? "white" : that.nav_gray))
.style("stroke", (d) => (d.last_one ? that.nav_gray : "white"))
.style("stroke-width", (d) => (d.last_one ? 3 : 0))
.on("click", (_, d) => {
if (d.id === that.click_ids.slice(-1)[0].id) return;
let i = 0;
for (; i < that.click_ids.length; i++) {
if (d.id === that.click_ids[i].id) break;
}
that.click_ids = that.click_ids.slice(0, i);
that.set_grid_layout_data(d);
});
nav_group
.style("opacity", 0)
.transition()
.duration(that.create_ani)
.delay(that.update_ani + that.remove_ani)
.style("opacity", 1);
};
this.update = function() {
that.set_update();
that.grid_update();
that.label_update();
that.nav_update();
};
this.set_update = function() {
that.e_sets
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")");
that.set_group
.selectAll(".set")
.select("rect.background")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("height", (d) => d.height);
that.e_sets
.selectAll("g.detection-result")
.selectAll(".image-shadow")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
// .attr("height", d => that.get_expand_set_id() === -1 ? d.vis_h : 0);
.attr("width", (d) => d.vis_w)
.attr("height", (d) => d.vis_h)
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", "none");
that.set_group
.selectAll(".set")
.selectAll("g.detection-result")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr(
"transform",
(d) => "translate(" + d.x + ", " + that.top_image_margin + ")"
);
that.set_group
.selectAll(".set")
.selectAll("g.detection-result")
.selectAll("image")
.attr(
"href",
(d) => that.server_url + `/image/image?filename=${d.idx}.jpg`
)
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("width", (d) => d.vis_w)
.attr("height", (d) => d.vis_h)
// .attr("height", d => that.get_expand_set_id() === -1 ? d.vis_h : 0);
.style("opacity", (d) => (that.visible(d) ? 1 : 0))
.style("pointer-events", (d) => (that.visible(d) ? "auto" : "none"));
// .style("pointer-events", "auto");
// that.set_group
// .selectAll(".set")
// .selectAll("g.detection-result")
// .selectAll("rect.box")
// .transition()
// .duration(that.update_ani)
// .delay(that.remove_ani)
// .style("opacity", (d) =>
// (that.visible(d) ? 1 : 0))
// .style("pointer-events", (d) => (that.visible(d) ? "auto" : "none"));
that.set_group
.selectAll(".set")
.selectAll(".collapse-path")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("d", d => d.collapse ? Global.collapse_icon(11, d.height / 2, 0)
: Global.collapse_icon(-11, d.height / 2 - 10, 1))
.style("opacity", that.get_expand_set_id() == -1 ? 1 : 0)
.style("pointer-events", that.get_expand_set_id() == -1 ? "auto" : "none");
that.e_sets
.select(".expand-path")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("d", (d) =>
d.id === that.get_expand_set_id()
? Global.minus_path_d(-11, 0, 10, 10, 2)
: Global.plus_path_d(-11, 0, 10, 10, 2)
)
.style("opacity", (d) =>
that.visible(d) || that.get_expand_set_id() === d.id
? 1
: 0
);
that.e_sets
.select(".expand-rect")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.style("opacity", (d) =>
that.visible(d) || that.get_expand_set_id() === d.id
? 1
: 0
);
};
this.grid_update = function() {
that.e_grids
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")");
that.e_grids
.select(".boundingbox")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("x", 0 + grid_margin)
.attr("y", 0 + grid_margin)
.attr("width", (d) => d.width - 2 * grid_margin)
.attr("height", (d) => d.width - 2 * grid_margin)
.style("fill", (d) => (d.mismatch > 0 ? mismatch_color : match_color));
that.e_grids
.select(".display")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("x", 0.5 * that.boundingbox_width + grid_margin)
.attr("y", 0.5 * that.boundingbox_width + grid_margin)
.attr("width", (d) => d.width - that.boundingbox_width - 2 * grid_margin)
.attr("height", (d) => d.width - that.boundingbox_width - 2 * grid_margin)
.attr("xlink:href", (d) =>
that.use_label_layout
? null
: that.server_url + `/image/image?filename=${d.img_id}.jpg`
);
that.e_grids
.selectAll("rect.box")
.data((d) => {
let dets = d.d;
let res = [];
for (let i = 0; i < dets.length; i++) {
let tmp = dets[i].slice(0, 4);
tmp.width = d.width;
res.push(tmp);
}
return res;
})
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr(
"x",
(d) =>
0.5 * that.boundingbox_width +
(d.width - that.boundingbox_width) * d[0]
)
.attr(
"y",
(d) =>
0.5 * that.boundingbox_width +
(d.width - that.boundingbox_width) * d[1]
)
.attr("width", (d) => (d.width - that.boundingbox_width) * (d[2] - d[0]))
.attr("height", (d) => (d.width - that.boundingbox_width) * (d[3] - d[1]))
.style(
"opacity",
!that.use_label_layout && that.get_expand_set_id() > -1 ? 1 : 0
)
.style(
"pointer-events",
!that.use_label_layout && that.get_expand_set_id() > -1 ? 1 : "none"
);
};
this.label_update = function() {
that.e_labels
.select("rect")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("x", (d) => d.grid.x + offset_x)
.attr("y", (d) => d.grid.y + offset_y)
.attr("width", (d) => d.grid.w)
.attr("height", (d) => d.grid.h);
that.e_labels
.select("image")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("x", (d) => d.label.x + offset_x)
.attr("y", (d) => d.label.y + offset_y)
.attr("width", (d) => d.label.w)
.attr("height", (d) => d.label.h)
.attr(
"xlink:href",
(d) => that.server_url + `/image/image?filename=${d.img_id}.jpg`
);
// that.e_labels
// .selectAll("rect.box")
// .transition()
// .duration(that.update_ani)
// .delay(that.remove_ani)
// .style("opacity", that.get_expand_set_id() > -1 ? 1 : 0)
// .style("pointer-events", that.get_expand_set_id() > -1 ? 1 : "none");
};
this.nav_update = function() {
that.e_navs
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
.style("fill", (d) => (d.last_one ? "white" : that.nav_gray))
.style("stroke", (d) => (d.last_one ? that.nav_gray : "white"))
.style("stroke-width", (d) => (d.last_one ? 3 : 0));
that.nav_group
.select("rect")
.transition()
.duration(that.update_ani)
.delay(that.remove_ani)
.attr(
"height",
that.click_ids.length === 0
? 0
: that.nav_margin * (that.click_ids.length - 1)
);
};
this.remove = function() {
that.set_remove();
that.grid_remove();
that.label_remove();
that.nav_remove();
};
this.set_remove = function() {
that.e_sets
.exit()
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.remove();
};
that.grid_remove = function() {
that.e_grids
.exit()
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.remove();
};
this.label_remove = function() {
that.e_labels
.exit()
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.remove();
};
this.nav_remove = function() {
that.e_navs
.exit()
.transition()
.duration(that.remove_ani)
.style("opacity", 0)
.remove();
};
that.set_mode = function(mode) {
console.log("set mode", mode);
that.mode = mode;
if (mode === "cropping") {
d3.select("#cropping")
.select("path")
.attr("d", Global.d_rollback);
d3.select("#selecting")
.select("path")
.attr("d", Global.d_select);
that.enter_overview();
} else if (mode === "selecting") {
d3.select("#selecting")
.select("path")
.attr("d", Global.d_rollback);
d3.select("#cropping")
.select("path")
.attr("d", Global.d_scan);
that.enter_overview();
} else if (mode === "exploring") {
d3.select("#cropping")
.select("path")
.attr("d", Global.d_scan);
d3.select("#selecting")
.select("path")
.attr("d", Global.d_select);
that.quit_overview();
}
};
that.get_mode = function() {
return that.mode;
};
that.enter_overview = function() {
that.overview_group
.select("#overview-1")
.attr("x", offset_x)
.attr("y", offset_y)
.attr("width", plot_width)
.attr("height", plot_height);
// that.overview_group.select("#overview-2")
// .attr("x", small_x_2)
// .attr("y", small_y_2)
// .attr("width", small_grid_width)
// .attr("height", small_grid_width);
that.overview_group.style("visibility", "visible");
that.overview_group.select("#viewbox").style("visibility", "hidden");
that.confirm_button.style("visibility", "hidden");
};
that.quit_overview = function() {
that.overview_group.style("visibility", "hidden");
that.overview_group.select("#viewbox").style("visibility", "hidden");
that.confirm_button
.select("#confirm-resample")
.style("visibility", "hidden");
};
that._init = function() {
that.overview_group = that.parent.svg
.append("g")
.attr("id", "overview-group");
that.overview_group
.attr("transform", "translate(" + 0 + "," + that.text_height + ")")
.style("visibility", "hidden");
that.overview_group
.append("rect")
.attr("id", "overview-1")
.attr("class", "overview-box");
// that.overview_group.append("rect")
// .attr("id", "overview-2")
// .attr("class", "overview-box");
that.overview_group
.selectAll(".overview-box")
.attr("x", 0)
.attr("y", 0)
.style("fill", "white")
.style("stroke", "grey")
.style("stroke-width", 5)
.style("opacity", 0.3);
that.overview_group
.append("rect")
.attr("id", "viewbox")
.style("stroke-dasharray", "5, 5")
.style("fill", "white")
.style("stroke", "grey")
.style("stroke-width", 5)
.style("opacity", 0.5);
d3.select("#cropping").on("click", function() {
var mode =
d3
.select(this)
.select("path")
.attr("d") === Global.d_scan
? "cropping"
: "exploring";
that.set_mode(mode);
});
d3.select("#selecting").on("click", function() {
var mode =
d3
.select(this)
.select("path")
.attr("d") === Global.d_select
? "selecting"
: "exploring";
that.set_mode(mode);
});
d3.select("#home").on("click", () => {
console.log("home buttom click");
// let d = that.click_ids[0];
// that.click_ids = that.click_ids.slice(0, i);
that.update_ani = 2000;
that.create_ani = 2000;
let d = that.click_ids[1];
that.click_ids = that.click_ids.slice(0, 1);
that.set_grid_layout_data(d);
});
function adjust_sampling_area(area) {
that.relative_sampling_area = area;
// console.log("relative_sampling are", that.relative_sampling_area);
that.overview_group
.select("#viewbox")
.attr("x", that.relative_sampling_area.x * plot_width + offset_x)
.attr("y", that.relative_sampling_area.y * plot_height + offset_y) //- that.text_height)
.attr("width", that.relative_sampling_area.w * plot_width)
.attr("height", that.relative_sampling_area.h * plot_height);
}
function compute_viewbox(x1, y1, x2, y2) {
var min_x = Math.min(x1, x2),
max_x = Math.max(x1, x2),
min_y = Math.min(y1, y2),
max_y = Math.max(y1, y2);
var new_area = {
x: (min_x - plot_x) / plot_width,
y: (min_y - plot_y) / plot_height,
w: (max_x - min_x) / plot_width,
h: (max_y - min_y) / plot_height,
};
if (new_area.x + new_area.w > 1 && new_area.x < 1) {
return that.relative_sampling_area;
} else {
return new_area;
}
}
that.overview_group
.on("mousedown", function(ev) {
// var offset = $(d3.select(this).node()).offset();
plot_x = offset_x;
plot_y = offset_y + that.text_height;
mouse_pos = {
x: ev.offsetX,
y: ev.offsetY,
};
console.log(plot_x, plot_y, mouse_pos);
mouse_pressed = d3.select(this).attr("id");
that.overview_group.select("#viewbox").style("visibility", "visible");
that.confirm_button.style("visibility", "hidden");
adjust_sampling_area(
compute_viewbox(mouse_pos.x, mouse_pos.y, mouse_pos.x, mouse_pos.y)
);
})
.on("mousemove", function(ev) {
if (!mouse_pressed) {
return;
}
adjust_sampling_area(
compute_viewbox(mouse_pos.x, mouse_pos.y, ev.offsetX, ev.offsetY)
);
let left_x = that.relative_sampling_area.x;
let top_y = that.relative_sampling_area.y;
let right_x = left_x + that.relative_sampling_area.w;
let bottom_y = top_y + that.relative_sampling_area.h;
// console.log("relative sampling area", left_x, right_x, top_y, bottom_y);
if (that.get_mode() !== "exploring") {
let grid_data = that.get_grid_data();
grid_data.forEach((d) => {
let x = d.pos[0];
let y = d.pos[1];
let width = d.normed_w;
if (
x + width > left_x &&
x < right_x &&
y + width > top_y &&
y < bottom_y
) {
if (d.selected === false) {
d.selected = true;
d3.select("#grid-id-" + d.img_id)
.select(".boundingbox")
.style(
"fill",
d.mismatch > 0
? d3.rgb(Global.Orange).darker(1.5)
: d3.rgb(Global.GrayColor).darker(1.5)
);
d3.select("#grid-id-" + d.img_id)
.select(".display")
.style(
"fill",
d.mismatch > 0
? d3.rgb(Global.Orange).darker(1.5)
: d3.rgb(Global.GrayColor).darker(1.5)
);
}
} else {
if (d.selected === true) {
d.selected = false;
d3.select("#grid-id-" + d.img_id)
.select(".boundingbox")
.style(
"fill",
d.mismatch > 0 ? Global.Orange : Global.GrayColor
);
d3.select("#grid-id-" + d.img_id)
.select(".display")
.style(
"fill",
d.mismatch > 0 ? Global.Orange : Global.GrayColor
);
}
}
});
}
})
.on("mouseup", function(ev) {
if (!mouse_pressed) {
return;
}
mouse_pressed = false;
adjust_sampling_area(
compute_viewbox(mouse_pos.x, mouse_pos.y, ev.offsetX, ev.offsetY)
);
let button_x =
(that.relative_sampling_area.x + that.relative_sampling_area.w) *
plot_width +
margin_size +
offset_x -
10;
let button_y =
(that.relative_sampling_area.y + that.relative_sampling_area.h) *
plot_height +
// margin_top_size +
that.text_height +
offset_y;
that.confirm_button
.attr("transform", "translate(" + button_x + ", " + button_y + ")")
.style("visibility", "visible");
});
that.confirm_button = that.parent.svg
.append("g")
.attr("id", "confirm-resample")
.style("visibility", "hidden");
that.confirm_button
.append("circle")
.attr("r", 20)
.attr("fill", "grey");
that.confirm_button
.append("text")
.attr("class", "glyphicon")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("dy", "0.25em")
.style("fill", "white")
.style("opacity", 1)
.style("font-size", "20px")
.style("cursor", "hand")
.text("\ue015");
that.confirm_button.on("click", function(ev) {
console.log("confirm buttom click");
if (that.get_mode() === "cropping") {
that.click_count = that.click_ids.slice(-1)[0].id;
// that.click_ids.push(that.click_count);
let query = {
// "image_cluster_id": that.expand_set_id,
cat_ids: that.parent.selected_node.node_ids,
"left-x": that.relative_sampling_area.x,
"top-y": that.relative_sampling_area.y,
width: that.relative_sampling_area.w,
height: that.relative_sampling_area.h,
"node-id": that.click_count,
image_cluster_id: that.get_expand_set_id(),
};
that.fetch_grid_layout(query);
} else if (that.get_mode() === "selecting") {
// let selected_items_id = [];
// for (let i = 0; i < train_data.length; i++) {
// if (train_data[i].selected === true) {
// selected_items_id.push(train_data[i].get_id());
// }
// }
// for (let i = 0; i < test_data.length; i++) {
// if (test_data[i].selected === true) {
// selected_items_id.push(test_data[i].get_id());
// }
// }
}
that.set_mode("exploring");
d3.select(this).style("visibility", "hidden");
ev.stopPropagation();
});
}.call();
that.label_layout = function(data, plot_size, labels) {
let padding_label = 40;
if (data.length <= 25 ** 2) {
labels = Array();
return [];
}
var grid_N = Math.ceil(Math.sqrt(data.length));
var grid_size = plot_size / grid_N;
var img_size = img_width;
var new_labels = [];
var intersect = function(rect1, rect2) {
return !(
rect1.x + rect1.w + padding_label < rect2.x ||
rect2.x + rect2.w + padding_label < rect1.x ||
rect1.y + rect1.h + padding_label < rect2.y ||
rect2.y + rect2.h + padding_label < rect1.y
);
};
var legal = function(rect) {
return (
rect.x > 0 &&
rect.y > 0 &&
rect.x + rect.w < plot_size &&
rect.y + rect.h < plot_size
);
};
const offset = [
{ x: 0, y: 0 },
{ x: 0, y: -img_size },
{ x: -img_size, y: -img_size },
{ x: -img_size, y: 0 },
];
data.sort((x, y) => y.mismatch - x.mismatch);
for (let d of data) {
var center = {
x: d.pos[0] * plot_size + 0.5 * grid_size,
y: d.pos[1] * plot_size + 0.5 * grid_size,
};
var tmp_grid = {
x: d.pos[0] * plot_size,
y: d.pos[1] * plot_size,
w: d.normed_w * plot_size,
h: d.normed_w * plot_size,
};
var tmp_label;
var can_placed;
var prev_label = labels.filter((lbl) => lbl.id === d.id);
if (prev_label.length > 0) {
var direction = prev_label[0].label.dir;
tmp_label = {
dir: direction,
x: center.x + offset[direction].x,
y: center.y + offset[direction].y,
w: img_size,
h: img_size,
};
can_placed = true;
for (let r of new_labels) {
if (
intersect(tmp_label, r.label) ||
intersect(tmp_grid, r.label) ||
intersect(tmp_label, r.grid) ||
intersect(tmp_grid, r.grid) ||
!legal(tmp_label)
) {
can_placed = false;
break;
}
}
if (can_placed) {
new_labels.push({
label: tmp_label,
grid: tmp_grid,
...d,
});
}
} else {
for (var i = 0; i < 4; ++i) {
tmp_label = {
dir: i,
x: center.x + offset[i].x,
y: center.y + offset[i].y,
w: img_size,
h: img_size,
};
can_placed = true;
for (let r of new_labels) {
if (
intersect(tmp_label, r.label) ||
intersect(tmp_grid, r.label) ||
intersect(tmp_label, r.grid) ||
intersect(tmp_grid, r.grid) ||
!legal(tmp_label)
) {
can_placed = false;
break;
}
}
if (can_placed) {
new_labels.push({
label: tmp_label,
grid: tmp_grid,
...d,
});
break;
}
}
}
}
return new_labels;
};
that.box_highlight = function(ev) {
let element = ev.target;
while (element.tagName !== "g" || element.className.baseVal !== "set") {
element = element.parentElement;
}
let self = d3.select(element);
let d = self.data()[0];
let group = that.set_group.select("#set-" + d.id);
group
.selectAll(".background")
// .style("stroke", "rgb(128, 128, 128)");
.style("stroke", "rgb(237,129,55)")
.style("stroke-width", 2);
group.selectAll(".expand-rect").style("stroke", "rgb(38, 38, 38)");
if (Global.linked_highlight) that.connection_highlight(d);
};
that.box_dehighlight = function() {
let groups = that.set_group.selectAll(".set");
groups
.selectAll(".background")
.style("stroke", "rgb(208, 208, 208)")
.style("stroke-width", 1);
groups.selectAll(".expand-rect").style("stroke", "gray");
if (Global.linked_highlight) that.connection_dehighlight();
};
that.image_highlight = function(ev) {
// console.log('image highlight');
let element = ev.target;
let self = d3.select(element.parentElement).selectAll(".image-shadow");
self.style("stroke", "rgb(237,129,55)");
};
that.image_dehighlight = function() {
that.set_group
.selectAll(".set")
.selectAll(".image-shadow")
.style("stroke", "white");
if (that.selected_image_idx !== null) {
d3.select("#image-shadow-" + that.selected_image_idx).style(
"stroke",
"rgb(237,129,55)"
);
}
};
that.connection_highlight = function(node) {
that.parent.connection_view.highlight_by_image_cluster(node);
};
that.connection_dehighlight = function() {
that.parent.connection_view.dehighlight();
};
that.clean = function() {
that.set_group.selectAll(".set").remove();
};
};
export default ImageCards;