mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-19 03:21:36 +08:00
837 lines
30 KiB
Java
837 lines
30 KiB
Java
/*
|
|
* Copyright (C) 2024 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.google.android.material.shape;
|
|
|
|
import static com.google.android.material.shape.MaterialShapeUtils.createRotationMatrix;
|
|
import static com.google.android.material.shape.MaterialShapeUtils.createScaleMatrix;
|
|
import static com.google.android.material.shape.MaterialShapeUtils.createSkewMatrix;
|
|
import static java.lang.Math.PI;
|
|
|
|
import android.graphics.Matrix;
|
|
import android.graphics.PointF;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RestrictTo;
|
|
import androidx.annotation.RestrictTo.Scope;
|
|
import androidx.graphics.shapes.CornerRounding;
|
|
import androidx.graphics.shapes.RoundedPolygon;
|
|
import androidx.graphics.shapes.RoundedPolygonKt;
|
|
import androidx.graphics.shapes.ShapesKt;
|
|
import androidx.graphics.shapes.Shapes_androidKt;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import org.jetbrains.annotations.Contract;
|
|
|
|
/**
|
|
* A utility class providing static methods for creating Material endorsed shapes using the {@code
|
|
* androidx.graphics.shapes} library.
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(Scope.LIBRARY_GROUP)
|
|
public final class MaterialShapes {
|
|
// Cache various roundings for use below
|
|
private static final CornerRounding CORNER_ROUND_10 =
|
|
new CornerRounding(/* radius= */ .1f, /* smoothing= */ 0f);
|
|
private static final CornerRounding CORNER_ROUND_15 =
|
|
new CornerRounding(/* radius= */ .15f, /* smoothing= */ 0f);
|
|
private static final CornerRounding CORNER_ROUND_20 =
|
|
new CornerRounding(/* radius= */ .2f, /* smoothing= */ 0f);
|
|
private static final CornerRounding CORNER_ROUND_30 =
|
|
new CornerRounding(/* radius= */ .3f, /* smoothing= */ 0f);
|
|
private static final CornerRounding CORNER_ROUND_40 =
|
|
new CornerRounding(/* radius= */ .4f, /* smoothing= */ 0f);
|
|
private static final CornerRounding CORNER_ROUND_50 =
|
|
new CornerRounding(/* radius= */ .5f, /* smoothing= */ 0f);
|
|
private static final CornerRounding CORNER_ROUND_100 =
|
|
new CornerRounding(/* radius= */ 1f, /* smoothing= */ 0f);
|
|
|
|
/** A circle shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon CIRCLE =
|
|
MaterialShapeUtils.normalize(getCircle(), /* radial= */ true);
|
|
|
|
/** A rounded square shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon SQUARE =
|
|
MaterialShapeUtils.normalize(getSquare(), /* radial= */ true);
|
|
|
|
/** A slanted rounded square shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon SLANTED_SQUARE =
|
|
MaterialShapeUtils.normalize(getSlantedSquare(), /* radial= */ true);
|
|
|
|
/** An arch shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon ARCH =
|
|
MaterialShapeUtils.normalize(getArch(), /* radial= */ true);
|
|
|
|
/** A fan shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon FAN =
|
|
MaterialShapeUtils.normalize(getFan(/* rotateDegrees= */ -45f), /* radial= */ true);
|
|
|
|
/** An arrow shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon ARROW =
|
|
MaterialShapeUtils.normalize(getArrow(), /* radial= */ true);
|
|
|
|
/** A semi-circle shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon SEMI_CIRCLE =
|
|
MaterialShapeUtils.normalize(getSemiCircle(), /* radial= */ true);
|
|
|
|
/** An oval shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon OVAL =
|
|
MaterialShapeUtils.normalize(getOval(/* rotateDegrees= */ -45f), /* radial= */ true);
|
|
|
|
/** A pill shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon PILL =
|
|
MaterialShapeUtils.normalize(getPill(/* rotateDegrees= */ -45f), /* radial= */ true);
|
|
|
|
/** A triangle shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon TRIANGLE =
|
|
MaterialShapeUtils.normalize(getTriangle(/* rotateDegrees= */ -90f), /* radial= */ true);
|
|
|
|
/** A diamond shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon DIAMOND =
|
|
MaterialShapeUtils.normalize(getDiamond(), /* radial= */ true);
|
|
|
|
/** A clam-shell shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon CLAM_SHELL =
|
|
MaterialShapeUtils.normalize(getClamShell(), /* radial= */ true);
|
|
|
|
/** A pentagon shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon PENTAGON =
|
|
MaterialShapeUtils.normalize(
|
|
getPentagon(/* rotateDegrees= */ -18f), /* radial= */ true);
|
|
|
|
/** A gem shape {@link RoundedPolygon} fitting in a unit circle. */
|
|
public static final RoundedPolygon GEM =
|
|
MaterialShapeUtils.normalize(getGem(/* rotateDegrees= */ -90f), /* radial= */ true);
|
|
|
|
public static final RoundedPolygon SUNNY =
|
|
MaterialShapeUtils.normalize(getSunny(), /* radial= */ true);
|
|
public static final RoundedPolygon VERY_SUNNY =
|
|
MaterialShapeUtils.normalize(getVerySunny(), /* radial= */ true);
|
|
public static final RoundedPolygon COOKIE_4 =
|
|
MaterialShapeUtils.normalize(getCookie4(), /* radial= */ true);
|
|
public static final RoundedPolygon COOKIE_6 =
|
|
MaterialShapeUtils.normalize(getCookie6(), /* radial= */ true);
|
|
public static final RoundedPolygon COOKIE_7 =
|
|
MaterialShapeUtils.normalize(getCookie7(), /* radial= */ true);
|
|
public static final RoundedPolygon COOKIE_9 =
|
|
MaterialShapeUtils.normalize(getCookie9(), /* radial= */ true);
|
|
public static final RoundedPolygon COOKIE_12 =
|
|
MaterialShapeUtils.normalize(getCookie12(), /* radial= */ true);
|
|
public static final RoundedPolygon GHOSTISH =
|
|
MaterialShapeUtils.normalize(getGhostish(), /* radial= */ true);
|
|
public static final RoundedPolygon CLOVER_4 =
|
|
MaterialShapeUtils.normalize(getClover4(), /* radial= */ true);
|
|
public static final RoundedPolygon CLOVER_8 =
|
|
MaterialShapeUtils.normalize(getClover8(), /* radial= */ true);
|
|
public static final RoundedPolygon BURST =
|
|
MaterialShapeUtils.normalize(getBurst(), /* radial= */ true);
|
|
public static final RoundedPolygon SOFT_BURST =
|
|
MaterialShapeUtils.normalize(getSoftBurst(), /* radial= */ true);
|
|
public static final RoundedPolygon BOOM =
|
|
MaterialShapeUtils.normalize(getBoom(), /* radial= */ true);
|
|
public static final RoundedPolygon SOFT_BOOM =
|
|
MaterialShapeUtils.normalize(getSoftBoom(), /* radial= */ true);
|
|
public static final RoundedPolygon FLOWER =
|
|
MaterialShapeUtils.normalize(getFlower(), /* radial= */ true);
|
|
public static final RoundedPolygon PUFFY =
|
|
MaterialShapeUtils.normalize(getPuffy(), /* radial= */ true);
|
|
public static final RoundedPolygon PUFFY_DIAMOND =
|
|
MaterialShapeUtils.normalize(getPuffyDiamond(), /* radial= */ true);
|
|
public static final RoundedPolygon PIXEL_CIRCLE =
|
|
MaterialShapeUtils.normalize(getPixelCircle(), /* radial= */ true);
|
|
public static final RoundedPolygon PIXEL_TRIANGLE =
|
|
MaterialShapeUtils.normalize(getPixelTriangle(), /* radial= */ true);
|
|
public static final RoundedPolygon BUN =
|
|
MaterialShapeUtils.normalize(getBun(), /* radial= */ true);
|
|
public static final RoundedPolygon HEART =
|
|
MaterialShapeUtils.normalize(getHeart(), /* radial= */ true);
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getCircle() {
|
|
return ShapesKt.circle(RoundedPolygon.Companion, /* numVertices= */ 10);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getSquare() {
|
|
return ShapesKt.rectangle(
|
|
RoundedPolygon.Companion,
|
|
/* width= */ 1f,
|
|
/* height= */ 1f,
|
|
/* rounding= */ CORNER_ROUND_30,
|
|
/* perVertexRounding= */ null,
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getSlantedSquare() {
|
|
return Shapes_androidKt.transformed(getSquare(), createSkewMatrix(-.1f, 0f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getArch() {
|
|
return Shapes_androidKt.transformed(
|
|
RoundedPolygonKt.RoundedPolygon(
|
|
/* numVertices= */ 4,
|
|
/* radius= */ 1f,
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f,
|
|
/* rounding= */ CornerRounding.Unrounded,
|
|
/* perVertexRounding= */ Arrays.asList(
|
|
CORNER_ROUND_100, CORNER_ROUND_100, CORNER_ROUND_20, CORNER_ROUND_20)),
|
|
createRotationMatrix(-135));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getFan() {
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
/* numVertices= */ 4,
|
|
/* radius= */ 1f,
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f,
|
|
CornerRounding.Unrounded,
|
|
/* perVertexRounding= */ Arrays.asList(
|
|
CORNER_ROUND_100, CORNER_ROUND_20, CORNER_ROUND_20, CORNER_ROUND_20));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getFan(float rotateDegrees) {
|
|
return Shapes_androidKt.transformed(getFan(), createRotationMatrix(rotateDegrees));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getArrow() {
|
|
return triangleChip(
|
|
/* innerRadius= */ .3375f,
|
|
/* cornerRounding= */ new CornerRounding(/* radius= */ .25f, /* smoothing= */ .48f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getSemiCircle() {
|
|
return ShapesKt.rectangle(
|
|
RoundedPolygon.Companion,
|
|
/* width= */ 1.6f,
|
|
/* height= */ 1f,
|
|
/* rounding= */ CornerRounding.Unrounded,
|
|
/* perVertexRounding= */ Arrays.asList(
|
|
CORNER_ROUND_20, CORNER_ROUND_20, CORNER_ROUND_100, CORNER_ROUND_100),
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getOval() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.circle(RoundedPolygon.Companion),
|
|
createScaleMatrix(/* scaleX= */ 1f, /* scaleY= */ .7f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getOval(float rotateDegrees) {
|
|
return Shapes_androidKt.transformed(getOval(), createRotationMatrix(rotateDegrees));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPill() {
|
|
return ShapesKt.pill(RoundedPolygon.Companion, /* width= */ 1.25f, /* height= */ 1f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPill(float rotateDegrees) {
|
|
return Shapes_androidKt.transformed(getPill(), createRotationMatrix(rotateDegrees));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getTriangle() {
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
/* numVertices= */ 3,
|
|
/* radius= */ 1f,
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f,
|
|
/* rounding= */ CORNER_ROUND_20);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getTriangle(float rotateDegrees) {
|
|
return Shapes_androidKt.transformed(getTriangle(), createRotationMatrix(rotateDegrees));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getDiamond() {
|
|
return Shapes_androidKt.transformed(
|
|
RoundedPolygonKt.RoundedPolygon(
|
|
/* numVertices= */ 4,
|
|
/* radius= */ 1f,
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f,
|
|
/* rounding= */ CORNER_ROUND_30),
|
|
createScaleMatrix(/* scaleX= */ 1f, /* scaleY= */ 1.2f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getClamShell() {
|
|
float cornerInset = .6f;
|
|
float edgeInset = .4f;
|
|
float height = .7f;
|
|
float[] hexPoints =
|
|
new float[] {
|
|
1f,
|
|
0f,
|
|
cornerInset,
|
|
height,
|
|
edgeInset,
|
|
height,
|
|
-edgeInset,
|
|
height,
|
|
-cornerInset,
|
|
height,
|
|
-1f,
|
|
0f,
|
|
-cornerInset,
|
|
-height,
|
|
-edgeInset,
|
|
-height,
|
|
edgeInset,
|
|
-height,
|
|
cornerInset,
|
|
-height
|
|
};
|
|
List<CornerRounding> perVertexRounding =
|
|
Arrays.asList(
|
|
CORNER_ROUND_30,
|
|
CORNER_ROUND_30,
|
|
CornerRounding.Unrounded,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_30,
|
|
CORNER_ROUND_30,
|
|
CORNER_ROUND_30,
|
|
CornerRounding.Unrounded,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_30);
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
hexPoints, /* rounding= */ CornerRounding.Unrounded, perVertexRounding);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPentagon() {
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
/* numVertices= */ 5,
|
|
/* radius= */ 1f,
|
|
/* centerX= */ 0f,
|
|
/* centerY= */ 0f,
|
|
/* rounding= */ CORNER_ROUND_30);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPentagon(float rotateDegrees) {
|
|
return Shapes_androidKt.transformed(getPentagon(), createRotationMatrix(rotateDegrees));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getGem() {
|
|
// This shape looks like a rounded hexagon with a small offset in the second and last vertices.
|
|
int numVertices = 6;
|
|
float radius = 1f;
|
|
float[] points = new float[numVertices * 2];
|
|
for (int i = 0; i < numVertices; i++) {
|
|
PointF vertex = radialToCartesian(new PointF(radius, (float) (2 * PI / numVertices * i)));
|
|
points[i * 2] = vertex.x;
|
|
points[i * 2 + 1] = vertex.y;
|
|
}
|
|
// Offsets the second vertex (in the first quadrant) by (-0.1, -0.1) towards the center.
|
|
points[2] -= .1f;
|
|
points[3] -= .1f;
|
|
// Offsets the last vertex (in the fourth quadrant) by (-0.1, 0.1) towards the center.
|
|
points[10] -= .1f;
|
|
points[11] += .1f;
|
|
return RoundedPolygonKt.RoundedPolygon(points, CORNER_ROUND_40);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getGem(float rotateDegrees) {
|
|
return Shapes_androidKt.transformed(getGem(), createRotationMatrix(rotateDegrees));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getSunny() {
|
|
return ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 8,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.8f,
|
|
/* rounding= */ CORNER_ROUND_15);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getVerySunny() {
|
|
return ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 8,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.65f,
|
|
/* rounding= */ CORNER_ROUND_10);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getCookie4() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 4,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.5f,
|
|
/* rounding= */ CORNER_ROUND_30),
|
|
createRotationMatrix(-45f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getCookie6() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 6,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.75f,
|
|
/* rounding= */ CORNER_ROUND_30),
|
|
createRotationMatrix(-90f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getCookie7() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 7,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.75f,
|
|
/* rounding= */ CORNER_ROUND_50),
|
|
createRotationMatrix(-90f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getCookie9() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 9,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.8f,
|
|
/* rounding= */ CORNER_ROUND_50),
|
|
createRotationMatrix(-90f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getCookie12() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 12,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.8f,
|
|
/* rounding= */ CORNER_ROUND_50),
|
|
createRotationMatrix(-90f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getGhostish() {
|
|
float inset = .46f;
|
|
float h = 1.2f;
|
|
float[] points = new float[] {-1f, -h, 1f, -h, 1f, h, 0f, inset, -1f, h};
|
|
List<CornerRounding> perVertexRounding =
|
|
Arrays.asList(
|
|
CORNER_ROUND_100, CORNER_ROUND_100, CORNER_ROUND_50, CORNER_ROUND_100, CORNER_ROUND_50);
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
points, /* rounding= */ CornerRounding.Unrounded, perVertexRounding);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getClover4() {
|
|
// No inner rounding.
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 4,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.2f,
|
|
/* rounding= */ CORNER_ROUND_40,
|
|
/* innerRounding= */ CornerRounding.Unrounded),
|
|
createRotationMatrix(45f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getClover8() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 8,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.65f,
|
|
/* rounding= */ CORNER_ROUND_30,
|
|
/* innerRounding= */ CornerRounding.Unrounded),
|
|
createRotationMatrix(360 / 16f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getBurst() {
|
|
return ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 12,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.7f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getSoftBurst() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 10,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.65f,
|
|
/* rounding= */ CORNER_ROUND_10,
|
|
/* innerRounding= */ CORNER_ROUND_10),
|
|
createRotationMatrix(360 / 20f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getBoom() {
|
|
return Shapes_androidKt.transformed(
|
|
ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 15,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.42f),
|
|
createRotationMatrix(360 / 60f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getSoftBoom() {
|
|
float[] pointsXyTemplate =
|
|
new float[] {
|
|
0.456f, 0.224f,
|
|
0.460f, 0.170f,
|
|
0.500f, 0.100f,
|
|
0.540f, 0.170f,
|
|
0.544f, 0.224f,
|
|
0.538f, 0.308f
|
|
};
|
|
CornerRounding[] roundingsTemplate =
|
|
new CornerRounding[] {
|
|
new CornerRounding(/* radius= */ 0.020f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.143f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.025f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.143f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.190f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0f, /* smoothing= */ 0f)
|
|
};
|
|
|
|
float[] pointsXy = new float[pointsXyTemplate.length * 16];
|
|
CornerRounding[] roundings = new CornerRounding[roundingsTemplate.length * 16];
|
|
|
|
repeatAroundCenter(
|
|
pointsXyTemplate,
|
|
pointsXy,
|
|
roundingsTemplate,
|
|
roundings,
|
|
16,
|
|
0.5f,
|
|
0.5f,
|
|
-360f / 32,
|
|
false);
|
|
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
pointsXy,
|
|
/* rounding= */ CornerRounding.Unrounded,
|
|
Arrays.asList(roundings),
|
|
/* centerX= */ 0.5f,
|
|
/* centerY= */ 0.5f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getFlower() {
|
|
return ShapesKt.star(
|
|
RoundedPolygon.Companion,
|
|
/* numVerticesPerRadius= */ 8,
|
|
/* radius= */ 1f,
|
|
/* innerRadius= */ 0.588f,
|
|
/* rounding= */ new CornerRounding(/* radius= */ 0.12f, /* smoothing= */ 0.48f),
|
|
/* innerRounding= */ CornerRounding.Unrounded);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPuffy() {
|
|
float[] pointsXyTemplate =
|
|
new float[] {
|
|
0.501f, 0.260f,
|
|
0.526f, 0.188f,
|
|
0.676f, 0.226f,
|
|
0.660f, 0.300f,
|
|
0.734f, 0.230f,
|
|
0.838f, 0.350f,
|
|
0.782f, 0.418f,
|
|
0.874f, 0.414f
|
|
};
|
|
CornerRounding[] roundingsTemplate =
|
|
new CornerRounding[] {
|
|
CornerRounding.Unrounded,
|
|
new CornerRounding(/* radius= */ 0.095f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.095f, /* smoothing= */ 0f),
|
|
CornerRounding.Unrounded,
|
|
new CornerRounding(/* radius= */ 0.095f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.095f, /* smoothing= */ 0f),
|
|
CornerRounding.Unrounded,
|
|
new CornerRounding(/* radius= */ 0.095f, /* smoothing= */ 0f)
|
|
};
|
|
|
|
float[] pointsXy = new float[pointsXyTemplate.length * 4];
|
|
CornerRounding[] roundings = new CornerRounding[roundingsTemplate.length * 4];
|
|
|
|
repeatAroundCenter(
|
|
pointsXyTemplate, pointsXy, roundingsTemplate, roundings, 4, 0.5f, 0.5f, 0f, true);
|
|
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
pointsXy,
|
|
/* rounding= */ CornerRounding.Unrounded,
|
|
Arrays.asList(roundings),
|
|
/* centerX= */ 0.5f,
|
|
/* centerY= */ 0.5f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPuffyDiamond() {
|
|
float[] pointsXyTemplate =
|
|
new float[] {
|
|
0.390f, 0.260f,
|
|
0.390f, 0.130f,
|
|
0.610f, 0.130f,
|
|
0.610f, 0.260f,
|
|
0.740f, 0.260f
|
|
};
|
|
CornerRounding[] roundingsTemplate =
|
|
new CornerRounding[] {
|
|
new CornerRounding(/* radius= */ 0.000f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.104f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.104f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.000f, /* smoothing= */ 0f),
|
|
new CornerRounding(/* radius= */ 0.104f, /* smoothing= */ 0f)
|
|
};
|
|
|
|
float[] pointsXy = new float[pointsXyTemplate.length * 4];
|
|
CornerRounding[] roundings = new CornerRounding[roundingsTemplate.length * 4];
|
|
|
|
repeatAroundCenter(
|
|
pointsXyTemplate, pointsXy, roundingsTemplate, roundings, 4, 0.5f, 0.5f, -24.63f, false);
|
|
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
pointsXy,
|
|
/* rounding= */ CornerRounding.Unrounded,
|
|
Arrays.asList(roundings),
|
|
/* centerX= */ 0.5f,
|
|
/* centerY= */ 0.5f);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPixelCircle() {
|
|
PointF start = new PointF();
|
|
PointF[] offsets =
|
|
new PointF[] {
|
|
new PointF(0.4f, -1f),
|
|
new PointF(0f, 0.14f),
|
|
new PointF(0.28f, 0f),
|
|
new PointF(0f, 0.16f),
|
|
new PointF(0.16f, 0f),
|
|
new PointF(0f, 0.3f),
|
|
new PointF(0.16f, 0f)
|
|
};
|
|
float[] pointsXyTemplate = new float[offsets.length * 2];
|
|
for (int i = 0; i < offsets.length; i++) {
|
|
start.offset(offsets[i].x, offsets[i].y);
|
|
pointsXyTemplate[i * 2] = start.x;
|
|
pointsXyTemplate[i * 2 + 1] = start.y;
|
|
}
|
|
float[] pointsXy = new float[pointsXyTemplate.length * 4];
|
|
repeatAroundCenter(pointsXyTemplate, pointsXy, null, null, 4, 0f, 0f, 0f, true);
|
|
return RoundedPolygonKt.RoundedPolygon(pointsXy, /* rounding= */ CornerRounding.Unrounded);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getPixelTriangle() {
|
|
PointF start = new PointF();
|
|
PointF[] offsets =
|
|
new PointF[] {
|
|
new PointF(),
|
|
new PointF(56f, 0f),
|
|
new PointF(0f, 28f),
|
|
new PointF(44f, 0f),
|
|
new PointF(0f, 26f),
|
|
new PointF(44f, 0f),
|
|
new PointF(0f, 32f),
|
|
new PointF(38f, 0f),
|
|
new PointF(0f, 26f),
|
|
new PointF(38f, 0f),
|
|
new PointF(0f, 32f),
|
|
new PointF(32f, 0f)
|
|
};
|
|
float[] pointsXyTemplate = new float[offsets.length * 2];
|
|
for (int i = 0; i < offsets.length; i++) {
|
|
start.offset(offsets[i].x, offsets[i].y);
|
|
pointsXyTemplate[i * 2] = start.x;
|
|
pointsXyTemplate[i * 2 + 1] = start.y;
|
|
}
|
|
// Moves start to the center of the shape.
|
|
start.offset(-start.x / 2f, 19f);
|
|
float[] pointsXy = new float[pointsXyTemplate.length * 2];
|
|
repeatAroundCenter(pointsXyTemplate, pointsXy, null, null, 2, start.x, start.y, -90, true);
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
pointsXy,
|
|
/* rounding= */ CornerRounding.Unrounded,
|
|
/* perVertexRounding= */ null,
|
|
/* centerX= */ start.x,
|
|
/* centerY= */ start.y);
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getBun() {
|
|
float inset = .4f;
|
|
float[] sandwichPoints =
|
|
new float[] {
|
|
1f, 1f, inset, 1f, -inset, 1f, -1f, 1f, -1f, 0f, -inset, 0f, -1f, 0f, -1f, -1f, -inset,
|
|
-1f, inset, -1f, 1f, -1f, 1f, 0f, inset, 0f, 1f, 0f
|
|
};
|
|
CornerRounding[] roundings =
|
|
new CornerRounding[] {
|
|
CORNER_ROUND_100,
|
|
CornerRounding.Unrounded,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_100,
|
|
CORNER_ROUND_100,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_100,
|
|
CORNER_ROUND_100,
|
|
CornerRounding.Unrounded,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_100,
|
|
CORNER_ROUND_100,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_100
|
|
};
|
|
return RoundedPolygonKt.RoundedPolygon(
|
|
sandwichPoints, CornerRounding.Unrounded, Arrays.asList(roundings));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon getHeart() {
|
|
float[] points =
|
|
new float[] {
|
|
.2f, 0f, -.4f, .5f, -1f, 1f, -1.5f, .5f, -1f, 0f, -1.5f, -.5f, -1f, -1f, -.4f, -.5f
|
|
};
|
|
CornerRounding[] roundings =
|
|
new CornerRounding[] {
|
|
CornerRounding.Unrounded,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_100,
|
|
CORNER_ROUND_100,
|
|
CornerRounding.Unrounded,
|
|
CORNER_ROUND_100,
|
|
CORNER_ROUND_100,
|
|
CornerRounding.Unrounded,
|
|
};
|
|
return Shapes_androidKt.transformed(
|
|
RoundedPolygonKt.RoundedPolygon(points, CornerRounding.Unrounded, Arrays.asList(roundings)),
|
|
createRotationMatrix(90f));
|
|
}
|
|
|
|
@NonNull
|
|
private static RoundedPolygon triangleChip(float innerRadius, CornerRounding cornerRounding) {
|
|
PointF[] radialPoints =
|
|
new PointF[] {
|
|
new PointF(0.888f, (float) Math.toRadians(270f)),
|
|
new PointF(1f, (float) Math.toRadians(30f)),
|
|
new PointF(innerRadius, (float) Math.toRadians(90f)),
|
|
new PointF(1f, (float) Math.toRadians(150f))
|
|
};
|
|
PointF[] cartesianPoints = new PointF[radialPoints.length];
|
|
for (int i = 0; i < radialPoints.length; i++) {
|
|
cartesianPoints[i] = radialToCartesian(radialPoints[i]);
|
|
}
|
|
float[] cartesianPointsXy = new float[cartesianPoints.length * 2];
|
|
for (int i = 0; i < cartesianPoints.length; i++) {
|
|
cartesianPointsXy[2 * i] = cartesianPoints[i].x;
|
|
cartesianPointsXy[2 * i + 1] = cartesianPoints[i].y;
|
|
}
|
|
return RoundedPolygonKt.RoundedPolygon(cartesianPointsXy, cornerRounding);
|
|
}
|
|
|
|
private static void repeatAroundCenter(
|
|
@NonNull float[] srcPointsXy,
|
|
@NonNull float[] dstPointsXy,
|
|
@Nullable CornerRounding[] srcRoundings,
|
|
@Nullable CornerRounding[] dstRoundings,
|
|
int reps,
|
|
float centerX,
|
|
float centerY,
|
|
float srcRotateOffset,
|
|
boolean alternate) {
|
|
float rotationOffset = 360f / reps;
|
|
int srcPointsCount = srcPointsXy.length / 2;
|
|
// Moves src points centered at (0, 0) and rotates src points to start from 0 degree.
|
|
Matrix preTransform = createRotationMatrix(-srcRotateOffset);
|
|
preTransform.preTranslate(-centerX, -centerY);
|
|
preTransform.mapPoints(srcPointsXy);
|
|
// Start repeat src points to dst points.
|
|
int dstRoundingsWriteIndex = 0;
|
|
int dstPointsXyWriteIndex = 0;
|
|
for (int i = 0; i < reps; i++) {
|
|
// Flips the odd indexed iteration direction, if alternate is true.
|
|
boolean flip = alternate && (i % 2 == 1);
|
|
for (int j = 0; j < srcPointsCount; j++) {
|
|
// Reverts the src points order in dst points, if flips.
|
|
int indexRounding = flip ? srcPointsCount - 1 - j : j;
|
|
int indexX = indexRounding * 2;
|
|
int indexY = indexX + 1;
|
|
if (srcRoundings != null && dstRoundings != null) {
|
|
dstRoundings[dstRoundingsWriteIndex++] = srcRoundings[indexRounding];
|
|
}
|
|
// Flips the src points along y axis, if flips.
|
|
dstPointsXy[dstPointsXyWriteIndex++] = srcPointsXy[indexX] * (flip ? -1 : 1);
|
|
dstPointsXy[dstPointsXyWriteIndex++] = srcPointsXy[indexY];
|
|
}
|
|
// Rotates 1 more offset rotation, if flips.
|
|
createRotationMatrix(rotationOffset * (flip ? i + 1 : i))
|
|
.mapPoints(
|
|
dstPointsXy,
|
|
srcPointsXy.length * i,
|
|
dstPointsXy,
|
|
srcPointsXy.length * i,
|
|
srcPointsCount);
|
|
}
|
|
// Rotates dst points with the same offset and moves them back to the original center.
|
|
Matrix postTransform = createRotationMatrix(srcRotateOffset);
|
|
postTransform.postTranslate(centerX, centerY);
|
|
postTransform.mapPoints(dstPointsXy);
|
|
}
|
|
|
|
@NonNull
|
|
@Contract("_ -> new")
|
|
private static PointF radialToCartesian(@NonNull PointF radialPoint) {
|
|
float radius = radialPoint.x;
|
|
float angle = radialPoint.y;
|
|
return new PointF((float) (radius * Math.cos(angle)), (float) (radius * Math.sin(angle)));
|
|
}
|
|
|
|
private MaterialShapes() {}
|
|
}
|