Commit e2397ae4 authored by Kento HASEGAWA's avatar Kento HASEGAWA
Browse files

Add 3D viewer to show the solution

parent 5e4c621e
Loading
Loading
Loading
Loading
+112 −3
Changes for comm/server/main.py: 112 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ def load_questions():
            "board_size": board_size,
            "line_num": line_num,
            "last_req": None,
            "answers": [],
            "answers": OrderedDict(),
            }

        update_answer_list(_name)
@@ -87,7 +87,7 @@ def update_answer_list(qname):
    global app_args
    global questions

    questions[qname]['answers'] = []
    questions[qname]['answers'] = OrderedDict()

    _folder_name = os.path.splitext(qname)[0]
    _answers_path = "{}/{}".format(app_args['out'], _folder_name)
@@ -97,9 +97,10 @@ def update_answer_list(qname):
        answer_log_file.reverse()

        for v2 in answer_log_file:
            json_name = os.path.basename(v2)
            with open(v2, "r") as fp:
                answer_log = json.load(fp)
            questions[qname]['answers'].append(answer_log)
            questions[qname]['answers'][json_name] = answer_log

@app.before_request
def before_request():
@@ -282,6 +283,109 @@ def get_status():

    return json.dumps(res)

@app.route("/board-data", methods=["POST"])
def get_board_data():

    global questions

    qname = request.form['qname']
    json_name = request.form['jname']

    lines = questions[qname]['answers'][json_name]['answer'].split("\n")

    # board_size = [int(v) for v in lines[1].rstrip().split()[1].split("X")]
    board_size = [int(v) for v in questions[qname]['board_size'].split("X")]

    raw_data = [[[0 for x in range(board_size[0])] for y in range(board_size[1])] for z in range(board_size[2])]
    data = []

    max_line_num = 0

    for l in lines[1:]:
        if l.rstrip() == "":
            continue
        elif l.startswith("LAYER"):
            z = int(l.rstrip().split()[1])-1
            y = 0
            continue
        else:
            ldat = l.rstrip().split(",")
            for x, v in enumerate(ldat):
                raw_data[z][y][x] = int(v)
                max_line_num = max(max_line_num, int(v))
            y += 1

    terminals = [[] for v in range(max_line_num+1)]

    for z in range(board_size[2]):
        for y in range(board_size[1]):
            for x in range(board_size[0]):
                line_num = raw_data[z][y][x]

                if line_num == 0:
                    continue

                adj_cnt = 0
                if((0 <= x-1 < board_size[0]) and (raw_data[z][y][x-1] == line_num)):
                    adj_cnt += 1
                if((0 <= x+1 < board_size[0]) and (raw_data[z][y][x+1] == line_num)):
                    adj_cnt += 1
                if((0 <= y-1 < board_size[1]) and (raw_data[z][y-1][x] == line_num)):
                    adj_cnt += 1
                if((0 <= y+1 < board_size[1]) and (raw_data[z][y+1][x] == line_num)):
                    adj_cnt += 1
                if((0 <= z-1 < board_size[2]) and (raw_data[z-1][y][x] == line_num)):
                    adj_cnt += 1
                if((0 <= z+1 < board_size[2]) and (raw_data[z+1][y][x] == line_num)):
                    adj_cnt += 1

                if adj_cnt == 1:
                    terminals[line_num].append((x, y, z))

    lines = {}

    for i in range(1, max_line_num+1):
        if len(terminals[i]) != 2:
            continue
        start = terminals[i][0]
        goal = terminals[i][1]

        lines[i] = []
        lines[i].append(start)
        lines[i].append(start)

        now = start
        _next = (0, 0, 0)
        while now != goal:
            x, y, z = now

            adj_cnt = 0
            if((0 <= x-1 < board_size[0]) and (raw_data[z][y][x-1] == i) and (lines[i][-2] != (x-1, y, z))):
                _next = (x-1, y, z)
            elif((0 <= x+1 < board_size[0]) and (raw_data[z][y][x+1] == i) and (lines[i][-2] != (x+1, y, z))):
                _next = (x+1, y, z)
            elif((0 <= y-1 < board_size[1]) and (raw_data[z][y-1][x] == i) and (lines[i][-2] != (x, y-1, z))):
                _next = (x, y-1, z)
            elif((0 <= y+1 < board_size[1]) and (raw_data[z][y+1][x] == i) and (lines[i][-2] != (x, y+1, z))):
                _next = (x, y+1, z)
            elif((0 <= z-1 < board_size[2]) and (raw_data[z-1][y][x] == i) and (lines[i][-2] != (x, y, z-1))):
                _next = (x, y, z-1)
            elif((0 <= z+1 < board_size[2]) and (raw_data[z+1][y][x] == i) and (lines[i][-2] != (x, y, z+1))):
                _next = (x, y, z+1)
            else:
                print("Error: Not found an adjacent node")
                _next = goal

            lines[i].append(_next)
            now = _next

    res = {
        'line': lines,
        'board': board_size,
    }

    return json.dumps(res)

@app.route("/get_clients")
def get_clients():
    global clients
@@ -294,6 +398,11 @@ def get_clients():

    return json.dumps(res)

@app.route("/board-viewer", methods=["GET"])
def show_board_viewer():

    return render_template("board-viewer.html")

@app.route("/get_question_table")
def question_table():

+1051 −0

File added.

Preview size limit exceeded, changes collapsed.

+7 −0
Changes for comm/server/static/js/pynq-manager.js: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -166,6 +166,13 @@ $(function(){
            $("#client-control-pane").find(".stop-button").eq(0).click(function(){
                pm.sendStop();
            });

            $(".answer-detail-row td").click(function(){
                var json_name = $(this).parent("tr").data("json");
                var qname = $(this).parent("tr").data("qname");
                var viewer_url = "/board-viewer#" + qname + "," + json_name
                window.open(viewer_url, null);
            })
        });
    }

+949 −0

File added.

Preview size limit exceeded, changes collapsed.

+292 −0
Changes for comm/server/templates/board-viewer.html: 292 added lines, 0 removed lines.
Original line number Diff line number Diff line
<!DOCTYPE html>
<html>
<head>
    <title>PYNQ Router Control Panel for ADC2018</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/css/pynq-manager.css">
    <script src="/static/js/three.min.js"></script>
    <script src="/static/js/OrbitControls.js"></script>
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.bundle.min.js"></script>
    <!-- <script src="/static/js/pynq-manager.js"></script> -->
<script>
$(function(){
   
    // サイズを指定
    const _width = $("#view_area_wrapper").width();
    const _height = $(window).height();

    const colors = [0x0000FF, 0x00FF00, 0xFF0000, 0x00FFFF, 0xFF00FF, 0xFFFF00];
    const _linewidth = 1;
    const _linespace = 3;
    const _linemargin = 1;

    // レンダラーを作成
    const renderer = new THREE.WebGLRenderer({
        canvas: document.querySelector('#viewer_area')
    });
    renderer.setClearColor(new THREE.Color(0xEEEEEE));
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(_width, _height);

    // シーンを作成
    const scene = new THREE.Scene();

    // カメラを作成
    // const camera = new THREE.PerspectiveCamera(45, width / height);
    const camera = new THREE.PerspectiveCamera(45, _width / _height);

    const controls = new THREE.OrbitControls(camera, document.querySelector('#viewer_area'));

    var draw_answer = function (qname, jname) {

        $.ajax({
            url: "./board-data",
            type: "POST",
            data: {
                qname: qname,
                jname: jname
            },
            dataType: "json"
        }).done((data) => {

            scene.remove.apply(scene, scene.children);

            var board_x = data['board'][0];
            var board_y = data['board'][1];
            var board_z = data['board'][2];

            camera.position.set(_linespace * board_y / 2, _linespace * board_x / 2, +500);
            controls.target.set(_linespace * board_y / 2, _linespace * board_x / 2, _linespace * board_z / 2);

            for (var i = 0; i <= board_x; i++) {
                var geometry = new THREE.Geometry();
                geometry.vertices.push(new THREE.Vector3(0, _linespace * i, _linespace * board_z));
                geometry.vertices.push(new THREE.Vector3(0, _linespace * i, 0));
                geometry.vertices.push(new THREE.Vector3(_linespace * board_y, _linespace * i, 0));
                var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0x888888, transparent: true, opacity: 0.5 }));
                scene.add(line);
            }

            for (var i = 0; i <= board_y; i++) {
                var geometry = new THREE.Geometry();
                geometry.vertices.push(new THREE.Vector3(_linespace * i, 0, _linespace * board_z));
                geometry.vertices.push(new THREE.Vector3(_linespace * i, 0, 0));
                geometry.vertices.push(new THREE.Vector3(_linespace * i, _linespace * board_x, 0));
                var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0x888888, transparent: true, opacity: 0.5 }));
                scene.add(line);
            }

            for (var i = 0; i <= board_z; i++) {
                var geometry = new THREE.Geometry();
                geometry.vertices.push(new THREE.Vector3(0, _linespace * board_x, _linespace * i));
                geometry.vertices.push(new THREE.Vector3(0, 0, _linespace * i));
                geometry.vertices.push(new THREE.Vector3(_linespace * board_y, 0, _linespace * i));
                var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0x888888, transparent: true, opacity: 0.5 }));
                scene.add(line);
            }

            var line_data = data["line"];
            var max_line_length = 0;

            for (var i = 0; i < Object.keys(line_data).length; i++) {

                var line_num = Number(Object.keys(line_data)[i]);

                if (line_num == 0) continue;

                max_line_length = Math.max(max_line_length, line_data[line_num].length);

                const _color = colors[line_num % colors.length];

                var _material = new THREE.LineBasicMaterial({ linewidth: _linewidth, color: _color, transparent: true, opacity: 0.5 });
                var _geometry = new THREE.Geometry();

                var _box_material = new THREE.MeshBasicMaterial({ color: _color, transparent: true, opacity: 0.5 });
                var _box_geometry = new THREE.BoxGeometry(_linewidth, _linewidth, _linewidth);

                for (var j = 1; j < line_data[line_num].length; j++) {

                    var _x = line_data[line_num][j][0];
                    var _y = line_data[line_num][j][1];
                    var _z = line_data[line_num][j][2];

                    _geometry.vertices.push(new THREE.Vector3(_linespace * _y + _linemargin, _linespace * _x + _linemargin, _linespace * _z + _linemargin));

                    var _cube = new THREE.Mesh(_box_geometry, _box_material);
                    _cube.position.x = _linespace * _y + _linemargin + _linewidth / 2
                    _cube.position.y = _linespace * _x + _linemargin + _linewidth / 2
                    _cube.position.z = _linespace * _z + _linemargin + _linewidth / 2
                    scene.add(_cube);

                    if (j >= 2) {
                        var _prev_x = line_data[line_num][j - 1][0];
                        var _prev_y = line_data[line_num][j - 1][1];
                        var _prev_z = line_data[line_num][j - 1][2];

                        var _padbox_material = new THREE.MeshBasicMaterial({ color: _color, transparent: true, opacity: 0.5 });

                        if (_x - _prev_x == -1) {
                            var _padbox_geometry = new THREE.BoxGeometry(_linewidth, 2 * _linemargin, _linewidth);
                            var _pad_cube = new THREE.Mesh(_padbox_geometry, _padbox_material);
                            _pad_cube.position.x = _linespace * _y + _linemargin + _linewidth / 2
                            _pad_cube.position.y = _linespace * (_x + 1) - _linemargin / 2 + _linewidth / 2
                            _pad_cube.position.z = _linespace * _z + _linemargin + _linewidth / 2
                            scene.add(_pad_cube);
                        }
                        if (_x - _prev_x == 1) {
                            var _padbox_geometry = new THREE.BoxGeometry(_linewidth, 2 * _linemargin, _linewidth);
                            var _pad_cube = new THREE.Mesh(_padbox_geometry, _padbox_material);
                            _pad_cube.position.x = _linespace * _y + _linemargin + _linewidth / 2
                            _pad_cube.position.y = _linespace * _x - _linemargin / 2 + _linewidth / 2
                            _pad_cube.position.z = _linespace * _z + _linemargin + _linewidth / 2
                            scene.add(_pad_cube);
                        }
                        if (_y - _prev_y == -1) {
                            var _padbox_geometry = new THREE.BoxGeometry(2 * _linemargin, _linewidth, _linewidth);
                            var _pad_cube = new THREE.Mesh(_padbox_geometry, _padbox_material);
                            _pad_cube.position.x = _linespace * (_y + 1) - _linemargin / 2 + _linewidth / 2
                            _pad_cube.position.y = _linespace * _x + _linemargin + _linewidth / 2
                            _pad_cube.position.z = _linespace * _z + _linemargin + _linewidth / 2
                            scene.add(_pad_cube);
                        }
                        if (_y - _prev_y == 1) {
                            var _padbox_geometry = new THREE.BoxGeometry(2 * _linemargin, _linewidth, _linewidth);
                            var _pad_cube = new THREE.Mesh(_padbox_geometry, _padbox_material);
                            _pad_cube.position.x = _linespace * _y - _linemargin / 2 + _linewidth / 2
                            _pad_cube.position.y = _linespace * _x + _linemargin + _linewidth / 2
                            _pad_cube.position.z = _linespace * _z + _linemargin + _linewidth / 2
                            scene.add(_pad_cube);
                        }
                        if (_z - _prev_z == -1) {
                            var _padbox_geometry = new THREE.BoxGeometry(_linewidth, _linewidth, 2 * _linemargin);
                            var _pad_cube = new THREE.Mesh(_padbox_geometry, _padbox_material);
                            _pad_cube.position.x = _linespace * _y + _linemargin + _linewidth / 2
                            _pad_cube.position.y = _linespace * _x + _linemargin + _linewidth / 2
                            _pad_cube.position.z = _linespace * (_z + 1) - _linemargin / 2 + _linewidth / 2
                            scene.add(_pad_cube);
                        }
                        if (_z - _prev_z == 1) {
                            var _padbox_geometry = new THREE.BoxGeometry(_linewidth, _linewidth, 2 * _linemargin);
                            var _pad_cube = new THREE.Mesh(_padbox_geometry, _padbox_material);
                            _pad_cube.position.x = _linespace * _y + _linemargin + _linewidth / 2
                            _pad_cube.position.y = _linespace * _x + _linemargin + _linewidth / 2
                            _pad_cube.position.z = _linespace * _z - _linemargin / 2 + _linewidth / 2
                            scene.add(_pad_cube);
                        }
                    }

                }

                scene.add(new THREE.Line(_geometry, _material));

            }

            var board_info = "" + board_x + " x " + board_y + " x " + board_z + " ";
            $("#board_info_size").text(board_info);
            $("#board_info_linenum").text("" + Object.keys(line_data).length);
            $("#board_info_maxlength").text("" + max_line_length);

            $("#viewer_status").text("Complete.");

        });

    }
    
    $(window).on('hashchange', function () {
        var hash = location.hash.replace("#", "");
        if (hash == "") {
            $("#viewer_status").text("Please input the board name in the hash of URL.");
        } else {
            $("#viewer_status").text("Drawing...");
            $("#board_info_size").text("...");
            $("#board_info_linenum").text("...");
            $("#board_info_maxlength").text("...");
            controls.reset();
            var _data = hash.split(",")
            $("#board_info_qname").text("" + _data[0]);
            draw_answer(_data[0], _data[1]);
        }
    }).trigger('hashchange');

    $(window).on('resize', function () {
        var width = $("#view_area_wrapper").width();
        var height = $(window).height();
        renderer.setSize(width, height);
        camera.aspect = width / height;
    });

    tick();

    // 毎フレーム時に実行されるループイベントです
    function tick() {
        // レンダリング
        renderer.render(scene, camera);
        requestAnimationFrame(tick);
    }

});
</script>
<style>
html,
body {
    height: 100%;
}

body {
    overflow: hidden;
    padding: 0;
}

#wrapper {
    width: 100%;
    height: 100vh;
}

#view_board_info {
    position: fixed;
    top: 0;
    left: 0;
    overflow-y: auto;
    padding: 5px;
    background-color: rgba(255, 255, 255, 0.5);
    line-height: 1.2em;
}

#view_area_wrapper {
    width: 100%;
    height: 100vh;
}
</style>
</head>
<body>

<div id="wrapper">

    <div id="view_board_info">
        <dt>Status</dt>
        <dd><span id="viewer_status">Ready to start.</span></dd>
        
        <dt>Question name</dt>
        <dd><span id="board_info_qname"></span></dd>

        <dt>Board size</dt>
        <dd><span id="board_info_size"></span></dd>

        <dt># of lines</dt>
        <dd><span id="board_info_linenum"></span></dd>

        <dt>Max line length</dt>
        <dd><span id="board_info_maxlength"></span></dd>
    </div>

    <div id="view_area_wrapper">
        <canvas id="viewer_area"></canvas>
    </div>
</div>

</body>
</html>
Loading