All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dishevelled.piccolo.venn.TernaryVennNode Maven / Gradle / Ivy

The newest version!
/*

    dsh-piccolo-venn  Piccolo2D venn diagram nodes and supporting classes.
    Copyright (c) 2009-2019 held jointly by the individual authors.

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
    License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library;  if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.

    > http://www.fsf.org/licensing/licenses/lgpl.html
    > http://www.opensource.org/licenses/lgpl-license.php

*/
package org.dishevelled.piccolo.venn;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.piccolo2d.PNode;
import org.piccolo2d.nodes.PArea;
import org.piccolo2d.nodes.PPath;
import org.piccolo2d.nodes.PText;
import org.dishevelled.venn.TernaryVennModel;

/**
 * Ternary venn diagram node.
 *
 * @param  value type
 * @author  Michael Heuer
 */
public class TernaryVennNode
    extends AbstractTernaryVennNode
{
    /** Path node for the first set. */
    private final PPath first = new PPath.Double(FIRST_SHAPE, STROKE);

    /** Area node for the first only view. */
    private final PArea firstOnly = new PArea(AREA_STROKE);

    /** Label for the size of the first only view. */
    private final PText firstOnlySize = new PText();

    /** Path node for the second set. */
    private final PPath second = new PPath.Double(SECOND_SHAPE, STROKE);

    /** Area node for the second only view. */
    private final PArea secondOnly = new PArea(AREA_STROKE);

    /** Label for the size of the second only view. */
    private final PText secondOnlySize = new PText();

    /** Path node for the third set. */
    private final PPath third = new PPath.Double(THIRD_SHAPE, STROKE);

    /** Area node for the third only view. */
    private final PArea thirdOnly = new PArea(AREA_STROKE);

    /** Label for the size of the third only view. */
    private final PText thirdOnlySize = new PText();

    /** Area node for the first second view. */
    private final PArea firstSecond = new PArea(AREA_STROKE);

    /** Label for the size of the first second view. */
    private final PText firstSecondSize = new PText();

    /** Area node for the first third view. */
    private final PArea firstThird = new PArea(AREA_STROKE);

    /** Label for the size of the first third view. */
    private final PText firstThirdSize = new PText();

    /** Area node for the second third view. */
    private final PArea secondThird = new PArea(AREA_STROKE);

    /** Label for the size of the second third view. */
    private final PText secondThirdSize = new PText();

    /** Area node for the intersection view. */
    private final PArea intersection = new PArea(AREA_STROKE);

    /** Label for the size of the intersection view. */
    private final PText intersectionSize = new PText();

    /** List of nodes. */
    private final List nodes = Arrays.asList(new PNode[] { firstOnly, secondOnly, thirdOnly,
                                                                  firstSecond, firstThird, secondThird, intersection });

    /** List of size labels. */
    private final List sizeLabels = Arrays.asList(new PText[] { firstOnlySize, secondOnlySize, thirdOnlySize,
                                                                  firstSecondSize, firstThirdSize, secondThirdSize, intersectionSize });

    /** Cached area. */
    private Area f;

    /** Cached area. */
    private Area s;

    /** Cached area. */
    private Area t;

    /** Cached rectangle. */
    private Rectangle2D a = new Rectangle2D.Double();

    /** Cached area. */
    private Rectangle2D b = new Rectangle2D.Double();

    /** Cached point. */
    private Point2D c = new Point2D.Double();

    /** Label gap, 8.0d. */
    private static final double LABEL_GAP = 8.0d;

    /** Adjust label gap, 10.0d. */
    private static final double ADJUST_LABEL_GAP = 10.0d;

    /** First shape. */
    private static final Ellipse2D FIRST_SHAPE = new Ellipse2D.Double(0.0d, 0.0d, 128.0d, 128.0d);

    /** First paint. */
    private static final Paint FIRST_PAINT = new Color(30, 30, 30, 50);

    /** Second shape. */
    private static final Ellipse2D SECOND_SHAPE = new Ellipse2D.Double(((2.0d * 128.0d) / 3.0d), 0.0d, 128.0d, 128.0d);

    /** Second paint. */
    private static final Paint SECOND_PAINT = new Color(5, 37, 255, 50);

    /** Third shape. */
    private static final Ellipse2D THIRD_SHAPE = new Ellipse2D.Double(128.0d / 3.0d, (2.0d * 128.0d) / 3.0d, 128.0d, 128.0d);

    /** Third paint. */
    private static final Paint THIRD_PAINT = new Color(255, 100, 5, 50);

    /** Stroke. */
    private static final Stroke STROKE = new BasicStroke(0.5f);

    /** Stroke paint. */
    private static final Paint STROKE_PAINT = new Color(20, 20, 20);

    /** Area paint. */
    private static final Paint AREA_PAINT = new Color(0, 0, 0, 0);

    /** Area stroke. */
    private static final Stroke AREA_STROKE = null;


    /**
     * Create a new empty ternary venn node.
     */
    public TernaryVennNode()
    {
        super();
        initNodes();
        updateContents();
    }

    /**
     * Create a new ternary venn node with the specified sets.
     *
     * @param firstLabelText label text for the first set
     * @param first first set, must not be null
     * @param secondLabelText label text for the second set
     * @param second second set, must not be null
     * @param thirdLabelText label text for the third set
     * @param third third set, must not be null
     */
    public TernaryVennNode(final String firstLabelText, final Set first,
                             final String secondLabelText, final Set second,
                             final String thirdLabelText, final Set third)
    {
        super(firstLabelText, first, secondLabelText, second, thirdLabelText, third);
        initNodes();
        updateContents();
    }

    /**
     * Create a new ternary venn node with the specified model.
     *
     * @param model model for this ternary venn node, must not be null
     */
    public TernaryVennNode(final TernaryVennModel model)
    {
        super(model);
        initNodes();
        updateContents();
    }


    /**
     * Initialize nodes.
     */
    private void initNodes()
    {
        first.setPaint(FIRST_PAINT);
        first.setStrokePaint(STROKE_PAINT);
        second.setPaint(SECOND_PAINT);
        second.setStrokePaint(STROKE_PAINT);
        third.setPaint(THIRD_PAINT);
        third.setStrokePaint(STROKE_PAINT);
        firstOnly.setPaint(AREA_PAINT);
        secondOnly.setPaint(AREA_PAINT);
        thirdOnly.setPaint(AREA_PAINT);
        firstSecond.setPaint(AREA_PAINT);
        firstThird.setPaint(AREA_PAINT);
        secondThird.setPaint(AREA_PAINT);
        intersection.setPaint(AREA_PAINT);

        addChild(first);
        addChild(second);
        addChild(third);
        addChild(firstOnlySize);
        addChild(secondOnlySize);
        addChild(thirdOnlySize);
        addChild(firstSecondSize);
        addChild(firstThirdSize);
        addChild(secondThirdSize);
        addChild(intersectionSize);
        addChild(firstOnly);
        addChild(secondOnly);
        addChild(thirdOnly);
        addChild(firstSecond);
        addChild(firstThird);
        addChild(secondThird);
        addChild(intersection);
        addChild(getFirstLabel());
        addChild(getSecondLabel());
        addChild(getThirdLabel());
    }

    @Override
    protected void updateLabels()
    {
        super.updateLabels();

        if (firstOnlySize != null)
        {
            firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
            secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
            thirdOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
            firstSecondSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecond().isEmpty()));
            firstThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThird().isEmpty()));
            secondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThird().isEmpty()));
            intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
        }
    }

    @Override
    protected void updateContents()
    {
        firstOnlySize.setText(String.valueOf(getModel().firstOnly().size()));
        secondOnlySize.setText(String.valueOf(getModel().secondOnly().size()));
        thirdOnlySize.setText(String.valueOf(getModel().thirdOnly().size()));
        firstSecondSize.setText(String.valueOf(getModel().firstSecond().size()));
        firstThirdSize.setText(String.valueOf(getModel().firstThird().size()));
        secondThirdSize.setText(String.valueOf(getModel().secondThird().size()));
        intersectionSize.setText(String.valueOf(getModel().intersection().size()));

        firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
        secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
        thirdOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
        firstSecondSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecond().isEmpty()));
        firstThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThird().isEmpty()));
        secondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThird().isEmpty()));
        intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
    }

    @Override
    protected void layoutChildren()
    {
        f = new Area(first.getPathReference());
        s = new Area(second.getPathReference());
        t = new Area(third.getPathReference());

        firstOnly.reset();
        firstOnly.add(f);
        firstOnly.subtract(s);
        firstOnly.subtract(t);

        secondOnly.reset();
        secondOnly.add(s);
        secondOnly.subtract(f);
        secondOnly.subtract(t);

        thirdOnly.reset();
        thirdOnly.add(t);
        thirdOnly.subtract(f);
        thirdOnly.subtract(s);

        firstSecond.reset();
        firstSecond.add(f);
        firstSecond.intersect(s);
        firstSecond.subtract(t);

        firstThird.reset();
        firstThird.add(f);
        firstThird.intersect(t);
        firstThird.subtract(s);

        secondThird.reset();
        secondThird.add(s);
        secondThird.intersect(t);
        secondThird.subtract(f);

        intersection.reset();
        intersection.add(f);
        intersection.intersect(s);
        intersection.intersect(t);

        offset(firstOnly.getAreaReference(), firstOnlySize);
        offset(secondOnly.getAreaReference(), secondOnlySize);
        offset(thirdOnly.getAreaReference(), thirdOnlySize);
        offset(firstSecond.getAreaReference(), firstSecondSize);
        offset(firstThird.getAreaReference(), firstThirdSize);
        offset(secondThird.getAreaReference(), secondThirdSize);
        offset(intersection.getAreaReference(), intersectionSize);

        labelLeft(firstOnly.getAreaReference(), getFirstLabel());
        labelRight(secondOnly.getAreaReference(), getSecondLabel());
        labelCenter(thirdOnly.getAreaReference(), getThirdLabel());
        adjustLabels(getFirstLabel(), getSecondLabel());
    }

    /**
     * Offset the specified size label to the center of the specified area.
     *
     * @param area area
     * @param size size label
     */
    private void offset(final Area area, final PText size)
    {
        b = size.getFullBoundsReference();
        c = Centers.centerOf(area, c);
        size.setOffset(c.getX() - (b.getWidth() / 2.0d), c.getY() - (b.getHeight() / 2.0d));
    }

    /**
     * Offset the specified label to the top and left of the center of the specified area.
     *
     * @param area area
     * @param label label
     */
    private void labelLeft(final Area area, final PText label)
    {
        a = area.getBounds2D();
        b = label.getFullBoundsReference();
        c = Centers.centerOf(area, c);
        label.setOffset(c.getX() - ((2.0d * b.getWidth()) / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
    }

    /**
     * Offset the specified label to the top and right of the center of the specified area.
     *
     * @param area area
     * @param label label
     */
    private void labelRight(final Area area, final PText label)
    {
        a = area.getBounds2D();
        b = label.getFullBoundsReference();
        c = Centers.centerOf(area, c);
        label.setOffset(c.getX() - (b.getWidth() / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
    }

    /**
     * Offset the specified label to the bottom and center of the specified area.
     *
     * @param area area
     * @param label label
     */
    private void labelCenter(final Area area, final PText label)
    {
        a = area.getBounds2D();
        b = label.getFullBoundsReference();
        c = Centers.centerOf(area, c);
        label.setOffset(c.getX() - (b.getWidth() / 2.0d), a.getY() + a.getHeight() + LABEL_GAP);
    }

    /**
     * Adjust the horizontal offsets for the specified pair of labels if their bounds overlap.
     *
     * @param leftLabel left label
     * @param rightLabel right label
     */
    private void adjustLabels(final PText leftLabel, final PText rightLabel)
    {
        a = leftLabel.getFullBoundsReference();
        b = rightLabel.getFullBoundsReference();
        Rectangle2D.intersect(a, b, a);
        if (a.getWidth() > 0.0d)
        {
            leftLabel.offset(-1.0 * a.getWidth() / 2.0d - ADJUST_LABEL_GAP, 0.0d);
            rightLabel.offset(a.getWidth() / 2.0d + ADJUST_LABEL_GAP, 0.0d);
        }
    }

    /**
     * Return the path node for the first set.
     *
     * @return the path node for the first set
     */
    public PPath getFirst()
    {
        return first;
    }

    /**
     * Return the path node for the second set.
     *
     * @return the path node for the second set
     */
    public PPath getSecond()
    {
        return second;
    }

    /**
     * Return the path node for the third set.
     *
     * @return the path node for the third set
     */
    public PPath getThird()
    {
        return third;
    }

    /**
     * Return the area node for the first only view.
     *
     * @return the area node for the first only view
     */
    public PArea getFirstOnly()
    {
        return firstOnly;
    }

    /**
     * Return the area node for the second only view.
     *
     * @return the area node for the second only view
     */
    public PArea getSecondOnly()
    {
        return secondOnly;
    }

    /**
     * Return the area node for the third only view.
     *
     * @return the area node for the third only view
     */
    public PArea getThirdOnly()
    {
        return thirdOnly;
    }

    /**
     * Return the area node for the first second view.
     *
     * @return the area node for the first second view
     */
    public PArea getFirstSecond()
    {
        return firstSecond;
    }

    /**
     * Return the area node for the first third view.
     *
     * @return the area node for the first third view
     */
    public PArea getFirstThird()
    {
        return firstThird;
    }

    /**
     * Return the area node for the second third view.
     *
     * @return the area node for the second third view
     */
    public PArea getSecondThird()
    {
        return secondThird;
    }

    /**
     * Return the area node for the intersection view.
     *
     * @return the area node for the intersection view
     */
    public PArea getIntersection()
    {
        return intersection;
    }

    @Override
    public Iterable nodes()
    {
        return nodes;
    }

    @Override
    public PText labelForNode(final PNode node)
    {
        if (firstOnly.equals(node))
        {
            return getFirstOnlyLabel();
        }
        else if (secondOnly.equals(node))
        {
            return getSecondOnlyLabel();
        }
        else if (thirdOnly.equals(node))
        {
            return getThirdOnlyLabel();
        }
        else if (firstSecond.equals(node))
        {
            return getFirstSecondLabel();
        }
        else if (firstThird.equals(node))
        {
            return getFirstThirdLabel();
        }
        else if (secondThird.equals(node))
        {
            return getSecondThirdLabel();
        }
        else if (intersection.equals(node))
        {
            return getIntersectionLabel();
        }
        return null;
    }

    @Override
    public String labelTextForNode(final PNode node)
    {
        if (firstOnly.equals(node))
        {
            return getFirstOnlyLabelText();
        }
        else if (secondOnly.equals(node))
        {
            return getSecondOnlyLabelText();
        }
        else if (thirdOnly.equals(node))
        {
            return getThirdOnlyLabelText();
        }
        else if (firstSecond.equals(node))
        {
            return getFirstSecondLabelText();
        }
        else if (firstThird.equals(node))
        {
            return getFirstThirdLabelText();
        }
        else if (secondThird.equals(node))
        {
            return getSecondThirdLabelText();
        }
        else if (intersection.equals(node))
        {
            return getIntersectionLabelText();
        }
        return null;
    }

    @Override
    public Iterable sizeLabels()
    {
        return sizeLabels;
    }

    @Override
    public Set viewForNode(final PNode node)
    {
        if (firstOnly.equals(node))
        {
            return getModel().firstOnly();
        }
        else if (secondOnly.equals(node))
        {
            return getModel().secondOnly();
        }
        else if (thirdOnly.equals(node))
        {
            return getModel().thirdOnly();
        }
        else if (firstSecond.equals(node))
        {
            return getModel().firstSecond();
        }
        else if (firstThird.equals(node))
        {
            return getModel().firstThird();
        }
        else if (secondThird.equals(node))
        {
            return getModel().secondThird();
        }
        else if (intersection.equals(node))
        {
            return getModel().intersection();
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy