Skip to content

Commit

Permalink
Fix bug in Coons Patches + API alignement + Example (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
william-candillon authored Feb 2, 2022
1 parent 202efcb commit 9912c25
Show file tree
Hide file tree
Showing 26 changed files with 572 additions and 63 deletions.
8 changes: 4 additions & 4 deletions docs/docs/shapes/patch.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ import {Canvas, Patch, vec} from "@shopify/react-native-skia";
const PatchDemo = () => {
const colors = ["#61dafb", "#fb61da", "#61fbcf", "#dafb61"];
const C = 64;
const topLeft = { src: vec(0, 0), c1: vec(C, 0), c2: vec(0, C) };
const topRight = { src: vec(256, 0), c1: vec(256 + C, 0), c2: vec(256, C) };
const topLeft = { pos: vec(0, 0), c1: vec(C, 0), c2: vec(0, C) };
const topRight = { pos: vec(256, 0), c1: vec(256 + C, 0), c2: vec(256, C) };
const bottomRight = {
src: vec(256, 256),
pos: vec(256, 256),
c1: vec(256 - 2 * C, 256),
c2: vec(256, 256 - 2 * C),
};
const bottomLeft = {
src: vec(0, 256),
pos: vec(0, 256),
c1: vec(-2 * C, 256),
c2: vec(0, 256 - 2 * C),
};
Expand Down
8 changes: 8 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Filters } from "./Examples/Filters";
import { Gooey } from "./Examples/Gooey";
import { Hue } from "./Examples/Hue";
import { Matrix } from "./Examples/Matrix";
import { Aurora } from "./Examples/Aurora";
import { HomeScreen } from "./Home";

const App = () => {
Expand Down Expand Up @@ -39,6 +40,13 @@ const App = () => {
header: () => null,
}}
/>
<Stack.Screen
name="Aurora"
component={Aurora}
options={{
header: () => null,
}}
/>
<Stack.Screen name="Drawing" component={DrawingExample} />
<Stack.Screen name="Graphs" component={GraphsScreen} />
<Stack.Screen name="Animation" component={AnimationExample} />
Expand Down
12 changes: 6 additions & 6 deletions example/src/Examples/API/Shapes2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import {
Oval,
Line,
Points,
Patch,
vec,
rect,
rrect,
Paint,
DashPathEffect,
RoundedRect,
Patch,
} from "@shopify/react-native-skia";

import { Title } from "./components/Title";
Expand Down Expand Up @@ -59,10 +59,10 @@ const inner = rrect(
0
);

const topLeft = { src: vec(0, 0), c1: vec(0, 15), c2: vec(15, 0) };
const topRight = { src: vec(100, 0), c1: vec(100, 15), c2: vec(85, 0) };
const bottomRight = { src: vec(100, 100), c1: vec(100, 85), c2: vec(85, 100) };
const bottomLeft = { src: vec(0, 100), c1: vec(0, 85), c2: vec(15, 100) };
const topLeft = { pos: vec(16, 0), c1: vec(0, 15), c2: vec(15, 0) };
const topRight = { pos: vec(100, 0), c1: vec(80, 15), c2: vec(85, 0) };
const bottomRight = { pos: vec(100, 100), c1: vec(100, 85), c2: vec(85, 100) };
const bottomLeft = { pos: vec(16, 100), c1: vec(0, 85), c2: vec(15, 100) };

export const Shapes = () => {
return (
Expand Down Expand Up @@ -111,7 +111,7 @@ export const Shapes = () => {
<Canvas style={styles.container}>
<Patch
colors={["#61DAFB", "#fb61da", "#61fbcf", "#dafb61"]}
cubics={[topLeft, topRight, bottomRight, bottomLeft]}
patch={[topLeft, topRight, bottomRight, bottomLeft]}
/>
</Canvas>
</ScrollView>
Expand Down
60 changes: 60 additions & 0 deletions example/src/Examples/Aurora/Aurora.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from "react";

import { CoonsPatchMeshGradient } from "./components/CoonsPatchMeshGradient";

export const Aurora = () => {
return <CoonsPatchMeshGradient rows={3} cols={3} colors={palette.skia} />;
};

const palette = {
otto: [
"#FEF8C4",
"#E1F1D5",
"#C4EBE5",
"#ECA171",
"#FFFCF3",
"#D4B3B7",
"#B5A8D2",
"#F068A1",
"#EDD9A2",
"#FEEFAB",
"#A666C0",
"#8556E5",
"#DC4C4C",
"#EC795A",
"#E599F0",
"#96EDF2",
],
will: [
"#2D4CD2",
"#36B6D9",
"#3CF2B5",
"#37FF5E",
"#59FB2D",
"#AFF12D",
"#DABC2D",
"#D35127",
"#D01252",
"#CF0CAA",
"#A80DD8",
"#5819D7",
],
skia: [
"#61DAFB",
"#dafb61",
"#61fbcf",
"#61DAFB",
"#fb61da",
"#61fbcf",
"#dafb61",
"#fb61da",
"#61DAFB",
"#fb61da",
"#dafb61",
"#61fbcf",
"#fb61da",
"#61DAFB",
"#dafb61",
"#61fbcf",
],
};
38 changes: 38 additions & 0 deletions example/src/Examples/Aurora/components/BilinearGradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import type { ColorProp, Vector } from "@shopify/react-native-skia";
import {
processColorAsUnitArray,
Shader,
Skia,
} from "@shopify/react-native-skia";

const source = Skia.RuntimeEffect.Make(`
uniform vec2 size;
uniform vec4 color0;
uniform vec4 color1;
uniform vec4 color2;
uniform vec4 color3;
vec4 main(vec2 pos) {
vec2 uv = pos/size;
vec4 colorA = mix(color0, color1, uv.x);
vec4 colorB = mix(color2, color3, uv.x);
return mix(colorA, colorB, uv.y);
}`)!;

interface BilinearGradientProps {
size: Vector;
colors: ColorProp[];
}

export const BilinearGradient = ({ size, colors }: BilinearGradientProps) => {
const [color0, color1, color2, color3] = colors.map((cl) =>
processColorAsUnitArray(cl, 1)
);
return (
<Shader
source={source}
uniforms={{ size, color0, color1, color2, color3 }}
/>
);
};
157 changes: 157 additions & 0 deletions example/src/Examples/Aurora/components/CoonsPatchMeshGradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React from "react";
import type {
AnimationValue,
CubicBezierHandle,
} from "@shopify/react-native-skia";
import {
add,
useValue,
Canvas,
ImageShader,
Patch,
vec,
Paint,
processColor,
} from "@shopify/react-native-skia";
import { Dimensions } from "react-native";

import { bilinearInterpolate, symmetric } from "./Math";
import { Cubic } from "./Cubic";
import { Curves } from "./Curves";
import { useHandles } from "./useHandles";

const { width, height } = Dimensions.get("window");
const size = vec(width, height);

const rectToTexture = (
vertices: CubicBezierHandle[],
[tl, tr, br, bl]: readonly [number, number, number, number]
) =>
[
vertices[tl].pos,
vertices[tr].pos,
vertices[br].pos,
vertices[bl].pos,
] as const;

const rectToColors = (
colors: number[],
[tl, tr, br, bl]: readonly [number, number, number, number]
) => [colors[tl], colors[tr], colors[br], colors[bl]] as const;

const rectToPatch =
(mesh: AnimationValue<CubicBezierHandle[]>, indices: readonly number[]) =>
() => {
const tl = mesh.value[indices[0]];
const tr = mesh.value[indices[1]];
const br = mesh.value[indices[2]];
const bl = mesh.value[indices[3]];
return [
{
pos: tl.pos,
c1: tl.c2,
c2: tl.c1,
},
{
pos: tr.pos,
c1: symmetric(tr.c1, tr.pos),
c2: tr.c2,
},
{
pos: br.pos,
c1: symmetric(br.c2, br.pos),
c2: symmetric(br.c1, br.pos),
},
{
pos: bl.pos,
c1: bl.c1,
c2: symmetric(bl.c2, bl.pos),
},
] as const;
};

interface CoonsPatchMeshGradientProps {
rows: number;
cols: number;
colors: string[];
debug?: boolean;
lines?: boolean;
}

export const CoonsPatchMeshGradient = ({
rows,
cols,
colors: rawColors,
debug,
lines,
}: CoonsPatchMeshGradientProps) => {
const colors = rawColors.map((color) => processColor(color, 1));
const dx = width / cols;
const dy = height / rows;
const C = dx / 3;

const defaultMesh = new Array(cols + 1)
.fill(0)
.map((_c, col) =>
new Array(rows + 1).fill(0).map((_r, row) => {
const pos = vec(row * dx, col * dy);
return {
pos,
c1: add(pos, vec(C, 0)),
c2: add(pos, vec(0, C)),
};
})
)
.flat(2);

const mesh = useValue(defaultMesh);
const rects = new Array(rows)
.fill(0)
.map((_r, row) =>
new Array(cols).fill(0).map((_c, col) => {
const l = cols + 1;
const tl = row * l + col;
const tr = tl + 1;
const bl = (row + 1) * l + col;
const br = bl + 1;
return [tl, tr, br, bl] as const;
})
)
.flat();

const onTouch = useHandles(mesh, defaultMesh, width, height);
return (
<Canvas style={{ width, height }} onTouch={onTouch}>
<Paint>
<ImageShader
source={require("../../../assets/debug.png")}
tx="repeat"
ty="repeat"
/>
</Paint>
{rects.map((r, i) => {
const patch = rectToPatch(mesh, r);
return (
<React.Fragment key={i}>
<Patch
patch={patch}
colors={debug ? undefined : rectToColors(colors, r)}
texture={rectToTexture(defaultMesh, r)}
/>
{lines && <Curves patch={patch} />}
</React.Fragment>
);
})}
{defaultMesh.map(({ pos }, index) => {
const edge =
pos.x === 0 || pos.y === 0 || pos.x === width || pos.y === height;
if (edge) {
return null;
}
return (
<Cubic key={index} mesh={mesh} index={index} color={colors[index]} />
);
})}
</Canvas>
);
};
49 changes: 49 additions & 0 deletions example/src/Examples/Aurora/components/Cubic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import type {
AnimationValue,
CubicBezierHandle,
Vector,
} from "@shopify/react-native-skia";
import { Line, Paint, Circle } from "@shopify/react-native-skia";

import { symmetric } from "./Math";

interface CubicProps {
mesh: AnimationValue<CubicBezierHandle[]>;
index: number;
color: number;
}

export const Cubic = ({ mesh, index, color }: CubicProps) => {
return (
<>
<Line
strokeWidth={2}
color="white"
p1={() => mesh.value[index].c1}
p2={() => symmetric(mesh.value[index].c1, mesh.value[index].pos)}
/>
<Line
strokeWidth={2}
color="white"
p1={() => mesh.value[index].c2}
p2={() => symmetric(mesh.value[index].c2, mesh.value[index].pos)}
/>
<Circle c={() => mesh.value[index].pos} r={16} color={() => color}>
<Paint style="stroke" strokeWidth={4} color="white" />
</Circle>
<Circle c={() => mesh.value[index].c1} r={10} color="white" />
<Circle c={() => mesh.value[index].c2} r={10} color="white" />
<Circle
c={() => symmetric(mesh.value[index].c1, mesh.value[index].pos)}
r={10}
color="white"
/>
<Circle
c={() => symmetric(mesh.value[index].c2, mesh.value[index].pos)}
r={10}
color="white"
/>
</>
);
};
Loading

0 comments on commit 9912c25

Please sign in to comment.