1.5.0 Migration Issue

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
robertT
Posts: 8
Joined: Tue Jun 26, 2007 2:17 pm

1.5.0 Migration Issue

Post by robertT » Sun Nov 19, 2017 11:38 pm

I’ve been using JFreeChart for both 2D and 3D charts in a commercial product. I was looking to update to version 1.5. However, in the migration notes I see that the 3D classes have been removed. It is suggested to use Orson Charts. However, they are licenced under GPL, and I do not have funds to purchase commercial licence. Is there any way to get 3D support with 1.5, or am I stuck on 1.0.19?

Thanks

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: 1.5.0 Migration Issue

Post by david.gilbert » Mon Nov 20, 2017 7:30 am

As part of the streamlining of all my open source projects, I decided I don't want to maintain the "faux" 3D charts in JFreeChart, when there are better options in Orson Charts. Your options include:

[*] stick with JFreeChart 1.0.x - I will make a new release on that branch with the latest bug fixes, but I won't be actively maintaining it going forward (someone else is more than welcome to do it);
[*] merge the required classes into your own local version of JFreeChart;
[*] just use 2D versions of the same charts (they display the same data, but more cleanly) in JFreeChart 1.5.0.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

robertT
Posts: 8
Joined: Tue Jun 26, 2007 2:17 pm

Re: 1.5.0 Migration Issue

Post by robertT » Mon Nov 20, 2017 3:15 pm

Thanks for the reply,

I'm trying to decide on the best path forward. Will the new release on the 1.0 branch be functionally equivalent to 1.5? You mentioned that 2D charts display more cleanly in 1.5. Is this something that would be added to the 1.0 branch. I'm leaning towards moving on to 1.5, but want to understand what the next release on the 1.0 branch will include.

Thanks again.

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: 1.5.0 Migration Issue

Post by paradoxoff » Mon Nov 20, 2017 7:07 pm

david.gilbert wrote:[*] stick with JFreeChart 1.0.x - I will make a new release on that branch with the latest bug fixes, but I won't be actively maintaining it going forward (someone else is more than welcome to do it);
Does that mean than 1.0.20 is the last "official" version of JFreeChart 1.0.? Are their any requirements from your side regarding further changes in JFreeChart 1.0?
I have thought about creating my own branch of JFreeChart 1.0, but that would contain some major changes, some of which are not backwards compatible with previous versions. Some examples
- Removal of marker support
- Support of entities for CategoryAnnotations
- Use of generic collections where appropriate, instead of using an inheritance order like AbstractObjectList-> ObjectList -> PaintList. For example, an XYPlot will contain the lists ArrayList<XYItemRenderer>, ArrayList<XYDataset>, and so on. Likewise for the other plots.
- Some built-in intelligence for the strategy classes such as the RectangleEdge class.

Plus some more.

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: 1.5.0 Migration Issue

Post by david.gilbert » Mon Nov 20, 2017 9:20 pm

paradoxoff wrote:Does that mean than 1.0.20 is the last "official" version of JFreeChart 1.0.? Are their any requirements from your side regarding further changes in JFreeChart 1.0?
Yes. Ideally 1.0.x would stay API compatible, but if you want to maintain a fork then you are free to evolve it as you want.
paradoxoff wrote:- Removal of marker support
What's the rationale?
paradoxoff wrote:- Support of entities for CategoryAnnotations
I would merge this into 1.5.0, it is a missing feature.
paradoxoff wrote:- Use of generic collections where appropriate, instead of using an inheritance order like AbstractObjectList-> ObjectList -> PaintList. For example, an XYPlot will contain the lists ArrayList<XYItemRenderer>, ArrayList<XYDataset>, and so on. Likewise for the other plots.
This also I would consider for 1.5.x or 1.6.x (don't have a version number plan, it doesn't matter that much though).
paradoxoff wrote:- Some built-in intelligence for the strategy classes such as the RectangleEdge class.
What do you propose? Definitely open to ideas on that one.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: 1.5.0 Migration Issue

Post by david.gilbert » Mon Nov 20, 2017 9:22 pm

robertT wrote:Will the new release on the 1.0 branch be functionally equivalent to 1.5? You mentioned that 2D charts display more cleanly in 1.5. Is this something that would be added to the 1.0 branch. I'm leaning towards moving on to 1.5, but want to understand what the next release on the 1.0 branch will include.
It doesn't look so different to 1.0.19, there are just a few bug fixes that have already been committed that I can include in a new release. The current state is here:

https://github.com/jfree/jfreechart/tree/v1.0.x
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: 1.5.0 Migration Issue

Post by paradoxoff » Tue Nov 21, 2017 3:48 pm

david.gilbert wrote: What's the rationale for removal of marker support?
Code simplification. Everything that can be done with markers can also be done with Annotations (see patch 316). Type safety with Markers is also somewhat ill defined. CategoryMarkers can be added to an XYPlot, and can be added as range marker to a CategoryPlot, but will be drawn in neither case.
Several methods in CategoryPlot, XYPlot, AbstractCategoryItemRenderer, and AbstractXYItemRenderer would be obsolete.

Regarding "Some built-in intelligence for the strategy classes". A good example for what I mean is the calculateLabelAnchorPoint-method in the AbstractRenderer class. The ItemLabelAnchor class is basically used as some kind of marker interface. IMHO, and ItemLabelAnchor instance should be able to calculate the x and y offsets on its own. A further example are the drawTitle and createAlignedRectangle2D-methods of the JFreeChart class. IMHO, a RectangleEdge class should be able to calculate an "aligned rectangle" from a Size2D object, an existing Rectangle2D instance, and an instance of each HorizontalAlignment and VerticalAlignment.

[edit] I would also suggest that at least Java8 is required. That would allow to use streams, method references and lambda expressions. IMHO that has a great potential to make the code more conchise in various ways. For example, the methods selectHorizontalAutoTickUnit and selectVerticalAutoTickUnit in the NumberAxis class share a lot of common code. They both try to find a tick unit that is just larger than the size of a typical tick label. The only difference is whether the width or the height of a tick label is used to determine its "size". This could be done by an instance of BiFunction<Graphics2D, TickUnit, Double> that either points to estimateMaximumTickLabelWidth or estimateMaximumTickLabelHeight.

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: 1.5.0 Migration Issue

Post by paradoxoff » Fri Nov 24, 2017 5:24 pm

Thouhg this is now becoming more and more off-topic, I just wanted to provide an example for
paradoxoff wrote:IMHO, a RectangleEdge class should be able to calculate an "aligned rectangle" from a Size2D object, an existing Rectangle2D instance, and an instance of each HorizontalAlignment and VerticalAlignment.
Here are some modified strategy classes RectangleEdge, VerticalAlignment, and HorizontalAlignment with some built-in intelligence that makes extensive if-blocks in the calling code superfluous. For simplicity, I have embedded the new classes as static inner classes in a common outer class.

Code: Select all

public class JFreeLayoutClasses {

    static abstract class HorizontalAlignment implements Serializable {

        public static final HorizontalAlignment LEFT
                = new HorizontalAlignment("HorizontalAlignment.LEFT") {
            public double getAlignedMinX(Rectangle2D rectangle, Size2D size) {
                return rectangle.getMinX();
            }
        };

        public static final HorizontalAlignment RIGHT
                = new HorizontalAlignment("HorizontalAlignment.RIGHT") {
            public double getAlignedMinX(Rectangle2D rectangle, Size2D size) {
                return rectangle.getMaxX() - size.getWidth();
            }
        };

        public static final HorizontalAlignment CENTER
                = new HorizontalAlignment("HorizontalAlignment.CENTER") {
            public double getAlignedMinX(Rectangle2D rectangle, Size2D size) {
                return rectangle.getCenterX() - size.getWidth() / 2.0;
            }
        };

        private String name;

        private HorizontalAlignment(final String name) {
            this.name = name;
        }

        public String toString() {
            return this.name;
        }

        public abstract double getAlignedMinX(Rectangle2D rectangle, Size2D size);
    }

    static abstract class VerticalAlignment implements Serializable {

        private static final long serialVersionUID = 7272397034325429853L;

        public static final VerticalAlignment TOP
                = new VerticalAlignment("VerticalAlignment.TOP") {
            public double getAlignedTopY(Rectangle2D rectangle, Size2D size) {
                return rectangle.getMinY();
            }
        };

        public static final VerticalAlignment BOTTOM
                = new VerticalAlignment("VerticalAlignment.BOTTOM") {
            public double getAlignedTopY(Rectangle2D rectangle, Size2D size) {
                return rectangle.getMaxY() - size.getHeight();
            }
        };

        public static final VerticalAlignment CENTER
                = new VerticalAlignment("VerticalAlignment.CENTER") {
            public double getAlignedTopY(Rectangle2D rectangle, Size2D size) {
                return rectangle.getCenterY() - size.getHeight() / 2.0;
            }
        };

        private String name;

        private VerticalAlignment(final String name) {
            this.name = name;
        }

        public String toString() {
            return this.name;
        }

        public abstract double getAlignedTopY(Rectangle2D rectangle, Size2D size);
    }

    static abstract class RectangleEdge implements Serializable {

        public static final RectangleEdge TOP
                = new RectangleEdge("RectangleEdge.TOP") {
            public boolean isLeftOrRight() {
                return false;
            }

            public boolean isTopOrBottom() {
                return true;
            }

            public RectangleEdge opposite() {
                return BOTTOM;
            }

            public double coordinate(final Rectangle2D rectangle) {
                return rectangle.getMinY();
            }

            public Rectangle2D createAlignedRectangle(Rectangle2D original, Size2D size, VerticalAlignment va, HorizontalAlignment ha) {
                double x = ha.getAlignedMinX(original, size);
                double y = original.getMinY();
                return new Rectangle2D.Double(x, y, size.width, size.height);

            }
            public void shrink(Rectangle2D original, Size2D size) {
                original.setRect(original.getX(), original.getY()  +size.getHeight(), original.getWidth(), original.getHeight() - size.getHeight());
            }

        };

        public static final RectangleEdge BOTTOM
                = new RectangleEdge("RectangleEdge.BOTTOM") {
            public boolean isLeftOrRight() {
                return false;
            }

            public boolean isTopOrBottom() {
                return true;
            }

            public RectangleEdge opposite() {
                return TOP;
            }

            public double coordinate(final Rectangle2D rectangle) {
                return rectangle.getMaxY();
            }

            public Rectangle2D createAlignedRectangle(Rectangle2D original, Size2D size, VerticalAlignment va, HorizontalAlignment ha) {
                double x = ha.getAlignedMinX(original, size);
                double y = original.getMaxY() - size.getHeight();
                return new Rectangle2D.Double(x, y, size.width, size.height);

            }
            public void shrink(Rectangle2D original, Size2D size) {
                original.setRect(original.getX(), original.getY(), original.getWidth(), original.getHeight() - size.getHeight());
            }

        };

        public static final RectangleEdge LEFT
                = new RectangleEdge("RectangleEdge.LEFT") {
            public boolean isLeftOrRight() {
                return true;
            }

            public boolean isTopOrBottom() {
                return false;
            }

            public RectangleEdge opposite() {
                return RIGHT;
            }

            public double coordinate(final Rectangle2D rectangle) {
                return rectangle.getMinX();
            }

            public Rectangle2D createAlignedRectangle(Rectangle2D original, Size2D size, VerticalAlignment va, HorizontalAlignment ha) {
                double x = original.getMinX();
                double y = va.getAlignedTopY(original, size);
                return new Rectangle2D.Double(x, y, size.width, size.height);

            }
            public void shrink(Rectangle2D original, Size2D size) {
                original.setRect(original.getX() + size.getWidth(), original.getY(), original.getWidth()
                        - size.width, original.getHeight());
            }
        };

        public static final RectangleEdge RIGHT
                = new RectangleEdge("RectangleEdge.RIGHT") {
            public boolean isLeftOrRight() {
                return true;
            }

            public boolean isTopOrBottom() {
                return false;
            }

            public RectangleEdge opposite() {
                return RIGHT;
            }

            public double coordinate(final Rectangle2D rectangle) {
                return rectangle.getMaxY();
            }

            public Rectangle2D createAlignedRectangle(Rectangle2D original, Size2D size, VerticalAlignment va, HorizontalAlignment ha) {
                double x = original.getMaxX() - size.getWidth();
                double y = va.getAlignedTopY(original, size);
                return new Rectangle2D.Double(x, y, size.width, size.height);

            }
            public void shrink(Rectangle2D original, Size2D size) {
                original.setRect(original.getX(), original.getY(), original.getWidth()
                        - size.width, original.getHeight());
            }
        };

        private String name;

        private RectangleEdge(final String name) {
            this.name = name;
        }

        public String toString() {
            return this.name;
        }

        public abstract boolean isTopOrBottom();

        public abstract boolean isLeftOrRight();

        public abstract RectangleEdge opposite();

        public abstract double coordinate(final Rectangle2D rectangle);

        public abstract Rectangle2D createAlignedRectangle(Rectangle2D original, Size2D size, VerticalAlignment va, HorizontalAlignment ha);

        public abstract void shrink(Rectangle2D original, Size2D size);
    }
}
The following code of the drawTitle-method of the JFreeChart class :

Code: Select all

        if (position == RectangleEdge.TOP) {
            Size2D size = t.arrange(g2, constraint);
            titleArea = createAlignedRectangle2D(size, area,
                    t.getHorizontalAlignment(), VerticalAlignment.TOP);
            retValue = t.draw(g2, titleArea, p);
            area.setRect(area.getX(), Math.min(area.getY() + size.height,
                    area.getMaxY()), area.getWidth(), Math.max(area.getHeight()
                    - size.height, 0));
        } else if (position == RectangleEdge.BOTTOM) {
            Size2D size = t.arrange(g2, constraint);
            titleArea = createAlignedRectangle2D(size, area,
                    t.getHorizontalAlignment(), VerticalAlignment.BOTTOM);
            retValue = t.draw(g2, titleArea, p);
            area.setRect(area.getX(), area.getY(), area.getWidth(),
                    area.getHeight() - size.height);
        } else if (position == RectangleEdge.RIGHT) {
            Size2D size = t.arrange(g2, constraint);
            titleArea = createAlignedRectangle2D(size, area,
                    HorizontalAlignment.RIGHT, t.getVerticalAlignment());
            retValue = t.draw(g2, titleArea, p);
            area.setRect(area.getX(), area.getY(), area.getWidth()
                    - size.width, area.getHeight());
        } else if (position == RectangleEdge.LEFT) {
            Size2D size = t.arrange(g2, constraint);
            titleArea = createAlignedRectangle2D(size, area,
                    HorizontalAlignment.LEFT, t.getVerticalAlignment());
            retValue = t.draw(g2, titleArea, p);
            area.setRect(area.getX() + size.width, area.getY(), area.getWidth()
                    - size.width, area.getHeight());
        }
could then be rewritten to

Code: Select all

Size2D size = t.arrange(g2, constraint);
titleArea = position.createAlignedRectangle(area, size, t.getVerticalAlignment(), t.getHorizontalAlignment());
retValue = t.draw(g2, titleArea, p);
position.shrink(area, size);
While the new version isn't shorter than the old one (at least not if the changes in the strategy classes are counted), the new version will likely make other code fragements shorter, where the classes are used.

Locked