1c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy/*
2c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * Copyright 2018 The Android Open Source Project
3c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy *
4c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * Licensed under the Apache License, Version 2.0 (the "License");
5c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * you may not use this file except in compliance with the License.
6c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * You may obtain a copy of the License at
7c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy *
8c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy *      http://www.apache.org/licenses/LICENSE-2.0
9c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy *
10c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * Unless required by applicable law or agreed to in writing, software
11c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * distributed under the License is distributed on an "AS IS" BASIS,
12c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * See the License for the specific language governing permissions and
14c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy * limitations under the License.
15c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy */
16c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
17c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guypackage androidx.core.graphics;
18c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
19c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport android.graphics.Path;
20c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport android.graphics.PointF;
21c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
22c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport androidx.annotation.FloatRange;
23c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport androidx.annotation.NonNull;
24c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport androidx.annotation.RequiresApi;
25c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
26c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport java.util.ArrayList;
27c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport java.util.Collection;
28c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guyimport java.util.List;
29c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
30c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy/** A set of path-related utility methods. */
31c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guypublic final class PathUtils {
32c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    /**
33c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * Flattens (or approximate) a {@link Path} with a series of line segments using a 0.5 pixel
34c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * error.
35c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     *
36c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * <em>Note:</em> This method requires API 26 or newer.
37c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     *
38c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * @see #flatten(Path, float)
39c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     */
40c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    @RequiresApi(26)
41c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    @NonNull
42c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    public static Collection<PathSegment> flatten(@NonNull Path path) {
43c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        return flatten(path, 0.5f);
44c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    }
45c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
46c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    /**
47c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * Flattens (or approximate) a {@link Path} with a series of line segments.
48c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     *
49c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * <em>Note:</em> This method requires API 26 or newer.
50c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     *
51c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * @param error The acceptable error for a line on the Path. Typically this would be
52c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     *              0.5 so that the error is less than half a pixel.
53c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     *
54c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     * @see Path#approximate
55c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy     */
56c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    @RequiresApi(26)
57c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    @NonNull
58c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    public static Collection<PathSegment> flatten(@NonNull final Path path,
59c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            @FloatRange(from = 0) final float error) {
60c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        float[] pathData = path.approximate(error);
61c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        int pointCount = pathData.length / 3;
62c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        List<PathSegment> segments = new ArrayList<>(pointCount);
63c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        for (int i = 1; i < pointCount; i++) {
64c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            int index = i * 3;
65c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            int prevIndex = (i - 1) * 3;
66c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
67c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            float d = pathData[index];
68c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            float x = pathData[index + 1];
69c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            float y = pathData[index + 2];
70c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
71c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            float pd = pathData[prevIndex];
72c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            float px = pathData[prevIndex + 1];
73c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            float py = pathData[prevIndex + 2];
74c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
75c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            if (d != pd && (x != px || y != py)) {
76c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy                segments.add(new PathSegment(new PointF(px, py), pd, new PointF(x, y), d));
77c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy            }
78c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        }
79c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy        return segments;
80c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    }
81c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy
82c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    private PathUtils() {
83c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy    }
84c5032d73c8d389a2b62cc721bfecde2937d4a363Romain Guy}
85