/* * Freeplane - mind map editor * Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev * * This file is modified by Dimitry Polivaev in 2008. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.freeplane.view.swing.map.cloud; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Stroke; import java.util.LinkedList; import java.util.Random; import java.util.Vector; import org.freeplane.features.cloud.CloudController; import org.freeplane.features.cloud.CloudModel; import org.freeplane.features.map.NodeModel; import org.freeplane.view.swing.map.MapView; import org.freeplane.view.swing.map.NodeView; /** * This class represents a Cloud around a node. */ abstract public class CloudView { static final Stroke DEF_STROKE = new BasicStroke(1); /** the layout functions can get the additional height of the clouded node . * @param cloud */ static public int getAdditionalHeigth(CloudModel cloud, final NodeView source) { final CloudView heightCalculator = new CloudViewFactory().createCloudView(cloud, source); return (int) (2.2 * heightCalculator.getDistanceToConvexHull()); } protected CloudModel cloudModel; protected NodeView source; private final int iterativeLevel; private Random random; CloudView(final CloudModel cloudModel, final NodeView source) { this.cloudModel = cloudModel; this.source = source; iterativeLevel = getCloudIterativeLevel(); } private int getCloudIterativeLevel() { int iterativeLevel = 0; for (NodeView parentNode = source.getParentView(); parentNode != null; parentNode = parentNode.getParentView()) { if (null != parentNode.getCloudModel()) { iterativeLevel++; } } return iterativeLevel; } public Color getColor() { return source.getCloudColor(); } protected double getDistanceToConvexHull() { return 20 / (getIterativeLevel() + 1) * getZoom(); } public Color getExteriorColor(final Color color) { return color.darker(); } /** * getIterativeLevel() describes the n-th nested cloud that is to be * painted. */ protected int getIterativeLevel() { return iterativeLevel; } protected MapView getMap() { return source.getMap(); } protected CloudModel getModel() { return cloudModel; } /** * Get the width in pixels rather than in width constant (like -1) */ public int getRealWidth() { final int width = getWidth(); return (width < 1) ? 1 : width; } public Stroke getStroke() { final int width = getWidth(); if (width < 1) { return CloudView.DEF_STROKE; } return new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); } public int getWidth() { final NodeModel node = source.getModel(); return CloudController.getController(source.getMap().getModeController()).getWidth(node); } protected double getZoom() { return getMap().getZoom(); } public void paint(final Graphics graphics) { random = new Random(0); final Graphics2D g = (Graphics2D) graphics.create(); final Graphics2D gstroke = (Graphics2D) g.create(); final Color color = getColor(); g.setColor(color); /* set a bigger stroke to prevent not filled areas. */ g.setStroke(getStroke()); /* now bold */ gstroke.setColor(getExteriorColor(color)); gstroke.setStroke(getStroke()); /* * calculate the distances between two points on the convex hull * depending on the getIterativeLevel(). */ /** get coordinates */ final LinkedList<Point> coordinates = new LinkedList<Point>(); source.getCoordinates(coordinates); final ConvexHull hull = new ConvexHull(); final Vector<Point> res = hull.calculateHull(coordinates); final Polygon p = new Polygon(); Point lastPt = null; for (int i = 0; i < res.size(); ++i) { final Point pt = (Point) res.get(i); if(!pt.equals(lastPt)){ p.addPoint(pt.x, pt.y); lastPt = pt; } } final Point pt = (Point) res.get(0); p.addPoint(pt.x, pt.y); paintDecoration(p, g, gstroke); g.dispose(); } protected void paintDecoration(final Polygon p, Graphics2D g, Graphics2D gstroke){ fillPolygon(p, g); double middleDistanceBetweenPoints = calcDistanceBetweenPoints(); final int[] xpoints = p.xpoints; final int[] ypoints = p.ypoints; final Point lastPoint = new Point(xpoints[0], ypoints[0]); double x0, y0; x0 = lastPoint.x; y0 = lastPoint.y; /* close the path: */ double x2, y2; /* the drawing start points. */ x2 = x0; y2 = y0; for (int i = p.npoints - 2; i >= 0; --i) { final Point nextPoint = new Point(xpoints[i], ypoints[i]); double x1, y1, x3, y3, dx, dy, dxn, dyn; x1 = nextPoint.x; y1 = nextPoint.y; dx = x1 - x0; /* direction of p0 -> p1 */ dy = y1 - y0; final double length = Math.sqrt(dx * dx + dy * dy); dxn = dx / length; /* normalized direction of p0 -> p1 */ dyn = dy / length; for (int j = 0;;) { double distanceBetweenPoints = middleDistanceBetweenPoints * random(0.7); if (j + 2* distanceBetweenPoints < length) { j += distanceBetweenPoints; x3 = x0 + j * dxn; /* the drawing end point.*/ y3 = y0 + j * dyn; } else { /* last point */ break; } paintDecoration(g, gstroke, x2, y2, x3, y3); x2 = x3; y2 = y3; } paintDecoration(g, gstroke, x2, y2, x1, y1); x2 = x1; y2 = y1; x0 = x1; y0 = y1; } } protected void fillPolygon(final Polygon p, Graphics2D g) { g.fillPolygon(p); g.drawPolygon(p); } protected void paintDecoration(Graphics2D g, Graphics2D gstroke, double x0, double y0, double x1, double y1) { double dx, dy; dx = x1 - x0; dy = y1 - y0; final double length = Math.sqrt(dx * dx + dy * dy); double dxn, dyn; dxn = dx / length; dyn = dy / length; paintDecoration(g, gstroke, x0, y0, x1, y1, dx, dy, dxn, dyn); } abstract protected void paintDecoration(Graphics2D g, Graphics2D gstroke, double x0, double y0, double x1, double y1, double dx, double dy, double dxn, double dyn); protected double calcDistanceBetweenPoints() { final double distanceBetweenPoints = getDistanceToConvexHull(); return distanceBetweenPoints; } protected double random(double min) { return (min + (1-min) * random.nextDouble()); } }