From 48ef9a5edd04fb92cc1362208088a00ffa384867 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:09:06 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/d3/plot.js | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 js/d3/plot.js diff --git a/js/d3/plot.js b/js/d3/plot.js new file mode 100644 index 0000000..5e9d064 --- /dev/null +++ b/js/d3/plot.js @@ -0,0 +1,245 @@ +var fs = require('fs'); +var d3 = require('./d3'); +var D3Node = require('./d3-node'); +function log(msg) { + console.log('[PLOT] '+msg); +} + + +/** + * generic options = {margin, padding} + */ + +var Plot = function(options) { + var self=this; + // auto-new instance, so we always have 'this' + if (!(this instanceof Plot)) { + return new Plot(options) + } + + this.options=options; + if (this.options.margin==undefined) this,options.margin=0; + if (this.options.padding==undefined) this,options.padding=0; + + switch (options.type) { + case 'matrix': + if (!this.options.fontsize) this.options.fontsize=12; + case 'circle': + /* Circle 2D Scatter Plot + ** Text 2D Matrix Plot + ** + ** type options = { + ** x:number of circles/entries,y:number of circles/entries, + ** size:number of circle/element size, + ** color:string of circle fill color/text color, + ** min:number of smallest data value, max: number of biggest data value + ** grid:boolean for grid drawing, + ** map: function (d,i,j) is a data set mapping functions + ** + **/ + if (!this.options.color) this.options.color='green'; + this.d3n = new D3Node({d3Module:d3}); + this.width = 2*this.options.margin+ + 2*this.options.padding+ + this.options.size*(this.options.x+2); + this.height = 2*this.options.margin+ + 2*this.options.padding+ + this.options.size*(this.options.y+2); + log('Size of diagram: width='+this.width+', height='+this.height+' px'); + this.svg = this.d3n.createSVG() + .attr("width", this.width) + .attr("height", this.height); + break; + default: + throw Error ('[PLOT] Unknown plot diagram tyoe '+options.type); + } +} + +Plot.prototype.plot = function (dataset) { + var self=this, + i,j,g,d; + + if (!this.options.min) this.options.min=0; + if (!this.options.max) { + this.options.max=0; + for (j=0; j < dataset.length; j++) + for(i=0; i < dataset[j].length; i++) { + d=dataset[j][i]; + if (this.options.map) d=this.options.map(d,i,j); + if (d> this.options.max) this.options.max= d; + } + } + + log('Data set has min='+this.options.min+', max='+this.options.max); + switch (this.options.type) { + case 'circle': + d3.select(this.d3n.document.body) + + if (this.options.grid) { + g=this.svg.append("g") + .selectAll("g") + .data(dataset) + .enter() + .append("g") //removing + .selectAll("text") // these + .data( function(d,i,j) { return d; } ) //lines + .enter() + g.append('rect') + .attr("x", function(d,i,j) { + return self.options.padding+ + self.options.margin+ + (i * self.options.size) + self.options.size/2; + }) + .attr("y", function(d,i,j) { + return self.options.padding+ + self.options.margin+ + (j * self.options.size) + self.options.size/2; + }) + .attr("width", function(d,i,j) { + return self.options.size + }) + .attr("height", function(d,i,j) { + return self.options.size + }) + .attr("stroke", function(d,i,j) { + return '#AAAAAA' + }) + .attr("fill", function(d,i,j) { + return 'white' + }); + } + + g=this.svg.append("g") + .selectAll("g") + .data(dataset) + .enter() + .append("g") //removing + .selectAll("text") // these + .data( function(d,i,j) { return d; } ) //lines + .enter() // circle displays normally + g=g.append("circle") + .attr("cx", function(d,i,j) { + return self.options.padding+ + self.options.margin+ + (i * self.options.size) + self.options.size; + }) + .attr("cy", function(d,i,j) { + return self.options.padding+ + self.options.margin+(j * self.options.size) + self.options.size; + }) + .attr("r", function(d,i,j) { + if (self.options.map) d=self.options.map(d,i,j); + return ( + self.options.size/2*( + (d-self.options.min)/ + (self.options.max-self.options.min)) + ); + }) + .attr("fill", function(d,i,j) { + return self.options.color + }); + + break; + + case 'matrix': + d3.select(this.d3n.document.body) + if (this.options.grid) { + g=this.svg.append("g") + .selectAll("g") + .data(dataset) + .enter() + .append("g") //removing + .selectAll("text") // these + .data( function(d,i,j) { return d; } ) //lines + .enter(); + g.append('rect') + .attr("x", function(d,i,j) { + return self.options.padding+ + self.options.margin+ + (i * self.options.size) + self.options.size/2; + }) + .attr("y", function(d,i,j) { + return self.options.padding+ + self.options.margin+ + (j * self.options.size) + self.options.size/2; + }) + .attr("width", function(d,i,j) { + return self.options.size + }) + .attr("height", function(d,i,j) { + return self.options.size + }) + .attr("stroke", function(d,i,j) { + return '#AAAAAA' + }) + .attr("fill", function(d,i,j) { + return 'white' + }); + } + + + for(var index=0;index<(self.options.cell?self.options.cell.rows:1);index++) { + g=this.svg.append("g") + .selectAll("g") + .data(dataset) + .enter() + .append("g") //removing + .selectAll("text") // these + .data( function(d,i,j) { return d; } ) //lines + .enter(); // circle displays normal + + g=g.append("text") + .attr("x", function(d,i,j) { + if (self.options.map) d=self.options.map(d,i,j); + if (self.options.cell && self.options.cell.rows) d=d[index]; + return (self.options.padding+ + self.options.margin- + (d.length*self.options.fontsize/3)+ + (i * self.options.size) + self.options.size); + }) + .attr("y", function(d,i,j) { + var off; + if (self.options.cell && self.options.cell.rows) { + switch (index) { + case 0: off=-self.options.fontsize; break; + case 1: off=self.options.fontsize; break; + default: off=0; + } + } else + off=self.options.fontsize/3; + + return (self.options.padding+ + off+ + self.options.margin+(j * self.options.size) + self.options.size); + }) + .attr("font-size", self.options.fontsize+'px') + .attr("color", function(d,i,j) { + return self.options.color + }) + .attr("fill", function(d,i,j) { + return self.options.color + }) + .text(function (d,i,j) { + var lines=''; + if (self.options.map) d=self.options.map(d,i,j); + if (self.options.cell && self.options.cell.rows) d=d[index]; + return d + }); + } + break; + } +} + +Plot.prototype.output = function (outputName) { + fs.writeFile(outputName+'.html', this.d3n.html(), function () { + log(outputName+'.html created.'); + }); + var svgBuffer = new Buffer(this.d3n.svgString(), 'utf-8'); + fs.writeFile(outputName+'.svg', svgBuffer, function () { + log(outputName+'.svg created'); + }) +}; + +module.exports = { + Plot:Plot +}