class ADC2019BoardViewer { constructor(selector) { this.container = $(selector); this.width = this.container.width(); this.height = $(window).height(); this.svg = d3.select(selector).append('svg'); this.view = this.svg.append('g').attr('class', 'view'); this.currentTransform = null; this.cubeResolution = 50; this._init(); this._draw_slider(); } draw(data){ this._draw_init(); // this._get_board(data); this._draw_grid(data['problem']['w'], data['problem']['h']); this._draw_solution(data); } _init(){ var _self = this; if (this.currentTransform){ this.view.attr('transform', this.currentTransform); } } _draw_init(){ this.view.selectAll("g").remove(); } _draw_grid(x_cnt, y_cnt){ let _width = x_cnt * this.cubeResolution; let _height = y_cnt * this.cubeResolution; this.view.append("g") .attr("class", "x axis") .selectAll("line") .data(d3.range(0, _width+1, this.cubeResolution)) .enter().append("line") .attr("x1", function (d) { return d; }) .attr("y1", 0) .attr("x2", function (d) { return d; }) .attr("y2", _height) .attr("stroke", "#888"); this.view.append("g") .attr("class", "y axis") .selectAll("line") .data(d3.range(0, _height+1, this.cubeResolution)) .enter().append("line") .attr("x1", 0) .attr("y1", function (d) { return d; }) .attr("x2", _width) .attr("y2", function (d) { return d; }) .attr("stroke", "#888"); } _draw_slider(){ let _self = this; var zoom = d3.zoom() .scaleExtent([0.5, 5]) .translateExtent([ [-this.width * 2, -this.height * 2], [this.width * 2, this.height * 2] ]) .on("zoom", zoomed); function zoomed() { this.currentTransform = d3.event.transform; _self.view.attr("transform", this.currentTransform); // this.slider.property("value", d3.event.scale); } function slided(d) { zoom.scaleTo(this.svg, d3.select(this).property("value")); } // var slider = d3.select("body").append("input") // .datum({}) // .attr("type", "range") // .attr("value", 1) // .attr("min", zoom.scaleExtent()[0]) // .attr("max", zoom.scaleExtent()[1]) // .attr("step", (zoom.scaleExtent()[1] - zoom.scaleExtent()[0]) / 100) // .on("input", slided); this.svg.call(zoom).on('dblclick.zoom', null); } _draw_solution(data){ let cubeResolution = this.cubeResolution; // Draw outer box 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 // 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 // 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']; 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 { 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") .data(Object.values(blocks)) .enter() .append('g') .attr('class', 'itemContainer') .attr("transform", (d) => 'translate(' + 0 + ',' + 0 + ')') .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 % colors.length]) .attr('x', 0) .attr('y', 0); cellContainer.selectAll('g') .data((d) => d.cellpos) .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'); }); 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(('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 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]); } } } $(function(){ const board = new ADC2019BoardViewer("#board-container"); 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", data: JSON.stringify({ "problem": problem_name, "solution": solution_id }), contentType: 'application/json' }).done((d) => { board.draw(d); }).fail((d) => { console.log(d); }); });