1 /*
2  * Copyright (c) 2015, 2017, 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 sun.java2d.marlin;
27 
28 
29 final class DCollinearSimplifier implements DPathConsumer2D {
30 
31     enum SimplifierState {
32 
33         Empty, PreviousPoint, PreviousLine
34     };
35     // slope precision threshold
36     static final double EPS = 1e-4d; // aaime proposed 1e-3d
37 
38     DPathConsumer2D delegate;
39     SimplifierState state;
40     double px1, py1, px2, py2;
41     double pslope;
42 
DCollinearSimplifier()43     DCollinearSimplifier() {
44     }
45 
init(DPathConsumer2D delegate)46     public DCollinearSimplifier init(DPathConsumer2D delegate) {
47         this.delegate = delegate;
48         this.state = SimplifierState.Empty;
49 
50         return this; // fluent API
51     }
52 
53     @Override
pathDone()54     public void pathDone() {
55         emitStashedLine();
56         state = SimplifierState.Empty;
57         delegate.pathDone();
58     }
59 
60     @Override
closePath()61     public void closePath() {
62         emitStashedLine();
63         state = SimplifierState.Empty;
64         delegate.closePath();
65     }
66 
67     @Override
getNativeConsumer()68     public long getNativeConsumer() {
69         return 0;
70     }
71 
72     @Override
quadTo(double x1, double y1, double x2, double y2)73     public void quadTo(double x1, double y1, double x2, double y2) {
74         emitStashedLine();
75         delegate.quadTo(x1, y1, x2, y2);
76         // final end point:
77         state = SimplifierState.PreviousPoint;
78         px1 = x2;
79         py1 = y2;
80     }
81 
82     @Override
curveTo(double x1, double y1, double x2, double y2, double x3, double y3)83     public void curveTo(double x1, double y1, double x2, double y2,
84                         double x3, double y3) {
85         emitStashedLine();
86         delegate.curveTo(x1, y1, x2, y2, x3, y3);
87         // final end point:
88         state = SimplifierState.PreviousPoint;
89         px1 = x3;
90         py1 = y3;
91     }
92 
93     @Override
moveTo(double x, double y)94     public void moveTo(double x, double y) {
95         emitStashedLine();
96         delegate.moveTo(x, y);
97         state = SimplifierState.PreviousPoint;
98         px1 = x;
99         py1 = y;
100     }
101 
102     @Override
lineTo(final double x, final double y)103     public void lineTo(final double x, final double y) {
104         switch (state) {
105             case Empty:
106                 delegate.lineTo(x, y);
107                 state = SimplifierState.PreviousPoint;
108                 px1 = x;
109                 py1 = y;
110                 return;
111 
112             case PreviousPoint:
113                 state = SimplifierState.PreviousLine;
114                 px2 = x;
115                 py2 = y;
116                 pslope = getSlope(px1, py1, x, y);
117                 return;
118 
119             case PreviousLine:
120                 final double slope = getSlope(px2, py2, x, y);
121                 // test for collinearity
122                 if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) {
123                     // merge segments
124                     px2 = x;
125                     py2 = y;
126                     return;
127                 }
128                 // emit previous segment
129                 delegate.lineTo(px2, py2);
130                 px1 = px2;
131                 py1 = py2;
132                 px2 = x;
133                 py2 = y;
134                 pslope = slope;
135                 return;
136             default:
137         }
138     }
139 
emitStashedLine()140     private void emitStashedLine() {
141         if (state == SimplifierState.PreviousLine) {
142             delegate.lineTo(px2, py2);
143         }
144     }
145 
getSlope(double x1, double y1, double x2, double y2)146     private static double getSlope(double x1, double y1, double x2, double y2) {
147         double dy = y2 - y1;
148         if (dy == 0.0d) {
149             return (x2 > x1) ? Double.POSITIVE_INFINITY
150                    : Double.NEGATIVE_INFINITY;
151         }
152         return (x2 - x1) / dy;
153     }
154 }
155