protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int w = getWidth(); int h = getHeight(); double xScale = (w - 2 * PAD) / (xMax - xMin); double yScale = (h - 2 * PAD) / (yMax - yMin); if (firstTime) System.out.printf("xScale = %.1f yScale = %.1f%n", xScale, yScale); Point2D.Double origin = new Point2D.Double(); // Axes origin. Point2D.Double offset = new Point2D.Double(); // Locate data. if (xMax < 0) { origin.x = w - PAD; offset.x = origin.x - xScale * xMax; } else if (xMin < 0) { origin.x = PAD - xScale * xMin; offset.x = origin.x; } else { origin.x = PAD; offset.x = PAD - xScale * xMin; } if (yMax < 0) { origin.y = h - PAD; offset.y = origin.y - yScale * yMax; } else if (yMin < 0) { origin.y = PAD - yScale * yMin; offset.y = origin.y; } else { origin.y = PAD; offset.y = PAD - yScale * yMin; } if (firstTime) { System.out.printf("origin = [%6.1f, %6.1f]%n", origin.x, origin.y); System.out.printf("offset = [%6.1f, %6.1f]%n", offset.x, offset.y); } // Draw abcissa. g2.draw(new Line2D.Double(PAD, origin.y, w - PAD, origin.y)); // Draw ordinate. g2.draw(new Line2D.Double(origin.x, PAD, origin.x, h - PAD)); g2.setPaint(Color.red); // Mark origin. g2.fill(new Ellipse2D.Double(origin.x - 2, origin.y - 2, 4, 4)); // Plot data. g2.setPaint(Color.blue); for (int i = 0; i < x.length; i++) { double x1 = offset.x + xScale * x[i]; double y1 = offset.y + yScale * y[i]; if (firstTime) System.out.printf("i = %d x1 = %6.1f y1 = %.1f%n", i, x1, y1); g2.fill(new Ellipse2D.Double(x1 - 2, y1 - 2, 4, 4)); g2.drawString(String.valueOf(i), (float) x1 + 3, (float) y1 - 3); } // Draw extreme data values. g2.setPaint(Color.black); Font font = g2.getFont(); FontRenderContext frc = g2.getFontRenderContext(); LineMetrics lm = font.getLineMetrics("0", frc); String s = String.format("%.1f", xMin); float width = (float) font.getStringBounds(s, frc).getWidth(); double x = offset.x + xScale * xMin; g2.drawString(s, (float) x, (float) origin.y + lm.getAscent()); s = String.format("%.1f", xMax); width = (float) font.getStringBounds(s, frc).getWidth(); x = offset.x + xScale * xMax; g2.drawString(s, (float) x - width, (float) origin.y + lm.getAscent()); s = String.format("%.1f", yMin); width = (float) font.getStringBounds(s, frc).getWidth(); double y = offset.y + yScale * yMin; g2.drawString(s, (float) origin.x + 1, (float) y + lm.getAscent()); s = String.format("%.1f", yMax); width = (float) font.getStringBounds(s, frc).getWidth(); y = offset.y + yScale * yMax; g2.drawString(s, (float) origin.x + 1, (float) y); if (firstTime) System.out.println("------------------------------"); firstTime = false; }
public static List<BezierPath> fromPathData(String str) throws IOException { LinkedList<BezierPath> paths = new LinkedList<BezierPath>(); BezierPath path = null; Point2D.Double p = new Point2D.Double(); Point2D.Double c1 = new Point2D.Double(); Point2D.Double c2 = new Point2D.Double(); StreamTokenizer tt = new StreamTokenizer(new StringReader(str)); tt.resetSyntax(); tt.parseNumbers(); tt.whitespaceChars(0, ' '); tt.whitespaceChars(',', ','); char nextCommand = 'M'; char command = 'M'; while (tt.nextToken() != StreamTokenizer.TT_EOF) { if (tt.ttype > 0) { command = (char) tt.ttype; } else { command = nextCommand; tt.pushBack(); } BezierPath.Node node; switch (command) { // moveto case 'M': if (path != null) { paths.add(path); } path = new BezierPath(); if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.moveTo(p.x, p.y); nextCommand = 'L'; break; case 'm': if (path != null) { paths.add(path); } path = new BezierPath(); if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.moveTo(p.x, p.y); nextCommand = 'l'; // close path break; case 'Z': case 'z': p.x = path.get(0).x[0]; p.y = path.get(0).y[0]; path.setClosed(true); // lineto break; case 'L': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.lineTo(p.x, p.y); nextCommand = 'L'; break; case 'l': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.lineTo(p.x, p.y); nextCommand = 'l'; break; case 'H': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; path.lineTo(p.x, p.y); nextCommand = 'H'; break; case 'h': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; path.lineTo(p.x, p.y); nextCommand = 'h'; break; case 'V': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.lineTo(p.x, p.y); nextCommand = 'V'; break; case 'v': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.lineTo(p.x, p.y); nextCommand = 'v'; // curveto break; case 'C': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.y = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.y = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.curveTo(c1.x, c1.y, c2.x, c2.y, p.x, p.y); nextCommand = 'C'; break; case 'c': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.x = p.x + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.y = p.y + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.x = p.x + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.y = p.y + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.curveTo(c1.x, c1.y, c2.x, c2.y, p.x, p.y); nextCommand = 'c'; break; case 'S': node = path.get(path.size() - 1); c1.x = node.x[0] * 2d - node.x[1]; c1.y = node.y[0] * 2d - node.y[1]; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.y = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.curveTo(c1.x, c1.y, c2.x, c2.y, p.x, p.y); nextCommand = 'S'; break; case 's': node = path.get(path.size() - 1); c1.x = node.x[0] * 2d - node.x[1]; c1.y = node.y[0] * 2d - node.y[1]; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.x = p.x + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c2.y = p.y + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.curveTo(c1.x, c1.y, c2.x, c2.y, p.x, p.y); nextCommand = 's'; // quadto break; case 'Q': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.y = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.quadTo(c1.x, c1.y, p.x, p.y); nextCommand = 'Q'; break; case 'q': if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.x = p.x + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); c1.y = p.y + tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.quadTo(c1.x, c1.y, p.x, p.y); nextCommand = 'q'; break; case 'T': node = path.get(path.size() - 1); c1.x = node.x[0] * 2d - node.x[1]; c1.y = node.y[0] * 2d - node.y[1]; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x = tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y = tt.nval; path.quadTo(c1.x, c1.y, p.x, p.y); nextCommand = 'T'; break; case 't': node = path.get(path.size() - 1); c1.x = node.x[0] * 2d - node.x[1]; c1.y = node.y[0] * 2d - node.y[1]; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.x += tt.nval; if (tt.nextToken() != StreamTokenizer.TT_NUMBER) throw new IOException("Number expected"); p.y += tt.nval; path.quadTo(c1.x, c1.y, p.x, p.y); nextCommand = 's'; break; default: throw new IOException("Illegal command: " + command); } } if (path != null) { paths.add(path); } return paths; }