Skip to content
Snippets Groups Projects
graph2D.component.ts 27.8 KiB
Newer Older
import { Component } from '@angular/core';
import { GHCIService } from '../../../shared/services/ghci.service';
import functionPlot from 'function-plot';
import { Animation, toJSON, triggerDownload } from './graph2D.helper';
@Component({
    moduleId: module.id,
    selector: 'graph2D-component',
    templateUrl: './graph2D.component.html',
export class Graph2DComponent {
    private ghciServiceSub: any;

    private instance: null;

    private funciones= [];

    private id = 0;

    private conjunto= [];

        data: [],
        timer: null,
        currentFrame: 0,
        speed: 1000,
        playing: false,
        init: false,
        boton: true,
        zoo: 2000
    public setZoom = () => {
        this.animation.zoo = this.animation.zoo ;
    }
    public multiGraf = () => { 
        this.animation.boton = !this.animation.boton;
    }
    public constructor(private ghciService: GHCIService) {
        this.ghciServiceSub = ghciService.messages.subscribe(
            canvas => {
                // Stop Animation
                if (this.animation.init) {
                    this.stopAnimation();
                }
                switch(canvas.tipo) { 
                    case 'graph': {
                        var jsonCanvas = JSON.parse(canvas.resultado);
                        let fun = eval(this.generarFuncion(jsonCanvas));
                        var conjs = this.obtenerConjunto(jsonCanvas.funs[0]);
    
                        var d = conjs + "}"; //Leo
    
                        var obj = JSON.parse(d);
                        //Para las funciones 
                        if (obj.conj.sets.fdom == "function(x)") {
                            var nom = jsonCanvas.funs[0].dom;
                            var elemento1 = this.recursionfuncion(jsonCanvas.funs[0].sets, nom);
                            obj.conj.sets.fdom = function (x) { return eval(elemento1) }
                        if (obj.conj.sets.fcod == "function(x)") {
                            var nom = jsonCanvas.funs[0].cod;
                            var elemento2 = this.recursionfuncion(jsonCanvas.funs[0].sets, nom);
                            obj.conj.sets.fcod = function (x) { return (eval(elemento2)) }
                        //para Enumerados
                        if (obj.conj.dom == 'Numer') {
                            var cantElementos = obj.conj.sets.fdom.length;
                            var j = 0;
                            for (var fun of obj.conj.sets.fdom) {
                                //var newstr = nuevo2.replace(fun, j); 
                                j = j + 1;
                            }
    
                        }
                        if (obj.conj.cod == 'Numer') {
                            var cantElementos = obj.conj.sets.fcod.length;
                            var j = 0;
                            for (var fun of obj.conj.sets.fcod) {
                                //var newstr = nuevo2.replace(fun, j); 
                                j = j + 1;
                            }
    
                        }
    
                        var colores = ['pink', 'red', 'blue', 'orange', 'green']
                        var num = this.getRandomArbitrary(0, 4);
                        var color = colores[num];
    
                        var tipoGraf;
                        if (obj.conj.baseDom != 'R'){
                            tipoGraf = 'scatter';
                        }else{
                            tipoGraf = 'polyline';
                        }

                        if(this.animation.boton && obj.conj.cod != 'Numer' && obj.conj.dom != 'Numer'){
                            if(this.conjunto.length == 1){
                                this.id = 1;
                                this.conjunto.unshift({radio: 2, dom:this.conjunto[0].baseDom, cod:this.conjunto[0].baseCod, baseCod:this.conjunto[0].baseCod, baseDom:this.conjunto[0].baseDom, sets:{fdom:this.conjunto[0].baseDom,fcod:this.conjunto[0].baseCod}});
                                this.funciones[0].id = this.id;
                                
                            }
                            if (this.conjunto.length != 0){ 
                                if (obj.conj.baseDom == 'R'){
                                    this.conjunto[0].baseDom = 'R';
                                    this.conjunto[0].dom = 'R';
                                    this.conjunto[0].sets.fdom = 'R';
                                }
                                if (obj.conj.baseCod == 'R'){
                                    this.conjunto[0].baseCod = 'R';
                                    this.conjunto[0].cod = 'R';
                                    this.conjunto[0].sets.fcod = 'R';
                                }
                            }    
                            this.conjunto.push(obj.conj);

                            this.funciones.push({
                                id: this.funciones.length + this.id,
                                sampler: 'builtIn',
                                fn: function(scope) {
                                  return fun(scope.x)
                                },
                                graphType: tipoGraf,
                                color: color
                            });

                        }else{ 
                            this.conjunto = [];
                            this.conjunto.push(obj.conj);
                            this.id = 0;
                            this.funciones = [];
                            this.funciones.push({
                                id: this.funciones.length,
                                sampler: 'builtIn',
                                fn: function(scope) {
                                  return fun(scope.x)
                                },
                                graphType: tipoGraf,
                                color: color
                            });
                        }
                        this.instance = functionPlot({
                            target: '#graph2D-container',
                            width: 620,
                            height: 450,

                            tip: { color: 'green' },
                            xAxis: { label: 'x - axis',
                                scale: 'linear',
                                domain: { initial: [-4, 4],
                                        type: 'discrete' },
                                yAxis: { domain: [-4, 4] }
                            },
                            conj:this.conjunto,
                            data: this.funciones,
                            zoom:this.animation.zoo,
                            plugins: [
                                functionPlot.plugins.zoomBox()
                            ]
                        })
                        break; 
                    case 'canvas': {
                        var shapesData = JSON.parse(canvas.resultado);
                        var shapesDataNormalized = this.normalizeShapesData(shapesData);
                        this.instance = functionPlot({
                            target: '#graph2D-container',
                            width: 800,
                            height: 700,
                            xAxis: {
                                label: 'x - axis',
                                scale: 'linear',
                                domain: {
                                    initial: [-10, 10],
                                    type: 'discrete'
                                }
                            },
                            data: shapesDataNormalized,
                            plugins: [
                                functionPlot.plugins.zoomBox()
                            ]
                        })
                        break; 
                        var animationData = canvas.resultado.map(res => JSON.parse(res));
                        for (var frame of animationData) {
                            this.animation.data.push(this.normalizeShapesData(frame));
                        this.runAnimation();
                        this.animation.init = true;
            },
            error => {
    /**
     * Angular lifecycle hook.
     * called after Angular has fully initialized a component's view.
     */
    ngAfterViewInit() {
        if (!this.instance) {
            this.instance = functionPlot({
                target: '#graph2D-container',
                width: 800,
                height: 700,
                xAxis: {
                    label: 'x - axis',
                    scale: 'linear',
                    domain: {
                        initial: [-10, 10],
                        type: 'discrete'
                    }
                },
            })
        }
    }

    /**
     * Angular lifecycle hook.
     * called when a directive, pipe, or service is destroyed.
     */
    ngOnDestroy() {
        if (this.ghciServiceSub) {
          this.ghciServiceSub.unsubscribe();
        }
    }

    /**
     * @name updateFrame
     * @desc update data for Function Plot and redraw the graph
     */
    public updateFrame = function(d) {
        if (this.instance) {
            this.instance.options.data = d;
            this.instance.draw();
        } else {
            this.instance = functionPlot({
                target: '#graph2D-container',
                width: 800,
                height: 700,
                xAxis: {
                    label: 'x - axis',
                    scale: 'linear',
                    domain: {
                        initial: [-10, 10],
                        type: 'discrete'
                    }
                },
                data: d,
                plugins: [
                    functionPlot.plugins.zoomBox()
                ]
        // Update Frame
        this.animation.currentFrame = (this.animation.currentFrame + 1) % this.animation.data.length;
    }
    /**
     * @name runAnimation
     * @desc Run Shapes Animation
     */
    public runAnimation = function() {
        var $this = this;
        if ($this.animation.timer !== null) return;
        $this.updateFrame($this.animation.data[$this.animation.currentFrame]);
        $this.animation.timer = setInterval(function(){
            $this.updateFrame($this.animation.data[$this.animation.currentFrame]);
        }, $this.animation.speed);
        $this.animation.playing = true;
    }
    /**
     * @name pauseAnimation
     * @desc Pause Shapes Animation
     */
    public pauseAnimation = function() {
        var $this = this;
        clearInterval($this.animation.timer);
        $this.animation.timer = null;
        $this.animation.playing = false;
    /**
     * @name stopAnimation
     * @desc Stop Shapes Animation
     */
    public stopAnimation = function() {
        var $this = this;
        clearInterval($this.animation.timer);
        $this.animation.data = [];
        $this.animation.timer = null;
        $this.animation.currentFrame = 0;
        $this.animation.speed = 1000;
        $this.animation.playing = false;
Diego Rey's avatar
Diego Rey committed
        $this.animation.init = false;
        this.instance.removeAllGraphs();
    }
    /**
     * @name zoomOut
     * @desc Zoom Out Button Control
     */
    public zoomOut = function () {
        this.instance.zoomOut();
    /**
     * @name zoomIn
     * @desc Zoom In Button Control 
     */
    public zoomIn = function () {
        this.instance.zoomIn();
    }
    /**
     * @name recenterPlot
     * @desc center the plot and it returns to the initial state.
     */
    public recenterPlot = function () {
        this.instance.recenter();
    }
    /**
     * @name cleanPlot
     * @desc remove all the graph from the instance.
     */
    public cleanPlot = function () {
        if (this.animation.playing) {
            this.stopAnimation();
        } else {
            this.funciones = [];
            this.conjunto = [];
            this.id = 0;
            this.instance.removeAllGraphs();
        }
    }

    /**
     * @name exportPlot
     * @desc Download Plot as an SVG image.
     */
    public exportPlot = function() {
        // Objects
        var svg = document.querySelector('svg');
        var canvas = document.createElement("canvas");

        // Set dimensions of the image
        var svgSize = svg.getBoundingClientRect();
        canvas.width = svgSize.width;
        canvas.height = svgSize.height;

        // Convert SVG DOM structure to xml
        var ctx = canvas.getContext('2d');
        var data = new XMLSerializer().serializeToString(svg);

        // URL Object used to parse, construct, normalise, and encode URLs.
        var DOMURL = window.URL || (<any>window).webkitURL || window;

        var img = new Image();
        var svgBlob = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
        var url = DOMURL.createObjectURL(svgBlob);

        img.onload = function () {
            ctx.drawImage(img, 0, 0);
            DOMURL.revokeObjectURL(url);

            var imgURI = canvas
                .toDataURL('image/png')
                .replace('image/png', 'image/octet-stream');

            triggerDownload(imgURI);
        };
        img.src = url;
    }

    /**
     * @name normalizeRectData
     * @desc Normalize Rectangle data for Function Plot Library 
     * @param {Object} rectData Data of Rectangle to be normalized
     * @returns {Object}
     */
    public normalizeRectData = function ($rectData) {
        var $rectNormalized:any = {};
        var $shape:any = {};
        $shape.w = $rectData.w;
        $shape.h = $rectData.h;
        $shape.x = $rectData.x;
        $shape.y = $rectData.y;
        $rectData.color && ($shape.fill = $rectData.color);
        $rectData.rot !== 'undefined' && ($shape.rotation = $rectData.rot);

        $rectNormalized.shape = $shape;
        $rectNormalized.graphType = 'shape';
        $rectNormalized.shapeType = 'rect';
    
        return $rectNormalized;
    }
    /**
     * @name normalizeCircleData
     * @desc Normalize Circle data for Function Plot Library 
     * @param {Object} circleData Data of Circle to be normalized
     * @returns {Object}
     */
    public normalizeCircleData = function ($circleData) {
        var $circleNormalized:any = {};
        var $shape:any = {};
        $shape.r = $circleData.r;
        $shape.x = $circleData.x;
        $shape.y = $circleData.y;
        $circleData.color && ($shape.fill = $circleData.color);
        $circleData.rot !== 'undefined' && ($shape.rotation = $circleData.rot);

        $circleNormalized.shape = $shape;
        $circleNormalized.graphType = 'shape';
        $circleNormalized.shapeType = 'circle';
    
        return $circleNormalized;
    }
     * @name normalizeTextData
     * @desc Normalize Text data for Function Plot Library 
     * @param {Object} textData Data of Text to be normalized
     * @returns {Object}
     */
    public normalizeTextData = function ($textData) {
        var $textNormalized:any = {};
        var $shape:any = {};
        $shape.text = $textData.text;
        $shape.size = $textData.size;
        $shape.x = $textData.x;
        $shape.y = $textData.y;
        $textData.color && ($shape.fill = $textData.color);
        $textData.rot !== 'undefined' && ($shape.rotation = $textData.rot);

        $textNormalized.shape = $shape;
        $textNormalized.graphType = 'shape';
        $textNormalized.shapeType = 'text';
    
        return $textNormalized;
    }
    /**
     * @name normalizeLineData
     * @desc Normalize Line data for Function Plot Library 
     * @param {Object} lineData Data of Line to be normalized
     * @returns {Object}
     */
    // public normalizeLineData = function ($lineData) {
    //     var $lineNormalized:any = {};
    //     var $points = []
    //     for (var p of $lineData.pts) {
    //         $points.push([p[0],p[1]]);
    //     }
    //     $lineNormalized.points = $points;
    //     $lineNormalized.color = $lineData.color;
    //     $lineNormalized.rotation = $lineData.rot;
    //     $lineNormalized.fnType = 'points';
    //     $lineNormalized.polylineType = 'line';
    //     $lineNormalized.graphType = 'polyline';
    
    //     return $lineNormalized;
    // }
    /**
     * @name normalizePolygonData
     * @desc Normalize Polygon data for Function Plot Library 
     * @param {Object} textData Data of Polygon to be normalized
     * @returns {Object}
     */
    // public normalizePolygonData = function ($textData) {
    //     var $PoligonNormalized:any = {};
    //     var $shape:any = {};
    //     $shape.text = $textData.text;
    //     $shape.size = $textData.size;
    //     $shape.x = $textData.x;
    //     $shape.y = $textData.y;
    //     $textData.color && ($shape.fill = $textData.color);
    //     $textData.rot !== 'undefined' && ($shape.rotation = $textData.rot);

    //     $textNormalized.shape = $shape;
    //     $textNormalized.graphType = 'shape';
    //     $textNormalized.shapeType = 'text';
    
    //     return $textNormalized;
    // }

    /**
     * @name normalizeShapesData
     * @desc Normalize Shapes data for Function Plot Library 
     * @param {Array} shapesData Data of Shapes to be normalized 
     * @returns {Array}
     */
    public normalizeShapesData = function (shapesData) {
        var normalized:Array<Object> = [];
        for (var shape of shapesData) {
            switch(shape.kind) { 
                case 'rect': { 
                    normalized.push(this.normalizeRectData(shape)); 
                    break; 
                } 
                case 'circle': { 
                    normalized.push(this.normalizeCircleData(shape)); 
                    break; 
                } 
                case 'text': { 
                    normalized.push(this.normalizeTextData(shape)); 
                    break; 
                }
                // case 'line': { 
                //     normalized.push(this.normalizeLineData(shape)); 
                //     break; 
                // }
                // case 'polygon': { 
                //     normalized.push(this.normalizePolygonData(shape)); 
                //     break; 
                // } 
    getRandomArbitrary = function (min, max) {
        return Math.round(Math.random() * (max - min) + min);
    }

    generarFuncion = function (graph) {
        var funcionString = '';
        var grafica;
        for (var fun of graph.funs) {
            funcionString = 'var ' + fun.fun + ' = function(' + fun.args.join() + '){\n return ' + this.generarExpresion(fun.bdy) + '}\n' + funcionString;

            if (fun.fun == graph.graph) {
                funcionString += 'return ' + fun.fun + '(' + fun.args.join() + ');\n'
                grafica = fun;
        funcionString = '(' + grafica.args.join() + ')=>{\n' + funcionString + '}';

        return funcionString;
    }
    generarExpresion = function (exp) {
        var expresion = '';
        if (exp.kind == 'cnd') {
            expresion = ' (' + this.generarExpresion(exp.cond) + '?' + this.generarExpresion(exp.exp1) + ':' + this.generarExpresion(exp.exp2) + ') ';
        } else if (exp.kind == 'bop') {
            if (exp.op == '==') {
                expresion = ' Math.abs((' + this.generarExpresion(exp.exp1) + ') - (' + this.generarExpresion(exp.exp2) + ')) == 0 ';
            } else if (exp.op == '/=') {
                expresion = ' Math.abs((' + this.generarExpresion(exp.exp1) + ') - (' + this.generarExpresion(exp.exp2) + ')) == 0 ||  Math.abs((' + this.generarExpresion(exp.exp1) + ') - (' + this.generarExpresion(exp.exp2) + ')) == 0  ';
            } else if (exp.op == '^') {
                expresion = ' Math.pow(' + this.generarExpresion(exp.exp1) + ',' + this.generarExpresion(exp.exp2) + ') ';
            } else {
                expresion = ' (' + this.generarExpresion(exp.exp1) + ')' + exp.op + '(' + this.generarExpresion(exp.exp2) + ') ';
            }
        } else if (exp.kind == 'uop') {
            expresion = ' ' + exp.op + ' ' + this.generarExpresion(exp.exp) + ' ';
        } else if (exp.kind == 'app') {
			
			if (exp.fun == 'cos') {
				exp.fun = 'Math.cos'
			} else if (exp.fun == 'sin') {
				exp.fun = 'Math.sin'
			} else if (exp.fun == 'round') {
				exp.fun = 'Math.round'
			}
			expresion = ' ' + exp.fun + '(' + exp.args.map(e => this.generarExpresion(e)).join() + ') ';

        } else if (exp.kind == 'tup') {
            expresion = ' (' + exp.exps.map(e => this.generarExpresion(e)).join() + ') ';
        } else if (exp.kind == 'lit') {
            expresion = ' ' + exp.val + ' ';
        } else if (exp.kind == 'var') {
            expresion = ' ' + exp.var + ' ';
            expresion = ' undefined ';
    //Nuevo 20-07-2018
    obtenerConjunto = function (grf) {
        var dominio = '{\"conj\": {';
        if (grf.dom == 'R') {
            dominio += "\"radio\": 0.3, \"baseDom\": \"R\", \"dom\": \"R\"";
            setf += "\"fdom\": \"R\",";
        } else if (grf.dom == 'Z') {
            dominio += "\"radio\": 2, \"baseDom\": \"Z\", \"dom\": \"Z\"";
            setf += "\"fdom\": \"Z\",";
        }/* else if (grf.dom == 'N') {
            dominio += "\"radio\":2, \"baseDom\": \"N\", \"dom\": \"N\"";
            setf += "\"fdom\": \"N\",";
            if (Array.isArray(grf.sets[0][nom])) {
                var arreglo = grf.sets[0][nom];
                var arreglo2 = [];
                for (var item of arreglo) {
                    arreglo2.push("\"" + item + "\"");
                }
                dominio += "\"radio\":2, \"baseDom\": \"N\", \"dom\": \"Numer\"";
                setf += "\"fdom\": [" + arreglo2 + "], ";
            } else {

                dominio += this.recursivoDom(grf.sets, nom);
                setf += "\"fdom\":\"function(x)\",";
        dominio += ", ";
        if (grf.cod == 'R') {
            dominio += "\"baseCod\": \"R\", \"cod\": \"R\" ,";
            setf += "\"fcod\": \"R\"";
        } else if (grf.cod == 'Z') {
            dominio += "\"baseCod\": \"Z\", \"cod\": \"Z\" ,";
            setf += "\"fcod\": \"Z\"";
        } else if (grf.cod == 'N') {
            dominio += "\"baseCod\": \"N\", \"cod\": \"N\" ,";
            setf += "\"fcod\": \"N\"";
        } else {
            if (Array.isArray(grf.sets[0][nom])) {
                var arreglo = grf.sets[0][nom];
                var arreglo2 = [];
                for (var item of arreglo) {
                    arreglo2.push("\"" + item + "\"");
                }
                dominio += "\"baseCod\": \"N\", \"cod\": \"Numer\" ,";
                setf += '\"fcod\":[' + arreglo2 + ']';
            } else {
                dominio += this.recursivoCod(grf.sets, nom);
                setf += "\"fcod\": \"function(x)\"";
    recursionfuncion = function (func, nombre) {
        var fun = func[0][nombre].set;
        var resul = "";
        if (fun == 'R' || fun == 'Z' || fun == 'N') {
            resul += this.generarF(func[0][nombre].cond);
        } else {
            resul += this.generarF(func[0][nombre].cond) + " && " + this.recursionfuncion(func, fun);

    recursivoDom = function (sets, nom) {
        if (sets[0][nom].set == 'R') {
            domin += "\"radio\": 0.3, \"baseDom\": \"R\", \"dom\": \"Func\"";
        } else if (sets[0][nom].set == 'Z') {
            domin += "\"radio\": 2, \"baseDom\": \"Z\", \"dom\": \"Func\"";
        } else if (sets[0][nom].set == 'N') {
            domin += "\"radio\": 2, \"baseDom\": \"N\", \"dom\": \"Func\"";
        } else {
            var nombre = sets[0][nom].set;
            domin = this.recursivoDom(sets, nombre);

    recursivoCod = function (sets, nom) {
        if (sets[0][nom].set == 'R') {
            coodo += "\"baseCod\": \"R\", \"cod\": \"Func\",";
        } else if (sets[0][nom].set == 'Z') {
            coodo += "\"baseCod\": \"Z\", \"cod\": \"Func\",";
        } else if (sets[0][nom].set == 'N') {
            coodo += "\"baseCod\": \"N\", \"cod\": \"Func\",";
        } else {
            var nombre = sets[0][nom].set;
            coodo += this.recursivoDom(sets, nombre);

    generarF = function (exp) {
        var expresion = '';
        if (exp.kind == 'cond') {
            expresion = ' (' + this.generarF(exp.cond) + '?' + this.generarF(exp.exp1) + ':' + this.generarF(exp.exp2) + ') ';
        } else if (exp.kind == 'bop') {
            if (exp.op == '==') {
                expresion = ' Math.abs((' + this.generarF(exp.exp1) + ') - (' + this.generarF(exp.exp2) + ')) == 0 ';
            } else if (exp.op == '/=') {
                expresion = ' Math.abs((' + this.generarF(exp.exp1) + ') - (' + this.generarF(exp.exp2) + ')) == 0 ||  Math.abs((' + this.generarF(exp.exp1) + ') - (' + this.generarF(exp.exp2) + ')) == 0  ';
            } else if (exp.op == '^') {
                expresion = ' Math.pow(' + this.generarF(exp.exp1) + ',' + this.generarF(exp.exp2) + ') ';
            } else {
                expresion = ' (' + this.generarF(exp.exp1) + ')' + exp.op + '(' + this.generarF(exp.exp2) + ') ';
            }
        } else if (exp.kind == 'uop') {
            expresion = ' ' + exp.op + ' ' + this.generarF(exp.exp) + ' ';
        } else if (exp.kind == 'app') {
            if (exp.fun == 'cos') {
                exp.fun = 'Math.cos'
                exp.fun = 'Math.sin'
            } else if (exp.fun == 'round') {
                exp.fun = 'Math.round'
            }
            expresion = ' ' + exp.fun + '(' + exp.args.map(e => this.generarF(e)).join() + ') ';
        } else if (exp.kind == 'tup') {
            expresion = ' (' + exp.exps.map(e => this.generarF(e)).join() + ') ';
        } else if (exp.kind == 'lit') {
            expresion = ' ' + exp.val + ' ';
        } else if (exp.kind == 'var') {
            expresion = ' ' + exp.var + ' ';
            expresion = ' undefined ';
    generarFun = function (graph) {
        var funcionString = '';
        var grafica;
        for (var fun of graph.funs) {
            funcionString = 'var ' + fun.fun + ' = function(' + fun.args.join() + '){\n return ' + this.generarF(fun.bdy) + '}\n' + funcionString;

            if (fun.fun == graph.graph) {
                funcionString += 'return ' + fun.fun + '(' + fun.args.join() + ');\n'
                grafica = fun;
            }
        }
        funcionString = '(' + grafica.args.join() + ')=>{\n' + funcionString + '}';