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
26package sun.java2d.marlin;
27
28
29final 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
43    DCollinearSimplifier() {
44    }
45
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
54    public void pathDone() {
55        emitStashedLine();
56        state = SimplifierState.Empty;
57        delegate.pathDone();
58    }
59
60    @Override
61    public void closePath() {
62        emitStashedLine();
63        state = SimplifierState.Empty;
64        delegate.closePath();
65    }
66
67    @Override
68    public long getNativeConsumer() {
69        return 0;
70    }
71
72    @Override
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
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
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
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
140    private void emitStashedLine() {
141        if (state == SimplifierState.PreviousLine) {
142            delegate.lineTo(px2, py2);
143        }
144    }
145
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