Skip to content

Commit

Permalink
feat: improve custom renderer possibilities
Browse files Browse the repository at this point in the history
  • Loading branch information
exzos28 committed May 12, 2024
1 parent adb65d5 commit 9e975ed
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 79 deletions.
87 changes: 54 additions & 33 deletions src/QrCodeSvg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ export default function QrCodeSvg({
() => createMatrix(value, errorCorrectionLevel),
[errorCorrectionLevel, value]
);
const cell = round(frameSize / originalMatrix.length); // Ex. 3.141592653589793 -> 3.1
const cellSize = round(frameSize / originalMatrix.length); // Ex. 3.141592653589793 -> 3.1
const matrixRowLength = originalMatrix[0]?.length ?? 0;
const roundedContentCells =
(matrixRowLength - contentCells) % 2 === 0
? contentCells
: contentCells + 1;
const contentSize = cell * roundedContentCells;
const contentSize = cellSize * roundedContentCells;
const contentStartIndex = (matrixRowLength - roundedContentCells) / 2;
const contentEndIndex = contentStartIndex + roundedContentCells - 1;
const contentXY = contentStartIndex * cell;
const contentXY = contentStartIndex * cellSize;

const matrix = useMemo(
() =>
Expand All @@ -103,6 +103,12 @@ export default function QrCodeSvg({
[content, contentEndIndex, contentStartIndex, originalMatrix]
);

const matrixFocusSquareDeepIndex = matrix[0]?.findIndex((_) => _ === 0);
if (matrixFocusSquareDeepIndex === undefined) {
throw new Error("Focus square wasn't detected");
}
const matrixFocusSquareDeep = matrixFocusSquareDeepIndex;

const paths = useMemo(
() =>
matrix.flatMap((row, i) =>
Expand All @@ -116,13 +122,26 @@ export default function QrCodeSvg({
left: Boolean(row[j - 1]),
right: Boolean(row[j + 1]),
};
const x = j * cell;
const y = i * cell;
return [renderFigure(x, y, neighbors, cell, renderer)];
const x = j * cellSize;
const y = i * cellSize;
return [
renderFigure(
x,
y,
neighbors,
cellSize,
renderer,
matrixFocusSquareDeep,
i,
j,
matrix.length
),
];
})
),
[renderer, matrix, cell]
[matrix, cellSize, renderer, matrixFocusSquareDeep]
);

const dPath = paths
.filter((_) => _.type === 'path')
.map((_) => _.d)
Expand All @@ -145,33 +164,35 @@ export default function QrCodeSvg({
};
return (
<View testID="root" style={[{ backgroundColor }, style]}>
{Array.isArray(gradientColors) ? (
<GradientQr
{...qrProps}
gradientColors={gradientColors}
gradientProps={gradientProps}
/>
) : (
<DefaultQr {...qrProps} />
)}
<View>
{Array.isArray(gradientColors) ? (
<GradientQr
{...qrProps}
gradientColors={gradientColors}
gradientProps={gradientProps}
/>
) : (
<DefaultQr {...qrProps} />
)}

{content && (
<View
testID="content"
style={[
{
width: contentSize,
height: contentSize,
top: contentXY,
left: contentXY,
},
styles.content,
contentStyle,
]}
>
{content}
</View>
)}
{content && (
<View
testID="content"
style={[
{
width: contentSize,
height: contentSize,
top: contentXY,
left: contentXY,
},
styles.content,
contentStyle,
]}
>
{content}
</View>
)}
</View>
</View>
);
}
Expand Down
1 change: 0 additions & 1 deletion src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ describe('QrCodeSvg', () => {
<QrCodeSvg value="Test" frameSize={200} dotColor="red" />
);
const dots = getAllByTestId('dot');
dots.forEach((_) => console.log(_.props));
const allEqual = dots.every((_) => _.props.fill.payload === 4294901760); // is it red? wtf
expect(allEqual).toBe(true);
});
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_PADDING = 0.05;
27 changes: 23 additions & 4 deletions src/getCornets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ export default function getCorners(
cellSize: number,
padding: number
): Corners {
// q4 0 0 0 q1
const half = cellSize / 2;
// q4 0 d1 0 q1
// 0 0 0 0 0
// d4 0 0 0 d2
// 0 0 0 0 0
// 0 0 0 0 0
// q3 0 0 0 q2
// q3 0 d3 0 q2
const q1 = {
x: x + cellSize - padding,
y: y + padding,
Expand All @@ -35,5 +36,23 @@ export default function getCorners(
y: y + padding,
};
const center = { x: x + cellSize / 2, y: y + cellSize / 2 };
return { q1, q2, q3, q4, center };

const d1 = {
x: center.x,
y: center.y - half + padding,
};
const d2 = {
x: center.x + half - padding,
y: center.y,
};
const d3 = {
x: center.x,
y: center.y + half - padding,
};
const d4 = {
x: center.x - half + padding,
y: center.y,
};

return { q1, q2, q3, q4, center, d1, d2, d3, d4 };
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as QrCodeSvg } from './QrCodeSvg';
export * from './QrCodeSvg';
export * from './renderers';
export * from './types';
21 changes: 21 additions & 0 deletions src/isFocusSquareElem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default function isFocusSquareElem(
i: number,
j: number,
matrixSize: number,
matrixFocusSquareDeep: number
) {
if (i < matrixFocusSquareDeep && j < matrixFocusSquareDeep) {
return true;
} else if (
matrixSize - matrixFocusSquareDeep - 1 < j &&
i < matrixFocusSquareDeep
) {
return true;
} else if (
matrixSize - matrixFocusSquareDeep - 1 < i &&
j < matrixFocusSquareDeep
) {
return true;
}
return false;
}
40 changes: 33 additions & 7 deletions src/renderFigure.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,52 @@
import { type CustomRenderer, Kind, type Neighbors } from './types';
import getCorners from './getCornets';
import { DEFAULT_PADDING } from './constants';
import isFocusSquareElem from './isFocusSquareElem';

export type Figure = {
type: string;
d: string;
};

export default function renderFigure(
x: number,
y: number,
neighbors: Neighbors,
cell: number,
renderer: CustomRenderer
) {
const padding = renderer.options?.padding ?? 0.05;
const corners = getCorners(x, y, cell, padding);
cellSize: number,
renderer: CustomRenderer,
matrixFocusSquareDeep: number,
i: number,
j: number,
matrixSize: number
): Figure {
const padding = renderer.options?.padding ?? DEFAULT_PADDING;
const corners = getCorners(x, y, cellSize, padding);
const isSquareElem = isFocusSquareElem(
i,
j,
matrixSize,
matrixFocusSquareDeep
);
const params = {
neighbors,
corners,
cellSize,
padding,
isSquareElem,
i,
j,
};
if (
!(neighbors.top || neighbors.right || neighbors.bottom || neighbors.left)
) {
return {
type: 'circle',
d: renderer.render[Kind.Circle](neighbors, corners, cell, padding),
d: renderer.render[Kind.Circle](params),
};
}

return {
type: 'path',
d: renderer.render[Kind.Element](neighbors, corners, cell, padding),
d: renderer.render[Kind.Element](params),
};
}
63 changes: 33 additions & 30 deletions src/renderers.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,26 @@
import { type CustomRenderer, type Dot, Kind } from './types';
import { type Corners, type CustomRenderer, type Dot, Kind } from './types';

const pair = (d: Dot) => `${d.x} ${d.y}`;
const line = (d: Dot) => `L${pair(d)}`;

export const plainRenderer: CustomRenderer = {
render: {
[Kind.Circle]: (_, c) =>
`M${pair(c.q1)} ${line(c.q2)} ${line(c.q3)} ${line(c.q4)}`,
[Kind.Element]: (_, c) =>
`M${pair(c.q1)} ${line(c.q2)} ${line(c.q3)} ${line(c.q4)}`,
[Kind.Circle]: ({ corners: c }) => renderSquare(c),
[Kind.Element]: ({ corners: c }) => renderSquare(c),
},
};

export const defaultRenderer: CustomRenderer = {
render: {
[Kind.Circle]: (_, c, cell) => {
const half = cell / 2;
const { center } = c;
return `M${center.x + half} ${center.y} A${half} ${half} 0 1 0 ${center.x - half} ${center.y} A${half} ${half} 0 1 0 ${center.x + half} ${center.y}`;
},
[Kind.Element]: (neighbors, c, cell, padding) => {
const half = cell / 2;
const { center, q2, q3, q4, q1 } = c;
[Kind.Circle]: ({ corners, cellSize }) =>
renderCircle(corners.center, cellSize),
[Kind.Element]: ({ neighbors, corners }) => {
const { q2, q3, q4, q1, d1, d2, d4, d3 } = corners;
// q4 0 d1 0 q1
// 0 0 0 0 0
// d4 0 0 0 d2
// 0 0 0 0 0
// q3 0 d3 0 q2
const d1 = {
x: center.x,
y: center.y - half + padding,
};
const d2 = {
x: center.x + half - padding,
y: center.y,
};
const d3 = {
x: center.x,
y: center.y + half - padding,
};
const d4 = {
x: center.x - half + padding,
y: center.y,
};

const d1d2 =
neighbors.top || neighbors.right
? `L${q1.x} ${q1.y} L${d2.x} ${d2.y}`
Expand All @@ -65,3 +42,29 @@ export const defaultRenderer: CustomRenderer = {
},
},
};

export const triangleRenderer: CustomRenderer = {
render: {
[Kind.Circle]: ({ corners: c }) =>
`M${pair(c.d1)} ${line(c.d2)} ${line(c.d3)} ${line(c.d4)}`,
[Kind.Element]: ({ corners: c }) =>
`M${pair(c.d1)} ${line(c.d2)} ${line(c.d3)} ${line(c.d4)}`,
},
};

export const circleRenderer: CustomRenderer = {
render: {
[Kind.Circle]: ({ corners, cellSize }) =>
renderCircle(corners.center, cellSize),
[Kind.Element]: ({ corners, cellSize }) =>
renderCircle(corners.center, cellSize),
},
};

export const renderCircle = (center: Dot, cellSize: number) => {
const half = cellSize / 2;
return `M${center.x + half} ${center.y} A${half} ${half} 0 1 0 ${center.x - half} ${center.y} A${half} ${half} 0 1 0 ${center.x + half} ${center.y}`;
};

export const renderSquare = (c: Corners) =>
`M${pair(c.q1)} ${line(c.q2)} ${line(c.q3)} ${line(c.q4)}`;
19 changes: 15 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,24 @@ export type Corners = {
q3: Dot;
q4: Dot;
center: Dot;
d1: Dot;
d2: Dot;
d3: Dot;
d4: Dot;
};

export type RenderParams = {
neighbors: Neighbors;
corners: Corners;
cellSize: number;
padding: number;
isSquareElem: boolean;
i: number;
j: number;
};

export type CustomRenderer = {
render: Record<
Kind,
(n: Neighbors, c: Corners, cell: number, padding: number) => string
>;
render: Record<Kind, (params: RenderParams) => string>;
options?: {
padding: number;
};
Expand Down

0 comments on commit 9e975ed

Please sign in to comment.