pekingme 21ba18a4af [Shape] Added Material endorsed shapes.
PiperOrigin-RevId: 663370897
2024-08-16 17:34:51 +00:00

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() {}
}