You need to sign in or sign up before continuing.
Commit 79827d1a authored by Kento HASEGAWA's avatar Kento HASEGAWA

Support for the problem viewer and dragging blocks in viewers

parent fb0ef6af
......@@ -16,9 +16,9 @@ def webui_index():
# return adc2019system.role.role
return render_template('index.html')
@webui.route('/view/solution')
def webui_view_solution():
return render_template('view-solution.html')
@webui.route('/viewer')
def webui_viewer():
return render_template('viewer.html')
@webui.route('/part/problems')
def webui_part_problems():
......
......@@ -106,16 +106,17 @@ class Host(object):
else:
return {'status': 'unknown request'}
def get_solution_for_viewer(self, problem_key, solution_id):
def get_viewer_data(self, problem_key, solution_id):
problem = self.get_problem(problem_key)
if problem is None:
return None
problem_data = problem.get_d3json()
solution = problem.get_solution(solution_id)
if solution is None:
return None
solution_data = solution.get_d3json()
if solution_id is None:
solution_data = None
else:
solution = problem.get_solution(solution_id)
solution_data = solution.get_d3json()
return {
'problem': problem_data,
......@@ -146,10 +147,10 @@ class Host(object):
elif cmd == 'stop':
self.worker_manager.request_stop()
return {}
elif cmd == 'view/solution':
elif cmd == 'view':
problem_key = params['problem']
solution_id = params['solution']
return self.get_solution_for_viewer(problem_key, solution_id)
return self.get_viewer_data(problem_key, solution_id)
elif cmd == 'adccli/login':
with open('path-to-adccli-login', 'r') as fp:
d = json.load(fp)
......
......@@ -103,31 +103,33 @@ class ADC2019BoardViewer {
let cubeResolution = this.cubeResolution;
// Draw outer box
let _width = data['solution']['w'] * this.cubeResolution;
let _height = data['solution']['h'] * this.cubeResolution;
this.view.append("g")
.attr("class", "x axis")
.selectAll("line")
.data([0, _width])
.enter().append("line")
.attr("x1", function (d) { return d; })
.attr("y1", 0)
.attr("x2", function (d) { return d; })
.attr("y2", _height)
.attr("stroke", "#d00")
.attr("stroke-width", 2);
this.view.append("g")
.attr("class", "y axis")
.selectAll("line")
.data([0, _height])
.enter().append("line")
.attr("x1", 0)
.attr("y1", function (d) { return d; })
.attr("x2", _width)
.attr("y2", function (d) { return d; })
.attr("stroke", "#d00")
.attr("stroke-width", 2);
if(('solution' in data) && (data['solution'] != null)){
let _width = data['solution']['w'] * this.cubeResolution;
let _height = data['solution']['h'] * this.cubeResolution;
this.view.append("g")
.attr("class", "x axis")
.selectAll("line")
.data([0, _width])
.enter().append("line")
.attr("x1", function (d) { return d; })
.attr("y1", 0)
.attr("x2", function (d) { return d; })
.attr("y2", _height)
.attr("stroke", "#d00")
.attr("stroke-width", 2);
this.view.append("g")
.attr("class", "y axis")
.selectAll("line")
.data([0, _height])
.enter().append("line")
.attr("x1", 0)
.attr("y1", function (d) { return d; })
.attr("x2", _width)
.attr("y2", function (d) { return d; })
.attr("stroke", "#d00")
.attr("stroke-width", 2);
}
let colors = d3.schemeCategory10
......@@ -138,11 +140,18 @@ class ADC2019BoardViewer {
// Create data
var blocks = data['problem']['block'];
var block_nums = Object.keys(blocks).length;
var block_row = Math.ceil(Math.sqrt(block_nums));
var block_idx = 0;
for(var bi in blocks){
let bw = blocks[bi]['w'];
let bh = blocks[bi]['h'];
let bx = data['solution']['block'][bi]['x'];
let by = data['solution']['block'][bi]['y'];
var bx = (block_idx % block_row) * 3;
var by = (Math.floor(block_idx / block_row)) * 3;
if(('solution' in data) && (data['solution'] != null)){
bx = data['solution']['block'][bi]['x'];
by = data['solution']['block'][bi]['y'];
}
blocks[bi]['cellpos'] = Array();
for(var _h=0; _h<bh; _h++){
......@@ -161,6 +170,34 @@ class ADC2019BoardViewer {
blocks[bi]['x'] = bx;
blocks[bi]['y'] = by;
block_idx++;
}
function snapToGrid(p, r) {
return Math.round(p / r) * r;
}
var __view = this.view;
function dragged(d) {
var el = d3.select(this);
var _x = parseInt(el.attr('data-x'), 10) + d3.event.x;
var _y = parseInt(el.attr('data-y'), 10) + d3.event.y;
el.attr("transform", (d) => {
return 'translate(' + snapToGrid(_x, cubeResolution) + ',' + snapToGrid(_y, cubeResolution) + ')'
})
}
function dragended(d) {
var el = d3.select(this).classed("dragging", false);
var _x = parseInt(el.attr('data-x'), 10) + d3.event.x;
var _y = parseInt(el.attr('data-y'), 10) + d3.event.y;
d3.select(this)
.attr('data-x', snapToGrid(_x, cubeResolution))
.attr('data-y', snapToGrid(_y, cubeResolution));
}
function dragstarted(d) {
var el = d3.select(this);
el.raise().classed("dragging", true);
}
var itemContainer = this.view.selectAll("g.itemContainer")
......@@ -168,15 +205,19 @@ class ADC2019BoardViewer {
.enter()
.append('g')
.attr('class', 'itemContainer')
.attr("transform", (d) => 'translate(' + d.x + ',' + d.y + ')')
.attr("transform", (d) => 'translate(' + 0 + ',' + 0 + ')')
.attr('data-x', (d) => d.x)
.attr('data-y', (d) => d.y);
.attr('data-y', (d) => d.y)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var cellContainer = itemContainer.append('g')
.attr('class', 'cellContainer')
.attr('data-color', (d, i) => colors[i % colors.length])
.attr('x', 0)
.attr('y', 0)
.attr('y', 0);
cellContainer.selectAll('g')
.data((d) => d.cellpos)
......@@ -191,165 +232,100 @@ class ADC2019BoardViewer {
return d3.select(nodes[i].parentNode).attr('data-color');
});
cellContainer.selectAll('g')
.data((d) => d.cellpos)
.enter()
.append('text')
.attr('x', (d) => d[0] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('y', (d) => d[1] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('width', this.cubeResolution)
.attr('height', this.cubeResolution)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.attr('font-size', '20px')
.attr('cursor', 'move');
// .text((d) => d[2]);
let sw = data['solution']['w'];
let sh = data['solution']['h'];
var lines = Object();
for(var _h=0; _h<sh; _h++){
for(var _w=0; _w<sw; _w++){
let line_index = data['solution']['map'][_h][_w];
if(line_index == 0){
continue;
}
if(('solution' in data) && (data['solution'] != null)){
cellContainer.selectAll('g')
.data((d) => d.cellpos)
.enter()
.append('text')
.attr('x', (d) => d[0] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('y', (d) => d[1] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('width', this.cubeResolution)
.attr('height', this.cubeResolution)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.attr('font-size', '20px')
.attr('cursor', 'move');
}else{
cellContainer.selectAll('g')
.data((d) => d.cellpos)
.enter()
.append('text')
.attr('x', (d) => d[0] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('y', (d) => d[1] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('width', this.cubeResolution)
.attr('height', this.cubeResolution)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.attr('font-size', '20px')
.attr('cursor', 'move')
.text((d) => d[2]);
}
if(!lines.hasOwnProperty(line_index)){
lines[line_index] = Array();
}
lines[line_index].push([_w, _h, line_index]);
if((_w+1<sw) && (data['solution']['map'][_h][_w+1] == line_index)){
lines[line_index].push([_w+0.5, _h, '']);
}
if((_h+1<sh) && (data['solution']['map'][_h+1][_w] == line_index)){
lines[line_index].push([_w, _h+0.5, '']);
if(('solution' in data) && (data['solution'] != null)){
// Draw lines
let sw = data['solution']['w'];
let sh = data['solution']['h'];
var lines = Object();
for(var _h=0; _h<sh; _h++){
for(var _w=0; _w<sw; _w++){
let line_index = data['solution']['map'][_h][_w];
if(line_index == 0){
continue;
}
if(!lines.hasOwnProperty(line_index)){
lines[line_index] = Array();
}
lines[line_index].push([_w, _h, line_index]);
if((_w+1<sw) && (data['solution']['map'][_h][_w+1] == line_index)){
lines[line_index].push([_w+0.5, _h, '']);
}
if((_h+1<sh) && (data['solution']['map'][_h+1][_w] == line_index)){
lines[line_index].push([_w, _h+0.5, '']);
}
}
}
let lineColors = d3.schemePastel2
var lineItemContainer = this.view.selectAll("g.lineItemContainer")
.data(Object.values(lines))
.enter()
.append('g')
.attr('class', 'lineItemContainer');
var lineContainer = lineItemContainer.append('g')
.attr('class', 'lineContainer')
.attr('data-color', (d, i) => lineColors[i % lineColors.length]);
lineContainer.selectAll('g')
.data((d) => d)
.enter()
.append('rect')
.attr('x', (d) => (d[0]+0.25) * this.cubeResolution)
.attr('y', (d) => (d[1]+0.25) * this.cubeResolution)
.attr('width', (this.cubeResolution*0.5))
.attr('height', (this.cubeResolution*0.5))
.attr('fill', (d, i, nodes) => {
return d3.select(nodes[i].parentNode).attr('data-color');
});
lineContainer.selectAll('g')
.data((d) => d)
.enter()
.append('text')
.attr('x', (d) => d[0] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('y', (d) => d[1] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('width', (this.cubeResolution*0.5))
.attr('height', (this.cubeResolution*0.5))
.attr('text-anchor', 'middle')
.attr('fill', 'black')
.attr('font-size', '16px')
.text((d) => d[2]);
}
let lineColors = d3.schemePastel2
var lineItemContainer = this.view.selectAll("g.lineItemContainer")
.data(Object.values(lines))
.enter()
.append('g')
.attr('class', 'lineItemContainer');
var lineContainer = lineItemContainer.append('g')
.attr('class', 'lineContainer')
.attr('data-color', (d, i) => lineColors[i % lineColors.length]);
lineContainer.selectAll('g')
.data((d) => d)
.enter()
.append('rect')
.attr('x', (d) => (d[0]+0.25) * this.cubeResolution)
.attr('y', (d) => (d[1]+0.25) * this.cubeResolution)
.attr('width', (this.cubeResolution*0.5))
.attr('height', (this.cubeResolution*0.5))
.attr('fill', (d, i, nodes) => {
return d3.select(nodes[i].parentNode).attr('data-color');
});
lineContainer.selectAll('g')
.data((d) => d)
.enter()
.append('text')
.attr('x', (d) => d[0] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('y', (d) => d[1] * this.cubeResolution + 0.5 * this.cubeResolution)
.attr('width', (this.cubeResolution*0.5))
.attr('height', (this.cubeResolution*0.5))
.attr('text-anchor', 'middle')
.attr('fill', 'black')
.attr('font-size', '16px')
.text((d) => d[2]);
}
// _draw_blocks(blocks){
// let cubeResolution = this.cubeResolution;
// function snapToGrid(p, r) {
// return Math.round(p / r) * r;
// }
// function dragged(d) {
// var el = d3.select(this);
// var _x = parseInt(el.attr('data-x'), 10) + d3.event.x;
// var _y = parseInt(el.attr('data-y'), 10) + d3.event.y;
// el.attr("transform", (d) => {
// return 'translate(' + snapToGrid(_x, cubeResolution) + ',' + snapToGrid(_y, cubeResolution) + ')'
// })
// }
// function dragended(d) {
// var el = d3.select(this).classed("dragging", false);
// var _x = parseInt(el.attr('data-x'), 10) + d3.event.x;
// var _y = parseInt(el.attr('data-y'), 10) + d3.event.y;
// d3.select(this)
// .attr('data-x', snapToGrid(_x, cubeResolution))
// .attr('data-y', snapToGrid(_y, cubeResolution));
// }
// function dragstarted(d) {
// var el = d3.select(this);
// el.raise().classed("dragging", true);
// }
// let colors = d3.schemeCategory10
// // Reference
// // Grid: https://bl.ocks.org/ngminhtrung/7c5721a1504f3e29a36da9ddd9e5039b
// // Snap: https://bl.ocks.org/evanjmg/ea3e59e67b4256c8831d3fc80f71294b
// // Nested data structure: https://codeday.me/jp/qa/20190428/720184.html
// var itemContainer = this.view.selectAll("g.itemContainer")
// .data(blocks)
// .enter()
// .append('g')
// .attr('class', 'itemContainer')
// .attr("transform", (d) => 'translate(' + d.x + ',' + d.y + ')')
// .attr('data-x', (d) => d.x)
// .attr('data-y', (d) => d.y)
// .call(d3.drag()
// .on("start", dragstarted)
// .on("drag", dragged)
// .on("end", dragended));
// var cellContainer = itemContainer.append('g')
// .attr('class', 'cellContainer')
// .attr('data-color', (d, i) => colors[i])
// .attr('x', 0)
// .attr('y', 0)
// cellContainer.selectAll('g')
// .data((d) => d.cells)
// .enter()
// .append('rect')
// .attr('x', (d) => d[0] * this.cubeResolution)
// .attr('y', (d) => d[1] * this.cubeResolution)
// .attr('width', this.cubeResolution)
// .attr('height', this.cubeResolution)
// .attr('cursor', 'move')
// .attr('fill', (d, i, nodes) => {
// return d3.select(nodes[i].parentNode).attr('data-color');
// });
// cellContainer.selectAll('g')
// .data((d) => d.cells)
// .enter()
// .append('text')
// .attr('x', (d) => d[0] * this.cubeResolution + 0.5 * this.cubeResolution)
// .attr('y', (d) => d[1] * this.cubeResolution + 0.5 * this.cubeResolution)
// .attr('width', this.cubeResolution)
// .attr('height', this.cubeResolution)
// .attr('text-anchor', 'middle')
// .attr('fill', 'white')
// .attr('font-size', '20px')
// .attr('cursor', 'move')
// .text((d) => d[2]);
// }
}
$(function(){
......@@ -359,17 +335,26 @@ $(function(){
hash = location.hash.replace("#", "");
params = hash.split('/');
problem_name = params[0];
if(params.length > 1){
solution_id = params[1];
}else{
solution_id = null;
}
$.ajax({
type: "POST",
dataType: "json",
url: "/api/view/solution",
url: "/api/view",
data: JSON.stringify({
"problem": params[0],
"solution": params[1]
"problem": problem_name,
"solution": solution_id
}),
contentType: 'application/json'
}).done((d) => {
board.draw(d);
}).fail((d) => {
console.log(d);
});
});
......
......@@ -38,7 +38,7 @@ class StatusView {
_this.container.find('.solution-detail-row.valid-solution td').click((e) => {
var solution_id = $(e.target).parent("tr").data("solution-id");
var problem_name = $(e.target).parent("tr").data("problem");
var viewer_url = "/view/solution#" + problem_name + "/" + solution_id;
var viewer_url = "/viewer#" + problem_name + "/" + solution_id;
window.open(viewer_url, "_blank");
});
......
<div>
<h4 class='inline-heading' id='problem-status-title'>{{problem.name}}</h4>
<a href="/viewer#{{problem.name}}" target="_blank">Viewer</a>
{#% # if localmode %#}
<button class="btn btn-primary btn-lg start-button" type="button" data-qname="{{problem.name}}">Start</button>
<!-- <button class="btn btn-danger btn-lg stop-button" type="button" data-qname="all">Stop</button> -->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment