1 /* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.marlin; 27 28 29 30 public final class DCollinearSimplifier implements DPathConsumer2D { 31 32 enum SimplifierState { 33 34 Empty, PreviousPoint, PreviousLine 35 }; 36 // slope precision threshold 37 static final double EPS = 1e-4D; // aaime proposed 1e-3D 38 39 DPathConsumer2D delegate; 40 SimplifierState state; 41 double px1, py1, px2, py2; 42 double pslope; 43 44 DCollinearSimplifier() { 45 } 46 47 public DCollinearSimplifier init(DPathConsumer2D delegate) { 48 this.delegate = delegate; 49 this.state = SimplifierState.Empty; 50 51 return this; // fluent API 52 } 53 54 @Override 55 public void pathDone() { 56 emitStashedLine(); 57 state = SimplifierState.Empty; 58 delegate.pathDone(); 59 } 60 61 @Override 62 public void closePath() { 63 emitStashedLine(); 64 state = SimplifierState.Empty; 65 delegate.closePath(); 66 } 67 68 @Override 69 public void quadTo(double x1, double y1, double x2, double y2) { 70 emitStashedLine(); 71 delegate.quadTo(x1, y1, x2, y2); 72 // final end point: 73 state = SimplifierState.PreviousPoint; 74 px1 = x2; 75 py1 = y2; 76 } 77 78 @Override 79 public void curveTo(double x1, double y1, double x2, double y2, 80 double x3, double y3) { 81 emitStashedLine(); 82 delegate.curveTo(x1, y1, x2, y2, x3, y3); 83 // final end point: 84 state = SimplifierState.PreviousPoint; 85 px1 = x3; 86 py1 = y3; 87 } 88 89 @Override 90 public void moveTo(double x, double y) { 91 emitStashedLine(); 92 delegate.moveTo(x, y); 93 state = SimplifierState.PreviousPoint; 94 px1 = x; 95 py1 = y; 96 } 97 98 @Override 99 public void lineTo(final double x, final double y) { 100 switch (state) { 101 case Empty: 102 delegate.lineTo(x, y); 103 state = SimplifierState.PreviousPoint; 104 px1 = x; 105 py1 = y; 106 return; 107 108 case PreviousPoint: 109 state = SimplifierState.PreviousLine; 110 px2 = x; 111 py2 = y; 112 pslope = getSlope(px1, py1, x, y); 113 return; 114 115 case PreviousLine: 116 final double slope = getSlope(px2, py2, x, y); 117 // test for collinearity 118 if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) { 119 // merge segments 120 px2 = x; 121 py2 = y; 122 return; 123 } 124 // emit previous segment 125 delegate.lineTo(px2, py2); 126 px1 = px2; 127 py1 = py2; 128 px2 = x; 129 py2 = y; 130 pslope = slope; 131 return; 132 default: 133 } 134 } 135 136 private void emitStashedLine() { 137 if (state == SimplifierState.PreviousLine) { 138 delegate.lineTo(px2, py2); 139 } 140 } 141 142 private static double getSlope(double x1, double y1, double x2, double y2) { 143 double dy = y2 - y1; 144 if (dy == 0D) { 145 return (x2 > x1) ? Double.POSITIVE_INFINITY 146 : Double.NEGATIVE_INFINITY; 147 } 148 return (x2 - x1) / dy; 149 } 150 }