import { Component } from '@angular/core'; import { GHCIService } from '../../../shared/services/ghci.service'; import functionPlot from 'function-plot'; import { Animation } from './graph2D.helper'; @Component({ moduleId: module.id, selector: 'graph2D-component', templateUrl: './graph2D.component.html', host: { } }) export class Graph2DComponent { private ghciServiceSub: any; private instance: null; private animation: Animation = { data: [], timer: null, currentFrame: 0, speed: 1000, playing: false, init: false }; public constructor(private ghciService: GHCIService) { this.ghciServiceSub = ghciService.messages.subscribe( canvas => { 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 + "}"; var obj = JSON.parse(d); //Para las funciones if (obj.conj.sets.fdom == "function(x)") { var nom = jsonCanvas.funs[0].dom; var elemento = this.recursionfuncion(jsonCanvas.funs[0].sets, nom); obj.conj.sets.fdom = function (x) { return eval(elemento) } } if (obj.conj.sets.fcod == "function(x)") { var nom = jsonCanvas.funs[0].cod; var elemento = this.recursionfuncion(jsonCanvas.funs[0].sets, nom); obj.conj.sets.fcod = function (x) { return (eval(elemento)) } } //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'; } this.instance = functionPlot({ target: '#graph2D-container', width: 620, height: 450, conj:obj.conj, data: [{ sampler: 'builtIn', fn: function(scope) { return fun(scope.x) }, graphType: tipoGraf, color: color }], plugins: [ functionPlot.plugins.zoomBox() ] }) break; } case 'canvas': { console.log('TIPO', canvas.tipo); 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; } case 'animacion': { console.log('TIPO', canvas.tipo); if(!this.animation.init) { this.animation.init = true; var animationData = canvas.resultado.map(res => JSON.parse(res)); for (var frame of animationData) { this.animation.data.push(this.normalizeShapesData(frame)); } this.runAnimation(); } break; } } }, error => { } ) } /** * @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() ] }) } } /** * @name runAnimation * @desc Run Shapes Animation */ public runAnimation = function() { var $this = this; if ($this.animation.timer !== null) return; $this.animation.timer = setInterval(function(){ $this.updateFrame($this.animation.data[$this.animation.currentFrame]); $this.animation.currentFrame = ($this.animation.currentFrame + 1) % $this.animation.data.length; }, $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 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 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 normalizeTextData * @desc Normalize Text data for Function Plot Library * @param {Object} textData Data of Text 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 normalizeTextData * @desc Normalize Text data for Function Plot Library * @param {Object} textData Data of Text 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; // } } } return normalized; } 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() + ',delta,hayPunto)=>{\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) + ')) < delta && hayPunto() '; } else if (exp.op == '/=') { expresion = ' Math.abs((' + this.generarExpresion(exp.exp1) + ') - (' + this.generarExpresion(exp.exp2) + ')) > delta || Math.abs((' + this.generarExpresion(exp.exp1) + ') - (' + this.generarExpresion(exp.exp2) + ')) < delta && !hayPunto() '; } 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 + ' '; } else { expresion = ' undefined '; } return expresion; } //Nuevo 20-07-2018 obtenerConjunto = function (grf) { var setf = '\"sets\": {'; 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\","; } else { var nom = grf.dom; 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 { var nom = grf.cod; 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)\""; } } return dominio + setf + "}}"; } 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); } return resul; } recursivoDom = function (sets, nom) { var domin = ""; 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); } return domin; } recursivoCod = function (sets, nom) { var coodo = ""; 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); } return coodo; } 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' } 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.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 + ' '; } else { expresion = ' undefined '; } return expresion; } 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 + '}'; return funcionString; } ngOnDestroy() { if (this.ghciServiceSub) { this.ghciServiceSub.unsubscribe(); } } }