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