Select Git revision
graph2D.component.ts
graph2D.component.ts 24.83 KiB
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',
host: {
}
})
export class Graph2DComponent {
private ghciServiceSub: any;
private instance: null;
animation: Animation = {
data: [],
timer: null,
currentFrame: 0,
speed: 1000,
playing: false,
init: false
};
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 + "}";
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': {
var shapesData = JSON.parse(canvas.resultado);
var shapesDataNormalized = this.normalizeShapesData(shapesData);
this.instance = null;
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': {
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;
break;
}
}
},
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'
}
},
data: []
})
}
}
/**
* 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;
$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.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;
// }
}
}
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;
}
}