From ec017a6c7f4235da93fb32b86906bd5c9a5d7df8 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sun, 23 Feb 2020 14:03:07 -0500 Subject: [PATCH 01/10] scatter plot progress, we working some calculations still --- Caroline.vala | 269 ++++++++++++++++++++++++++++++++++++++------------ sample.vala | 5 +- 2 files changed, 209 insertions(+), 65 deletions(-) diff --git a/Caroline.vala b/Caroline.vala index 9fed6d4..78223e3 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -1,6 +1,6 @@ //============================================================+ // File name : Caroline.vala -// Last Update : 2020-2-2 +// Last Update : 2020-2-16 // // Description : This is an extension of a GTK Drawing Area. Its purpose is to make it easy for any level // of developer to use charts in their application. More in depth documentation is found in below and in the @@ -61,7 +61,10 @@ public class Caroline : Gtk.DrawingArea { public double b; } - public double[] DATA { get; set; } + public struct ScatterPoint { + public double x; + public double y; + } public int width { get; set; } public int height { get; set; } @@ -72,12 +75,13 @@ public class Caroline : Gtk.DrawingArea { public double lineThicknessPlane { get; set; } public double lineThicknessData { get; set; } public double spreadY { get; set; } + public double spreadX { get; set; } public string dataTypeY { get; set; } public string dataTypeX { get; set; } public string chartType { get; set; } - public ArrayList labelXList = new ArrayList(); + public ArrayList labelXList = new ArrayList(); public int pieChartXStart { get; set; } public int pieChartYStart { get; set; } @@ -90,6 +94,8 @@ public class Caroline : Gtk.DrawingArea { public ArrayList chartColorArray = new ArrayList(); + public ArrayList scatterArray = new ArrayList(); + construct{ //Initializing default values @@ -109,6 +115,7 @@ public class Caroline : Gtk.DrawingArea { this.width = 500; this.height = 500; + this.spreadX = 10; this.spreadY = 10; this.lineThicknessTicks = 0.5; this.lineThicknessData = 1; @@ -118,7 +125,6 @@ public class Caroline : Gtk.DrawingArea { this.gap = 0; this.min = 0; this.max = 0; - this.DATA = {1,2,3,4,5,6,7,8,10}; this.chartType = "line"; this.rectangleXOffset = 10; @@ -140,7 +146,7 @@ public class Caroline : Gtk.DrawingArea { * chartType - this can either be line, bar, or pie * generateColors - array for ChartColor structs */ - public Caroline(double[] data, string chartType, bool generateColors){ + public Caroline(double[] dataX, double[] dataY, string chartType, bool generateColors){ /*Since our widget will already be "realized" we want to use add_events, this function allows us to set the window event bit flags, which I document directly below. @@ -161,16 +167,34 @@ public class Caroline : Gtk.DrawingArea { this.width, this.height ); + var dataXTemp = dataX; + dataXTemp = this.arraySort(dataXTemp); + + for (int i = 0; i < dataX.length; i++) { + + Caroline.ScatterPoint scatterPoint = { + dataXTemp[i], + dataY[i] + }; + + this.scatterArray.add(scatterPoint); + + } + + var tick = dataXTemp[dataXTemp.length-1] / spreadX; + this.labelXList.add(0); + + for (int f = 0; f < this.spreadX; f++) { + + this.labelXList.add(tick+(tick*f)); + + } - this.DATA = data; this.chartType = chartType; if (generateColors) this.generateColors(); - for (int i = 0; i < this.DATA.length+1; i++) - this.labelXList.add(i.to_string().concat(this.dataTypeX)); - } /** @@ -217,8 +241,8 @@ public class Caroline : Gtk.DrawingArea { //Now we draw the x axis using the same methodolgy as the y axis directly above. cr.move_to( - width + this.chartPadding + (this.widthPadding / 3), - height + this.chartPadding + this.width + this.chartPadding + (this.widthPadding / 3), + this.height + this.chartPadding ); cr.line_to( this.chartPadding + (this.widthPadding / 3), @@ -234,7 +258,7 @@ public class Caroline : Gtk.DrawingArea { cr.set_line_width(this.lineThicknessTicks); //Figure out the spread of each of the y coordinates. - this.spreadFinalY = height/this.spreadY; + this.spreadFinalY = this.height/this.spreadY; /*We loop through all of the y labels and actually draw thes lines and add the actual text for each tick mark.*/ @@ -261,28 +285,32 @@ public class Caroline : Gtk.DrawingArea { /*Figure out the spread of each of the x coordinates, notice this is differnt from the y plane, we want to display each data point on the x axis here.*/ - this.spreadFinalX = width/this.DATA.length; + this.spreadFinalX = this.width/this.spreadY; /*We loop through all of the x labels and actually draw thes lines and add the actual text for each tick mark.*/ - for (int i = 0; i < this.DATA.length+1; i++){ + for (int i = 0; i < this.labelXList.size; i++){ + var test = this.labelXList.get(i) * (this.width/this.labelXList.get(this.labelXList.size-1)); + stdout.printf("x created: %f\n",this.chartPadding + this.spreadFinalX * i + (this.widthPadding / 3)); //line drawing cr.move_to( - this.chartPadding + this.spreadFinalX * i + (this.widthPadding / 3), + this.chartPadding + test + (this.widthPadding / 3), height + this.xTickStart ); + cr.line_to( - this.chartPadding + this.spreadFinalX * i + (this.widthPadding / 3), + this.chartPadding + test + (this.widthPadding / 3), height + this.xTickEnd ); //moves the current drawing area back and lists the x axis value below the x tick cr.move_to( - xTextStart + this.spreadFinalX * i + (this.widthPadding / 3), + xTextStart + test + (this.widthPadding / 3), height + this.xTextEnd ); - cr.show_text(this.labelXList.get(i)); + + cr.show_text(this.labelXList.get(i).to_string()); } @@ -299,29 +327,30 @@ public class Caroline : Gtk.DrawingArea { } - /*The next two sectors of code are for either line or bar charts. The developer can - decide which chart they want. Eventually more chart types will be added...*/ - - //If the developer picked the line chart - if (this.chartType == "line"){ - - lineChart(cr); - - //If the developer picked the bar chart - }else if(this.chartType == "bar"){ - - barChart(cr); - - //If the devloper didn't pick a valid we default to line chart - }else if(this.chartType == "pie"){ + if (this.chartType == "scatter"){ - pieChart(cr); - //If the devloper didn't pick a valid we default to line chart - }else{ - lineChart(cr); + } + /*This switch-case will execute the proper chart depending on what the + developer has choosen.*/ + switch (this.chartType) { + case "line": + lineChart(cr); + break; + case "bar": + barChart(cr); + break; + case "pie": + pieChart(cr); + break; + case "scatter": + scatterChart(cr); + break; + default: + lineChart(cr); + break; } return true; @@ -343,20 +372,20 @@ public class Caroline : Gtk.DrawingArea { private void calculations(){ /*This next sector of arithmetic is to find the max value of the data array*/ - this.max = this.DATA[0]; + this.max = this.scatterArray[0].y; //Loop and compare each value to our initial value to see if it becomes the max - for (int i = 0; i < this.DATA.length; i++) - if (this.DATA[i] > this.max) - this.max = this.DATA[i]; + for (int i = 0; i < this.scatterArray.size; i++) + if (this.scatterArray[i].y > this.max) + this.max = this.scatterArray[i].y; /*This next sector of arithmetic is to find the min value of the data array*/ this.min = 0; - //Loop and compare each value to our initial value to see if it becomes the min - for (int i = 0; i < this.DATA.length; i++) - if (this.DATA[i] < this.min) - this.min = this.DATA[i]; + //Loop and compare each scatterArray to our initial value to see if it becomes the min + for (int i = 0; i < this.scatterArray.size; i++) + if (this.scatterArray[i].y < this.min) + this.min = this.scatterArray[i].y; /*Finds the gap between each y axis label to be displayed. spreadY is set on the developers side, it is meant to tell Caroline how many y axis ticks needed.*/ @@ -399,7 +428,7 @@ public class Caroline : Gtk.DrawingArea { cr.set_source_rgba(0, 174, 174,0.8); //Getting a scaler, which will help put the line in the right spot - double scaler = ((this.DATA[0] - this.min) / (this.max - this.min)) * this.spreadY; + double scaler = ((this.scatterArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; //We want to move the pointer on the canvas to where we want the line graph to start. cr.move_to( @@ -410,10 +439,10 @@ public class Caroline : Gtk.DrawingArea { (this.height + this.chartPadding) - ((this.spreadFinalY * scaler)) ); - for (int i = 1; i < this.DATA.length; i++){ + for (int i = 1; i < this.scatterArray.size; i++){ //Recalculating the scaler to our current value - scaler = ((this.DATA[i] - this.min) / (this.max - this.min)) * this.spreadY; + scaler = ((this.scatterArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; /*line_to (https://valadoc.org/cairo/Cairo.Context.line_to.html) is a simple cario function that allows us to draw a line from the previous canvas pointer.*/ @@ -447,21 +476,27 @@ public class Caroline : Gtk.DrawingArea { private void barChart(Cairo.Context cr){ //Set the color of the line (this default color is blue) - cr.set_source_rgba(0, 174, 174,0.8); + //Getting a scaler, which will help put the line in the right spot - double scaler = ((this.DATA[0] - this.min) / (this.max - this.min)) * this.spreadY; + double scaler = ((this.scatterArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; - for (int i = 0; i < this.DATA.length; i++){ + double maxX = 0; + + for (int i = 0; i < this.scatterArray.size; i++) + if (this.scatterArray.get(i).x > maxX) + maxX = this.scatterArray.get(i).x; + + for (int i = 0; i < this.scatterArray.size; i++){ //Recalculating the scaler to our current value - scaler = ((this.DATA[i] - this.min) / (this.max - this.min)) * this.spreadY; + scaler = ((this.scatterArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; /*Rectangle takes x,y,width,height as doubles, which will position the rectangle at the pointer on the canvas*/ cr.rectangle( //We have a bit of a smaller buffer (10) since the rectangles should be centered on tick marks - (this.rectangleXOffset + this.spreadFinalX * (i + 1)) + (this.widthPadding / 3.35), + this.rectangleXOffset + this.scatterArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3), this.height+this.chartPadding, this.rectangleXOffset, /*We want to draw our height "upwards" on the 2d plane, since 0,0 on this canvas is in the @@ -469,11 +504,13 @@ public class Caroline : Gtk.DrawingArea { -(((this.spreadFinalY * scaler))) ); + //Fills the rectangle with the current color + cr.set_source_rgba(0, 174, 174, 0.2); + + cr.fill(); + } - /*Drawing operator that strokes the current path using the current settings that were - implemented eariler in this file.*/ - cr.stroke(); } @@ -494,10 +531,10 @@ public class Caroline : Gtk.DrawingArea { /*This for loop gets the total of all the data so we can scale the pie chart later on.*/ - for (int i = 0; i < this.DATA.length; i++) - total += this.DATA[i]; + for (int i = 0; i < this.scatterArray.size; i++) + total += this.scatterArray[i].x; - for (int i = 0; i < this.DATA.length; i++){ + for (int i = 0; i < this.scatterArray.size; i++){ //Uses the chart color arrya with the structs within it to set the color cr.set_source_rgb( @@ -512,13 +549,15 @@ public class Caroline : Gtk.DrawingArea { this.pieChartYStart, this.pieChartRadius, startAngle, - startAngle + (this.DATA[i] / total) * this.PIX + startAngle + (this.scatterArray[i].x / total) * this.PIX ); + stdout.printf("%f\n",startAngle + (this.scatterArray[i].x / total) * this.PIX); + /*Adds angle to startAngle to keep track of where to draw the next arc and then the code draws the straight lines to the middle of the circle, then fills the colors in, using cr.fill()*/ - startAngle += (this.DATA[i] / total) * this.PIX; + startAngle += (this.scatterArray[i].x / total) * this.PIX; cr.line_to(this.pieChartXStart, this.pieChartYStart); cr.fill(); @@ -538,7 +577,7 @@ public class Caroline : Gtk.DrawingArea { //set the color back to white for the text and write the amount next to the label cr.set_source_rgb(1, 1, 1); cr.move_to(this.width - this.pieChartLabelOffsetX, yOffset + this.pieChartLabelOffsetY); - cr.show_text(this.DATA[i].to_string()); + cr.show_text(this.scatterArray[i].x.to_string()); /*Drawing operator that strokes the current path using the current settings that were implemented eariler in this file.*/ @@ -548,6 +587,81 @@ public class Caroline : Gtk.DrawingArea { } + private void scatterChart(Cairo.Context cr){ + + //Setting thickness of the line using set_line_width which can take any double. + cr.set_line_width(this.lineThicknessData); + + //Set the color of the line (this default color is blue) + cr.set_source_rgba(0, 174, 174,0.8); + + double scaler = 0; + double maxX = 0; + double finalsf = 0; + double xAxisCalculation = 0; + + for (int i = 0; i < this.scatterArray.size; i++) + if (this.scatterArray.get(i).x > maxX) + maxX = this.scatterArray.get(i).x; + + for (int i = 0; i < this.scatterArray.size; i++){ + + scaler = ((this.scatterArray.get(i).y - this.min) / (this.max - this.min)) * this.spreadY; + xAxisCalculation = this.scatterArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3); + + cr.arc( + xAxisCalculation, + ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))), + 3, + 0, + 6.28 + ); + + cr.fill(); + + //make util function and optimize! + var yCount = 0; + int y = (int)this.scatterArray.get(i).y; + + while(y > 0){ + + y = y / 10; + yCount++; + + } + + var xCount = 0; + int x = (int)this.scatterArray.get(i).x; + + while(x > 0){ + + x = x / 10; + xCount++; + + } + + double spacingY = 3.5 * yCount; + double spacingX = 3.5 * xCount; + + stdout.printf("Y Count %f,%f\n",spacingY,spacingX); + + cr.move_to( + xAxisCalculation - (spacingY + spacingX), + (((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))))-5 + ); + cr.show_text( + this.scatterArray.get(i).y.to_string().concat( + ",",this.scatterArray.get(i).x.to_string()) + ); + + } + + /*Drawing operator that strokes the current path using the current settings that were + implemented eariler in this file.*/ + cr.stroke(); + + } + /** * Generates random colors for any of the charts * @@ -559,7 +673,7 @@ public class Caroline : Gtk.DrawingArea { */ private void generateColors(){ - for (int i = 0; i < this.DATA.length; i++){ + for (int i = 0; i < this.scatterArray.size; i++){ //Create color struct ChartColor chartColor = { @@ -574,4 +688,33 @@ public class Caroline : Gtk.DrawingArea { } + public double[] arraySort(double[] array){ + + bool swapped = true; + int j = 0; + double tmp; + + while (swapped) { + + swapped = false; + j++; + + for (int i = 0; i < array.length - j; i++) { + + if (array[i] > array[i + 1]) { + tmp = array[i]; + array[i] = array[i + 1]; + array[i + 1] = tmp; + swapped = true; + } + + } + + } + + return array; + + } + + } diff --git a/sample.vala b/sample.vala index 52103c4..3c81bf6 100644 --- a/sample.vala +++ b/sample.vala @@ -18,8 +18,9 @@ public void main (string[] args) { //Simply set Caroline to a variable var widget = new Caroline ( - {59,78,43,42,71,41,12}, //data - "pie", //chart type + {10,20,30,40,50,60,70,80,90,23,65,32,12,89,21}, //dataX + {1,35,68,20,30,40,4,12,60,90,83,36,34,56,78}, //dataY + "scatter", //chart type true //yes or no for generateColors function (needed in the case of the pie chart) ); From edf78db8cfd9ee7df8a828041e208d60a514d163 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sun, 23 Feb 2020 14:09:20 -0500 Subject: [PATCH 02/10] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d6367c0..6bac037 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ![alt text](data/logo.png "Caroline") - +
+Logo Created By @stsdc
A simple Cairo Chart Library for GTK and Vala From a0ef2e21602a45480361a69a6a47b97d78cc0441 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Fri, 6 Mar 2020 15:30:56 -0500 Subject: [PATCH 03/10] fixed bar chart issues --- Caroline.vala | 123 ++++++++++++++++++++++++++------------------------ sample.vala | 6 +-- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/Caroline.vala b/Caroline.vala index 78223e3..03d377d 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -61,7 +61,7 @@ public class Caroline : Gtk.DrawingArea { public double b; } - public struct ScatterPoint { + public struct Point { public double x; public double y; } @@ -94,7 +94,7 @@ public class Caroline : Gtk.DrawingArea { public ArrayList chartColorArray = new ArrayList(); - public ArrayList scatterArray = new ArrayList(); + public ArrayList pointsArray = new ArrayList(); construct{ @@ -167,33 +167,35 @@ public class Caroline : Gtk.DrawingArea { this.width, this.height ); - var dataXTemp = dataX; - dataXTemp = this.arraySort(dataXTemp); + + this.chartType = chartType; + var dataXTemp = this.arraySort(dataX); + this.labelXList.add(0); for (int i = 0; i < dataX.length; i++) { - Caroline.ScatterPoint scatterPoint = { + Caroline.Point point = { dataXTemp[i], dataY[i] }; - this.scatterArray.add(scatterPoint); + this.pointsArray.add(point); } - var tick = dataXTemp[dataXTemp.length-1] / spreadX; - this.labelXList.add(0); - - for (int f = 0; f < this.spreadX; f++) { + if (chartType == "pie" && generateColors) + this.generateColors(); - this.labelXList.add(tick+(tick*f)); + if (chartType != "bar"){ - } + var tick = dataXTemp[dataXTemp.length-1] / spreadX; - this.chartType = chartType; + for (int f = 0; f < this.spreadX; f++) + this.labelXList.add(tick+(tick*f)); - if (generateColors) - this.generateColors(); + }else + for (int i = 0; i < dataX.length; i++) + this.labelXList.add(dataX[i]); } @@ -290,26 +292,32 @@ public class Caroline : Gtk.DrawingArea { /*We loop through all of the x labels and actually draw thes lines and add the actual text for each tick mark.*/ for (int i = 0; i < this.labelXList.size; i++){ - var test = this.labelXList.get(i) * (this.width/this.labelXList.get(this.labelXList.size-1)); - stdout.printf("x created: %f\n",this.chartPadding + this.spreadFinalX * i + (this.widthPadding / 3)); + + double rawXCalculation = 0; + + if (this.chartType != "bar") + rawXCalculation = this.labelXList.get(i) * (this.width/this.labelXList.get(this.labelXList.size-1)); + else + rawXCalculation = this.spreadFinalX * i; //line drawing cr.move_to( - this.chartPadding + test + (this.widthPadding / 3), + this.chartPadding + rawXCalculation + (this.widthPadding / 3), height + this.xTickStart ); cr.line_to( - this.chartPadding + test + (this.widthPadding / 3), + this.chartPadding + rawXCalculation + (this.widthPadding / 3), height + this.xTickEnd ); //moves the current drawing area back and lists the x axis value below the x tick cr.move_to( - xTextStart + test + (this.widthPadding / 3), + xTextStart + rawXCalculation + (this.widthPadding / 3), height + this.xTextEnd ); + stdout.printf("%f\n",this.labelXList.get(i)); cr.show_text(this.labelXList.get(i).to_string()); } @@ -372,20 +380,20 @@ public class Caroline : Gtk.DrawingArea { private void calculations(){ /*This next sector of arithmetic is to find the max value of the data array*/ - this.max = this.scatterArray[0].y; + this.max = this.pointsArray[0].y; //Loop and compare each value to our initial value to see if it becomes the max - for (int i = 0; i < this.scatterArray.size; i++) - if (this.scatterArray[i].y > this.max) - this.max = this.scatterArray[i].y; + for (int i = 0; i < this.pointsArray.size; i++) + if (this.pointsArray[i].y > this.max) + this.max = this.pointsArray[i].y; /*This next sector of arithmetic is to find the min value of the data array*/ this.min = 0; //Loop and compare each scatterArray to our initial value to see if it becomes the min - for (int i = 0; i < this.scatterArray.size; i++) - if (this.scatterArray[i].y < this.min) - this.min = this.scatterArray[i].y; + for (int i = 0; i < this.pointsArray.size; i++) + if (this.pointsArray[i].y < this.min) + this.min = this.pointsArray[i].y; /*Finds the gap between each y axis label to be displayed. spreadY is set on the developers side, it is meant to tell Caroline how many y axis ticks needed.*/ @@ -428,7 +436,7 @@ public class Caroline : Gtk.DrawingArea { cr.set_source_rgba(0, 174, 174,0.8); //Getting a scaler, which will help put the line in the right spot - double scaler = ((this.scatterArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; + double scaler = ((this.pointsArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; //We want to move the pointer on the canvas to where we want the line graph to start. cr.move_to( @@ -439,10 +447,10 @@ public class Caroline : Gtk.DrawingArea { (this.height + this.chartPadding) - ((this.spreadFinalY * scaler)) ); - for (int i = 1; i < this.scatterArray.size; i++){ + for (int i = 1; i < this.pointsArray.size; i++){ //Recalculating the scaler to our current value - scaler = ((this.scatterArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; + scaler = ((this.pointsArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; /*line_to (https://valadoc.org/cairo/Cairo.Context.line_to.html) is a simple cario function that allows us to draw a line from the previous canvas pointer.*/ @@ -475,28 +483,24 @@ public class Caroline : Gtk.DrawingArea { */ private void barChart(Cairo.Context cr){ - //Set the color of the line (this default color is blue) - - //Getting a scaler, which will help put the line in the right spot - double scaler = ((this.scatterArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; - + double scaler = ((this.pointsArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; double maxX = 0; - for (int i = 0; i < this.scatterArray.size; i++) - if (this.scatterArray.get(i).x > maxX) - maxX = this.scatterArray.get(i).x; + for (int i = 0; i < this.pointsArray.size; i++) + if (this.pointsArray.get(i).x > maxX) + maxX = this.pointsArray.get(i).x; - for (int i = 0; i < this.scatterArray.size; i++){ + for (int i = 0; i < this.pointsArray.size; i++){ //Recalculating the scaler to our current value - scaler = ((this.scatterArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; + scaler = ((this.pointsArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; /*Rectangle takes x,y,width,height as doubles, which will position the rectangle at the pointer on the canvas*/ cr.rectangle( //We have a bit of a smaller buffer (10) since the rectangles should be centered on tick marks - this.rectangleXOffset + this.scatterArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3), + (this.rectangleXOffset + this.spreadFinalX * (i + 1)) + (this.widthPadding / 3.35), this.height+this.chartPadding, this.rectangleXOffset, /*We want to draw our height "upwards" on the 2d plane, since 0,0 on this canvas is in the @@ -511,7 +515,6 @@ public class Caroline : Gtk.DrawingArea { } - } /** @@ -531,10 +534,10 @@ public class Caroline : Gtk.DrawingArea { /*This for loop gets the total of all the data so we can scale the pie chart later on.*/ - for (int i = 0; i < this.scatterArray.size; i++) - total += this.scatterArray[i].x; + for (int i = 0; i < this.pointsArray.size; i++) + total += this.pointsArray[i].x; - for (int i = 0; i < this.scatterArray.size; i++){ + for (int i = 0; i < this.pointsArray.size; i++){ //Uses the chart color arrya with the structs within it to set the color cr.set_source_rgb( @@ -549,15 +552,15 @@ public class Caroline : Gtk.DrawingArea { this.pieChartYStart, this.pieChartRadius, startAngle, - startAngle + (this.scatterArray[i].x / total) * this.PIX + startAngle + (this.pointsArray[i].x / total) * this.PIX ); - stdout.printf("%f\n",startAngle + (this.scatterArray[i].x / total) * this.PIX); + stdout.printf("%f\n",startAngle + (this.pointsArray[i].x / total) * this.PIX); /*Adds angle to startAngle to keep track of where to draw the next arc and then the code draws the straight lines to the middle of the circle, then fills the colors in, using cr.fill()*/ - startAngle += (this.scatterArray[i].x / total) * this.PIX; + startAngle += (this.pointsArray[i].x / total) * this.PIX; cr.line_to(this.pieChartXStart, this.pieChartYStart); cr.fill(); @@ -577,7 +580,7 @@ public class Caroline : Gtk.DrawingArea { //set the color back to white for the text and write the amount next to the label cr.set_source_rgb(1, 1, 1); cr.move_to(this.width - this.pieChartLabelOffsetX, yOffset + this.pieChartLabelOffsetY); - cr.show_text(this.scatterArray[i].x.to_string()); + cr.show_text(this.pointsArray[i].x.to_string()); /*Drawing operator that strokes the current path using the current settings that were implemented eariler in this file.*/ @@ -600,14 +603,14 @@ public class Caroline : Gtk.DrawingArea { double finalsf = 0; double xAxisCalculation = 0; - for (int i = 0; i < this.scatterArray.size; i++) - if (this.scatterArray.get(i).x > maxX) - maxX = this.scatterArray.get(i).x; + for (int i = 0; i < this.pointsArray.size; i++) + if (this.pointsArray.get(i).x > maxX) + maxX = this.pointsArray.get(i).x; - for (int i = 0; i < this.scatterArray.size; i++){ + for (int i = 0; i < this.pointsArray.size; i++){ - scaler = ((this.scatterArray.get(i).y - this.min) / (this.max - this.min)) * this.spreadY; - xAxisCalculation = this.scatterArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3); + scaler = ((this.pointsArray.get(i).y - this.min) / (this.max - this.min)) * this.spreadY; + xAxisCalculation = this.pointsArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3); cr.arc( xAxisCalculation, @@ -621,7 +624,7 @@ public class Caroline : Gtk.DrawingArea { //make util function and optimize! var yCount = 0; - int y = (int)this.scatterArray.get(i).y; + int y = (int)this.pointsArray.get(i).y; while(y > 0){ @@ -631,7 +634,7 @@ public class Caroline : Gtk.DrawingArea { } var xCount = 0; - int x = (int)this.scatterArray.get(i).x; + int x = (int)this.pointsArray.get(i).x; while(x > 0){ @@ -650,8 +653,8 @@ public class Caroline : Gtk.DrawingArea { (((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))))-5 ); cr.show_text( - this.scatterArray.get(i).y.to_string().concat( - ",",this.scatterArray.get(i).x.to_string()) + this.pointsArray.get(i).y.to_string().concat( + ",",this.pointsArray.get(i).x.to_string()) ); } @@ -673,7 +676,7 @@ public class Caroline : Gtk.DrawingArea { */ private void generateColors(){ - for (int i = 0; i < this.scatterArray.size; i++){ + for (int i = 0; i < this.pointsArray.size; i++){ //Create color struct ChartColor chartColor = { diff --git a/sample.vala b/sample.vala index 3c81bf6..69f0ee4 100644 --- a/sample.vala +++ b/sample.vala @@ -18,9 +18,9 @@ public void main (string[] args) { //Simply set Caroline to a variable var widget = new Caroline ( - {10,20,30,40,50,60,70,80,90,23,65,32,12,89,21}, //dataX - {1,35,68,20,30,40,4,12,60,90,83,36,34,56,78}, //dataY - "scatter", //chart type + {10,20,30,40,50,60,70,80,90}, //dataX + {1,35,68,20,30,40,4,12,60}, //dataY + "pie", //chart type true //yes or no for generateColors function (needed in the case of the pie chart) ); From 2e980242e1ba6043ca670eff672aa626a5aa38e6 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sat, 7 Mar 2020 16:12:00 -0500 Subject: [PATCH 04/10] some progress benchmarking and smooth lines --- Caroline.vala | 208 ++++++++++++++++++++++++++++++++++++++++++-------- sample.vala | 30 ++++++-- 2 files changed, 202 insertions(+), 36 deletions(-) diff --git a/Caroline.vala b/Caroline.vala index 03d377d..17f7f6e 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -169,13 +169,19 @@ public class Caroline : Gtk.DrawingArea { ); this.chartType = chartType; - var dataXTemp = this.arraySort(dataX); this.labelXList.add(0); + if (dataX.length < 15 || this.chartType == "bar"){ + + this.spreadX = dataX.length; + this.spreadY = dataY.length; + + } + for (int i = 0; i < dataX.length; i++) { Caroline.Point point = { - dataXTemp[i], + dataX[i], dataY[i] }; @@ -183,19 +189,21 @@ public class Caroline : Gtk.DrawingArea { } + this.arrayListSort(); + if (chartType == "pie" && generateColors) this.generateColors(); - if (chartType != "bar"){ + if (chartType != "bar" && chartType != "line"){ - var tick = dataXTemp[dataXTemp.length-1] / spreadX; + var tick = this.pointsArray[dataX.length-1].x / spreadX; for (int f = 0; f < this.spreadX; f++) this.labelXList.add(tick+(tick*f)); }else - for (int i = 0; i < dataX.length; i++) - this.labelXList.add(dataX[i]); + for (int i = 0; i < pointsArray.size; i++) + this.labelXList.add(pointsArray[i].x); } @@ -287,15 +295,15 @@ public class Caroline : Gtk.DrawingArea { /*Figure out the spread of each of the x coordinates, notice this is differnt from the y plane, we want to display each data point on the x axis here.*/ - this.spreadFinalX = this.width/this.spreadY; + this.spreadFinalX = this.width/(this.spreadX-1); /*We loop through all of the x labels and actually draw thes lines and add the actual text for each tick mark.*/ - for (int i = 0; i < this.labelXList.size; i++){ + for (int i = 0; i < this.labelXList.size - 1; i++){ double rawXCalculation = 0; - if (this.chartType != "bar") + if (this.chartType != "line" && this.chartType != "bar") rawXCalculation = this.labelXList.get(i) * (this.width/this.labelXList.get(this.labelXList.size-1)); else rawXCalculation = this.spreadFinalX * i; @@ -317,8 +325,8 @@ public class Caroline : Gtk.DrawingArea { height + this.xTextEnd ); - stdout.printf("%f\n",this.labelXList.get(i)); - cr.show_text(this.labelXList.get(i).to_string()); + var roundedX = snipLongDouble(this.labelXList.get(i)); + cr.show_text(roundedX); } @@ -333,12 +341,6 @@ public class Caroline : Gtk.DrawingArea { //Saves the drawing area context and the attributes set before this save cr.save(); - } - - if (this.chartType == "scatter"){ - - - } /*This switch-case will execute the proper chart depending on what the @@ -347,6 +349,9 @@ public class Caroline : Gtk.DrawingArea { case "line": lineChart(cr); break; + case "smooth-line": + smoothLineChart(cr); + break; case "bar": barChart(cr); break; @@ -471,6 +476,134 @@ public class Caroline : Gtk.DrawingArea { } + private void smoothLineChart(Cairo.Context cr){ + + //Setting thickness of the line using set_line_width which can take any double. + cr.set_line_width(this.lineThicknessData); + + cr.set_line_cap(Cairo.LineCap.ROUND); + cr.set_line_width(2); + + //Set the color of the line (this default color is blue) + cr.set_source_rgba(0, 174, 174,0.8); + + //Getting a scaler, which will help put the line in the right spot + double scaler = ((this.pointsArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; + + //We want to move the pointer on the canvas to where we want the line graph to start. + cr.move_to( + //x axis set to "0", as 15 is the buffer in the widget + this.chartPadding + (this.widthPadding / 3), + /*y axis using our scaler value multiplied by how many y axis values we have, + then subtracted from the height*/ + (this.height + this.chartPadding) - ((this.spreadFinalY * scaler)) + ); + + double maxX = 0; + + for (int i = 0; i < this.pointsArray.size; i++) + if (this.pointsArray.get(i).x > maxX) + maxX = this.pointsArray.get(i).x; + + double previousX = this.chartPadding + (this.widthPadding / 3); + double previousY = (this.height + this.chartPadding) - ((this.spreadFinalY * scaler)); + double currentX = 0; + double currentY = 0; + double aheadX = 0; + double aheadY = 0; + double scalerAhead = 0; + double counter = 0; + + for (int i = 0; i < this.pointsArray.size; i++) + stdout.printf("X: %f | Y: %f \n",this.pointsArray[i].x,this.pointsArray[i].y); + + stdout.printf("======================================================="); + + for (int i = 0; i < this.pointsArray.size; i += 2){ + + + + //Recalculating the scaler to our current value + + if (this.pointsArray[i] != null){ + scaler = ((this.pointsArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; + + currentX = (this.chartPadding + this.spreadFinalX * (i)) + (this.widthPadding / 3); + currentY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))); + stdout.printf("CHECK X: %f | Y: %f \n",this.pointsArray[i].x,this.pointsArray[i].y); + + }else{ + + currentX = (this.chartPadding + this.spreadFinalX * (i)) + (this.widthPadding / 3); + currentY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))); + + } + + if (i+1 != this.pointsArray.size){ + scalerAhead = ((this.pointsArray[i+1].y - this.min) / (this.max - this.min)) * this.spreadY; + + aheadX = (this.chartPadding + this.spreadFinalX * (i+1)) + (this.widthPadding / 3); + aheadY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scalerAhead))); + + stdout.printf("CHECK X: %f | Y: %f \n",this.pointsArray[i+1].x,this.pointsArray[i+1].y); + + }else{ + + aheadX = (this.chartPadding + this.spreadFinalX * (i + 1)) + (this.widthPadding / 3); + aheadY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scalerAhead)))+10; + + } + + //stdout.printf("previous X: %f | Y: %f \n",previousX,previousY); + //stdout.printf("current X: %f | Y: %f \n",currentX,currentY); + //stdout.printf("ahead X: %f | Y: %f \n",aheadX,aheadY); + + cr.set_line_cap(Cairo.LineCap.ROUND); + + cr.curve_to( + previousX, + previousY, + currentX, + currentY, + aheadX, + aheadY + ); + + cr.set_line_cap(Cairo.LineCap.ROUND); + + double scalerTemp = ((this.pointsArray.get(i).y - this.min) / (this.max - this.min)) * this.spreadY; + double xAxisCalculation = this.pointsArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3); + + cr.move_to( + //x axis set to "0", as 15 is the buffer in the widget + aheadX, + /*y axis using our scaler value multiplied by how many y axis values we have, + then subtracted from the height*/ + aheadY + ); + + /*line_to (https://valadoc.org/cairo/Cairo.Context.line_to.html) is a simple + cario function that allows us to draw a line from the previous canvas pointer.*/ + /*cr.line_to( + /*axis, similar to move_to above, we just move the the line to the + next x axis tick. + (this.chartPadding + this.spreadFinalX * (i+1)) + (this.widthPadding / 3), + /*y axis using our scaler value multiplied by how many y axis values we have, + then subtracted from the height + ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))) + );*/ + + previousX = aheadX; + previousY = aheadY; + + } + + /*Drawing operator that strokes the current path using the current settings that were + implemented eariler in this file.*/ + cr.stroke(); + + } + /** * Draws a set of rectangles based on this.DATA * @@ -555,8 +688,6 @@ public class Caroline : Gtk.DrawingArea { startAngle + (this.pointsArray[i].x / total) * this.PIX ); - stdout.printf("%f\n",startAngle + (this.pointsArray[i].x / total) * this.PIX); - /*Adds angle to startAngle to keep track of where to draw the next arc and then the code draws the straight lines to the middle of the circle, then fills the colors in, using cr.fill()*/ @@ -633,6 +764,8 @@ public class Caroline : Gtk.DrawingArea { } + yCount++; + var xCount = 0; int x = (int)this.pointsArray.get(i).x; @@ -643,18 +776,18 @@ public class Caroline : Gtk.DrawingArea { } + xCount++; + double spacingY = 3.5 * yCount; double spacingX = 3.5 * xCount; - stdout.printf("Y Count %f,%f\n",spacingY,spacingX); - cr.move_to( xAxisCalculation - (spacingY + spacingX), (((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))))-5 ); cr.show_text( - this.pointsArray.get(i).y.to_string().concat( - ",",this.pointsArray.get(i).x.to_string()) + snipLongDouble(this.pointsArray.get(i).x).concat( + ",",snipLongDouble(this.pointsArray.get(i).y)) ); } @@ -691,31 +824,44 @@ public class Caroline : Gtk.DrawingArea { } - public double[] arraySort(double[] array){ + public void arrayListSort(){ bool swapped = true; int j = 0; - double tmp; + double tmpX,tmpY; + Caroline.Point point = {0,0}; while (swapped) { swapped = false; j++; - for (int i = 0; i < array.length - j; i++) { + for (int i = 0; i < this.pointsArray.size - j; i++) { + + if (this.pointsArray[i].x > this.pointsArray[i+1].x) { + + tmpX = this.pointsArray[i].x; + tmpY = this.pointsArray[i].y; + + point = {this.pointsArray[i+1].x,this.pointsArray[i+1].y}; + this.pointsArray.set(i,point); + + point = {tmpX,tmpY}; + this.pointsArray.set(i+1,point); - if (array[i] > array[i + 1]) { - tmp = array[i]; - array[i] = array[i + 1]; - array[i + 1] = tmp; swapped = true; + } } } - return array; + } + + public string snipLongDouble(double number){ + + return "%.1f".printf(number); } diff --git a/sample.vala b/sample.vala index 69f0ee4..e137cfc 100644 --- a/sample.vala +++ b/sample.vala @@ -6,6 +6,10 @@ using Cairo; public void main (string[] args) { + GLib.DateTime now = new GLib.DateTime.now_local(); + var sec = now.to_unix(); + var msecStart = (sec * 1000) + (now.get_microsecond () / 1000); + //Setting up the GTK window Gtk.init (ref args); var window = new Gtk.Window (); @@ -16,16 +20,32 @@ public void main (string[] args) { Gtk.Grid mainGrid = new Gtk.Grid (); mainGrid.orientation = Gtk.Orientation.VERTICAL; + int benchNumber = 10; + double[] x = new double[benchNumber]; + double[] y = new double[benchNumber]; + + for (int i = 0; i < benchNumber; ++i){ + + x[i] = Random.double_range(1,10); + y[i] = Random.double_range(1,10); + + } + //Simply set Caroline to a variable - var widget = new Caroline ( - {10,20,30,40,50,60,70,80,90}, //dataX - {1,35,68,20,30,40,4,12,60}, //dataY - "pie", //chart type + var carolineWidget = new Caroline ( + x, //dataX + y, //dataY + "smooth-line", //chart type true //yes or no for generateColors function (needed in the case of the pie chart) ); + now = new GLib.DateTime.now_local(); + sec = now.to_unix(); + var msecEnd = (sec * 1000) + (now.get_microsecond () / 1000); + stdout.printf("Time Taken: %f\n",msecEnd - msecStart); + //Add the Caroline widget tp the grid - mainGrid.attach (widget, 0, 0, 1, 1); + mainGrid.attach(carolineWidget, 0, 0, 1, 1); mainGrid.set_row_homogeneous(true); mainGrid.set_column_homogeneous(true); From da7317733e49cae3f6629744ae0b9e01747c06ed Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sat, 7 Mar 2020 16:48:41 -0500 Subject: [PATCH 05/10] moving smooth line to another branch --- Caroline.vala | 128 -------------------------------------------------- sample.vala | 6 +-- 2 files changed, 3 insertions(+), 131 deletions(-) diff --git a/Caroline.vala b/Caroline.vala index 17f7f6e..d3a275d 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -476,134 +476,6 @@ public class Caroline : Gtk.DrawingArea { } - private void smoothLineChart(Cairo.Context cr){ - - //Setting thickness of the line using set_line_width which can take any double. - cr.set_line_width(this.lineThicknessData); - - cr.set_line_cap(Cairo.LineCap.ROUND); - cr.set_line_width(2); - - //Set the color of the line (this default color is blue) - cr.set_source_rgba(0, 174, 174,0.8); - - //Getting a scaler, which will help put the line in the right spot - double scaler = ((this.pointsArray[0].y - this.min) / (this.max - this.min)) * this.spreadY; - - //We want to move the pointer on the canvas to where we want the line graph to start. - cr.move_to( - //x axis set to "0", as 15 is the buffer in the widget - this.chartPadding + (this.widthPadding / 3), - /*y axis using our scaler value multiplied by how many y axis values we have, - then subtracted from the height*/ - (this.height + this.chartPadding) - ((this.spreadFinalY * scaler)) - ); - - double maxX = 0; - - for (int i = 0; i < this.pointsArray.size; i++) - if (this.pointsArray.get(i).x > maxX) - maxX = this.pointsArray.get(i).x; - - double previousX = this.chartPadding + (this.widthPadding / 3); - double previousY = (this.height + this.chartPadding) - ((this.spreadFinalY * scaler)); - double currentX = 0; - double currentY = 0; - double aheadX = 0; - double aheadY = 0; - double scalerAhead = 0; - double counter = 0; - - for (int i = 0; i < this.pointsArray.size; i++) - stdout.printf("X: %f | Y: %f \n",this.pointsArray[i].x,this.pointsArray[i].y); - - stdout.printf("======================================================="); - - for (int i = 0; i < this.pointsArray.size; i += 2){ - - - - //Recalculating the scaler to our current value - - if (this.pointsArray[i] != null){ - scaler = ((this.pointsArray[i].y - this.min) / (this.max - this.min)) * this.spreadY; - - currentX = (this.chartPadding + this.spreadFinalX * (i)) + (this.widthPadding / 3); - currentY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))); - stdout.printf("CHECK X: %f | Y: %f \n",this.pointsArray[i].x,this.pointsArray[i].y); - - }else{ - - currentX = (this.chartPadding + this.spreadFinalX * (i)) + (this.widthPadding / 3); - currentY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))); - - } - - if (i+1 != this.pointsArray.size){ - scalerAhead = ((this.pointsArray[i+1].y - this.min) / (this.max - this.min)) * this.spreadY; - - aheadX = (this.chartPadding + this.spreadFinalX * (i+1)) + (this.widthPadding / 3); - aheadY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scalerAhead))); - - stdout.printf("CHECK X: %f | Y: %f \n",this.pointsArray[i+1].x,this.pointsArray[i+1].y); - - }else{ - - aheadX = (this.chartPadding + this.spreadFinalX * (i + 1)) + (this.widthPadding / 3); - aheadY = ((this.height + this.chartPadding) - ((this.spreadFinalY * scalerAhead)))+10; - - } - - //stdout.printf("previous X: %f | Y: %f \n",previousX,previousY); - //stdout.printf("current X: %f | Y: %f \n",currentX,currentY); - //stdout.printf("ahead X: %f | Y: %f \n",aheadX,aheadY); - - cr.set_line_cap(Cairo.LineCap.ROUND); - - cr.curve_to( - previousX, - previousY, - currentX, - currentY, - aheadX, - aheadY - ); - - cr.set_line_cap(Cairo.LineCap.ROUND); - - double scalerTemp = ((this.pointsArray.get(i).y - this.min) / (this.max - this.min)) * this.spreadY; - double xAxisCalculation = this.pointsArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3); - - cr.move_to( - //x axis set to "0", as 15 is the buffer in the widget - aheadX, - /*y axis using our scaler value multiplied by how many y axis values we have, - then subtracted from the height*/ - aheadY - ); - - /*line_to (https://valadoc.org/cairo/Cairo.Context.line_to.html) is a simple - cario function that allows us to draw a line from the previous canvas pointer.*/ - /*cr.line_to( - /*axis, similar to move_to above, we just move the the line to the - next x axis tick. - (this.chartPadding + this.spreadFinalX * (i+1)) + (this.widthPadding / 3), - /*y axis using our scaler value multiplied by how many y axis values we have, - then subtracted from the height - ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))) - );*/ - - previousX = aheadX; - previousY = aheadY; - - } - - /*Drawing operator that strokes the current path using the current settings that were - implemented eariler in this file.*/ - cr.stroke(); - - } - /** * Draws a set of rectangles based on this.DATA * diff --git a/sample.vala b/sample.vala index e137cfc..fc5baa3 100644 --- a/sample.vala +++ b/sample.vala @@ -20,7 +20,7 @@ public void main (string[] args) { Gtk.Grid mainGrid = new Gtk.Grid (); mainGrid.orientation = Gtk.Orientation.VERTICAL; - int benchNumber = 10; + int benchNumber = 100; double[] x = new double[benchNumber]; double[] y = new double[benchNumber]; @@ -33,8 +33,8 @@ public void main (string[] args) { //Simply set Caroline to a variable var carolineWidget = new Caroline ( - x, //dataX - y, //dataY + {1,2,3,4,5,6,7,8,9,10}, //dataX + {0,1,2,0,2,1,0,1,2,3}, //dataY "smooth-line", //chart type true //yes or no for generateColors function (needed in the case of the pie chart) ); From 5ffc04f4a5bd74d061b55b5664cc5bdbc8efd028 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sat, 7 Mar 2020 18:28:55 -0500 Subject: [PATCH 06/10] benching marking and bugs --- Caroline.vala | 91 +++++++++++++++++++++++++++++---------------------- sample.vala | 15 +++++---- 2 files changed, 59 insertions(+), 47 deletions(-) diff --git a/Caroline.vala b/Caroline.vala index d3a275d..3e0fcf5 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -96,6 +96,8 @@ public class Caroline : Gtk.DrawingArea { public ArrayList pointsArray = new ArrayList(); + public bool scatterLabels {get; set;} + construct{ //Initializing default values @@ -139,6 +141,8 @@ public class Caroline : Gtk.DrawingArea { this.pieChartLabelOffsetY = 10; this.PIX = 6.28; + this.scatterLabels = true; + } /** @@ -146,7 +150,7 @@ public class Caroline : Gtk.DrawingArea { * chartType - this can either be line, bar, or pie * generateColors - array for ChartColor structs */ - public Caroline(double[] dataX, double[] dataY, string chartType, bool generateColors){ + public Caroline(double[] dataX, double[] dataY, string chartType, bool generateColors, bool scatterPlotLabels){ /*Since our widget will already be "realized" we want to use add_events, this function allows us to set the window event bit flags, which I document directly below. @@ -168,6 +172,7 @@ public class Caroline : Gtk.DrawingArea { this.height ); + this.scatterLabels = scatterPlotLabels; this.chartType = chartType; this.labelXList.add(0); @@ -180,23 +185,20 @@ public class Caroline : Gtk.DrawingArea { for (int i = 0; i < dataX.length; i++) { - Caroline.Point point = { - dataX[i], - dataY[i] - }; - + Caroline.Point point = {dataX[i], dataY[i]}; this.pointsArray.add(point); } - this.arrayListSort(); + if (chartType != "pie" && chartType != "scatter") + this.arrayListSort(); if (chartType == "pie" && generateColors) this.generateColors(); - if (chartType != "bar" && chartType != "line"){ + if (chartType != "bar" ){ - var tick = this.pointsArray[dataX.length-1].x / spreadX; + double tick = this.pointsArray[dataX.length-1].x / spreadX; for (int f = 0; f < this.spreadX; f++) this.labelXList.add(tick+(tick*f)); @@ -299,7 +301,7 @@ public class Caroline : Gtk.DrawingArea { /*We loop through all of the x labels and actually draw thes lines and add the actual text for each tick mark.*/ - for (int i = 0; i < this.labelXList.size - 1; i++){ + for (int i = 0; i < this.labelXList.size; i++){ double rawXCalculation = 0; @@ -349,9 +351,6 @@ public class Caroline : Gtk.DrawingArea { case "line": lineChart(cr); break; - case "smooth-line": - smoothLineChart(cr); - break; case "bar": barChart(cr); break; @@ -462,7 +461,7 @@ public class Caroline : Gtk.DrawingArea { cr.line_to( /*axis, similar to move_to above, we just move the the line to the next x axis tick.*/ - (this.chartPadding + this.spreadFinalX * (i+1)) + (this.widthPadding / 3), + (this.chartPadding + this.spreadFinalX * (i)) + (this.widthPadding / 3), /*y axis using our scaler value multiplied by how many y axis values we have, then subtracted from the height*/ ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))) @@ -603,7 +602,6 @@ public class Caroline : Gtk.DrawingArea { double scaler = 0; double maxX = 0; - double finalsf = 0; double xAxisCalculation = 0; for (int i = 0; i < this.pointsArray.size; i++) @@ -625,42 +623,46 @@ public class Caroline : Gtk.DrawingArea { cr.fill(); - //make util function and optimize! - var yCount = 0; - int y = (int)this.pointsArray.get(i).y; + if (scatterLabels){ + + //make util function and optimize! + var yCount = 0; + int y = (int)this.pointsArray.get(i).y; - while(y > 0){ + while(y > 0){ + + y = y / 10; + yCount++; + + } - y = y / 10; yCount++; - } + var xCount = 0; + int x = (int)this.pointsArray.get(i).x; - yCount++; + while(x > 0){ - var xCount = 0; - int x = (int)this.pointsArray.get(i).x; + x = x / 10; + xCount++; - while(x > 0){ + } - x = x / 10; xCount++; - } - - xCount++; + double spacingY = 3.5 * yCount; + double spacingX = 3.5 * xCount; - double spacingY = 3.5 * yCount; - double spacingX = 3.5 * xCount; + cr.move_to( + xAxisCalculation - (spacingY + spacingX), + (((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))))-5 + ); + cr.show_text( + snipLongDouble(this.pointsArray.get(i).x).concat( + ",",snipLongDouble(this.pointsArray.get(i).y)) + ); - cr.move_to( - xAxisCalculation - (spacingY + spacingX), - (((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))))-5 - ); - cr.show_text( - snipLongDouble(this.pointsArray.get(i).x).concat( - ",",snipLongDouble(this.pointsArray.get(i).y)) - ); + } } @@ -698,6 +700,10 @@ public class Caroline : Gtk.DrawingArea { public void arrayListSort(){ + GLib.DateTime now = new GLib.DateTime.now_local(); + var sec = now.to_unix(); + var msecStart = (sec * 1000) + (now.get_microsecond () / 1000); + bool swapped = true; int j = 0; double tmpX,tmpY; @@ -729,11 +735,16 @@ public class Caroline : Gtk.DrawingArea { } + now = new GLib.DateTime.now_local(); + sec = now.to_unix(); + var msecEnd = (sec * 1000) + (now.get_microsecond () / 1000); + stdout.printf("Sort Time Taken: %f\n",msecEnd - msecStart); + } public string snipLongDouble(double number){ - return "%.1f".printf(number); + return "%0.1f".printf(number); } diff --git a/sample.vala b/sample.vala index fc5baa3..e9efd34 100644 --- a/sample.vala +++ b/sample.vala @@ -20,23 +20,24 @@ public void main (string[] args) { Gtk.Grid mainGrid = new Gtk.Grid (); mainGrid.orientation = Gtk.Orientation.VERTICAL; - int benchNumber = 100; + int benchNumber = 10000; double[] x = new double[benchNumber]; double[] y = new double[benchNumber]; for (int i = 0; i < benchNumber; ++i){ - x[i] = Random.double_range(1,10); - y[i] = Random.double_range(1,10); + x[i] = Random.double_range(0,10); + y[i] = Random.double_range(0,10); } //Simply set Caroline to a variable var carolineWidget = new Caroline ( - {1,2,3,4,5,6,7,8,9,10}, //dataX - {0,1,2,0,2,1,0,1,2,3}, //dataY - "smooth-line", //chart type - true //yes or no for generateColors function (needed in the case of the pie chart) + x, //dataX + y, //dataY + "line", //chart type + true, //yes or no for generateColors function (needed in the case of the pie chart), + false // yes or no for scatter plot labels ); now = new GLib.DateTime.now_local(); From 34933e84e889775b18896bab36cadae90b920a92 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Mon, 9 Mar 2020 19:01:28 -0400 Subject: [PATCH 07/10] finished test, documentation --- Caroline.vala | 99 ++++++++++++++++++++++++++++++++++----------------- sample.vala | 15 ++------ 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/Caroline.vala b/Caroline.vala index 3e0fcf5..8d636cf 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -149,6 +149,7 @@ public class Caroline : Gtk.DrawingArea { * data - the actual data for that charts * chartType - this can either be line, bar, or pie * generateColors - array for ChartColor structs + * scatterPlotLabels - show labels on the scatter plot */ public Caroline(double[] dataX, double[] dataY, string chartType, bool generateColors, bool scatterPlotLabels){ @@ -190,18 +191,21 @@ public class Caroline : Gtk.DrawingArea { } - if (chartType != "pie" && chartType != "scatter") + if (chartType != "pie") this.arrayListSort(); if (chartType == "pie" && generateColors) this.generateColors(); - if (chartType != "bar" ){ + if (chartType != "bar"){ double tick = this.pointsArray[dataX.length-1].x / spreadX; - for (int f = 0; f < this.spreadX; f++) + for (int f = 0; f < this.spreadX; f++){ this.labelXList.add(tick+(tick*f)); + stdout.printf("%f\n",tick+(tick*f)); + + } }else for (int i = 0; i < pointsArray.size; i++) @@ -422,7 +426,7 @@ public class Caroline : Gtk.DrawingArea { } /** - * Draws a line base on this.DATA + * Draws a line based on the pointsArray * * Uses the Cairo.Context to draw the line chart via the .move_to function * which allows you to move the current point to the necessary area and @@ -476,7 +480,7 @@ public class Caroline : Gtk.DrawingArea { } /** - * Draws a set of rectangles based on this.DATA + * Draws a set of rectangles based on the pointsArray * * Uses the Cairo.Context to draw a set of rectanges that will be positioned in a bar * chart format. The most important function used is rectangle, which allows us to quickly @@ -522,7 +526,7 @@ public class Caroline : Gtk.DrawingArea { } /** - * Draws a pie chart based on this.DATA + * Draws a pie chart based on the pointsArray * * Uses the Cairo.Context to draw the pie chart by first, figuring out the radians for each piece * of data. You can see that we need to find the total of the set of data. Then we loop over it @@ -582,7 +586,7 @@ public class Caroline : Gtk.DrawingArea { //set the color back to white for the text and write the amount next to the label cr.set_source_rgb(1, 1, 1); cr.move_to(this.width - this.pieChartLabelOffsetX, yOffset + this.pieChartLabelOffsetY); - cr.show_text(this.pointsArray[i].x.to_string()); + cr.show_text(snipLongDouble(this.pointsArray[i].x)); /*Drawing operator that strokes the current path using the current settings that were implemented eariler in this file.*/ @@ -592,43 +596,59 @@ public class Caroline : Gtk.DrawingArea { } + /** + * Draws a scatter plot based on the pointsArray + * + * Uses the Cairo.Context to draw each points (as an arc) based on its scaled x & y values. After + * the placement of the points, we have the option (based on scatterLabels) that if true will draw + * the points x and y numbers with an comma for aesthetic. If (scatterLabels) false the x & y values + * will now show as text (for larger data sets). + * + * @param type cr | Cairo.Context + * @return return void + */ private void scatterChart(Cairo.Context cr){ + //Setting up values + double maxX = 0, xAxisCalculation = 0, yAxisCalculation = 0; + //Setting thickness of the line using set_line_width which can take any double. cr.set_line_width(this.lineThicknessData); //Set the color of the line (this default color is blue) cr.set_source_rgba(0, 174, 174,0.8); - double scaler = 0; - double maxX = 0; - double xAxisCalculation = 0; - + //Find the max X value for the scaling calculations below for (int i = 0; i < this.pointsArray.size; i++) if (this.pointsArray.get(i).x > maxX) maxX = this.pointsArray.get(i).x; + //Looping through the points array for (int i = 0; i < this.pointsArray.size; i++){ - scaler = ((this.pointsArray.get(i).y - this.min) / (this.max - this.min)) * this.spreadY; + //Calculating both axis points for the point xAxisCalculation = this.pointsArray.get(i).x * (this.width/maxX) + this.chartPadding + (this.widthPadding / 3); + yAxisCalculation = (this.height + this.chartPadding) - ((this.spreadFinalY * ((this.pointsArray.get(i).y - + this.min) / (this.max - this.min)) * this.spreadY)); + //Drawing point cr.arc( xAxisCalculation, - ((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))), + yAxisCalculation, 3, 0, - 6.28 + this.PIX ); cr.fill(); + //If the developer wants to show labels, we continue if (scatterLabels){ - //make util function and optimize! - var yCount = 0; - int y = (int)this.pointsArray.get(i).y; + int yCount = 0, y = (int)this.pointsArray.get(i).y; + /*Figuring out how many decimal places there are in each number so we know how much + to offset the text to ensure the 'comma' is center on the dot.*/ while(y > 0){ y = y / 10; @@ -638,8 +658,7 @@ public class Caroline : Gtk.DrawingArea { yCount++; - var xCount = 0; - int x = (int)this.pointsArray.get(i).x; + int xCount = 0, x = (int)this.pointsArray.get(i).x; while(x > 0){ @@ -650,13 +669,17 @@ public class Caroline : Gtk.DrawingArea { xCount++; - double spacingY = 3.5 * yCount; - double spacingX = 3.5 * xCount; + //Creating literal spacing of the text needed. + double spacingY = 3.8 * yCount; + double spacingX = 3.8 * xCount; + //moving to direct point to write text cr.move_to( xAxisCalculation - (spacingY + spacingX), - (((this.height + this.chartPadding) - ((this.spreadFinalY * scaler))))-5 + yAxisCalculation - 5 ); + + //writing coordinates cr.show_text( snipLongDouble(this.pointsArray.get(i).x).concat( ",",snipLongDouble(this.pointsArray.get(i).y)) @@ -698,12 +721,18 @@ public class Caroline : Gtk.DrawingArea { } + /** + * Sort an array list + * + * Within this function we sort the array list by the x axis. Why? Well we need to know in what order to create + * bar and line charts. However in pie charts, we don't need to know the order, hence we exlude this + * sorting since its the main performance bottle neck within the system. + * + * @param type none + * @return return void + */ public void arrayListSort(){ - GLib.DateTime now = new GLib.DateTime.now_local(); - var sec = now.to_unix(); - var msecStart = (sec * 1000) + (now.get_microsecond () / 1000); - bool swapped = true; int j = 0; double tmpX,tmpY; @@ -716,12 +745,14 @@ public class Caroline : Gtk.DrawingArea { for (int i = 0; i < this.pointsArray.size - j; i++) { + /*if current x is bigger than the next x, we move it a position forward, along with its y counter + part (y coordinate).*/ if (this.pointsArray[i].x > this.pointsArray[i+1].x) { tmpX = this.pointsArray[i].x; tmpY = this.pointsArray[i].y; - point = {this.pointsArray[i+1].x,this.pointsArray[i+1].y}; + point = {this.pointsArray[i+1].x, this.pointsArray[i+1].y}; this.pointsArray.set(i,point); point = {tmpX,tmpY}; @@ -735,13 +766,17 @@ public class Caroline : Gtk.DrawingArea { } - now = new GLib.DateTime.now_local(); - sec = now.to_unix(); - var msecEnd = (sec * 1000) + (now.get_microsecond () / 1000); - stdout.printf("Sort Time Taken: %f\n",msecEnd - msecStart); - } + /** + * Cuts decimals off a float number + * + * In several areas within our system we need to remove some decimals within a number so they we can display + * readable numbers for the chart and developer. + * + * @param type number | double + * @return return string + */ public string snipLongDouble(double number){ return "%0.1f".printf(number); diff --git a/sample.vala b/sample.vala index e9efd34..3327a18 100644 --- a/sample.vala +++ b/sample.vala @@ -6,10 +6,6 @@ using Cairo; public void main (string[] args) { - GLib.DateTime now = new GLib.DateTime.now_local(); - var sec = now.to_unix(); - var msecStart = (sec * 1000) + (now.get_microsecond () / 1000); - //Setting up the GTK window Gtk.init (ref args); var window = new Gtk.Window (); @@ -20,7 +16,7 @@ public void main (string[] args) { Gtk.Grid mainGrid = new Gtk.Grid (); mainGrid.orientation = Gtk.Orientation.VERTICAL; - int benchNumber = 10000; + int benchNumber = 10; double[] x = new double[benchNumber]; double[] y = new double[benchNumber]; @@ -35,16 +31,11 @@ public void main (string[] args) { var carolineWidget = new Caroline ( x, //dataX y, //dataY - "line", //chart type + "bar", //chart type true, //yes or no for generateColors function (needed in the case of the pie chart), - false // yes or no for scatter plot labels + true // yes or no for scatter plot labels ); - now = new GLib.DateTime.now_local(); - sec = now.to_unix(); - var msecEnd = (sec * 1000) + (now.get_microsecond () / 1000); - stdout.printf("Time Taken: %f\n",msecEnd - msecStart); - //Add the Caroline widget tp the grid mainGrid.attach(carolineWidget, 0, 0, 1, 1); mainGrid.set_row_homogeneous(true); From 55324c7398a0cfb5b33562e208ac2de9f021487d Mon Sep 17 00:00:00 2001 From: David Johnson Date: Mon, 9 Mar 2020 19:05:34 -0400 Subject: [PATCH 08/10] updated readme --- README.md | 6 ++++++ sample.vala | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bac037..a497e47 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ some ramblings about the development process and inner-workings of Cairo and Car + ## Documentation * Getting Started @@ -139,6 +140,11 @@ This is used to offset the text next to the color box label for the y axis. This is an array of ChartColor structs that are used to color parts of the charts (mainly for pie chart) this array is populated upon creation of the widget. +**`scatterLabels DEFAULT: true boolean`** +This value is used to tell the scatter plot type if it should display the labels or not. Why +Not display the labels? Well, if you have 10000 points you might not want to show ever value. +True will display labels, false won't display labels. + #### Private Attributes **`spreadFinalY DEFAULT: 0 (double)`** diff --git a/sample.vala b/sample.vala index 3327a18..a31c326 100644 --- a/sample.vala +++ b/sample.vala @@ -31,7 +31,7 @@ public void main (string[] args) { var carolineWidget = new Caroline ( x, //dataX y, //dataY - "bar", //chart type + "scatter", //chart type true, //yes or no for generateColors function (needed in the case of the pie chart), true // yes or no for scatter plot labels ); From 6a3f56806030bcd39405cc96266cf46b420faebd Mon Sep 17 00:00:00 2001 From: David Johnson Date: Mon, 9 Mar 2020 19:06:33 -0400 Subject: [PATCH 09/10] updated readme --- demo | Bin 0 -> 69776 bytes screenshots/5.png | Bin 0 -> 32434 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 demo create mode 100644 screenshots/5.png diff --git a/demo b/demo new file mode 100755 index 0000000000000000000000000000000000000000..4b1034bfe653c460c9f9d62fcf7a8e2181c4137a GIT binary patch literal 69776 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 z z+FxI7$r?*bESYY zdYO7nZv3$#{LykTvl_ijsMcdaWyNluWEOaqmhxrm5)_qoPM)&G9?WLqGBdMG#k*tJ z$qSH$rJGk|6I78^ZoVwt@Eux5y`}4CYEGt;*Gv`^rz+%43gAc7md2CU@emE0g$Wpc zsdP0F@GkNJr65z0&I0#hU$#?Q928rzEMQ{8hUx@!MCg?bnUG;G zNL}de-UOzsJFWL{v|iy7{h@%Gst4}i@Erov@aacc8WL5Ng?BrSbq)TcjZMeFPNkeo zCy<{a$0xhb1@H)DA!&aWRUqrGR6VMUMfh;zzc?iC3 zS5JdWqM-&O+_Vj9FIBCqZWPhb+A-mad6al#MO;jBIow>lGMx*(pHC-0M2AKoE(>nR zj~}0srktcSi2JI8(B5oZ=ec8xoM6c>eA^G#CBvwVBAP0U@ZSyu3L-)+AeBbkn^kKI z)dPqkUdd5!Giy=R-gj50gL$OQrDWe;OJLE7`$W}}b+3UQ!=!B0&oS@n($L@gc%Mkg@tmPKPRq7RCWrEU@0yg;FP|v~^_hyNaMU`$7=pw8;;x1v9^E z8{M_)QbL9WJWfgNXvs6_V?r{k1n$TcQ7}@82^2+}KaGCB{aaR#yfO}JNw&OgF25!8 zsrr$prk~?nT-@GEs>DS6GhRl$kP#F80jRzCP!4ApzZQjU>HlirYpW3_#Ov%8WR z;e!pQr-wZtfu%d#F}+zL_hgCDVxiC$_ziB5oW6?tj`owL;Z4Y3l`Hm~_e_ec=CH0p zJW7dAzu2(oI?z$0l+rppFH~FpMu$gTmKkHrOik|fK@Pe=P%J500lE3kZ{WIsm-Xw9pf48yUywY|G~N;dzViIzM6a5_aXKA1{$IU)c{G&o|M&D! zX(34}WQ!6)S<9O2`#P4HknEke|-f21o1Y*thf7rg=f6a=zAb7)>2wrQJb3*6DCGid}G$#9V6;-x$MK8K&vUV zmYs!VNu{XK84*r1Z~+Z00Y7hW)YLWJw&H-^*RKy65C0!!0#N#4(b;7fFlV7$@8lE$?p_$oSC(tNmq$ z|2cfHtq1luNX!MM@eqafwopWAzYF+1zR!Jr@4CY1rwVWArP`W)JP*DxZ%4Uw$-Fef zy7>mzo0kt;n@)lCtZcW#J{n#t(Us1tNgpuoPb^D(+E7JE&%eUA!P>UZEPiK7Khe^x zvI1k?`Vsy`9H}1|NY1Wnnm0UOQ5*++veen38fz@~e@K@{!>PARN=Twg9wboy#t(i%= zQlRUpn{IBr{cNWqzm$P?p^PL7Ym`{b$eD_EN*ks3zsn_jd7)5QTv{mFiE4H_SO7#y z&`%BxvZM4lV5qEIy=^l5(ywaw(-%m#-Q0V^MIOo@I2aB&ApOiK)LO5C~hU~vTwGa)V z^7jqTm30`1cYuYtJdCs)cZ{m|3ogL*|L*x-bYKb25{ipw;s>vW!KEvweljSC0S7we$Z&L zCa@+ShYE0A5y$5)98^3FVGM;koEo*cJ}mA@^RiEreS$A58=KF4D!GeZ^7cS@bUtxo z-oQBEm!5{dPxZRJ>FgP}5>dzVO!;B?=jyb~ai^XgU6_Z$7t5{MUmS2X|n+}?T_ zu&#lQ{%xwgEAY24_y6<5*$hu*K0ly8l@WlyPZ;IjKht;;6ngD%dGG9r!)yZd;@ekX z$#V$ozfRq!kZW#nr(G#L=fL2|VuUp3I=7m6&qk$E=|T{-?1V00M_MPzAsTn)=_}p@ z3jgjAc(NAkD0|-@5=~Hu@SN`?+ zqqMUrrb{c;Y-7BjoPc%Rzh>VK=V5pG4@}_3uAGd6 z{YF0IN8d-)a(D6wUWQ~fK<&CMQ~U#nSWBC#7~d}{e?bPQg)!|H5@m!IDDkqmG+St-mepx%^HUHRRs}E|A(&=zjiu<_p8y`+6!bcqCp} zV_MfLpPW9NQ;qxy7hTx0k z^IdITKVILrT-vZsenz*b;tNhn{h)kx;g`||!C&9pc7z&;?@FQ z$S%uY;&_aYUGO(QIldX|V99NvT7%hbLR>Nd1(`^u3VxpI5lj-1|d zqGhUOishXqd3Yx$`6-xr=0mZfWGZ^>X~T~hZ1w`Av$kXJ(OL|j9*6>`VSFc?92Nd@ zXNA#?u=^HNG-GxG>8jhV#?VsPn;T*dyq{YQVQi{l3g-D2`q>Ur&e1W9A2r160BuxU zw#Ac$Uc`2IwrT17C)}-^J%5!vB`U%S%q5+=pEj*Z7iC>q<>!JNR<4MH2_EPWa(%nL zaB}k#4Gk&$Wxu4*LaylSehszF^zfA7tCZ)|xa;)2jLr%>%~plIoTr(j^S#)`AEOpK z(ILgrA#0zeLk1^%0MB+b74A77j?YQJ)zoDN#9hDx1k>u=P^t=FDDYDY$=9x}ukMZ) zb$kvLZog^Nz2AFK(%u^^|2y>v+Q}|RpB&gbN2sb6HzWM0cc0_AD9gAgO4$#t=b1@H zUKN4um<5ESK&9WlEM0l+{LL~Zg_9~)^M@UDv_1Z+tg;Ha($M^gkH*^C0`gCM=d94YC;0wq+1-<6!!b8+qqm8>y)t)ontV%FoiU`BytlX+#R`(vE zQLZj!XgcnoW+|vm967tTnK3N-`0`8w3s|f5YqNd|k|Uc{i$z14MT^BCCF*KxWU|qs zhZCx}R2XcO&e^W(nk?@#Dhf#in@d_-DJCUPm^l#CZFp~Z!aG&2b2Djhm;Fu_(#h2r z`GZVBgmhUKBzH1&{sxEeKgVKw-gtX2^lvs>lQDr=U6%SSy=o@j z!aF)Ux8SNlIzs&QjBjp(!RF87;xDCt3V9mLB?LJjsi6uYrAwVz><&`At*1>wv@SL) z@6HELtDzdC7@sbTuXRG=3ubBf{xN=uKjJ3tqG8TyGUgiGrPH-mb;9W#Go9Q9BEr=- zs_&b5;jh@52iND!>Y69K7nR|*uw-`Ai20sdno(Fl_+gi z+(e8?@u%9dz`&C+Sv{pqHM#e%=DwJClBIT#5k7G6p1Ys!cRx{nlgfp-SPP;$ z(C37X2(S!2yDv`N#f{>EY*%RTn2?Fn@89#=y%OLOvmi$+_&&4J<@(;Qz7AyKmyP_& zJVSOrq@EFPeTZpxwtAwonfl1*QLI-bzF?>T&D??h6}vQ!og3jsJvLET9`;WjAh7n_ zISz7c*q2euqeSvIvi&H%%S)7BDW31TzT{7dTy7|vZ(&uqC>`e_FlqMTepzi z`V<=nyo2AoPxin!hXcg3n7=N+1JtIwX&tHuO?=On1Hl>ZOoIZU{XwdW*N(IuLfYAf z2~S5hHfLUV-HkumNBxgIGpQ&xBxz}Xn9Us~FNDIPx@PtxdxEF2xpc(@5476aTPvVg z{1-8MVU=0!qvg;O{z`G|TIYc_RP0B*^P&pMhkx(W@jJ)FLsbEfYhT+HvE~Wp6mnDH zGonj%ZQe(QQR80^|oy~orPo4en5>Up$E0e&kIyQL5Z+t-1Of#;Ter=K6x zYoCY%8J|5~$eU(cIbG=&ny3g=RrTbJeaw_#) zps*(B@f84pntacxJDB4+e=z8lEJ?z68HHrZUvn7zv!P$H8;!T}rydC6wz7k**1VhY zqhL>uh3+4M;bzN*)eGo(Da1rdu5YFBR2Qd2Gh>5$YfDGA$$IVLtjf|O`vLswS_ZVY zlRM;|{{an8_(jPezv!bq8r8NJ6DjsYw1Ko};qH?w-O7`Dw%*gF;@DskKv)f9cv1}zwceDS#9JoMr zTM$Q2Vule5_=%lrHRT5Q?PFJy!TT#7S`PcnSJ8TCJcBkGuuhS`9SdTg&}h&N^zUpM(1Ukf1yDDWt|GM;ym$FngJwyx zw>GXxs08Q}Tm9z?mXq_Vs_>h^S&aWCF5j}F4;HeA2pU{q>4@@!j(YL6B7E*?hY!yY zY9-!%6#pjM?c^uVk~f!UyGWZY!TF#*KlEN?1F?Cmjth~>xX+_Xij!n-t71HqisrMA zO?9f9dY%6%KTeskK43EVtPB2Rd(}mcPzxg|J|}%{zx8kA z1vXH3zf!{bs%@Q1VEPn5i~jbZ^ditx5SQS(ZS6Q=(B;zalcTt&TH4#?J&YxU)`MI9O%iTS_hOZj$s4{YDm*2Y7RgDBB!x_^b{Z-;D4El29=i5VNu zFR2~O2-ckdVF7jW^Be1!Ht_olWR7x3<@m|#M}9g3T-fcED^pduuf8%(vA~$_!hu{a zUY7C3MA=k+V}o&hMw|8WH?NH=3|(M_(Rsf;F)oopuNv1j05K2N7rWF7^$BT&m>#XmVCqQB9VL(P;tn}^=G6R9}ox`Te-!h zvhI_8$L5V4fwIYX{je8<AphF9ums7r~Po!a=TT-drb(vQu6kcLA~!9F_&&` zk@4elh~C>5W=1YXluUd{W__xqVT{LZ4_#uj{PH*zkh^<96xXt(4si?LNl*NGb@*Yk)z(7n2_6Mn{6xZ7^ z9`Yb70R6OKMz+c(wTm-GwejQFq#L3s?n|Phuw{&nuu!3AS=pA=N2WLyM**&vKmHw6 zD%1IM6Lhazb)igCoKtEYWM$){I!(bT9TVB*f?QS3Lt{mO&(n|5I1OzRZ&c^p6D_r| zOtgLuy2};~+bn4Vs0oxlUvEOx4GFQ}rIR!ko%|QoqrvOR`c8Rm;;eBUV5qGn48)WeL1%#-G)TQgIcQ1ySx!IZTu%n;* zn91PRy<*@lwcC?wXeH`O6yMGsE>(ramkmHY0e{#CVqWe6KITQ!J+NFzs+lBi#|{2( z8j&PFYh9L#&4K;I&CA*Y8SmOlYp^+#sJI$bbFBd$Nhd>MGa>r`uCf=P z7v;Ge8KM`c6dMpj=81CA0n6v-yZpG^E>_im2r8dyp#ohKFUnPW>5m@5^V&Z#K2@qA zy%K)8ZHF^%Aek#>t|G*&-^LO=;JbHVJ+x1(S{lO^Kf6c@d?%9W>hxmTS&i zBSpAoVcz4dTSUOP9LqmH3E>Czg&$82oR2aGs`e-PYJ`PQdy{d+m(rZ`!Q2LV(v~U= zUP!=Gi@aX+kDC#-YtzdE^iP75J*T<5nvJ2=PekPlx{JQct$%G|*$u9%Kc5vC<5$hz zeu=OI>$%>%^FV=lhe=&p)9Q)It6`w-Rs*0uxueAK*2x9M$E8JES@oh0sm^qO>SQev zAMH#laKFbCITgj??tW9S+PQST)+BBx6%gjvYor4^A>(x$e#oKapKDAY_mAsNqd{A- z+Rb%qp0?|6iBvk%D3`RblOeQsFU)>x$YF?zpM1i)w;w|KreY9fcztt6Tuje=JHs`2 z(`9OWB^bBEd_-!+&l+2i73SsE$WPZ;lz zkVuP=RRUT*Ozg+a`|R+8ZaZEZFV3XmKUU+{>SKNM>2WA)Ot2B5Q8?u)_$v6n#kdI; za9e9t3RLpq_WeyCJ6MBNdN#cImJT3A?`2%@;`QAYIhV$E3LPeYA=Ws>)XEECrqkA_ z#W8ax+8^QVK|*8?F(lBEa9%0pvJl43#^lsCr`}>GNF`Ae)A(Z0DD0-W$1SZ~FJ{_L2`koS>gC#ow!-toK_&OkEz_ zO{`f5T0O3}sg+w~9|Zl7(5Rn$0GKq4KNawAz1S5rPXrS4PVWGN*An**p%v)ySDJ~5 znyKp<))Hmq%lBY?28UA9`^QY3oovaiK6hH{@64X7J%q@&CtMF^aI$xpFi3^a%)Sia2{qMP&0#($;YIaqFAz$?48g&s560NFM8b=%|pR zr(^d``!v0O9^0%rFqx6wCFW%#He*#=lWJWsU{g>`vr@A~Ql?ZsW9~`~wM$z^n zm+mgV(+@T}T~8F&d!xzVDC_C%-Tck33IaXP8JPLtwf3BVI6TaK!#kpRXiK_z5{WOp_$bN)bwJD)e{#K69mSK{ z(Eye}Pk~RG7A4Ksiw?uOoX?5nYmyeLiU29GOkBy$;=AT34Vo)h1vzf>gHQ@HRQ`!DF|C~2Akg`jMJl`%WUW#mk+sabf2vGT7C zg6TEdtyl8$e=r^GLS2TJ8@CMxo_UT7QEcxG9BQL_h-_~g&erw1slj7i&XV5>?PH}M>AYXD8&T%O&+#ZB*Y&$zB$)Za#@Yc8b~2oqGMymtMO;%^2;>?*GBxmhD9?Exz31ch{hKjLi7- zr4=bvX)v#A#N}JW+qdxrePKBN^4fABEq$Tew6aQg>*x8*AcDQ_Czw}S?-b_iLNvH6 z8Yq3PpLs7D!5&^i5$08BifT~SLoyXUK7@w)*Vyy+uW#M`w0u?^6S$^;VC?YHzO~6_ zJPo>R`?nWL!E3y)`xPrV(oHtu%(9SpA|I((zV^eWu`DnQmO{Ob=uHz5#AM(QCIT3S z-|R!4KL4U`Hx?8Sz1#zXx=m|v7Q%;~AXVmec9qp*GY)FU^%Qzqrg)wUwuDR=S-8p; z=CK#K-zY8h;PZZ$t(f&2r2?jTODy!Dp;$zqS%bdilzGGH4}W&9lnA=$-$7`_zGZQL zP9LR6cnEO*@+~<&I*{8tBVhZHI2W*EF0}_AJr-b=k#%v-s%Wz8*N0P; zf-XrB&cd>?=dFV=*A2{Aoz0_oYUf|Jm}CO;eI>l!QTR@7<;N<&ds&ZXTgP4XqONBd zq27^GxLm7++i%|vlLHJ>Q?x|dMqO<~O%cvhKmFU6IWuMgQ)#OH1TQbYk^9d-2&MghoJSt<9&%o{4omHg{1o2kpAY^BIQGvOpnpt{X ztwnpi!&$b-k8kC9&B2mleSQjQRNr1k=A!SH3g>A6O<1SAeH&5KX89hUCozy`Y_nI> zaBiS)uimONdj-%DzhU@=lFPFZpELrio2&?bM3B3f*>Q#UIQ2>-0~0 z`r}1hze{$nIYWNg+Y=E8Nt%)LG&isPRFD!V=x6+_c4j!RW%9{(Q?w#^l+hO`c9-wu zS{i(;<2~MMa+Bn>w~vh>Ns%=#yO^adyo28no5QOf@?XT@AAs$ieCV%R7H0799)Ni) z+CnD2)6(FJq}t#SZB-Jbyns2pj0~J^u@UBB5C+4r!WD9dW1n6`7kglY= zup!;3+1(|6@rO;5N$~U;SpZZ#`vTaa?a*7}-iQkx)6G1TCFrn9V%9TrXaN7ZCD2DF zWORcu4!cw2=*ZkIFc$0jl)U7|Pe*(OKuB?$xXjUZ{+`tjor1XY!r@2TAdxL3q|hMH zmMwIDNcYSi#aD6F#^_zo{H6t8X#693aglM?NdeQt%R+$5#@-H!Esx>gj^=6`pKcXkymcpuI*A!%SSG$3t=abWWA27&AnEE*F zrCu7gXdM#&NcJc(GszV3XpMV3wNnlRnw9dRZf``?QvNC;AyF;vvx>aJP=rm8_);VB zj~}{aC2rqfuH+&&2=uHfq<&ZBstMYW<~N;&Br9f^U@LX1z$W@aDoH)?p3lR&!+--G z*D5Yd?EGtkK2a;7n;EuLgof}6D{X@qXYP7nSsj`U^|D7^)d&^&&WHZ~ z{7`VL=xV#WI&Yv#J#2*TIAr73h$(ea>)Ae=DttIm?~viFZB^u2pAl3nfAE3=)&T^9 zl-RZLZ*f8B8PT)yz5N@F<8_O2O|+ri*$a1JwIzIN8;X3AWOnJj#Q}7z_2()nl9*4U zpCnK0>W_)3;;v@Cg!3x*V@K<>?1+?9+ODEl8l2kWH|SYU^7*;(ehW=K@s}v*{S9@E zH`$Zydxk1&9Dhn>7dEqTRZzz!@py}kx`hm;&Q%|43{Lu?Y7yAW!7xnqA z3)*qcHciN@6k=RQsp5$j#&kY|U7mLF8eSZ`P%N4MRYxhh**ZSC?J16C9DJ{dNkp3%rIy4hUN=d%H@~{e#Kake1kCp$2Jz#S zNcGO%KWho41}bit-~F2vQ19H|d^fT>ysX)!EXN@4lXVOXT=a+}1b z(M}w;C~HlGHnDR3gq5$djrQgG=;02^=mWNN`fj2pbBA`gvqaaIqoGM(ysvx6Hf^yc zZhTFF3i#}Qt5ms++%UAd-+_zxj*||BzVn6o1f*Q}4X@eZ+nL4Y+vr3$Jy zZ{<$D7#v@4XG@}+a#z~%A2ZxF-8N%DG=^*MVgxO?xRIc10k zZ`VMV-MsTvl99C(l>ZRA%>K2X_~~=<8Yhb><3hGlkL7uE3#^&47p^dTjQ0`X}C=4$*ZT4vk(+nX`QN+7w15arDZt;c;*V$P9@uS>X3q` z^%c{^CF?i138}G>?Z_|nRv1rHJI))861v363rC@qYV|H7OPwQ59@#Uq7p`Tsj1pEF z(sgzp^WkV0=-hJ&HTUPAp-G3=42pn_Z@kep_)^Q)7L(xpG zV@NI>QXICSN%Tx2%h$KrVC=0=E9vgg)F0&+RodI_M*jx3Q2Q<42)*68phcA=I*9AZ zV(0j2F5$b#SNeFzYAb-OQQxLjuAMUSnuzb}Z z6$bxs`NgmQ5@9(`ODVH%3%D8M+vHEU^AgTKT^HGw_Z+Io$47%#^rqKfl&I%pSUgB% zD~z(c3g~W#r+nYw{PXouD~w1lT899qaGk;-MdRibO_nF?)YNEe&?0*k4B0ov?|8d5 zk{yb}Ll9ZGw_Z9*jW`>+5Spezrt|3rF6TWT3C{LXNr<`ZJgH!a^!N>WuMI5SS-5(* zKKk+Ore82qfi>m<`ays%P41wd>pE=XA+a$I5{Ki(B{d{o zj`YxSF1ZDucjyBGNVYT*4s8yc`dT_iN{D+EVYh0k_4!b=(hg>fA` zb1A;$rLk$bvc(~pxkbD6Q*V06gN=CYdwFEPdISb2RRDqhp!x;vFamS_ugT~xt`mU9 z@z+P_@H)>G7{vkYw;f>U1?(ZU9pOZnP5se!2!cV!%1OudEnn8c+_hPFA5RFM=!a9_ z4gGCq{}8SlXYU=!!OL>UX8^G(jWT@=D-qc3SmwXfjyzIh6%gMxVbuo5*#}f3cnEK9 z_Ql{5d!*e?)Hsml?aD|p9(Xl8^Wf2wZ)22o>`z1hN1hIz7Ewoz0^zS7R!{v_PU4uW zvtQ7K*Ka4a@gSM{KIVwi(X7A1CzKwv`(*yzQ-j{Op{e3Hylr* zd8TZSSg{ELtKPzaN1<%00S5KZ%)GNhdI8l_{N)o%A>W-ys%)r;!tt#h6K3uFwm z9C>++mq;I-7CE8V$$j!wEmD4gmS@HL&&0?m;n5Xg(hv_b6apBX&R~sPRx zf!72w(>e5j&|ITm?RZVsR%zM<3E#|6u*KMk91i5TfGw(-x@r}nhmo#l@_``u4~Nk+ zMFCR8S$_ny9xHGpKgH)NrDN}CwQDMj_amkKELZ-R0rgnB_A+L~UN6vAspWV7(>}q4 zoR-mlW-V~YX{syRzPqep-NT0TJ8|6+KeU0F0W0*fz}^9ZEVV1B9YW6FEj@#Dh#6cH zTdPJVD1ty+Lxz}qQk}pWOD38$f7aF;dRW*4F;lB>FIt*4Z4A$c2B4Vih3Z*HFBEBy zpFOGxx_b#i5g+M7D*csDWyn^cXI`-@BCi?}M~O>k=MW87RhcGDKa&S;!^k zk#}9N#n)5kDSX!!(1CE6j{76u;QVY_H<1~1*Yo+0libZ=XLvFq^7}88m*)huX%2Mi!CrpJxk2rp+Tbg$tHglYr^XR6xb5 z|CN`$>q&wG#42Gve(;C#SM3&O6q7TK5tuS0hN(xV)O$cM$J&wgFke+i86D4EoKgIa z`n1-$6GuRfC%7L#4(A;YtAEUKZM9AI{HF1`at_z{Kvdx zy_lo9e(R=np0}Iqv#cV1D=QtnxP z<9tAPl;ufatujL$QUp$B@%^_KM!(Z;U|{f>UFxaU;goC-AlJ$df+D5s-{!qQ$peEmZ zWCfwTt&jGig5O>V1d6Q@H$+%~XupG%a`*-EqM-Ra6*S;2vbsbf0fiS3?A*&4oUa=1!19rTfjA8LL$4Q% z4fNA_z78}rNB1R9dZ#CymwlIc$XXCgeB>GldaSo8^iPdw6(D^L^EwTq5I@vKzoY<) z2Wb5o^6Q!P)wRA(IB-sOTHp$@fhM62xPkyx@1sH|PcM)$-+uZlke||b*rp-$?koa! zrWY7~T-^BgP@vub;2H$hixC%kfx>T^4x3ZtfBoN~?)d-aPvrXn+cRmc?Jx0}Kr<;2 ONKeaHv;5wJu>S>7NCNNx literal 0 HcmV?d00001 From c8b04774cce072ecacd12233e2871d10e56bd02b Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sat, 14 Mar 2020 10:46:22 -0400 Subject: [PATCH 10/10] bumped date and version, final tests --- Caroline.vala | 4 +++- demo | Bin 69776 -> 0 bytes 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100755 demo diff --git a/Caroline.vala b/Caroline.vala index 8d636cf..9a201da 100644 --- a/Caroline.vala +++ b/Caroline.vala @@ -1,6 +1,8 @@ //============================================================+ // File name : Caroline.vala -// Last Update : 2020-2-16 +// Last Update : 2020-3-14 +// +// Version: 0.1.0 // // Description : This is an extension of a GTK Drawing Area. Its purpose is to make it easy for any level // of developer to use charts in their application. More in depth documentation is found in below and in the diff --git a/demo b/demo deleted file mode 100755 index 4b1034bfe653c460c9f9d62fcf7a8e2181c4137a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69776 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 QRR;Hy598=Laey