Giter Club home page Giter Club logo

jplotter's People

Contributors

dependabot[bot] avatar hageldave avatar lvcarx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jplotter's Issues

HiDPI scaling creates flickering and scaling artifacts

We are trying to integrate the library into our open source Data Science Platform.
Our Application runs fine on windows with high dpi scaling, with the exception of the plots.

What happens:
When the Scaling is set to 150% in windows, the canvas will have a logical size of say 1000x1000. But in fact it should be backed by 1500 x1500 pixel image. When drawn, it will only fill up the space by 2/3rd. Then it seems to be scalled by some Java internals to 1500 but creating obvious artifacts.

What we tried:
We retrieved the current scaling factor and extended the FBOCanvas to apply it to the Frame Buffer and the image painted. We simply made it bigger and painted on the right position. It worked and gives us a crisp image without flickering.

Whats still the problem:
While the above worked for painting, the mouse events are still off. Even if correcting the mouse event coordinates, the calculations for selection will always use the Canvas size, which is not reliably related to the open gl image size. There are probably more like that and we would have to rewrite the entire code. So my idea was to get in connection with you as maintainers.

Global saturation change of basic renderables (Points,Lines, Triangles...)

The basic Renderables (Points, Lines, Curves, Triangles) already support global alpha scaling for the colors.
A similar mechanism for saturation of the elements colors would be great for operations like desaturating every element.
Such a mechanism should not change the elements colors but change the saturation of the colors during rendering (e.g. in shader or awt draw calls). This way changing the saturation of thousands of points is easy, reversible, and has good runtime performance.

Custom Color Scheme for CoordSysRenderer

The CoordSysRenderer has hardcoded colors for its axes, axis-labels, ticks and guides (grid lines).
This works well with a light background color (e.g. white) but not with dark ones. It may also not integrate well into applications with a specific color scheme.

To allow for custom colors of the coordinate system elements, a color scheme class and respective setter in CoordSysRenderer has to be implemented.


A similar (and probably coupled) mechanism has to be implemented for Legend.

Make FooDetails objects appearance (coloring/size) more sharable

Especially for Brush&Link operations, it is desired to change the color or size of many objetcs such as points at once (like desaturating them or making them translucent).
Currently classes like PointDetails have the coloring and scaling information stored as primitive types, so that each point instance has its own memory for these attributes.

It may be desired to have a single coloring information object for all PointDetails objects in a Points object or across Points objects, in order to save memory and in order to be able to change color of all points with a single operation without the need to loop through the PointDetails objects.
By using a reference to some kind of ColorInformation object within a Details object, sharing can be made possible.

The API can also be simplified by only requiring positional data in the add methods, which can return the PointDetails object so that further appearance attributes can be set (like ColorInformation). This will break chainable add calls, but should be worth it since adding these graphical primitives is seldomly done this way.

Of course this also applies to SegmentDetails and TriangleDetails.

Please help with java.util.ConcurrentModificationException

Experiencing the java.util.ConcurrentModificationException:

Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1631)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at hageldave.jplotter.renderables.Triangles.getIntersectingTriangles(Triangles.java:453)
at hageldave.jplotter.renderers.TrianglesRenderer.renderFallback(TrianglesRenderer.java:263)
at hageldave.jplotter.renderers.CoordSysRenderer.renderFallback(CoordSysRenderer.java:715)
at hageldave.jplotter.canvas.BlankCanvasFallback.render(BlankCanvasFallback.java:115)
at hageldave.jplotter.canvas.BlankCanvasFallback.render(BlankCanvasFallback.java:106)
at hageldave.jplotter.canvas.BlankCanvasFallback.repaint(BlankCanvasFallback.java:70)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

and quite dirty source code of JPanel (that draws 60 OHLC of price candles).

public class CandlesPanel extends BlankCanvasFallback /* BlankCanvas */ implements SignalSubscriber {

private static final Logger LOGGER = LoggerFactory.getLogger(CandlesPanel.class);

List<ClickSubscriber> clickSubscribers = new ArrayList<>();

CircularFifoQueue<Candle> candlesQueue = new CircularFifoQueue(60);

CircularFifoQueue<Triangles> candlesTriangles = new CircularFifoQueue(60);

Candle currentCandle;

PickingRegistry<Integer> candlePickingRegistry = new PickingRegistry<>();

long viewLow = 13_350;
long viewHigh = 13_450;

Calendar viewStart;
Calendar viewEnd;

public void addClickSubscriber(ClickSubscriber subscriber){
    clickSubscribers.add(subscriber);
}

public class Candle{

    public int id;
    public Calendar startTime = Calendar.getInstance();
    public double open;
    public double high;
    public double low;
    public double close;

    @Override
    public String toString() {
        return "Candle: id:" + id + " " + calendarToString(startTime) + " OHLC: " + open + " " + high + " " + low + " " + close;
    }

    public void recreateTrianglesForCandle(Integer position, Triangles c) {
        c.removeAllTriangles();
        // candle body
        c.addQuad(new Rectangle2D.Double(position,
                close > open ? open : close,
                0.9,
                abs(close - open)));

        // candle low and high pins
        c.addQuad(new Rectangle2D.Double(position + 0.4,
                low,
                0.12,high - low));

        c.getTriangleDetails().forEach(tri->{
            int red=0xffe41a1c, green= new Color(0, 204,0).getRGB();
            tri.setColor(close < open ? red:green);

            // set picking color for candle
            int pickingColor = candlePickingRegistry.register(position);
            tri.setPickColor(pickingColor);
        });
    }

}


TrianglesRenderer panelRenderer = new TrianglesRenderer();
Triangles priceHorizontalLineTriangles = new Triangles();
Rectangle2D.Double priceLine = new Rectangle2D.Double(0, 0, 60, 0.075);

@Override
public void newTx(long time, double price) {

    // horizontal line handling
    priceHorizontalLineTriangles.removeAllTriangles();
    priceLine.y = price;
    priceHorizontalLineTriangles.addQuad(priceLine);
    priceHorizontalLineTriangles.getTriangleDetails().forEach(triangleDetails -> triangleDetails.setColor(Color.MAGENTA));

    // candles queue handling
    if (isNewMinute(time)) {

        // new minute, new candle
        currentCandle = new Candle();
        currentCandle.open = price;
        currentCandle.high = price;
        currentCandle.low = price;
        currentCandle.close = price;
        currentCandle.startTime.setTimeInMillis(time);

        candlesQueue.add(currentCandle);

        // repaint all candles including new one
        for (int i = 0; i < numberOfCandles; i++) {
            Candle candle = candlesQueue.get(i);
            candle.recreateTrianglesForCandle(i, candlesTriangles.get(i));
        }

    } else {
        // update current candle
        currentCandle.close = price;
        if(currentCandle.high < price) currentCandle.high = price;
        if(currentCandle.low > price) currentCandle.low = price;

        // repaint only last candle
        Candle candle = candlesQueue.get(numberOfCandles - 1);
        candle.recreateTrianglesForCandle(numberOfCandles - 1, candlesTriangles.get(numberOfCandles - 1));

    }

    this.repaint();

}

Calendar calendar = Calendar.getInstance();
private boolean isNewMinute(long time) {

    // get minute
    calendar.setTimeInMillis(time);
    int minute = calendar.get(Calendar.MINUTE);

    // check if it is new minute
    if(currentCandle == null ||
            currentCandle.startTime.get(Calendar.MINUTE) != minute){
        return true;
    }
    return false;
}

int numberOfCandles = 60;

Point mousePt;

CoordSysRenderer coordsys = new CoordSysRenderer();

// We want to display Triangles, so we need the appropriate renderer.
public CandlesPanel(){

    panelRenderer.addItemToRender(priceHorizontalLineTriangles);

    // add all candles
    for (int i = 0; i < numberOfCandles; i++) {
        Triangles t = new Triangles();
        candlesTriangles.add(t);
        panelRenderer.addItemToRender(t);
        candlesQueue.add(new Candle());
    }

    // 1 hour of view, 60 minutes
    viewStart = Calendar.getInstance();
    viewStart.set(2023, Calendar.APRIL, 28, 10, 00, 00); // 20230428 10:00:00.000
    viewStart.clear(Calendar.MILLISECOND);

    viewEnd = Calendar.getInstance();
    viewEnd.set(2023, Calendar.APRIL, 28, 11, 00, 00); // 20230428 11:00:00.000
    viewEnd.clear(Calendar.MILLISECOND);

    // use a coordinate system for display
    coordsys.setCoordinateView(0, viewLow, numberOfCandles, viewHigh);

    // set the content renderer of the coordinate system
    coordsys.setContent(panelRenderer);

    this.setRenderer(coordsys);

    this.asComponent().addMouseListener(new MouseAdapter() {

        @Override
        public void mouseClicked(MouseEvent e) {
            // get candle over which mouse is hovering
            int pickingPixel = getPixel(e.getX(), e.getY(), true, 3);
            Integer index = candlePickingRegistry.lookup(pickingPixel);
            if(index != null){
                Candle candle = candlesQueue.get(index);
                System.out.println("Clicked candle: " + candle);
                candleClicked(candle);
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {
            mousePt = e.getPoint();
        }

    });

    //this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

    this.asComponent().addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            int dx = e.getX() - mousePt.x;
            int dy = e.getY() - mousePt.y;

            viewLow += dy / 5;
            viewHigh += dy / 5;

            coordsys.setCoordinateView(0, viewLow, numberOfCandles, viewHigh);
            mousePt = e.getPoint();

            repaint();
        }
    });

}

private void candleClicked(Candle candle) {

    clickSubscribers.forEach(subscriber -> subscriber.candleClicked(candle));

}

public void addHorizontalLine(double price, Color color) {

    Triangles priceHorizontalLine = new Triangles();

    priceHorizontalLine.addQuad(new Rectangle2D.Double(0, price, 60, 0.075));
    priceHorizontalLine.getTriangleDetails().forEach(triangleDetails -> triangleDetails.setColor(color));

    panelRenderer.addItemToRender(priceHorizontalLine);

    this.repaint();
}
}

Legend with pickable labels

The Legend class currently does not allow creation of labels with a picking color, but this would be super useful in order to link a label with its corresponding primitives

Triangle Rendering

In order to display colored areas e.g. area under curve, isobands, bars, hull curves, selection areas... a means to render triangles needs to be provided.

Triangles should be colorable per vertex to allow for color gradients. Picking color per triangle should allow for quite flexible identification.

A Triangles class will act as the collection of triangles that are drawn with a single draw call using a vertexarray.

I need the T-Drive dataset urgently.

Excuse me.The dataset of the Project T-Drive on the Microsoft web can't be dowloaded an more, but I need the data urgently. Do you have the dataset? Could you send it to me? Thank you!

Vector Graphics Export

The ability to export a visualization as vector graphics is desirable for using them in print media such as pdf.
Since JPlotters graphics are entirely based on OpenGL, a way to 'render' to a vector graphics format such as SVG has to be designed.

A possibility would be to extend the Renderer interface by a method renderAsVectorGraphics().

legend element for colormap

The Legend can only list line modalities and glyphs at the moment to explain the visual mappings.
For ColorMaps (e.g. when used for isolines or bands) a way to show it in the legend needs to be implemented.

Ready-to-use scatter plot class

When creating a scatter plot in JPlotter, the usual setup works something like this:

  1. create Points objects and fill with coordinates from data
  2. put Points in new PointsRenderer
  3. set or add PointsRenderer as content of new CoordSysRenderer
  4. set CoordSysRenderer as renderer of your JPlotterCanvas instance
  5. optional: setup interaction capabilities, e.g. CoordSysScrollZoom, CoordSysAreaSelector, CoordSysPanning
  6. optional: setup custom interaction schemes through mouse/key listeners (e.g. highlighting points on mouse over)

This is a lot of setup for a standard chart.

To allow developers to quickly create a scatterplot of their data, an extensible convenience class for a scatter plot needs to be developed.
The class should also allow for an easy setup of interaction schemes, e.g. mouseover, point selection, area selection.

Replaceable Tickmark and Label Generator

Currently tick marks are generated automatically using the extended wilkinson algorithm for the CoordSysCanvas. The algorithm tries to come up with nice tick marks such as 0, 0.5, 1, 1.5, 2 given a value range.
Depending on the context of the data or function being displayed, these ticks may not be optimal, for example a trigonometric curve would much rather be labeled in increments of pi/c. Not only the tick values but also their labels need to be generated accordingly.

An interface for getting tick values and corresponding labels for a specified value range needs to be created and used by the CoordSysCanvas.

GUI for exploring/debugging renderers of a canvas

This feature contains the development of a little gui tool that can show all the Renderers that are on a JPlotterCanvas.
The canvas renders its contents through the set renderer (see JPlotterCanvas.getRenderer()). Such a renderer can consist of several other renderers e.g. ChainedRenderer, CompleteRenderer, CoordsSysRenderer, ... which ultimately use some of the 'core'-renderers that actually take care of drawing onto the canvas (such as Points-, Lines-, or TextRenderer).

The gui tool should be able to show this structure of nested renderers, preferably in a tree like structure such as javax.swing.JTree. (resource for how to JTree)

Since we can never be sure what kind of renderer is set on the canvas (could be a coordsys, but could also be a third party renderer implementation, or a renderer that we will add to JPlotter somewhen in the future), the implementation should use the java reflection API to search the renderer object for nested instances of the Renderer interface. Here is a quick overview of the reflection API.

As this is a larger system to complement JPlotter, we should discuss if it makes sense to make it a standalone project that is available in a different maven artifact (which helps in separating core functionality from special features but has other downsides)


Follow up tasks to the display of nested renderers are

  • display of render items (such as Lines, Points, Triangles objects)
  • editable renderer properties (e.g. PointsRenderer.glyphScaling or Renderer.setEnabled(boolean))

These will be further concretized once we are done with the display

Point / Glyph Rendering

In order to plot a 2D point dataset or realise a quiver plot, a glyph renderer is needed.
This could be done by a Points class that is a collection of points that share the same visual representation (glyph).
Each point may differ in glyph size, color, orientation, picking color and position of course.

For efficient rendering, an instanced draw call can be used to easily render a huge amount of data.

An interface that defines the glyph is needed which fills the vertex array buffers for the glyph shape. It possibly defines the rendering primitive as well for greater flexibility.

Labeling for time axis

For data that corresponds to time, a TickMarkGenerator that translates numerical values (possibly seconds) to a formatted date string has to be implemented.

Overview of the branches' progress currently being developed

Branches

This issue provides an overview of the different branches currently being developed and their progress.

rope-selection

Additional Information:
The rope selection uses the ColorScheme API and the new KeyMaskListener API introduced in the ready-to-use-scatterplot branch.
Therefore it should only be merged if the ready-to-use-scatterplot branch is considered stable and ready to merge.
The ScatterPlot class also already utilizes the RopeSelection feature, so there might be merge conflicts in this class.

Known Issues:
None

Dependencies to other branches:
ready-to-use-scatterplot

Documentation ready?
Yes, Proofreading not necessary.

Mergeable?
Only if ready-to-use-scatterplot is ready

pdf-demo-refactor

Additional Information:
Just a slight refactor of the pdf demo class, as there were a couple of lines that were commented out and provided no value. Also naming of variables was inconsistent.

Known Issues:
None

Dependencies to other branches:
None

Documentation ready?
No additional documentation necessary.

Mergeable?
Yes

gui-tool

Additional Information:
This branch introduces a bigger new feature. Because of this there might still be issues, but none were found in my brief testing with some of the already existing demo classes.

Known Issues:
None

Dependencies to other branches:
None

Documentation ready?
Yes, Proofreading necessary.

Mergeable?
Should be ready.

time-axis-labels

Additional Information:
The time axis label branch is probably done, but there still is a performance problem (as described in #41)
As this isn’t exactly a problem of the time axis implementation, i would consider it as stable.

Known Issues:
Performance problem (#41)

Dependencies to other branches:
None

Documentation ready?
Yes, Proofreading necessary.

Mergeable?
Yes

overlay-fix

Additional Information:
Fixes the behaviour of the coordsys overlay. Implementation work is done, but the changed behaviour has to be tested if it fits the needs of the overlay.

Known Issues:
Changed behaviour in pdf/svg exporting has to be examined

Dependencies to other branches:
None

Documentation ready?
No additional documentation required.

Mergeable?
Yes, if the changed behaviour is better than before.

ready-to-use-charts

Additional Information:
Merges all the the ready-to-use branches and implements the abstracted data model in every r-t-u class that exists currently.

Known Issues:
None

Dependencies to other branches:
parallel-coords-renderer, data-model-abstraction, ready-to-use-line-chart, ready-to-use-scatterplot, barchart

Documentation ready?
Depends on the other branches.

Mergeable?
Only if all the ready-to-use branches are done.

parallel—coordinates-renderer

Additional Information:

Known Issues:
The setupAndLayout method in the ParallelCoordsRenderer (as used in the CoordSysRenderer) is a bit bloated at the moment. This might need a rework. No bugs known.

Dependencies to other branches:
None

Documentation ready?
Yes, but proofreading is necessary.

Mergeable?
Yes, if the documentation is ready.

barchart

Additional Information:
None

Known Issues:
None

Dependencies to other branches:
None

Documentation ready?
Yes, Proofreading required.

Mergeable?
Yes

temp-scatterplot-and-barchart

Additional Information:
Temporary branch to have both of the branches in one single branch. There are fixes contained in the branch, that might be good to be merged into the masterbranch.

Known Issues:

Dependencies to other branches:
ready-to-use-scatterplot, barchart

Documentation ready?

Mergeable?
If the other branches are ready.

ready-to-use-scatterplot

Additional Information:
Introduces a couple of new features, such as the KeyMaskListener API and the Ready-to-use ScatterPlot.

Known Issues:
None

Dependencies to other branches:
None

Documentation ready?
Yes

Mergeable?
Should be ready.

ready-to-use-line-chart

Additional Information:
The ready-to-use-line-chart is definitely not done currently. The integration of scatterplot and line chart is too clunky at the moment and needs rethinking.

Known Issues:
Nothing specifically, overall integration of scatterplot into line chart is clunky.

Dependencies to other branches:
ready-to-use-scatterplot

Documentation ready?
No

Mergeable?
No

data-model-abstraction

Additional Information:
Abstracts the data models of the ready-to-use charts to reduce code duplication.

Known Issues:
None

Dependencies to other branches:
ready-to-use-scatterplot, ready-to-use-line-chart

Documentation ready?
No

Mergeable?
If the ready-to-use-scatterplot, ready-to-use-line-chart branches are ready.

make Legend more interactive

Legend elements should support highlighting (e.g. posiibility to change font type plain -> bold, color maybe)
selection model under legend items, so that clicking an item fires a selection event for example.

Triangle Glyphs broken in GL

Seems like Triangle glyphs are broken in GL. May be platform and driver dependent however. Need to look into this.
Screenshot 2021-09-07 164258

Create shaders of renderers only once per context

Currently each newly created Renderer like LinesRenderer or TextRenderer will create a new Shader for itself to use. However many of the same renderers can be in use by the same FBOCanvas and thus pollute the context with identical shaders.
This is unnesseccary and should be changed.

Multisampled FBO shows random artifacts when resizing

When resizing the FBOCanvas, sometimes there are artifacts looking like a non written texture in the upper area right.
Until now I confirmed with an empty canvas where no shaders write to the FBO and the FBO is blit to the back buffer right away that the issue persists.
Another test using a shader that draws a viewport filling qhite quad could not recreate the issue, so I'm assuming it has something to do with the drawbuffer not being fully drawn to.

As a fix the FBOCanvas can draw the clear colors using a viewport filling quad at the start of the rendering sequence.

Replace DynamicText and StaticText by a simple Text class

Currently there is a differentiation between static text (which never changes) and dynamic text (fixed length string but changeable characters). The runtime benefit from never checking for dirtiness in static text and only having to update texture coordinates in dynamic text is marginal. As a consequence both those classes will be replaced by a single Text class that allows for complete update to the encapsulated string (string may be replaced by another in length differing string).
Instead of just updating the vertex array's texture coordinates, also the vertex coordinates and indices are then updated (can optimize to only texture coordinates when same length string).

Decouple canvas and content

In order to be able to change the complete displayed content of a Canvas, the content has to be decoupled from the canvas. For CoordSysCanvas this is only partially the case, only the content within the coordinate system is decoupled. If I suddenly changed my mind and dont want to render a coordinate system anymore, I would need to replace the whole canvas, which is a costly operation due to the attached GL context.

Instead a coordinatesystem renderer would be appropriate, which could be replaced easily by another renderer. (renderer not neccesarily in the sense of the intarface implemented by linesrenderer, or pointsrenderer, but maybe)

Zooming in on Bezier Curves leads to freeze

The subdivision routine generates segments of max 30 (?) pixels length to yield a smooth looking curve. When zooming in on a bezier curve many of these segments are generated for the whole curve even when they are outside of the view. In extreme cases so many segments are created that building the vertex array (presumably) takes very long so that the UI freezes on draw.

The subdivision routine needs to cull (or not generate) segments that are outside of the view to avoid this issue.

Line drawing stalls on small ranges

Extended wilkinson struggles to compute good ticks (takes very long) when the range of values is very small (e.g. xhigh-xlow < 1e-8) but values are large (e.g. xlow > 10.000).

PDF Rendering: rendering issues in various renderers

Currently the rendering works fine in Preview (macOS) and Safari, but the clipping seems to cause problems in Acrobat Reader (and possibly other viewers).

Preview:
image

Acrobat:
image

Without clipping enabled the rendering seems to work fine.

64 bit vertex support

Hi,

First, I'm very impressed with this codebase - thank you for your contribution!

I have successfully prototyped 64 bit GLSL support for line vertices in a forked JPlotter in order to support some high density data sets we're working with and would love to have it added to JPlotter.

Even thought the current SegmentDetails accepts doubles, they currently get downcast to floats for the GPU and this doesn't have enough precision when zooming in. I understand there might be workaround tricks like using doubles for your transform matrix calculation and rescaling my coordinates, but this seemed like a nice general solution. On my GPU I didn't see a performance difference.

Overall it was only a handful of line changes but it requires alternate shaders and requires OpenGL 4.3 (but there could be a graceful degradation back to floats if not available)

I'm an old timer (pre-GLSL) OpenGLer and had to do some learning - this may be obvious to implement - but if you want to see my notes I'd be happy to share. I only did a non-elegant prototype for Lines.

Regards,

32

64

Steve

Not releasing native/GL resources?

Version: 0.4.0
OS: Windows 10

Hi, I'm exploring JPlotter for use in a high-speed "oscilloscope" display and am pushing +1M line segments per second to a CoordSysRenderer - while disposing the previous segments. I can't seem to stop runaway (native) memory leaks outside the JVM. My 2G JVM stays happy but actual memory usage flies to 8G ~within a minute. It's in-use memory as I froze my 32GB machine during early tests.

Below is my (most extreme) attempt to clean up resources by locally allocating and closing as much as I can.

Thanks,

Steve

    // Init static vertice array
    private final static double[] sIndices;
    static
    {
        sIndices = new double[1_000_000];
        for (int i = 0; i < sIndices.length; i++)
        {
            sIndices[i] = i;
        }
    }
    // Update line strip - and release
    private void processNewResults(ScopeData ignoredForIssue)
    {
        Lines lines = new Lines();
        lines.addLineStrip(sIndices, sIndices);
    
        LinesRenderer linesRenderer = new LinesRenderer();
        linesRenderer.addItemToRender(lines);
    
        CoordSysRenderer coordsys = new CoordSysRenderer();
        fCanvas.setRenderer(coordsys);
        coordsys.setCoordinateView(0, 0, sIndices.length, sIndices.length);
        coordsys.setContent(linesRenderer);
        
        fCanvas.repaint();
        
        lines.close();
        linesRenderer.close();
        coordsys.close();
        
    }

BarycentricGradientPaint triangle does not overlap with a triangle painted using drawPolygon

Using this, you can see that a white line appears, even if the fill takes place after the polygonFill
public class TriangleExample {

public static void main(String[] args) throws Exception {
    // Create a new BufferedImage object
    int width = 400;
    int height = 400;
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    // Get the Graphics2D object
    Graphics2D g2d = image.createGraphics();

    // Define three vertices of the triangle
    int[] xPoints = {50, 350, 200};
    int[] yPoints = {50, 50, 350};

    // Define three colors for the gradient
    Color color1 = Color.RED;
    Color color2 = Color.GREEN;
    Color color3 = Color.BLUE;

    // Draw the triangle with a linear gradient
    Polygon triangle = new Polygon(xPoints, yPoints, 3);
    Point2D p1 = new Point2D.Float(xPoints[0], yPoints[0]);
    Point2D p2 = new Point2D.Float(xPoints[1], yPoints[1]);
    Point2D p3 = new Point2D.Float(xPoints[2], yPoints[2]);

    g2d.setColor(Color.WHITE);
    g2d.drawPolygon(triangle);
    BarycentricGradientPaint gradient = new BarycentricGradientPaint(p1, p2, p3, color1, color2, color3);
    g2d.setPaint(gradient);
    g2d.fill(triangle);


    // Save the image to a file
    File output = new File("triangle.png");
    ImageIO.write(image, "png", output);
}

Issue with text rendering in SVG

While working on the "new" text renderer i found an issue with text rendering in svg:

When setting a background color to the text, the bounds of the resulting svg text elements are not computed correctly.
This leads to the following result, where the background is shorter than the text:

What it should look like:
image

SVG:
image

This is because we can't compute the actual width of the text element, as we only create the xml structure of the svg document, but we don't actually render something (this is done individually by the browser for example).

There are solutions to this exact problem which we can use to fix this issue (see the approach in this branch), but we will face it again with the planned features for the new text renderer sadly, as we need the bounds calculation for those.

Fix

The changes in the svg-text-background-fix branch fix the text background bug:

Expected:
image

SVG:
image

Currently the rendering seems to be a bit blurry in the macOS svg preview tool (not officially supported as stated by mdn webdocs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFlood), but Chrome and Firefox are rendering it correctly.

possibility to adapt view to aspect ratio of coordinate system

When resizing the canvas holding a CoordSysRenderer its coordinate view stays the same but is stretched visually to span over the available area. This distorts the contents of the coordinate system and can be undesired. See the example below where the coordinate view stays the same but the coordinate area changed and squishes the lines and distorts the shape.

matching view and viewport different aspect ratios desired behaviour
Screenshot from 2022-06-07 12-47-59 Screenshot from 2022-06-07 13-05-15 Screenshot from 2022-06-07 13-08-21

It is not always problematic when aspect ratios of view and viewport (coordsys area) do not match, but there should be a mechanism that automatically adapts the view to keep up with the areas aspect ratio.

Proposed mechanism:
There will be a new field Rectangle2D desiredCoordinateView which holds the view that should be shown leastwise. The existing field Rectangle2D coordinateView is then sized accordingly so that desiredCoordinateView fits inside. This means that coordinateView is allowed to be either taller or wider than desiredCoordinateView and can be sized so it matches the aspect ratio of the coordinate system area (getCoordSysArea()).

For the automatic coordinate view adaption the CoordSysRenderer has to be made aware of 'componentResized(..)' events of its canvas. When the canvas is resized, the view is resized so that it has the same aspect ratio as the view area and contains the desired view.

Refined Mechanism Proposal:
With the introduction of a second view field in CoordSysRenderer and the need for componentResized event awareness we complicate the API of the CoordSysRenderer class (which is already quite large). Instead we could introduce a new class for controlling CoordSysRenderer.coordinateView.
We already have the interaction classes CoordSysPanning, CoordSysViewSelector and CoordSysScrollZoom that change the coordinate view and listen to events of the canvas. Similarly we could introduce a class CoordSysViewController that holds the field Rectangle2D desiredCoordinateView and adapts CoordSysRenderer.coordinateView when canvas is resized or desiredCoordinateView changes.

Implication: The already mentioned interaction classes for changing the view will have to use the new CoordSysViewController instead of directly manipulating the view. Otherwise changes to the view will be overwritten when component is resized.

Glyph associated with a Points objects does not have to be final

Changing the glyph of a Points object is not possible since the field is final.
However, there is no need for it to be final. Even for GL rendering it does not make a difference since the glyph is instanced by the renderer and not used for creating the vertexarray.
We should change that. This issue is a mere reminder.

En/Disable picking color attachment within renderer

When a renderer does not support writing to the second color attachment of the FBO, the attachment should be disabled (i.e. not be set as drawbuffer).
Unfortunately this cannot be ensured automatically, which is why every renderer has the responsibility to en/disable the second color attachment before executing its shaders.
For this each renderer needs to be passed an interface at rendering through which it can do so.

Use shared GL context for all FBOCanvases

As was pointed out here, AWTGLCanvas can use a shared GL context so that GL resources can be shared among Canvases.
Due to a misconception (and poor research) on my part the FBOCanvas features an id that is accessible during render which is used by CharacterAtlas to return the correct atlas for a context.

This should be ditched and instead all FBOCanvases should share the same context.
This also allows for the shaders to be created only once.

Ready-to-use line chart class

When creating a line chart in JPlotter, the usual setup works something like this:

  1. create Lines objects and fill with line segment from data/sampled function
    1. for figuring out the segments coordinates it may be required to transform from some datatype to a numerical value. E.g. from LocalDateTime to double when dealing with time oriented data.
  2. put Lines in new LinesRenderer
  3. set or add LinesRenderer as content of new CoordSysRenderer
  4. set CoordSysRenderer as renderer of your JPlotterCanvas instance
  5. optional: add Points + PointsRenderer for points on the line from data
  6. optional: setup interaction capabilities, e.g. CoordSysScrollZoom, CoordSysAreaSelector, CoordSysPanning
  7. optional: setup custom interaction schemes through mouse/key listeners (e.g. highlighting line on mouse over)

This is a lot of setup for a standard chart.

To allow developers to quickly create a line chart of their data, an extensible convenience class for a scatter plot needs to be developed.
The class should also allow for an easy setup of interaction schemes, e.g. mouseover, line selection, ...


There is also special care to be taken for the tick mark labeling of the axes depending on the unit that is represented (e.g. time). Unfortunately we cannot support every possible unit, so instead a generic approach has to be thought of where deevelopers need to provide some kind of converter from numerical value (double) to a string representation of the respective datatype/unit.
This is related to issue #8. However, I think this tick mark labeling topic may be a whole issue on its own. So for the moment we will ignore it, and integrate it later into the line chart class.

PDF export

JPlotter currently supports exporting to SVG through the renderSVG(Document doc, Element parent, int w, int h) method of Renderer or SVGRenderer.
However, SVG is incapable of shading triangles with 3 different colors (defined per vertex) i.e. interpolating barycentrically. There also seems to be an issue with triangle meshes where the edges of individual triangles are visible due to very thin gaps between adjacent triangles showing.

PDF is also capable to represent vector graphics, and also supports triangle meshes with accurate shading.
See PDF Reference 4.6.3 > Shading Types > Type 4

To support accurate (correctly shaded) export of JPlotter visualizations as vector graphics, a 'render to PDF' functionality similar to the SVG variant needs to be implemented.

Key Masking

For CoordsysScrollZoom,CoordsysPanning and CoordsysViewSelector, the KeyMaskListener mechanic should allow for the respective action (zooming, panning, area selecting) to happen, if and only if the specified combination of keys is pressed.
This has been implemented as of af38781 , but the most important special case is not covered, which is when no key is required.

When one of the actions should work without key press, we use 0 as argument to the kml constructor.
Now the desired behavior is: when keys are pressed, the mask does not allow for the action to happen.
Currently, the action is allowed in any case, ignoring the pressed keys. This is problematic since it interferes with other actions that do require key presses, which then happen simultaneously.

Bezier

Implement a spline renderer so that we are not limited to straight lines.

SVG rendering inconsistency

There seems to be a minor issue in SVG rendering.

When exporting the StatLogSPLOMViz plot, there's an inconsistency in the legend area.
In the Java version the "Bpv Open" entry is missing, whereas it's present in the svg export.
Also the icon of the entry is missing in the svg export.

This might be correlated to clipping, but further investigation is necessary.

Java:
Pasted Graphic 1

SVG:
Pasted Graphic

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.