grafica library

Hi!

I would like to announce a new library for creating 2D data plots with processing. It's called grafica and you can download it from the "add library" menu.

You can read more about it at the library github page, but probably the best introduction is to try the examples.

Let me know if you have any suggestion for future improvements!

«1

Comments

  • I just skipped through the examples and I'm blown away. I will get back at you when I'm using it. Props!

  • Thank you mrzl! Your feedback will be highly appreciated!

  • edited October 2013

    I am also very impressed, I am tempted to port the examples to ruby-processing, there are plenty of rubyists out there looking for cool ways to present their data. Painless here it is in ruby-processing

  • Wow, thanks a lot monkstone! I never used Ruby and I didn't know that it could use java libraries so easily. Unfortunately I don't think it will be that easy to port it to processingjs :(

  • Yeah it is dead easy to use processing libraries in ruby-processing, they just need to be installed for vanilla processing and we can pick them up. But better than that, just see how easy it is to work with csv file in ruby-processing (cf vanilla processing) in your Oktoberfest example.

  • Nice! Thank you for porting the examples, monkstone!

  • This is a really nice library =D> and perfectly fits my need for a project I am currently working on.

    I do have some suggestions for the next version.

    (1) I suggest two more constructors for the GPlot class

    public GPlot(PApplet parent, float plotPosX, float plotPosY) {

    public GPlot(PApplet parent, float plotPosX, float plotPosY, float plotWidth, float plotHeight) {

    It is most unlikely the user will want to use the default position and size so this makes it easier to create, position and size the graph in one statement. It took me awhile to find the setOuterDim method.

    (2) Although the mouse controlled zooming, centering and panning work well individually they don't work well in combination. For instance if I have panning and centering both switched on, when I attempt to pan if centers on mouse down then pans on drag. Also I soon got 'lost' when zooming and centering were both switched on.

    I would recommend that you look at alternative ways of doing this. I have some ideas about resolving this area if you are interested. :)

    (3) Provide a zoom method with one parameter which zooms in and out on the graph center.

    (4) Might be nice to have a method to reset the graph back to its un-zoomed, un-centred, un-panned state.

    These should not be taken as criticisms, they are as I said suggestions.

    Again congratulations on a job well done.

  • @jagracar - very nice indeed. However, just a note, and it is likely my issue as I'm fairly new to using Chrome - BUT, your examples run in FireFox, but not in my Chrome. In FireFox, every openprocessing.org sketch causes FF to display the risk dialog, to which I click Run; I'm used to this. Chrome doesn't do this; just presents the blank sketch window.

  • @clair - perhaps it's refusing even an option to run Java applets b/c you got an old version.
    Make sure you have the latest Java SE (JDK 7) installed! %%-

  • @GoToLoop --

    BUT - GoTo -- other processing sketches run fine in Chrome - so that can't be it.

  • edited April 2014

    OpenProcessing.org site hosts both Java & JS modes. It's Java 1s which demand Java applet and permission to run! ~:>

    And a side note: Chrome is a privacy hostile browser. I advise you to replace it by Chromium or SRWare Iron instead. (*)

  • @jagracar - since the library examples show in FF means ANY problems with showing them in Chrome has NOTHING to do with this library so if you are still having problems please create a new Topic. Let us keep this topic free for comments about the Graphica library

  • Thank you for the suggestions, quark!

    (1) You are right, I will include those additional constructors in the new release.

    (2) I have to think about the combination of panning and centering. Maybe when the user enables them, there should also be the option to specify the mouse button, or something like that. Which was your idea?

    (3) There is a public zoom method already in GPlot, but it might not be very useful for the user, because it uses the x and y sketch positions:

    public void zoom(float factor, float xScreen, float yScreen)

    I will create a similar one using the relative position in the plot, so you can use zoom(2, 0.5, 0.5) to zoom it in the center.

    (4) might not be that easy to implement... I have to think about it :)

    @clair I have no clue what the problem could be. For me Chrome runs it with no problem (in Linux). Btw, I exported them with quark's exporting tool ;)

    Thanks again for all your comments!

  • edited October 2013

    I only suggested (4) because I got lost very fast when I enabled zooming, centering and panning together, if that problem is resolved then it is not needed.

    Since I last posted I have been playing with Graphica because I want to use it to create examples for a new library I have developed which evaluates expressions and algorithms entered by the user at run-time (QScript)

    While looking at the source I found the zoom method, I agree its not much use to the user and I like your suggestion but I would also include a zoom method with a single parameter which zooms on the centre of the graph.

    So now we come to the tricky bit - zooming, centering and panning together. It would be nice if the user can select to have them all active but then the library make intelligent decisions based on user input.

    My first suggestion is that zoom and centering is implemented on the CLICK event rather than PRESS event this will immediately separate these functions from panning. If labeling is active then we need to distinguish between a click for labels and a click for zoom/center, I suggest that a mouse click when the shift key is pressed be used for labels, but if the shift key is not down then either zoom or center or both.

    Next the current zoom code in the mouse event handler not only zooms but centers! I suggest that this code be modified to just zoom on the current center of the graph.

    Next implementing the behaviour when the mouse is clicked and the shift button is not down. If zooming OR centering is activated (but not both) there is no problem simply perform that task. The problem comes when both are activated. The approach I suggest is that if the mouse is near the centre of the graph then zoom, if it is further away centre the graph. This has some logic in that you are more likely to center on a position away from the current center than one that is already close to the centre.

    I hope all this makes sense because I have modified your mouse event code to implement these ideas and posted it below.

    The code below can be tidied up but I leave that to you since you might want to play with it and make changes.

    public void mouseEvent(MouseEvent event) {
        if (zoomingIsActive) {
            if (event.getAction() == MouseEvent.CLICK  && !event.isShiftDown()) {
                float xMouse = event.getX();
                float yMouse = event.getY();
    
                if (isOverBox(xMouse, yMouse) && (!centeringIsActive || closeToCenter(xMouse, yMouse, 0.1f))) {
                    if (event.getButton() == LEFT) {
                        zoom(zoomFactor, pos[0] + dim[0]/2 + mar[1], pos[1] + dim[1]/2 + mar[2]);
                    } else if (event.getButton() == RIGHT) {
                        zoom(1 / zoomFactor, pos[0] + dim[0]/2 + mar[1], pos[1] + dim[1]/2 + mar[2]);
                    }
                }
            }
        }
    
        if (centeringIsActive) {
            if (event.getAction() == MouseEvent.CLICK && !event.isShiftDown()) {
                float xMouse = event.getX();
                float yMouse = event.getY();
    
                if (isOverBox(xMouse, yMouse) && (!zoomingIsActive || !closeToCenter(xMouse, yMouse, 0.1f))) {
                    center(xMouse, yMouse);
                }
            }
        }
    
        if (panningIsActive) {
            if (event.getAction() == MouseEvent.DRAG) {
                float xMouse = event.getX();
                float yMouse = event.getY();
    
                if (panningReferencePoint != null) {
                    align(panningReferencePoint, xMouse, yMouse);
                } else if (isOverBox(xMouse, yMouse)) {
                    panningReferencePoint = getValueAt(xMouse, yMouse);
                }
            } else if (event.getAction() == MouseEvent.RELEASE) {
                panningReferencePoint = null;
            }
        }
    
        if (labelingIsActive) {
            if (event.getAction() == MouseEvent.CLICK && event.isShiftDown()) {
                mousePos = new float[] { event.getX(), event.getY() };
            } else {
                mousePos = null;
            }
        }
    }
    
    private boolean closeToCenter(float x, float y, float closeness){
        float[] mp = getRelativePlotPosAt(x, y);
        float low = 0.5f - closeness, high = 0.5f + closeness;
        return mp[0] > low  && mp[0] < high && mp[1] > low  && mp[1] < high;
    }
    
  • Thanks for the code Quark. I'm quite busy in the next weeks, but I will implement the changes after that. Good to see I'm not the only one that is using the library :)

  • I'm quite busy in the next weeks

    No problem - obviously you will need time to test out my code and see if it does what you expect and want.

    Personally I am using graphica with G4P to create examples for my new library, QScript (yet to be released). It means that the outer dimension is important to me so that it doesn't clash with the G4P controls. I notice that when I change the margins with setMar() it changes the outerDim rather than the inner dimension. That leads me to another suggestion, while reading the reference I found it difficult to work out whether it was referring to outer dim or the actual graph area. You might want to look into that ambiguous references make the learning curve steeper.

    Good to see I'm not the only one that is using the library

    I am sure there are others using it, unfortunately neither GitHub or Processing report the number of downloads.

    It might be interesting to add methods to extract information from Processing's Table class and display the data. Also methods that take 'raw' data and does the conversion to GPointArray for the user would male it more accessible to novices.

    Overall I think graphica is great and there is plenty of scope for further development. :)

  • Hi jagracar, I'm using your library and find it most useful for my needs. =D>

    Could you have a look at my preferred way of zooming with the mouse wheel?

    Also, I like "autoscaling" to find my way back to the original plot display. In the example below a double click achieves that, although a simple right click would be enough if your default panning only reacted to the leftmouse button.

    Thanks and here's a slightly modified version of your DefaultPlot example.

    import grafica.*;
    import java.awt.event.MouseWheelEvent;
    import java.awt.event.MouseWheelListener;
    
    // Create a new plot
    GPlot plot = new GPlot(this);
    
    float[] xLimAutoscale;
    float[] yLimAutoscale;
    
    void setup() {
      size(500, 350);
      background(150);
    
      // Prepare the points for the plot
      int nPoints = 100;
      GPointsArray points = new GPointsArray(nPoints);
    
      for (int i = 0; i < nPoints; i++) {
        points.add(i, 10*noise(0.1*i));
      }
    
      // Set the plot position on the screen
      plot.setPos(25, 25);
    
      // Set the plot title and the axis labels
      plot.setPoints(points);
      plot.getXAxis().setAxisLabelText("x axis");
      plot.getYAxis().setAxisLabelText("y axis");
      plot.setTitleText("A very simple example");
    
      // Activate panning
      plot.activatePanning();
    
      // Preserve initial XLim and YLim for autoscale
      xLimAutoscale = plot.getXLim();
      yLimAutoscale = plot.getYLim();
    }
    
    
    void draw() {
      // Draw it!
      plot.defaultDraw();
    }
    
    
    // Zoom with mouse wheel
    void mouseWheel(MouseEvent event) {
      if (plot.isOverBox(mouseX, mouseY)) {
        int delta = (int) event.getAmount();
        float zoomFactor = pow(1.1, -delta);
        float dim[] = plot.getDim();
        float plotPos[] = plot.getPlotPosAt(mouseX, mouseY);
        float corrX = (plotPos[0] - dim[0] / 2);
        float corrY = (plotPos[1] + dim[1] / 2);
        plot.zoom(zoomFactor, mouseX - corrX / zoomFactor, mouseY - corrY / zoomFactor);
      }
    }  
    
    
    // Autoscale with double click
    void mousePressed() {
      if (plot.isOverBox(mouseX, mouseY)) {
        if (mouseEvent.getClickCount()==2) {
          plot.setXLim(xLimAutoscale);
          plot.setYLim(yLimAutoscale);
        }
      }
    }
    
  • Thank you for your comment gyula! And thank you for your code improvement suggestions. I was busy with other projects during the last weeks, but I hope to have some time this week to improve the library. I have to think on a better way to deal with the mouse events. I realized I need to give as much freedom as posible to the user, letting him to decide the mouse button to use, the number of clicks etc.

    I will let you know when I upload the next version.

    Thank again for all your feedback guys!

  • But in any case, your example just shows that the library is versatile (and hopefully clear) enough to let you implement your own mouse event methods. I'm happy about that :)

  • I hope to have some time this week to improve the library.

    @jagracar - Thanks a lot in advance!

  • @jagracar - Hi, still on autoscaling... :)

    I've noticed that whenever XLim and YLim are calculated or updated automatically, you rely only on the main layer. That could easily hide points on other layers in a multi-layer plot.

    See this example sketch, where "layer 1" contains a shifted and extended version of the points of the main layer:

    import grafica.*;
    
    void setup(){
      size(500, 350);
      background(150);
    
      // Prepare the points for the main layer
      int nPoints = 50;
      GPointsArray points = new GPointsArray(nPoints);
    
      for(int i = 0; i < nPoints; i++){
        points.add(i, cos(i/PI));
      }
    
      // Prepare the points for another layer
      GPointsArray points1 = new GPointsArray(2*nPoints);
    
      for(int i = 0; i < 2*nPoints; i++){
        points1.add(i, cos(i/PI) + 1);
      }
    
      // Create a new plot and set its position on the screen
      GPlot plot = new GPlot(this);
      plot.setPos(25, 25);
    
      // Setup for the plot
      plot.setPoints(points);
      plot.addLayer("layer 1", points1);
      plot.getLayer("layer 1").setPointColors(new int[] {color(0, 0, 255)});
    
      // Draw it!
      plot.defaultDraw();
    }
    
  • Yes, that is not a bug, but a feature... Or at least that is what I thought when I coded it :)

    grafica gives more importance to the main layer (the first one) than the other ones. I guess we can find good and bad cases where this is good. One good thing is that it's faster, because it doesn't have to loop over the layers to calculate the limits.

    What I can do is to provide a method that uses all layers to calculate the limits. That should be easy.

  • @jagracar - Or you could perhaps make the protected methods obtainXLim() and obtainYLim() public, again leaving it to the user to loop over the layers.

    Just an idea, and thanks again.

  • This is a really good library, but am I able to change the size of the graphs? I am working on a project due in a few weeks.

  • edited November 2013

    Hi masseyj1. Sure you can :) You can fine tune almost all the plot parameters in grafica. Take a look at the examples that come with the library. In particular the multiplePanels example shows how you can change the plot margins and the plot dimensions. You can change the margins, the outer dimension and the inner dimension (the area were the points are actually plot) using the following commands:

    plot1.setMar(new float[] {50, 20, 50, 0});
    plot.setOuterDim(new float[] {300, 300} );
    plot1.setDim(new float[]{ 200, 200});
    

    If the dimensions are too small, you may want to change also the text fonts with the following commands

    plot.setFontSize(fontSize);
    plot.setFontProperties(String fontName, int fontColor, int fontSize)
    

    For more options, take a look at the javadoc that comes with the library.

    Cheers!

  • edited November 2013

    Hi! I finally found a relax weekend to work on the library and I'm happy to announce a new version! :)

    I tried to include all your suggestions. These are the main changes:

    • New GPlot constructors, as demanded by quark.
    • Enhanced mouse events! Now you can choose the mouse button (including the mouse wheel) and the key modifier (check the multiplePlots example and the javadoc). Y also added a "reset" mouse event: plot.activateReset(). You can use it to restore the plot limits to the values before any mouse interaction.
    • Added a simple zoom method that zooms in and out the plot center.
    • Added a new constructor for the class GPointsArray: GPointsArray(float[] x, float[] y, String[] labels)
    • Added a new method to update the plot limits: plot.updateLimits(). You may need to unfix the axis limits before: plot.setFixedXLim(false), plot.setFixedXLim(false).
    • Now grafica uses by default all the plot layers to calculate the axis limits. Before it was only using the main Layer. If you want to use the main layer only type plot.setIncludeAllLayersInLim(false)

    If everything goes fine, you should get the new version of the library in a couple of days. But if you don't want to wait, you can download it directly from the github page:

    https://raw.github.com/jagracar/grafica/master/releases/grafica.zip

    Have fun and let me know if you find any bugs! javier

  • Thanks jagracar! You're a lifesaver! I'll come back if I have any more questions!

  • Hi I'm using your library and I have one problem. How can I delete draw and insert new points for new draw? I receives a packet of data from the RS interfaces and would like to be represented on the graph.

  • Thanks for this great library!

    It would be great if you could consider the following future enhancements:

    1) Ability to display bitmaps (png, jpg, etc) similar to the current .svg vector image support. I found that saving my bitmaps as an .svg file didn't work most of the time. I believe it's because the .svg importer in Processing is extremely "picky" about what's in the image. Being able to use loadImage() versus loadShape() in the graphs would allow me to use .png images.

    2) Ability to set the time interval between grid ticks as opposed to the default auto mode.

  • Hi PaJaPa! You have to create a new GPointsArray each time with the points that you want to represent and add them to the plot with the .setPoints() method. Check this example for more info:

    http://www.openprocessing.org/sketch/103902

    I must admit that this is not very efficient, because it creates a new object each time that a point is included. I will try to create some addPoint() methods soon.

  • edited March 2014

    Thank you for your suggestions dttworld!

    I will include your first point in the next release. I don't know why I didn't think about that before :)

    I have to think about your second point. Do you refer to the drawGridLines() method? That method uses the xAxis and yAxis ticks, which you can modify if you want. To do it you need to get the axes first with the getXAxis() and getYAxis() methods:

    GAxis xaxis = plot.getXAxis();
    GAxis yaxis = plot.getYAxis();
    

    Once you have them, you can use the setTicks(float[] ticks) and setNTicks(int nTicks) methods.

    But I agree that this is not optimal. I will add those methods to the GPlot class to remove the need of getting the axes.

    I will also add a setTickSeparation(float interval) method.

    Cheers!

  • Jagracar,

    The setNTicks will do for now.

    Thanks!

  • Hey community...

    I'm very glad to use this powerful library, it help me to save a lots of time to draw 2D graphics.

    However, i have an issue to ask. I want to know how can i set the scale of both axis (x and y), because i'm getting different scale for each axis and that cannot provide to me a good understanding of line plotted.

    Thanks in advance, JS

  • I love this library.

    Look at the MultiplePlots example to see how to set the scale: example:

    plot1.setXLim(1, 100); plot1.setYLim(0.1, 3);

    Only thing missing for me is being able to set different Y value scales when a plot has multiple graphs. Perhaps the author could consider that in the next release .....

  • I'm happy to announce a new version of the library :)

    The new version is more memory efficient and handles a bit better the point addition and removal with the following new GPplot methods:

    • setPoints(GPointsArray points)
    • setPoints(GPointsArray points, String layerId)
    • addPoint(float x, float y, String label)
    • addPoint(float x, float y, String label, String layerId)
    • addPoint(float x, float y)
    • addPoint(GPoint newPoint)
    • addPoint(GPoint newPoint, String layerId)
    • addPoint(int index, GPoint newPoint)
    • addPoint(int index, GPoint newPoint, String layerId)
    • addPoints(GPointsArray newPoints)
    • addPoints(GPointsArray newPoints, String layerId)
    • removePoint(int index)
    • removePoint(int index, String layerId)

    There are also some new GPlot methods to specify axes ticks separation or the total number of ticks:

    • setHorizontalAxesTicksSeparation(float ticksSeparation)
    • setVerticalAxesTicksSeparation(float ticksSeparation)
    • setHorizontalAxesNTicks(int nTicks)
    • setVerticalAxesNTicks(int nTicks)

    And finally, I added a new method to plot points using a PImage:

    • drawPoint(GPoint point, PImage pointImg)

    I updated the examples to reflect some of the changes, and added two new examples.

    Let me know what you think! javier

  • edited April 2014

    @dttworld : I think your suggestion of multiple xLims or yLims would complicate the GPlot class in excess. You can use a trick to obtain the same result:

    • Create two plots at the same position. Each one with two different data sets and limits
    • Plot the first plot with the desired vertical and horizontal axes.
    • Plot the second plot with the other axes, but don't use drawBackground() and drawBox()

    I will create an example when I find some time.

    Cheers and thanks!

    javier

  • Javier,

    I have tested the PImage and it works fine. Unfortunately, I just found out that the .scale and .translate don't work with PImage in Processing :-(

    That's a good idea for plotting multiple Y scales. Maybe in the future you can add a way to set the position of the y scale numbers (either right or left side of the graph).

    Thanks for your efforts in this new release!

  • edited April 2014

    Hi dttworld,

    The code to plot the PImage is using the imageMode(CENTER) option, so the image should be centered on the point position. To change the size you can use the img.resize(w, h) method from PImage.

    To set the label separation you need to get the axis first and then use the setTickLabelOffset and setOffset methods:

      plot.getYAxis().setTickLabelOffset(20.5);
      plot.getYAxis().setOffset(10.5);
    

    And to draw the labels in the right and top axis:

    plot.getRightAxis().setDrawAxisLabel(true);
    plot.getTopAxis().setDrawAxisLabel(true);
    

    I highly recommend you to check the javadoc documentation for all the grafica classes. There you will find all the methods available, that in many cases are not covered with the examples.

  • Javier,

    I have tested the PImage and it works fine. Unfortunately, I just found out that the .scale and .translate don't work with PImage in Processing :-(

    That's a good idea for plotting multiple Y scales. Maybe in the future you can add a way to set the position of the y scale numbers (either right or left side of the graph).

    Thanks for your efforts in this new release!

  • Javier,

    Thanks. I really would have not figured this out without your pointers. Reading the Javadocs is second nature to a guru but it is still confusing for many beginners.

  • You are welcome, ddtworld.

    I really recommend you to spend some time trying to learn how to read javadocs. It's really not that difficult and you will win a lot in the long run. Specially if you plan to use java libraries with very few examples like mine ;)

  • @jagracar - Hi, I took a deep breath and started using your upgraded library. Now it's much simpler for me to zoom and reset the plot the way I like it:

    import grafica.*;
    
    // Create a new plot and set its position on the screen
    GPlot plot = new GPlot(this, 25, 25);
    
    void setup() {
      size(500, 350);
      //  frameRate(10);  
    
      // Prepare the points for the plot
      int nPoints = 100;
      GPointsArray points = new GPointsArray(nPoints);
    
      for (int i = 0; i < nPoints; i++) {
        points.add(i, 10*noise(0.1*i));
      }
    
      // Add the points
      plot.setPoints(points);
    
      // Activate panning using the LEFT button
      plot.activatePanning();
    
      // Activate autoscale using the RIGHT button
      plot.activateReset();
    
      // Activate zooming using the mouse wheel
      plot.activateZooming(1.3, CENTER, CENTER);
    }
    
    void draw() {
      // Draw it!
      plot.defaultDraw();
    }
    

    What I still could not achieve was limit zooming, panning etc to only one direction: x or y. I thought the methods setFixedXLim(true) and setFixedYLim(true) were meant for that, but they didn't work for me.

    Could you give me a hint as to how I should use them? Thanks!

  • edited June 2014

    Hi @gyula. I must admit I didn't consider that possibility. The setFixedXLim(true) and setFixedYLim(true) methods fix the axis limits to the current ones, with only one exception: when the zooming and panning are activated and the user starts to use them. At that moment, the GPlot class unfixes the limits again.

    But I agree with you that it will be useful to keep the limits fixed in some cases. I will create two new methods:

    • plot.activatePanning(boolean fixLimits);
    • plot.activateZooming(boolean fixLimits);

    If fixLimits=true, then it will produce what you really want, and if fixLimits=false, will do the same as plot.activatePanning() and plot.activateZooming().

    I will let you know when I have it ready. Cheers! javier

  • edited June 2014

    @jagracar - Thanks.

    If fixLimits=true, then it will produce what you really want, and if fixLimits=false, will do the same as plot.activatePanning() and plot.activateZooming().

    What I really want is fixing the limits of only one axis (x or y) at a time with a key modifier WHILE using the mouse button of my choice (wheel for zooming, left or right button for panning, not the default buttons that plot.activatePanning() and plot.activateZooming() offer).

    Since both plot.activatePanning() and plot.activateZooming() have several overloaded variants, wouldn't it be simpler to let the user decide if she wants to fix the axis limits? I cannot understand why this decision is overruled by panning and zooming.

    Thanks again for your efforts. :)

  • hi guys, is this library compatible with android mode?

    thanks!

  • Hi!

    In case you are working with p5.js, you should know that I translated the grafica lib to javascript. You can download it here.

    Cheers!

  • What a wonderful library =D>

  • edited August 2015

    Hi. The library is very useful for me. Thank you! Is there a way to change symbol type, text size etc. in legend?

    Thanks

  • edited August 2015

    Hi gostinets! The drawLegend method is using internally the drawAnnotation method, that uses the GLayer font. You can change the layer font properties with the setFontName(), setFontColor(), setFontSize() and setFontProperties() methods. But that will change the font in the point labels too.

    Unfortunately, you cannot change the legend symbol. Currently the method is only using squares. If I find some time, i will try to create a separate GLegend object with it's own text and symbol properties.

    Another option is to use processing to write your own legend over the plot.

    Let me know if that fixes your problem! Cheers, javier

Sign In or Register to comment.