/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.visualvm.modules.tracer.impl.timeline; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Stroke; import java.util.List; import org.netbeans.lib.profiler.charts.ItemSelection; import org.netbeans.lib.profiler.charts.swing.Utils; import org.netbeans.lib.profiler.charts.xy.XYItem; import org.netbeans.lib.profiler.charts.xy.synchronous.SynchronousXYChartContext; /** * * @author Jiri Sedlacek */ final class DiscreteXYPainter extends TimelineXYPainter { private static final Polygon POLYGON = new Polygon(); private static final int[] x3arr = new int[3]; private static final int[] y3arr = new int[3]; private static final int[] x4arr = new int[4]; private static final int[] y4arr = new int[4]; protected final int lineWidth; protected final Color lineColor; protected final Color fillColor; protected final Color definingColor; protected final Stroke lineStroke; protected final int width; protected final boolean fixedWidth; protected final boolean topLineOnly; protected final boolean outlineOnly; private final PointsComputer computer; DiscreteXYPainter(float lineWidth, Color lineColor, Color fillColor, int width, boolean fixedWidth, boolean topLineOnly, boolean outlineOnly, double dataFactor, PointsComputer computer) { super((int)Math.ceil(lineWidth), fillColor != null || (!topLineOnly && !outlineOnly), dataFactor); if (lineColor == null && fillColor == null) throw new IllegalArgumentException("lineColor or fillColor must not be null"); // NOI18N this.lineWidth = (int)Math.ceil(lineWidth); this.lineColor = Utils.checkedColor(lineColor); this.fillColor = Utils.checkedColor(fillColor); definingColor = lineColor != null ? lineColor : fillColor; this.lineStroke = new BasicStroke(lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); this.width = width; this.fixedWidth = fixedWidth; this.topLineOnly = topLineOnly; this.outlineOnly = outlineOnly; this.computer = computer; } protected Color getDefiningColor() { return definingColor; } protected void paint(XYItem item, List<ItemSelection> highlighted, List<ItemSelection> selected, Graphics2D g, Rectangle dirtyArea, SynchronousXYChartContext context) { int zeroY = 0; int zeroYLimit = 0; if (fillColor != null || !topLineOnly) { zeroY = Utils.checkedInt(context.getViewY(context.getDataOffsetY())); zeroY = Math.max(Utils.checkedInt(context.getViewportOffsetY()), zeroY); zeroY = Math.min(Utils.checkedInt(context.getViewportOffsetY() + context.getViewportHeight()), zeroY); zeroYLimit = zeroY - lineWidth + 1; } int outlineWidth = lineColor == null || topLineOnly || outlineOnly ? 0 : Math.max(1, (width > 0 ? 2 : 1) * lineWidth - 1); int valuesCount = item.getValuesCount(); Rectangle dirtyExt = new Rectangle(dirtyArea); dirtyExt.x -= lineWidth; dirtyExt.width += lineWidth * 2; if (width > 0 || lineColor == null || topLineOnly) { int[][] idxs = computer.getVisible(dirtyExt, valuesCount, context, 1, 0); if (idxs == null) return; int[] visibleIndexes = idxs[0]; int npoints = idxs[1][0]; int[][] points = computer.createPoints(visibleIndexes, npoints, item, dataFactor, context); int[] xpoints = points[0]; int[] ypoints = points[1]; int startX = xpoints[0]; int stopX; int currentX = startX; int nextX = 0; int itemsOffset = fixedWidth ? 0 : width; for (int i = 0; i < npoints; i++) { int height = zeroY - ypoints[i]; if (fixedWidth) { startX = Math.max(startX, currentX - width / 2); nextX = i == npoints - 1 ? xpoints[i] : xpoints[i + 1]; stopX = currentX + Math.min(width / 2, (nextX - currentX) / 2); } else { nextX = i == npoints - 1 ? xpoints[i] : xpoints[i + 1]; int diff = nextX - currentX; itemsOffset = Math.min(diff, width + 1); stopX = currentX + (diff - itemsOffset) / 2; } int segmentWidth = stopX - startX; if (fillColor != null && segmentWidth >= outlineWidth) { g.setColor(fillColor); g.fillRect(startX, zeroY - height, segmentWidth + 1, height); } if (lineColor != null) { g.setColor(lineColor); g.setStroke(lineStroke); if (topLineOnly) { g.drawLine(startX, zeroY - height, Math.max(startX + 1, stopX), zeroY - height); } else if (segmentWidth + 1 <= lineWidth) { g.drawLine(startX, zeroY - height, startX, zeroYLimit); } else { int[] xx; int[] yy; if (i == 0) { xx = xArr(startX, stopX, stopX); yy = yArr(zeroY - height, zeroY - height, zeroYLimit); } else if (i == npoints - 1) { xx = xArr(startX, startX, stopX); yy = yArr(zeroYLimit, zeroY - height, zeroY - height); } else { xx = xArr(startX, startX, stopX, stopX); yy = yArr(zeroYLimit, zeroY - height, zeroY - height, zeroYLimit); } g.drawPolyline(xx, yy, xx.length); } } currentX = nextX; startX = stopX + itemsOffset; } } else { int extraPoints = fillColor != null ? 2 : 0; int[][] idxs = computer.getVisible(dirtyExt, valuesCount, context, 2, extraPoints); if (idxs == null) return; int[] visibleIndexes = idxs[0]; int npoints = idxs[1][0]; int[][] points = computer.createPoints(visibleIndexes, npoints, item, dataFactor, context); int[] xpoints = points[0]; int[] ypoints = points[1]; int npointse = npoints; npoints -= extraPoints; int index = 1; int lastX = xpoints[0]; while (index < npoints - 2) { int currentX = xpoints[index + 1]; currentX -= (currentX - lastX) / 2; xpoints[index] = currentX; lastX = xpoints[index + 1]; xpoints[index + 1] = currentX; index += 2; } if (fillColor != null) { xpoints[npointse - 2] = xpoints[npointse - 3]; ypoints[npointse - 2] = zeroY; xpoints[npointse - 1] = xpoints[0]; ypoints[npointse - 1] = ypoints[npointse - 2]; POLYGON.xpoints = xpoints; POLYGON.ypoints = ypoints; POLYGON.npoints = npointse; g.setPaint(fillColor); g.fill(POLYGON); } g.setColor(lineColor); g.setStroke(lineStroke); g.drawPolyline(xpoints, ypoints, npoints); if (!outlineOnly) { g.setColor(lineColor); g.setStroke(lineStroke); int i = 1; while (i < npoints - 1) { int y = ypoints[i] + lineWidth / 2; if (y < zeroYLimit) g.drawLine(xpoints[i], y, xpoints[i], zeroYLimit); i += 3; if (i >= npoints - 1) break; y = ypoints[i] + lineWidth / 2; if (y < zeroYLimit) g.drawLine(xpoints[i], y, xpoints[i], zeroYLimit); i++; } } } } private static int[] xArr(int... vals) { if (vals.length == 3) { x3arr[0] = vals[0]; x3arr[1] = vals[1]; x3arr[2] = vals[2]; return x3arr; } else { x4arr[0] = vals[0]; x4arr[1] = vals[1]; x4arr[2] = vals[2]; x4arr[3] = vals[3]; return x4arr; } } private static int[] yArr(int... vals) { if (vals.length == 3) { y3arr[0] = vals[0]; y3arr[1] = vals[1]; y3arr[2] = vals[2]; return y3arr; } else { y4arr[0] = vals[0]; y4arr[1] = vals[1]; y4arr[2] = vals[2]; y4arr[3] = vals[3]; return y4arr; } } }