hageldave / jplotter Goto Github PK
View Code? Open in Web Editor NEWOpenGL based 2D Plotting Library for Java using AWT and LWJGL
Home Page: https://github.com/hageldave/JPlotter/wiki
License: MIT License
OpenGL based 2D Plotting Library for Java using AWT and LWJGL
Home Page: https://github.com/hageldave/JPlotter/wiki
License: MIT License
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
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,
Steve
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.
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).
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 |
---|---|---|
![]() |
![]() |
![]() |
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.
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.
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).
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);
}
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.
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.
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:
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.
The changes in the svg-text-background-fix branch fix the text background bug:
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.
When creating a scatter plot in JPlotter, the usual setup works something like this:
Points
objects and fill with coordinates from dataPoints
in new PointsRenderer
PointsRenderer
as content of new CoordSysRenderer
CoordSysRenderer
as renderer of your JPlotterCanvas
instanceCoordSysScrollZoom
, CoordSysAreaSelector
, CoordSysPanning
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.
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.
This issue provides an overview of the different branches currently being developed and their progress.
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
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
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.
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
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.
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.
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.
Additional Information:
None
Known Issues:
None
Dependencies to other branches:
None
Documentation ready?
Yes, Proofreading required.
Mergeable?
Yes
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.
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.
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
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.
For data that corresponds to time, a TickMarkGenerator that translates numerical values (possibly seconds) to a formatted date string has to be implemented.
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.
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.
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)
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.
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();
}
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
.
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
.
Rotated text looks pretty bad in JPlotter at the moment (if the angle is not a multiple of pi/2). To get sharp text even at arbitrary angles the signed distance field technique should be used.
Paper: https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf
Blog article: https://blog.mapbox.com/drawing-text-with-signed-distance-fields-in-mapbox-gl-b0933af6f817
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.
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.
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!
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()
.
The basic Renderable
s (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.
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();
}
}
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.
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.
Are there any plans for supporting logarithmic axes? (base 10 is fine).
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.
Implement a spline renderer so that we are not limited to straight lines.
This feature contains the development of a little gui tool that can show all the Renderer
s 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
Lines
, Points
, Triangles
objects)PointsRenderer.glyphScaling
or Renderer.setEnabled(boolean)
)These will be further concretized once we are done with the display
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
.
Dear David,
Would it be possible to update dependencies that JPlotter uses lwjgl 3.3.x that seems to run on Mac with M1/M2 ARM cpus?
See: libgdx/libgdx#6084 (comment)
And here: https://github.com/LWJGLX/lwjgl3-awt/blob/main/pom.xml (profile lwjgl-natives-macos-aarch64)
Currently on Mac w/ M1 I am using BlankCanvasFallback instead of /BlankCanvas/
Hope this update will make things paint faster... much faster.
Thanks!
When creating a line chart in JPlotter, the usual setup works something like this:
Lines
objects and fill with line segment from data/sampled function
LocalDateTime
to double
when dealing with time oriented data.Lines
in new LinesRenderer
LinesRenderer
as content of new CoordSysRenderer
CoordSysRenderer
as renderer of your JPlotterCanvas
instancePoints
+ PointsRenderer
for points on the line from dataCoordSysScrollZoom
, CoordSysAreaSelector
, CoordSysPanning
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.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.