remove presets build step w/ raw? + rewrite import in dev

main
Kyle Zheng 2024-10-02 21:29:20 -04:00
rodzic 8a7ac66481
commit e939aa76fc
42 zmienionych plików z 87 dodań i 3291 usunięć

Wyświetl plik

@ -11,7 +11,7 @@ Blatantly inspired by [QRBTF](https://qrbtf.com) and [Anthony Fu's QR Toolkit](h
> [!CAUTION]
> These example QR codes may not be reliably scannable! Results may vary drastically based on device and scanner!
THIS PROJECT IS A TOOL TO MAKE DESIGNS, THESE ARE ONLY EXAMPLES
This project is a tool to create designs! These are only examples!
<table>
<tbody>
@ -105,6 +105,10 @@ THIS PROJECT IS A TOOL TO MAKE DESIGNS, THESE ARE ONLY EXAMPLES
</tbody>
</table>
## Create/modify designs with code
![code and parameter editor ui](./examples/ui2.png)
## Features
- Customize data:
@ -121,13 +125,6 @@ THIS PROJECT IS A TOOL TO MAKE DESIGNS, THESE ARE ONLY EXAMPLES
- Generated SVGs are not sanitized. This is an impossible task and attempting it breaks perfectly fine SVGs, makes debugging harder, and adds latency to previewing changes.
- These should be non-issues, but even if you copy-and-paste and run malware there's no secrets to leak.
### Use existing presets
![style select ui](./examples/ui1.png)
### Customizable parameters defined in code
![code and parameter editor ui](./examples/ui2.png)
## Creating a preset

Wyświetl plik

@ -4,10 +4,12 @@ import UnoCSS from "unocss/vite";
import wasmpack from "vite-plugin-wasm-pack";
export default defineConfig({
server: { preset: "vercel" },
server: {
preset: "vercel",
},
ssr: true,
vite: {
plugins: [UnoCSS(), wasmpack([], ["fuqr"])],
plugins: [UnoCSS(), wasmpack([], ["fuqr"]), blobRewriter()],
resolve: {
alias: {
// https://christopher.engineering/en/blog/lucide-icons-with-vite-dev-server/
@ -21,3 +23,40 @@ export default defineConfig({
},
},
});
// Rewrites imports inside blobs in dev mode
function blobRewriter() {
const virtualModuleId = "virtual:blob-rewriter";
const resolvedVirtualModuleId = "\0" + virtualModuleId;
"help".replace(/test/, "help");
return {
name: "blob-rewriter",
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
if (process.env.NODE_ENV !== 'development') {
return 'export {}'
}
return `
if (!import.meta.env.SSR) {
const originalBlob = window.Blob;
window.Blob = function(array, options) {
if (options.type === "text/javascript") {
array = array.map(item => {
return item.replace("https://qrframe.kylezhe.ng", "http://localhost:3000");
});
}
return new originalBlob(array, options);
}
}
`;
}
},
};
}

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 87 KiB

Wyświetl plik

@ -2,7 +2,7 @@
"name": "qrframe",
"type": "module",
"scripts": {
"dev": "nr presets dev && vinxi dev",
"dev": "vinxi dev",
"build": "vinxi build",
"start": "vinxi start",
"presets": "node updatePresets"

Wyświetl plik

@ -1,6 +1,6 @@
// Based on QRBTF's Line style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRLine.tsx
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,6 +1,6 @@
// Based on QRBTF's DSJ style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRDsj.tsx
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,6 +1,6 @@
// Based on QRBTF's Bubble style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRBubble.tsx
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Foreground: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
import rough from "https://esm.sh/roughjs";
export const paramsSchema = {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Image: {
@ -92,10 +92,10 @@ export async function renderCanvas(qr, params, canvas) {
}
ctx.filter = `brightness(${params["Brightness"]}) contrast(${params["Contrast"]})`;
const imgScale = params["Image scale"]
const imgScale = params["Image scale"];
const imgSize = Math.floor(imgScale * canvasSize);
const imgOffset = Math.floor((canvasSize - imgSize) / 2)
ctx.drawImage(image, imgOffset, imgOffset,imgSize, imgSize);
const imgOffset = Math.floor((canvasSize - imgSize) / 2);
ctx.drawImage(image, imgOffset, imgOffset, imgSize, imgSize);
ctx.filter = "none";
const imageData = ctx.getImageData(0, 0, canvasSize, canvasSize);

Wyświetl plik

@ -1,4 +1,4 @@
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Foreground: {

Wyświetl plik

@ -1,6 +1,6 @@
// Based on QRBTF's A1P style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRNormal.tsx
import { Module, getSeededRand } from "REPLACE_URL/utils.js";
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,4 @@
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {

Wyświetl plik

@ -1,4 +1,5 @@
import { Module } from "REPLACE_URL/utils.js";
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",

Wyświetl plik

@ -17,8 +17,9 @@ import { TextInput, TextareaInput } from "../TextInput";
import { CodeEditor } from "./CodeEditor";
import { Settings } from "./Settings";
import { ParamsEditor } from "./ParamsEditor";
import { Tutorial } from "~/lib/presets/Tutorial";
import Tutorial from "../../../presets/Tutorial?raw";
import { ContentMenuTrigger, ContextMenuProvider } from "../ContextMenu";
import "virtual:blob-rewriter"
type Props = {
class?: string;

Wyświetl plik

@ -1,19 +1,19 @@
import { Alien } from "./presets/Alien";
import { Basic } from "./presets/Basic";
import { Blocks } from "./presets/Blocks";
import { Bubbles } from "./presets/Bubbles";
import { Camo } from "./presets/Camo";
import { Circle } from "./presets/Circle";
import { Dots } from "./presets/Dots";
import { Drawing } from "./presets/Drawing";
import { Glass } from "./presets/Glass";
import { Halftone } from "./presets/Halftone";
import { Layers } from "./presets/Layers";
import { Minimal } from "./presets/Minimal";
import { Mondrian } from "./presets/Mondrian";
import { Neon } from "./presets/Neon";
import { Quantum } from "./presets/Quantum";
import { Tile } from "./presets/Tile";
import Alien from "../../presets/Alien?raw";
import Basic from "../../presets/Basic?raw";
import Blocks from "../../presets/Blocks?raw";
import Bubbles from "../../presets/Bubbles?raw";
import Camo from "../../presets/Camo?raw";
import Circle from "../../presets/Circle?raw";
import Dots from "../../presets/Dots?raw";
import Drawing from "../../presets/Drawing?raw";
import Glass from "../../presets/Glass?raw";
import Halftone from "../../presets/Halftone?raw";
import Layers from "../../presets/Layers?raw";
import Minimal from "../../presets/Minimal?raw";
import Mondrian from "../../presets/Mondrian?raw";
import Neon from "../../presets/Neon?raw";
import Quantum from "../../presets/Quantum?raw";
import Tile from "../../presets/Tile?raw";
export const PRESET_CODE = {
Basic,

Wyświetl plik

@ -1,145 +0,0 @@
export const Alien = `// Based on QRBTF's Line style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRLine.tsx
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Background: {
type: "color",
default: "#ffffff",
},
Dots: {
type: "color",
default: "#000000",
},
Lines: {
type: "color",
default: "#000000",
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const rangeStr = (min, max) => (rand() * (max - min) + min).toFixed(2);
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const bg = params["Background"];
const dots = params["Dots"];
const lines = params["Lines"];
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
let linesLayer = \`<g stroke="\${lines}">\`;
let dotsLayer = \`<g fill="\${dots}">\`;
function matrix(x, y) {
return qr.matrix[y * matrixWidth + x];
}
const rightVisited = Array(matrixWidth * matrixWidth).fill(false);
const leftVisited = Array(matrixWidth * matrixWidth).fill(false);
function visited1(x, y) {
return rightVisited[y * matrixWidth + x];
}
function visited2(x, y) {
return leftVisited[y * matrixWidth + x];
}
function setVisited1(x, y) {
rightVisited[y * matrixWidth + x] = true;
}
function setVisited2(x, y) {
leftVisited[y * matrixWidth + x] = true;
}
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
dotsLayer += \`<circle cx="\${x + 3.5}" cy="\${y + 3.5}" r="1.5"/>\`;
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 0.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 3.5}" cy="\${y + 0.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 6.5}" cy="\${y + 0.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 3.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 6.5}" cy="\${y + 3.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 6.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 3.5}" cy="\${y + 6.5}" r="0.5"/>\`;
dotsLayer += \`<circle cx="\${x + 6.5}" cy="\${y + 6.5}" r="0.5"/>\`;
linesLayer += \`<line x1="\${x + 0.5}" y1="\${y + 0.5}" x2="\${x + 6.5}" y2="\${y + 6.5}" stroke-width="\${rangeStr(0.3, 0.6)}"/>\`;
linesLayer += \`<line x1="\${x + 6.5}" y1="\${y + 0.5}" x2="\${x + 0.5}" y2="\${y + 6.5}" stroke-width="\${rangeStr(0.3, 0.6)}"/>\`;
}
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = matrix(x, y);
if (module & Module.FINDER) continue;
if (!(module & Module.ON)) continue;
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 0.5}" r="\${rangeStr(0.2, 0.4)}"/>\`;
if (!visited1(x, y)) {
let nx = x + 1;
let ny = y + 1;
while (
nx < matrixWidth &&
ny < matrixWidth &&
matrix(nx, ny) & Module.ON &&
!visited1(nx, ny)
) {
setVisited1(nx, ny);
nx++;
ny++;
}
if (ny - y > 1) {
linesLayer += \`<line x1="\${x + 0.5}" y1="\${y + 0.5}" x2="\${nx - 0.5}" y2="\${ny - 0.5}" stroke-width="\${rangeStr(0.1, 0.3)}"/>\`;
}
}
if (!visited2(x, y)) {
let nx = x - 1;
let ny = y + 1;
while (
nx >= 0 &&
ny < matrixWidth &&
matrix(nx, ny) & Module.ON &&
!visited2(nx, ny)
) {
setVisited2(nx, ny);
nx--;
ny++;
}
if (ny - y > 1) {
linesLayer += \`<line x1="\${x + 0.5}" y1="\${y + 0.5}" x2="\${nx + 1.5}" y2="\${ny - 0.5}" stroke-width="\${rangeStr(0.1, 0.3)}"/>\`;
}
}
}
}
linesLayer += \`</g>\`;
svg += linesLayer;
dotsLayer += \`</g>\`;
svg += dotsLayer;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,224 +0,0 @@
export const Basic = `import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Foreground: {
type: "color",
default: "#000000",
},
Background: {
type: "color",
default: "#ffffff",
},
Shape: {
type: "select",
options: ["Square-Circle", "Diamond-Squircle"],
},
Frame: {
type: "select",
options: ["None", "Corners"],
},
Roundness: {
type: "number",
min: 0,
max: 1,
step: 0.01,
default: 0,
},
"Pixel size": {
type: "number",
min: 0.5,
max: 1.5,
step: 0.1,
default: 1,
},
Logo: {
type: "file",
accept: ".jpeg, .jpg, .png, .svg",
},
"Logo size": {
type: "number",
min: 0,
max: 1,
step: 0.01,
default: 0.25,
},
"Show data behind logo": {
type: "boolean",
},
};
export async function renderSVG(qr, params) {
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const fg = params["Foreground"];
const bg = params["Background"];
const defaultShape = params["Shape"] === "Square-Circle";
const roundness = params["Roundness"];
const file = params["Logo"];
const logoRatio = params["Logo size"];
const showLogoData = params["Show data behind logo"];
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
svg += \`<g fill="\${fg}">\`;
if (params["Frame"] === "Corners") {
const bracketRadius = 2.2 * roundness;
const bracketStraight = 5 + margin / 2 - bracketRadius;
svg += brackets(
-margin / 2,
-margin / 2,
size - margin,
bracketRadius,
bracketStraight,
fg
);
}
svg += \`<path d="\`;
const lgRadius = 3.5 * roundness;
const mdRadius = 2.5 * roundness;
const smRadius = 1.5 * roundness;
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
if (defaultShape) {
svg += roundedRect(x, y, 7, lgRadius, true);
svg += roundedRect(x + 1, y + 1, 5, mdRadius, false);
svg += roundedRect(x + 2, y + 2, 3, smRadius, true);
} else {
svg += squircle(x, y, 7, lgRadius, true);
svg += squircle(x + 1, y + 1, 5, mdRadius, false);
svg += squircle(x + 2, y + 2, 3, smRadius, true);
}
}
svg += \`"/>\`;
const dataSize = params["Pixel size"];
const dataRadius = (roundness * dataSize) / 2;
const dataOffset = (1 - dataSize) / 2;
if (!defaultShape || !roundness) svg += \`<path d="\`;
const logoInner = Math.floor(((1 - logoRatio) * size) / 2 - margin);
const logoUpper = matrixWidth - logoInner;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (
file &&
!showLogoData &&
x >= logoInner &&
y >= logoInner &&
x < logoUpper &&
y < logoUpper
) {
continue;
}
const module = qr.matrix[y * matrixWidth + x];
if (!(module & Module.ON)) continue;
if (module & Module.FINDER) continue;
if (defaultShape) {
if (roundness) {
svg += \`<rect x="\${fmt(x + dataOffset)}" y="\${fmt(y + dataOffset)}" width="\${dataSize}" height="\${dataSize}" rx="\${fmt(dataRadius)}"/>\`;
} else {
svg += \`M\${x + dataOffset},\${y + dataOffset}h\${dataSize}v\${dataSize}h-\${dataSize}z\`;
}
} else {
svg += squircle(
x + dataOffset,
y + dataOffset,
dataSize,
dataRadius,
true
);
}
}
}
if (!defaultShape || !roundness) svg += \`"/>\`;
svg += \`</g>\`;
if (file != null) {
const bytes = new Uint8Array(await file.arrayBuffer());
const b64 = btoa(
Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("")
);
const logoSize = fmt(logoRatio * size);
const logoOffset = fmt(((1 - logoRatio) * size) / 2 - margin);
svg += \`<image x="\${logoOffset}" y="\${logoOffset}" width="\${logoSize}" height="\${logoSize}" href="data:\${file.type};base64,\${b64}"/>\`;
}
svg += \`</svg>\`;
return svg;
}
// reduce file bloat from floating point math
const fmt = (n) => n.toFixed(2).replace(/.00$/, "");
function squircle(x, y, width, handle, cw) {
const half = fmt(width / 2);
if (handle === 0) {
return cw
? \`M\${fmt(x + width / 2)},\${fmt(y)}l\${half},\${half}l-\${half},\${half}l-\${half},-\${half}z\`
: \`M\${fmt(x + width / 2)},\${fmt(y)}l-\${half},\${half}l\${half},\${half}l\${half},-\${half}z\`;
}
const h = fmt(handle);
const hInv1 = fmt(half - handle);
const hInv2 = fmt(-(half - handle));
return cw
? \`M\${fmt(x + width / 2)},\${fmt(y)}c\${h},0 \${half},\${hInv1} \${half},\${half}s\${hInv2},\${half} -\${half},\${half}s-\${half},\${hInv2} -\${half},-\${half}s\${hInv1},-\${half} \${half},-\${half}\`
: \`M\${fmt(x + width / 2)},\${fmt(y)}c-\${h},0 -\${half},\${hInv1} -\${half},\${half}s\${hInv1},\${half} \${half},\${half}s\${half},\${hInv2} \${half},-\${half}s\${hInv2},-\${half} -\${half},-\${half}\`;
}
function roundedRect(x, y, width, radius, cw) {
if (radius === 0) {
return cw
? \`M\${fmt(x)},\${fmt(y)}h\${width}v\${width}h-\${width}z\`
: \`M\${fmt(x)},\${fmt(y)}v\${width}h\${width}v-\${width}z\`;
}
if (radius === width / 2) {
const r = fmt(radius);
const cwFlag = cw ? "1" : "0";
return \`M\${fmt(x + radius)},\${fmt(y)}a\${r},\${r} 0,0,\${cwFlag} 0,\${width}a\${r},\${r} 0,0,\${cwFlag} \${0},-\${width}\`;
}
const r = fmt(radius);
const side = fmt(width - 2 * radius);
return cw
? \`M\${fmt(x + radius)},\${fmt(y)}h\${side}a\${r},\${r} 0,0,1 \${r},\${r}v\${side}a\${r},\${r} 0,0,1 -\${r},\${r}h-\${side}a\${r},\${r} 0,0,1 -\${r},-\${r}v-\${side}a\${r},\${r} 0,0,1 \${r},-\${r}\`
: \`M\${fmt(x + radius)},\${fmt(y)}a\${r},\${r} 0,0,0 -\${r},\${r}v\${side}a\${r},\${r} 0,0,0 \${r},\${r}h\${side}a\${r},\${r} 0,0,0 \${r},-\${r}v-\${side}a\${r},\${r} 0,0,0 -\${r},-\${r}\`;
}
function brackets(x, y, width, radius, straight, stroke) {
const bracket = radius + straight;
const side = fmt(width - 2 * bracket);
const r = fmt(radius);
const cap = radius === 0 ? "square" : "round";
let svg = \`<path fill="none" stroke="\${stroke}" stroke-linecap="\${cap}" d="\`;
svg += \`M\${x},\${fmt(y + bracket)}\`;
svg += \`v-\${straight}a\${r},\${r} 0,0,1 \${r},-\${r}h\${straight}m\${side},0\`;
svg += \`h\${straight}a\${r},\${r} 0,0,1 \${r},\${r}v\${straight}m0,\${side}\`;
svg += \`v\${straight}a\${r},\${r} 0,0,1 -\${r},\${r}h-\${straight}m-\${side},0\`;
svg += \`h-\${straight}a\${r},\${r} 0,0,1 -\${r},-\${r}v-\${straight}\`;
svg += \`"/>\`;
return svg;
}
`

Wyświetl plik

@ -1,198 +0,0 @@
export const Blocks = `// Based on QRBTF's DSJ style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRDsj.tsx
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Background: {
type: "color",
default: "#ffffff",
},
Finder: {
type: "color",
default: "#131d87",
},
Horizontal: {
type: "color",
default: "#dc9c07",
},
Vertical: {
type: "color",
default: "#d21313",
},
Cross: {
type: "color",
default: "#131d87",
},
"Horizontal thickness": {
type: "number",
min: 0,
max: 1,
step: 0.1,
default: 0.7,
},
"Vertical thickness": {
type: "number",
min: 0,
max: 1,
step: 0.1,
default: 0.7,
},
"Cross thickness": {
type: "number",
min: 0,
max: 1,
step: 0.1,
default: 0.7,
},
};
export function renderSVG(qr, params) {
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const bg = params["Background"];
const fc = params["Finder"];
const hc = params["Horizontal"];
const ht = params["Horizontal thickness"];
const ho = (1 - ht) / 2;
const vc = params["Vertical"];
const vt = params["Vertical thickness"];
const vo = (1 - vt) / 2;
const cc = params["Cross"];
const ct = params["Cross thickness"];
const co = ct / Math.sqrt(8); // offset
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
let crossLayer = \`<g stroke-width="\${ct}" stroke="\${cc}">\`;
let vLayer = \`<g fill="\${vc}">\`;
let hLayer = \`<g fill="\${hc}">\`;
function matrix(x, y) {
return qr.matrix[y * matrixWidth + x];
}
const visitedMatrix = Array(matrixWidth * matrixWidth).fill(false);
function visited(x, y) {
return visitedMatrix[y * matrixWidth + x];
}
function setVisited(x, y) {
visitedMatrix[y * matrixWidth + x] = true;
}
svg += \`<g fill="\${fc}">\`;
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
svg += \`<rect x="\${x + 2}" y="\${y}" width="3" height="1"/>\`;
svg += \`<rect x="\${x + 2}" y="\${y + 2}" width="3" height="3"/>\`;
svg += \`<rect x="\${x}" y="\${y + 2}" width="1" height="3"/>\`;
svg += \`<rect x="\${x + 6}" y="\${y + 2}" width="1" height="3"/>\`;
svg += \`<rect x="\${x + 2}" y="\${y + 6}" width="3" height="1"/>\`;
}
svg += \`</g>\`;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = matrix(x, y);
if (module & Module.FINDER) continue;
if (!(module & Module.ON)) continue;
if (visited(x, y)) continue;
setVisited(x, y);
if (
y < matrixWidth - 2 &&
x < matrixWidth - 2 &&
matrix(x + 2, y) &
matrix(x, y + 2) &
matrix(x + 1, y + 1) &
matrix(x + 2, y + 2) &
1
) {
if (
!visited(x + 1, y) &&
!visited(x + 2, y) &&
!visited(x, y + 1) &&
!visited(x + 2, y + 1)
) {
crossLayer += \`<g>\`;
crossLayer += \`<line x1="\${x + co}" y1="\${y + co}" x2="\${x + 3 - co}" y2="\${y + 3 - co}"/>\`;
crossLayer += \`<line x1="\${x + 3 - co}" y1="\${y + co}" x2="\${x + co}" y2="\${y + 3 - co}"/>\`;
crossLayer += \`</g>\`;
setVisited(x + 2, y);
setVisited(x, y + 2);
setVisited(x + 1, y + 1);
setVisited(x + 2, y + 2);
continue;
}
}
if (
y < matrixWidth - 1 &&
x < matrixWidth - 1 &&
matrix(x + 1, y) & matrix(x, y + 1) & matrix(x + 1, y + 1) & Module.ON
) {
if (
!visited(x + 1, y) &&
!visited(x + 1, y + 1) &&
!visited(x, y + 1)
) {
crossLayer += \`<g>\`;
crossLayer += \`<line x1="\${x + co}" y1="\${y + co}" x2="\${x + 2 - co}" y2="\${y + 2 - co}"/>\`;
crossLayer += \`<line x1="\${x + 2 - co}" y1="\${y + co}" x2="\${x + co}" y2="\${y + 2 - co}"/>\`;
crossLayer += \`</g>\`;
setVisited(x + 1, y);
setVisited(x, y + 1);
setVisited(x + 1, y + 1);
continue;
}
}
let ny = y + 1;
while (ny < matrixWidth && matrix(x, ny) & Module.ON && !visited(x, ny)) {
ny++;
}
if (ny - y > 2) {
vLayer += \`<rect x="\${x + vo}" y="\${y + vo}" width="\${vt}" height="\${ny - y - 1 - 2 * vo}" fill="\${vc}"/>\`;
vLayer += \`<rect x="\${x + vo}" y="\${ny - 1 + vo}" width="\${vt}" height="\${1 - 2 * vo}" fill="\${vc}"/>\`;
for (let i = y + 1; i < ny; i++) {
setVisited(x, i);
}
continue;
}
let nx = x + 1;
while (nx < matrixWidth && matrix(nx, y) & Module.ON && !visited(nx, y)) {
setVisited(nx, y);
nx++;
}
hLayer += \`<rect x="\${x + ho}" y="\${y + ho}" width="\${nx - x - 2 * ho}" height="\${ht}" fill="\${hc}"/>\`;
}
}
vLayer += \`</g>\`;
svg += vLayer;
hLayer += \`</g>\`;
svg += hLayer;
crossLayer += \`</g>\`;
svg += crossLayer;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,172 +0,0 @@
export const Bubbles = `// Based on QRBTF's Bubble style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRBubble.tsx
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Background: {
type: "color",
default: "#ffffff",
},
Finder: {
type: "color",
default: "#141e92",
},
"Large circle": {
type: "color",
default: "#10a8e9",
},
"Medium circle": {
type: "color",
default: "#1aa8cc",
},
"Small circle": {
type: "color",
default: "#0f8bdd",
},
"Tiny circle": {
type: "color",
default: "#012c8f",
},
"Randomize circle size": {
type: "boolean",
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const rangeStr = params["Randomize circle size"]
? (min, max) => (rand() * (max - min) + min).toFixed(2)
: (min, max) => ((max - min) / 2 + min).toFixed(2);
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const bg = params["Background"];
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
let layer1 = \`<g fill="none" stroke="\${params["Large circle"]}" stroke-width="0.6">\`;
let layer2 = \`<g fill="none" stroke="\${params["Medium circle"]}" stroke-width="0.5">\`;
let layer3 = \`<g fill="none" stroke="\${params["Small circle"]}" stroke-width="0.4">\`;
let layer4 = \`<g fill="\${params["Tiny circle"]}">\`;
function matrix(x, y) {
return qr.matrix[y * matrixWidth + x];
}
const visitedMatrix = Array(matrixWidth * matrixWidth).fill(false);
function visited(x, y) {
return visitedMatrix[y * matrixWidth + x];
}
function setVisited(x, y) {
visitedMatrix[y * matrixWidth + x] = true;
}
const fc = params["Finder"];
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 3.5}" r="3" fill="none" stroke="\${fc}" stroke-width="1"/>\`;
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 3.5}" r="1.5" fill="\${fc}"/>\`;
}
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = matrix(x, y);
if (module & Module.FINDER) continue;
if (visited(x, y)) continue;
if (
y < matrixWidth - 2 &&
x < matrixWidth - 2 &&
matrix(x + 1, y) &
matrix(x, y + 1) &
matrix(x + 2, y + 1) &
matrix(x + 1, y + 2) &
1 &&
!visited(x + 1, y) &&
!visited(x + 2, y) &&
!visited(x + 1, y + 1) &&
!visited(x + 2, y + 1)
) {
layer1 += \`<circle cx="\${x + 1.5}" cy="\${y + 1.5}" r="\${rangeStr(0.8, 1.2)}"/>\`;
setVisited(x + 1, y);
setVisited(x, y + 1);
setVisited(x + 2, y + 1);
setVisited(x + 1, y + 2);
continue;
}
if (!(module & Module.ON)) continue;
setVisited(x, y);
if (
y < matrixWidth - 1 &&
x < matrixWidth - 1 &&
matrix(x + 1, y) &
matrix(x, y + 1) &
matrix(x + 1, y + 1) &
Module.ON &&
!visited(x + 1, y) &&
!visited(x + 1, y + 1)
) {
layer2 += \`<circle cx="\${x + 1}" cy="\${y + 1}" r="\${rangeStr(0.4, 0.6)}"/>\`;
setVisited(x + 1, y);
setVisited(x, y + 1);
setVisited(x + 1, y + 1);
continue;
}
if (
x < matrixWidth - 1 &&
matrix(x + 1, y) & Module.ON &&
!visited(x + 1, y)
) {
layer3 += \`<circle cx="\${x + 1}" cy="\${y + 0.5}" r="\${rangeStr(0.4, 0.6)}"/>\`;
setVisited(x + 1, y);
continue;
}
if (
y < matrixWidth - 1 &&
matrix(x, y + 1) & Module.ON &&
!visited(x, y + 1)
) {
layer3 += \`<circle cx="\${x + 0.5}" cy="\${y + 1}" r="\${rangeStr(0.3, 0.5)}"/>\`;
setVisited(x, y + 1);
continue;
}
layer4 += \`<circle cx="\${x + 0.5}" cy="\${y + 0.5}" r="\${rangeStr(0.2, 0.4)}"/>\`;
}
}
layer1 += \`</g>\`;
svg += layer1;
layer2 += \`</g>\`;
svg += layer2;
layer3 += \`</g>\`;
svg += layer3;
layer4 += \`</g>\`;
svg += layer4;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,289 +0,0 @@
export const Camo = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Foreground: {
type: "color",
default: "#1c4a1a",
},
Background: {
type: "color",
default: "#e3d68a",
},
Margin: {
type: "number",
min: 0,
max: 10,
default: 3,
},
"Quiet zone": {
type: "number",
min: 0,
max: 10,
default: 1,
},
Invert: {
type: "boolean",
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const margin = params["Margin"];
const quietZone = params["Quiet zone"];
const fg = params["Foreground"];
const bg = params["Background"];
const qrWidth = qr.version * 4 + 17;
const matrixWidth = qrWidth + 2 * margin;
const newMatrix = Array(matrixWidth * matrixWidth).fill(0);
const visited = new Uint16Array(matrixWidth * matrixWidth);
// Copy qr to matrix with margin and randomly set pixels in margin
for (let y = 0; y < margin - quietZone; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
}
for (let y = margin - quietZone; y < margin + qrWidth + quietZone; y++) {
for (let x = 0; x < margin - quietZone; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
if (y >= margin && y < margin + qrWidth) {
for (let x = margin; x < matrixWidth - margin; x++) {
newMatrix[y * matrixWidth + x] =
qr.matrix[(y - margin) * qrWidth + x - margin];
}
}
for (let x = margin + qrWidth + quietZone; x < matrixWidth; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
}
for (let y = margin + qrWidth + quietZone; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
}
if (quietZone === 0 && margin > 0) {
for (let x = margin; x < margin + 7; x++) {
newMatrix[(margin - 1) * matrixWidth + x] = 0;
newMatrix[(margin - 1) * matrixWidth + x + qrWidth - 7] = 0;
}
for (let y = margin; y < margin + 7; y++) {
newMatrix[y * matrixWidth + margin - 1] = 0;
newMatrix[y * matrixWidth + matrixWidth - margin] = 0;
}
for (let y = margin + qrWidth - 7; y < margin + qrWidth; y++) {
newMatrix[y * matrixWidth + margin - 1] = 0;
}
for (let x = margin; x < margin + 7; x++) {
newMatrix[(matrixWidth - margin) * matrixWidth + x] = 0;
}
}
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 \${matrixWidth} \${matrixWidth}">\`;
svg += \`<rect width="\${matrixWidth}" height="\${matrixWidth}" fill="\${bg}"/>\`;
svg += \`<g fill="\${fg}">\`;
const xMax = matrixWidth - 1;
const yMax = matrixWidth - 1;
let baseX;
let baseY;
const on = params["Invert"]
? (x, y) => (newMatrix[y * matrixWidth + x] & Module.ON) === 0
: (x, y) => (newMatrix[y * matrixWidth + x] & Module.ON) !== 0;
function goRight(x, y, path, cw) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = path;
while (x < xMax) {
const right = on(x + 1, y);
const vertRight = y > 0 && on(x + 1, y - 1);
if (!right || vertRight) {
vert = right && vertRight;
break;
}
x++;
visited[y * matrixWidth + x] = path;
}
paths[path] += \`h\${x - sx}\`;
if (vert) {
paths[path] += \`a.5.5 0,0,0 .5-.5\`;
goUp(x + 1, y - 1, path, cw);
} else {
paths[path] += \`a.5.5 0,0,1 .5.5\`;
goDown(x, y, path, cw);
}
}
function goLeft(x, y, shape, cw) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = shape;
while (x > 0) {
const left = on(x - 1, y);
const vertLeft = y < yMax && on(x - 1, y + 1);
if (!left || vertLeft) {
vert = left && vertLeft;
break;
}
x--;
visited[y * matrixWidth + x] = shape;
}
if (!cw && x === baseX && y === baseY) {
paths[shape] += "z";
return;
}
paths[shape] += \`h\${x - sx}\`;
if (vert) {
paths[shape] += \`a.5.5 0,0,0 -.5.5\`;
goDown(x - 1, y + 1, shape, cw);
} else {
paths[shape] += \`a.5.5 0,0,1 -.5-.5\`;
goUp(x, y, shape, cw);
}
}
function goUp(x, y, shape, cw) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = shape;
while (y > 0) {
const up = on(x, y - 1);
const horzUp = x > 0 && on(x - 1, y - 1);
if (!up || horzUp) {
horz = up && horzUp;
break;
}
y--;
visited[y * matrixWidth + x] = shape;
}
if (cw && x === baseX && y === baseY) {
paths[shape] += "z";
return;
}
paths[shape] += \`v\${y - sy}\`;
if (horz) {
paths[shape] += \`a.5.5 0,0,0 -.5-.5\`;
goLeft(x - 1, y - 1, shape, cw);
} else {
paths[shape] += \`a.5.5 0,0,1 .5-.5\`;
goRight(x, y, shape, cw);
}
}
function goDown(x, y, shape, cw) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = shape;
while (y < yMax) {
const down = on(x, y + 1);
const horzDown = x < xMax && on(x + 1, y + 1);
if (!down || horzDown) {
horz = down && horzDown;
break;
}
y++;
visited[y * matrixWidth + x] = shape;
}
paths[shape] += \`v\${y - sy}\`;
if (horz) {
paths[shape] += \`a.5.5 0,0,0 .5.5\`;
goRight(x + 1, y + 1, shape, cw);
} else {
paths[shape] += \`a.5.5 0,0,1 -.5.5\`;
goLeft(x, y, shape, cw);
}
}
const stack = [];
for (let x = 0; x < matrixWidth; x++) {
if (!on(x, 0)) stack.push([x, 0]);
}
for (let y = 1; y < yMax; y++) {
if (!on(0, y)) stack.push([0, y]);
if (!on(xMax, y)) stack.push([xMax, y]);
}
for (let x = 0; x < matrixWidth; x++) {
if (!on(x, yMax)) stack.push([x, yMax]);
}
// recursion dfs limited to ~4000
// visit all whitespace connected to edges
function dfsOff() {
while (stack.length > 0) {
const [x, y] = stack.pop();
if (visited[y * matrixWidth + x]) continue;
visited[y * matrixWidth + x] = 1;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dy === 0 && dx === 0) continue;
let nx = x + dx;
let ny = y + dy;
if (nx < 0 || nx > xMax || ny < 0 || ny > yMax) continue;
if (on(nx, ny)) continue;
stack.push([nx, ny]);
}
}
}
}
dfsOff();
const paths = [""];
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (visited[y * matrixWidth + x]) continue;
if (!on(x, y)) {
const shape = visited[y * matrixWidth + x - 1];
paths[shape] += \`M\${x + 0.5},\${y}a.5.5 0,0,0 -.5.5\`;
// these indexes are correct, think about it
baseY = y - 1;
baseX = x;
goDown(x - 1, y, shape, false);
stack.push([x, y]);
dfsOff();
continue;
}
if (y > 0 && on(x, y - 1) && visited[(y - 1) * matrixWidth + x]) {
visited[y * matrixWidth + x] = visited[(y - 1) * matrixWidth + x];
continue;
}
if (x > 0 && on(x - 1, y) && visited[y * matrixWidth + x - 1]) {
visited[y * matrixWidth + x] = visited[y * matrixWidth + x - 1];
continue;
}
paths.push(\`<path d="M\${x},\${y + 0.5}a.5.5 0,0,1 .5-.5\`);
baseY = y;
baseX = x;
goRight(x, y, paths.length - 1, true);
}
}
paths.forEach((path, i) => {
if (i === 0) return;
svg += path;
svg += \`"/>\`;
});
svg += \`</g></svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,221 +0,0 @@
export const Circle = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 20,
step: 0.1,
default: 8,
},
"Radius offset": {
type: "number",
min: -10,
max: 10,
default: 0,
},
Foreground: {
type: "color",
default: "#000000",
},
Background: {
type: "color",
default: "#ffffff",
},
"Frame thickness": {
type: "number",
min: 0,
max: 10,
step: 0.1,
},
"Finder pattern": {
type: "select",
options: ["Default", "Circle", "Square"],
},
"Alignment pattern": {
type: "select",
options: ["Default", "Circle", "Square"],
},
Logo: {
type: "file",
accept: ".jpeg, .jpg, .png, .svg",
},
"Logo size": {
type: "number",
min: 0,
max: 1,
step: 0.01,
default: 0.25,
},
"Show data behind logo": {
type: "boolean",
},
"Pixel size": {
type: "select",
options: ["None", "Center", "Edge", "Random"],
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
const fmt = (n) => n.toFixed(2).replace(/.00$/, "");
export async function renderSVG(qr, params) {
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const fg = params["Foreground"];
const bg = params["Background"];
const rOffset = params["Radius offset"];
const file = params["Logo"];
const logoRatio = params["Logo size"];
const showLogoData = params["Show data behind logo"];
const rand = getSeededRand(params["Seed"]);
const range = (min, max) => rand() * (max - min) + min;
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
// nearest odd number
let diameter = Math.round(Math.sqrt(2) * matrixWidth) + 2 * rOffset;
if (!(diameter & 1)) diameter += 1;
const frameThick = params["Frame thickness"];
if (frameThick) {
const frameR = diameter / 2 + 1 + frameThick / 2;
svg += \`<circle cx="\${matrixWidth / 2}" cy="\${matrixWidth / 2}" r="\${frameR}" fill="none" stroke="\${fg}" stroke-width="\${frameThick}"/>\`;
if (rOffset < -1) {
const c = matrixWidth / 2;
const offset = (frameR * Math.sqrt(2)) / 2;
const r = (-rOffset + 1) * Math.max(frameThick / 2, 1);
svg += \`<circle cx="\${c - offset}" cy="\${c - offset}" r="\${r}" fill="\${bg}"/>\`;
svg += \`<circle cx="\${c + offset}" cy="\${c - offset}" r="\${r}" fill="\${bg}"/>\`;
svg += \`<circle cx="\${c - offset}" cy="\${c + offset}" r="\${r}" fill="\${bg}"/>\`;
if (rOffset < -2) {
svg += \`<circle cx="\${c + offset}" cy="\${c + offset}" r="\${r}" fill="\${bg}"/>\`;
}
}
}
if (params["Finder pattern"] !== "Default") {
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
if (params["Finder pattern"] === "Circle") {
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 3.5}" r="3" fill="none" stroke="\${fg}" stroke-width="1"/>\`;
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 3.5}" r="1.5" fill="\${fg}"/>\`;
} else {
svg += \`<path d="M\${x},\${y}h7v7h-7zM\${x + 1},\${y + 1}v5h5v-5zM\${x + 2},\${y + 2}h3v3h-3z"/>\`;
}
}
}
svg += \`<path fill="\${fg}" d="\`;
const maxDist = Math.sqrt(2) * (matrixWidth / 2);
const lower = Math.min(-(diameter - matrixWidth) / 2, 0);
const upper = Math.max(diameter - lower, matrixWidth);
const logoInner = Math.floor(((1 - logoRatio) * size) / 2 - margin);
const logoUpper = matrixWidth - logoInner;
for (let y = lower; y < upper; y++) {
for (let x = lower; x < upper; x++) {
if (
file &&
!showLogoData &&
x >= logoInner &&
y >= logoInner &&
x < logoUpper &&
y < logoUpper
) {
continue;
}
// Quiet zone around qr
const xRange1 = x >= -1 && x < 8;
const yRange1 = y >= -1 && y < 8;
const yRange2 = y > matrixWidth - 9 && y <= matrixWidth;
const xRange2 = x > matrixWidth - 9 && x <= matrixWidth;
if (
(x === -1 && (yRange1 || yRange2)) ||
(y === -1 && (xRange1 || xRange2)) ||
(x === matrixWidth && yRange1) ||
(y === matrixWidth && xRange1)
) {
continue;
}
const dx = x - (matrixWidth - 1) / 2;
const dy = y - (matrixWidth - 1) / 2;
const dist = Math.sqrt(dx * dx + dy * dy);
if (x >= 0 && x < matrixWidth && y >= 0 && y < matrixWidth) {
const module = qr.matrix[y * matrixWidth + x];
if (!(module & Module.ON)) continue;
if (params["Finder pattern"] !== "Default" && module & Module.FINDER) {
continue;
}
if (
params["Alignment pattern"] !== "Default" &&
module & Module.ALIGNMENT
) {
if (module & Module.MODIFIER) {
if (params["Alignment pattern"] === "Circle") {
svg += \`M\${x + 0.5},\${y - 2}a2.5,2.5 0,0,0 0,5a2.5,2.5 0,0,0 0,-5\`;
svg += \`M\${x + 0.5},\${y - 1}a1.5,1.5 0,0,1 0,3a1.5,1.5 0,0,1 0,-3\`;
svg += \`M\${x + 0.5},\${y}a.5,.5 0,0,0 0,1a.5,.5 0,0,0 0,-1\`;
} else {
svg += \`M\${x - 2},\${y - 2}h5v5h-5zM\${x - 1},\${y - 1}v3h3v-3zM\${x},\${y}h1v1h-1z\`;
}
}
continue;
}
} else if (dist > diameter / 2) {
continue;
} else if (rand() > 0.5) {
continue;
}
let ratio;
switch (params["Pixel size"]) {
case "Center":
ratio = 1 - dist / maxDist + 0.8;
break;
case "Edge":
ratio = dist / maxDist + 0.8;
break;
case "Random":
ratio = range(0.8, 1.2);
break;
default:
ratio = 1;
}
const radius = fmt(0.5 * ratio);
svg += \`M\${x + 0.5},\${y + 0.5 - radius}a\${radius},\${radius} 0,0,0 0,\${2 * radius}a\${radius},\${radius} 0,0,0 0,\${-2 * radius}\`;
}
}
svg += \`"/>\`;
if (file != null) {
const bytes = new Uint8Array(await file.arrayBuffer());
const b64 = btoa(
Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("")
);
const logoSize = fmt(logoRatio * size);
const logoOffset = fmt(((1 - logoRatio) * size) / 2 - margin);
svg += \`<image x="\${logoOffset}" y="\${logoOffset}" width="\${logoSize}" height="\${logoSize}" href="data:\${file.type};base64,\${b64}"/>\`;
}
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,119 +0,0 @@
export const Dots = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Density: {
type: "number",
min: 2,
max: 10,
default: 4,
},
"Finder clarity": {
type: "number",
min: 1,
max: 1.5,
step: 0.1,
default: 1.3,
},
Foreground: {
type: "array",
resizable: true,
props: {
type: "color",
},
default: ["#f7158b", "#02d1fd", "#1f014b"],
},
Background: {
type: "color",
default: "#ffffff",
},
// See browser compatibility issues here
// https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
"Mix blend mode": {
type: "select",
options: [
"normal",
"multiply",
"screen",
"overlay",
"darken",
"lighten",
"color-dodge",
"color-burn",
"hard-light",
"soft-light",
"difference",
"exclusion",
"hue",
"saturation",
"color",
"luminosity",
"plus-darker",
"plus-lighter",
],
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const unit = params["Density"];
const rand = getSeededRand(params["Seed"]);
const rangeStr = (min, max) => (rand() * (max - min) + min).toFixed(2);
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"] * unit;
const colors = params["Foreground"];
const bg = params["Background"];
const size = matrixWidth * unit + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
const center = (matrixWidth * unit) / 2;
svg += \`<defs><g id="dots">\`;
const dotRadius = 1;
const dotSpace = 2.2;
const maxRadius = Math.sqrt((unit * unit * matrixWidth * matrixWidth) / 2);
for (let r = 0.1; r < maxRadius; r += dotSpace) {
const angleInc = dotSpace / r;
for (let theta = 0; theta < 2 * Math.PI - angleInc / 2; theta += angleInc) {
const x = r * Math.cos(theta);
const y = r * Math.sin(theta);
const qx = Math.floor((x + center) / unit);
const qy = Math.floor((y + center) / unit);
if (qx >= 0 && qx < matrixWidth && qy >= 0 && qy < matrixWidth) {
if (qr.matrix[qy * matrixWidth + qx] & Module.ON) {
const rad =
qr.matrix[qy * matrixWidth + qx] & Module.FINDER
? params["Finder clarity"]
: dotRadius;
svg += \`<circle cx="\${(center + x).toFixed(2)}" cy="\${(center + y).toFixed(2)}" r="\${rad}" />\`;
}
}
}
}
svg += \`</g></defs>\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
svg += \`<g style="mix-blend-mode:\${params["Mix blend mode"]}">\`;
colors.forEach(
(color) =>
(svg += \`<use href="#dots" fill="\${color}" fill-opacity="0.75" transform="translate(\${rangeStr(-1, 1)},\${rangeStr(-1, 1)}) rotate(\${rangeStr(-1, 1)})" transform-origin="\${center} \${center}"/>\`)
);
svg += \`</g>\`;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,339 +0,0 @@
export const Drawing = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
import rough from "https://esm.sh/roughjs";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
default: 2,
},
"Fill style": {
type: "select",
options: [
"Hachure",
"Solid",
"Zigzag",
"Cross-hatch",
"Dots",
"Dashed",
"Zigzag-line",
],
default: "Zigzag",
},
Fill: {
type: "color",
default: "#ffffff",
},
"Fill weight": {
type: "number",
min: 0,
max: 10,
default: 2,
},
"Fill gap": {
type: "number",
min: 1,
max: 10,
default: 4,
},
Stroke: {
type: "color",
default: "#ffffff",
},
"Stroke width": {
type: "number",
min: 0,
max: 10,
default: 1,
},
Invert: {
type: "boolean",
default: true,
},
Roughness: {
type: "number",
min: 0,
max: 10,
default: 1,
},
Bowing: {
type: "number",
min: 0,
max: 10,
default: 1,
},
Background: {
type: "color",
default: "#222222",
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
const domMock = {
ownerDocument: {
createElementNS: (_ns, tagName) => {
const children = [];
const attributes = {};
return {
tagName,
attributes,
setAttribute: (key, value) => (attributes[key] = value),
appendChild: (node) => children.push(node),
children,
};
},
},
};
export function renderSVG(qr, params) {
const roughSVG = rough.svg(domMock, {
options: {
roughness: params["Roughness"],
bowing: params["Bowing"],
fillStyle: params["Fill style"].toLowerCase(),
fillWeight: params["Fill weight"],
fill: params["Fill weight"] === 0 ? "none" : params["Fill"],
strokeWidth: params["Stroke width"],
stroke: params["Stroke width"] === 0 ? "none" : params["Stroke"],
hachureGap: params["Fill gap"],
seed: params["Seed"],
fixedDecimalPlaceDigits: 2,
},
});
let matrix = qr.matrix;
let matrixWidth = qr.version * 4 + 17;
if (params["Invert"]) {
matrixWidth += 2;
matrix = [];
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (
x === 0 ||
y === 0 ||
x === matrixWidth - 1 ||
y === matrixWidth - 1
) {
matrix.push(0);
} else {
matrix.push(qr.matrix[(y - 1) * (matrixWidth - 2) + x - 1]);
}
}
}
}
const visited = new Uint16Array(matrixWidth * matrixWidth);
const unit = 10;
const margin = params["Margin"] * unit;
const size = matrixWidth * unit + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${params["Background"]}"/>\`;
const xMax = matrixWidth - 1;
const yMax = matrixWidth - 1;
let baseX;
let baseY;
const on = params["Invert"]
? (x, y) => (matrix[y * matrixWidth + x] & Module.ON) === 0
: (x, y) => (matrix[y * matrixWidth + x] & Module.ON) !== 0;
function goRight(x, y, shape, cw) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = shape;
while (x < xMax) {
const right = on(x + 1, y);
const vertRight = y > 0 && on(x + 1, y - 1);
if (!right || vertRight) {
vert = right && vertRight;
break;
}
x++;
visited[y * matrixWidth + x] = shape;
}
paths[shape] += \`h\${(x - sx + 1) * unit}\`;
if (vert) {
goUp(x + 1, y - 1, shape, cw);
} else {
goDown(x, y, shape, cw);
}
}
function goLeft(x, y, shape, cw) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = shape;
while (x > 0) {
const left = on(x - 1, y);
const vertLeft = y < yMax && on(x - 1, y + 1);
if (!left || vertLeft) {
vert = left && vertLeft;
break;
}
x--;
visited[y * matrixWidth + x] = shape;
}
if (!cw && x === baseX && y === baseY) {
paths[shape] += "z";
return;
}
paths[shape] += \`h\${(x - sx - 1) * unit}\`;
if (vert) {
goDown(x - 1, y + 1, shape, cw);
} else {
goUp(x, y, shape, cw);
}
}
function goUp(x, y, shape, cw) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = shape;
while (y > 0) {
const up = on(x, y - 1);
const horzUp = x > 0 && on(x - 1, y - 1);
if (!up || horzUp) {
horz = up && horzUp;
break;
}
y--;
visited[y * matrixWidth + x] = shape;
}
if (cw && x === baseX && y === baseY) {
paths[shape] += "z";
return;
}
paths[shape] += \`v\${(y - sy - 1) * unit}\`;
if (horz) {
goLeft(x - 1, y - 1, shape, cw);
} else {
goRight(x, y, shape, cw);
}
}
function goDown(x, y, shape, cw) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = shape;
while (y < yMax) {
const down = on(x, y + 1);
const horzDown = x < xMax && on(x + 1, y + 1);
if (!down || horzDown) {
horz = down && horzDown;
break;
}
y++;
visited[y * matrixWidth + x] = shape;
}
paths[shape] += \`v\${(y - sy + 1) * unit}\`;
if (horz) {
goRight(x + 1, y + 1, shape, cw);
} else {
goLeft(x, y, shape, cw);
}
}
const stack = [];
for (let x = 0; x < matrixWidth; x++) {
if (!on(x, 0)) stack.push([x, 0]);
}
for (let y = 1; y < yMax; y++) {
if (!on(0, y)) stack.push([0, y]);
if (!on(xMax, y)) stack.push([xMax, y]);
}
for (let x = 0; x < matrixWidth; x++) {
if (!on(x, yMax)) stack.push([x, yMax]);
}
// recursion dfs limited to ~4000
// visit all whitespace connected to edges
function dfsOff() {
while (stack.length > 0) {
const [x, y] = stack.pop();
if (visited[y * matrixWidth + x]) continue;
visited[y * matrixWidth + x] = 1;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dy === 0 && dx === 0) continue;
let nx = x + dx;
let ny = y + dy;
if (nx < 0 || nx > xMax || ny < 0 || ny > yMax) continue;
if (on(nx, ny)) continue;
stack.push([nx, ny]);
}
}
}
}
dfsOff();
const paths = [""];
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (visited[y * matrixWidth + x]) continue;
if (!on(x, y)) {
const shape = visited[y * matrixWidth + x - 1];
paths[shape] += \`M\${x * unit},\${y * unit}\`;
baseY = y - 1;
baseX = x;
goDown(x - 1, y, shape, false);
stack.push([x, y]);
dfsOff();
continue;
}
if (y > 0 && on(x, y - 1) && visited[(y - 1) * matrixWidth + x]) {
visited[y * matrixWidth + x] = visited[(y - 1) * matrixWidth + x];
continue;
}
if (x > 0 && on(x - 1, y) && visited[y * matrixWidth + x - 1]) {
visited[y * matrixWidth + x] = visited[y * matrixWidth + x - 1];
continue;
}
paths.push(\`M\${x * unit},\${y * unit}\`);
baseY = y;
baseX = x;
goRight(x, y, paths.length - 1, true);
}
}
function domToString(node) {
const attrs = Object.entries(node.attributes)
.map(([key, value]) => \`\${key}="\${value}"\`)
.join(" ");
svg += \`<\${node.tagName} \${attrs}>\`;
node.children.forEach(domToString);
svg += \`</\${node.tagName}>\`;
}
paths.forEach((path, i) => {
if (i === 0) return;
const g = roughSVG.path(path);
domToString(g);
});
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,278 +0,0 @@
export const Glass = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
default: 2,
},
Foreground: {
type: "color",
default: "#000000",
},
Background: {
type: "color",
default: "#fcb9ff",
},
Shapes: {
type: "number",
min: 1,
max: 400,
default: 100,
},
"Stroke width": {
type: "number",
min: 0,
max: 4,
step: 0.1,
default: 1.5,
},
"Shape gap": {
type: "number",
min: -4,
max: 4,
default: 0,
},
"Shape opacity": {
type: "number",
min: 0,
max: 1,
step: 0.1,
default: 0.3,
},
"QR layer": {
type: "select",
options: ["Above", "Below"],
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const margin = params["Margin"];
const unit = 4;
const offset = params["Shape gap"] / 2;
const thin = unit - params["Shape gap"];
const matrixWidth = qr.version * 4 + 17 + 2 * margin;
const size = matrixWidth * unit;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 \${size} \${size}">\`;
svg += \`<rect width="\${size}" height="\${size}" fill="\${params["Background"]}"/>\`;
function getRGB() {
const r = Math.floor(rand() * 255);
const g = Math.floor(rand() * 255);
const b = Math.floor(rand() * 255);
return \`#\${r.toString(16).padStart(2, "0")}\${g.toString(16).padStart(2, "0")}\${b.toString(16).padStart(2, "0")}\`;
}
const groups = params["Shapes"];
let groupLayer = "";
const group = Array.from({ length: matrixWidth * matrixWidth }).fill(0);
const visited = Array.from({ length: matrixWidth * matrixWidth }).fill(false);
const queue = [];
while (queue.length < groups) {
const x = Math.floor(rand() * matrixWidth);
const y = Math.floor(rand() * matrixWidth);
if (queue.some((seed) => seed.x === x && seed.y === y)) {
continue;
}
queue.push([x, y]);
group[y * matrixWidth + x] = queue.length;
}
while (queue.length) {
const [x, y] = queue.shift();
const id = group[y * matrixWidth + x];
if (x > 0 && !group[y * matrixWidth + x - 1]) {
queue.push([x - 1, y]);
group[y * matrixWidth + x - 1] = id;
}
if (y > 0 && !group[(y - 1) * matrixWidth + x]) {
queue.push([x, y - 1]);
group[(y - 1) * matrixWidth + x] = id;
}
if (x < matrixWidth - 1 && !group[y * matrixWidth + x + 1]) {
queue.push([x + 1, y]);
group[y * matrixWidth + x + 1] = id;
}
if (y < matrixWidth - 1 && !group[(y + 1) * matrixWidth + x]) {
queue.push([x, y + 1]);
group[(y + 1) * matrixWidth + x] = id;
}
}
const xMax = matrixWidth - 1;
const yMax = matrixWidth - 1;
let baseX;
let baseY;
function goRight(x, y, id) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = true;
while (x < xMax) {
const right = group[x + 1 + y * matrixWidth] === id;
const vertRight = y > 0 && group[x + 1 + (y - 1) * matrixWidth] === id;
if (!right || vertRight) {
vert = right && vertRight;
break;
}
x++;
visited[y * matrixWidth + x] = true;
}
if (vert) {
groupLayer += \`h\${(x - sx + 1) * unit}v\${-2 * offset}\`;
goUp(x + 1, y - 1, id);
} else {
groupLayer += \`h\${(x - sx) * unit + thin}\`;
goDown(x, y, id);
}
}
function goLeft(x, y, id) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = true;
while (x > 0) {
const left = group[x - 1 + y * matrixWidth] === id;
const vertLeft = y < yMax && group[x - 1 + (y + 1) * matrixWidth] === id;
if (!left || vertLeft) {
vert = left && vertLeft;
break;
}
x--;
visited[y * matrixWidth + x] = true;
}
if (vert) {
groupLayer += \`h\${(x - sx - 1) * unit}v\${2 * offset}\`;
goDown(x - 1, y + 1, id);
} else {
groupLayer += \`h\${(x - sx) * unit - thin}\`;
goUp(x, y, id);
}
}
function goUp(x, y, id) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = true;
while (y > 0) {
const up = group[x + (y - 1) * matrixWidth] === id;
const horzUp = x > 0 && group[x - 1 + (y - 1) * matrixWidth] === id;
if (!up || horzUp) {
horz = up && horzUp;
break;
}
y--;
visited[y * matrixWidth + x] = true;
}
if (x === baseX && y === baseY) {
groupLayer += "z";
return;
}
if (horz) {
groupLayer += \`v\${(y - sy - 1) * unit}h\${-2 * offset}\`;
goLeft(x - 1, y - 1, id);
} else {
groupLayer += \`v\${(y - sy) * unit - thin}\`;
goRight(x, y, id);
}
}
function goDown(x, y, id) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = true;
while (y < yMax) {
const down = group[x + (y + 1) * matrixWidth] === id;
const horzDown = x < xMax && group[x + 1 + (y + 1) * matrixWidth] === id;
if (!down || horzDown) {
horz = down && horzDown;
break;
}
y++;
visited[y * matrixWidth + x] = true;
}
if (horz) {
groupLayer += \`v\${(y - sy + 1) * unit}h\${2 * offset}\`;
goRight(x + 1, y + 1, id);
} else {
groupLayer += \`v\${(y - sy) * unit + thin}\`;
goLeft(x, y, id);
}
}
const matrix = Array.from({ length: matrixWidth * matrixWidth }).fill(0);
const qrWidth = qr.version * 4 + 17;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (
y >= margin &&
y < matrixWidth - margin &&
x >= margin &&
x < matrixWidth - margin
) {
matrix[y * matrixWidth + x] =
qr.matrix[(y - margin) * qrWidth + (x - margin)];
}
}
}
let qrLayer = \`<path fill="\${params["Foreground"]}" d="\`;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (matrix[y * matrixWidth + x] & Module.ON) {
qrLayer += \`M\${x * unit},\${y * unit}h\${unit}v\${unit}h-\${unit}z\`;
}
if (visited[y * matrixWidth + x]) continue;
const id = group[y * matrixWidth + x];
if (
y > 0 &&
group[(y - 1) * matrixWidth + x] === id &&
visited[(y - 1) * matrixWidth + x]
) {
visited[y * matrixWidth + x] = true;
continue;
}
if (
x > 0 &&
group[y * matrixWidth + x - 1] === id &&
visited[y * matrixWidth + x - 1]
) {
visited[y * matrixWidth + x] = true;
continue;
}
const color = getRGB();
groupLayer += \`<path stroke="\${params["Foreground"]}" stroke-width="\${params["Stroke width"]}" fill="\${color}" fill-opacity="\${params["Shape opacity"]}" d="M\${x * unit + offset},\${y * unit + offset}\`;
baseX = x;
baseY = y;
goRight(x, y, id);
groupLayer += \`"/>\`;
}
}
if (params["QR layer"] === "Below") svg += qrLayer + \`"/>\`;
svg += groupLayer;
if (params["QR layer"] === "Above") svg += qrLayer + \`"/>\`;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,173 +0,0 @@
export const Halftone = `import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Image: {
type: "file",
},
"Image scale": {
type: "number",
min: 0,
max: 1,
step: 0.01,
default: 1,
},
Contrast: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 1,
},
Brightness: {
type: "number",
min: 0,
max: 5,
step: 0.1,
default: 1.8,
},
"QR background": {
type: "boolean",
},
"Alignment pattern": {
type: "boolean",
default: true,
},
"Timing pattern": {
type: "boolean",
},
Margin: {
type: "number",
min: 0,
max: 10,
default: 2,
},
Foreground: {
type: "color",
default: "#000000",
},
Background: {
type: "color",
default: "#ffffff",
},
};
export async function renderCanvas(qr, params, canvas) {
const unit = 3;
const pixel = 1;
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const fg = params["Foreground"];
const bg = params["Background"];
const alignment = params["Alignment pattern"];
const timing = params["Timing pattern"];
let file = params["Image"];
if (file == null) {
file = await fetch(
"https://upload.wikimedia.org/wikipedia/commons/1/14/The_Widow_%28Boston_Public_Library%29_%28cropped%29.jpg"
).then((res) => res.blob());
}
const image = await createImageBitmap(file);
const pixelWidth = matrixWidth + 2 * margin;
const canvasSize = pixelWidth * unit;
const ctx = canvas.getContext("2d");
ctx.canvas.width = canvasSize;
ctx.canvas.height = canvasSize;
ctx.fillStyle = bg;
ctx.fillRect(0, 0, canvasSize, canvasSize);
if (params["QR background"]) {
ctx.fillStyle = fg;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = qr.matrix[y * matrixWidth + x];
if (module & Module.ON) {
const px = x + margin;
const py = y + margin;
ctx.fillRect(px * unit, py * unit, unit, unit);
}
}
}
}
ctx.filter = \`brightness(\${params["Brightness"]}) contrast(\${params["Contrast"]})\`;
const imgScale = params["Image scale"]
const imgSize = Math.floor(imgScale * canvasSize);
const imgOffset = Math.floor((canvasSize - imgSize) / 2)
ctx.drawImage(image, imgOffset, imgOffset,imgSize, imgSize);
ctx.filter = "none";
const imageData = ctx.getImageData(0, 0, canvasSize, canvasSize);
const data = imageData.data;
for (let y = imgOffset; y < imgOffset + imgSize; y++) {
for (let x = imgOffset; x < imgOffset + imgSize; x++) {
const i = (y * canvasSize + x) * 4;
if (data[i + 3] === 0) continue;
// Convert to grayscale and normalize to 0-255
const oldPixel =
(data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114) | 0;
let newPixel;
if (oldPixel < 128) {
newPixel = 0;
ctx.fillStyle = fg;
} else {
newPixel = 255;
ctx.fillStyle = bg;
}
ctx.fillRect(x * pixel, y * pixel, pixel, pixel);
data[i] = data[i + 1] = data[i + 2] = newPixel;
const error = oldPixel - newPixel;
// Distribute error to neighboring pixels
if (x < canvasSize - 1) {
data[i + 4] += (error * 7) / 16;
}
if (y < canvasSize - 1) {
if (x > 0) {
data[i + canvasSize * 4 - 4] += (error * 3) / 16;
}
data[i + canvasSize * 4] += (error * 5) / 16;
if (x < canvasSize - 1) {
data[i + canvasSize * 4 + 4] += (error * 1) / 16;
}
}
}
}
const dataOffset = (unit - pixel) / 2;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = qr.matrix[y * matrixWidth + x];
if (module & Module.ON) {
ctx.fillStyle = fg;
} else {
ctx.fillStyle = bg;
}
const px = x + margin;
const py = y + margin;
if (
module & Module.FINDER ||
(alignment && module & Module.ALIGNMENT) ||
(timing && module & Module.TIMING)
) {
ctx.fillRect(px * unit, py * unit, unit, unit);
} else {
ctx.fillRect(
px * unit + dataOffset,
py * unit + dataOffset,
pixel,
pixel
);
}
}
}
}
`

Wyświetl plik

@ -1,106 +0,0 @@
export const Layers = `import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Background: {
type: "color",
default: "#163157",
},
// See browser compatibility issues here
// https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
"Mix blend mode": {
type: "select",
options: [
"normal",
"multiply",
"screen",
"overlay",
"darken",
"lighten",
"color-dodge",
"color-burn",
"hard-light",
"soft-light",
"difference",
"exclusion",
"hue",
"saturation",
"color",
"luminosity",
"plus-darker",
"plus-lighter",
],
default: "difference",
},
Foreground: {
type: "array",
resizable: true,
props: {
type: "color",
},
default: ["#e80004", "#000000", "#ca70cf", "#000000", "#ffffff"],
},
"Offset x": {
type: "array",
resizable: true,
props: {
type: "number",
min: -1,
step: 0.1,
max: 1,
},
default: [0.6, 0.4, 0.2, 0, -0.2],
},
"Offset y": {
type: "array",
resizable: true,
props: {
type: "number",
min: -1,
step: 0.1,
max: 1,
},
default: [0.6, 0.4, 0.2, 0, -0.2],
},
};
export function renderSVG(qr, params) {
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const colors = params["Foreground"];
const offsetX = params["Offset x"];
const offsetY = params["Offset y"];
const bg = params["Background"];
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
svg += \`<defs>\`;
svg += \`<path id="base" d="\`;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = qr.matrix[y * matrixWidth + x];
if (module & Module.ON) {
svg += \`M\${x},\${y}h1v1h-1z\`;
}
}
}
svg += \`"/>\`;
svg += \`</defs>\`;
svg += \`<g style="mix-blend-mode:\${params["Mix blend mode"]}">\`;
colors.forEach((color, i) => {
svg += \`<use fill="\${color}" transform="translate(\${offsetX[i]},\${offsetY[i]})" href="#base"/>\`;
});
svg += \`</g>\`;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,70 +0,0 @@
export const Minimal = `import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
default: 2,
},
"Data pixel size": {
type: "number",
min: 1,
max: 20,
default: 3,
},
Background: {
type: "boolean",
},
};
export function renderSVG(qr, params) {
const matrixWidth = qr.version * 4 + 17;
const unit = 10;
const dataSize = params["Data pixel size"];
const margin = params["Margin"] * unit;
const fg = "#000";
const bg = "#fff";
const size = matrixWidth * unit + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
if (params["Background"]) {
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
}
svg += \`<path fill="\${fg}" d="\`;
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
svg += \`M\${(x + 3) * unit},\${y * unit}h\${unit}v\${unit}h-\${unit}z\`;
svg += \`M\${x * unit},\${(y + 3) * unit}h\${unit}v\${unit}h-\${unit}z\`;
svg += \`M\${(x + 6) * unit},\${(y + 3) * unit}h\${unit}v\${unit}h-\${unit}z\`;
svg += \`M\${(x + 3) * unit},\${(y + 6) * unit}h\${unit}v\${unit}h-\${unit}z\`;
svg += \`M\${(x + 2) * unit},\${(y + 2) * unit}h\${unit * 3}v\${unit * 3}h-\${unit * 3}z\`;
}
const offset = (unit - dataSize) / 2;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = qr.matrix[y * matrixWidth + x];
if (module & Module.FINDER) {
continue;
}
if (module & Module.ON) {
const sx = x * unit + offset;
const sy = y * unit + offset;
svg += \`M\${sx},\${sy}h\${dataSize}v\${dataSize}h-\${dataSize}z\`;
}
}
}
svg += \`"/></svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,128 +0,0 @@
export const Mondrian = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
default: 2,
},
Foreground: {
type: "array",
props: {
type: "color",
},
resizable: true,
default: ["#860909", "#0e21a0", "#95800f"],
},
Background: {
type: "color",
default: "#ffffff",
},
Lines: {
type: "color",
default: "#000000",
},
"Line thickness": {
type: "number",
min: -10,
max: 10,
default: 2,
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const margin = params["Margin"];
const unit = 20;
const matrixWidth = qr.version * 4 + 17 + 2 * margin;
const size = matrixWidth * unit;
const gap = params["Line thickness"];
const offset = gap / 2;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 \${size} \${size}">\`;
svg += \`<rect width="\${size}" height="\${size}" fill="\${params["Lines"]}"/>\`;
let lightLayer = \`<path fill="\${params["Background"]}" d="\`;
const darkLayers = params["Foreground"].map(
(color) => \`<path fill="\${color}" d="\`
);
const visited = Array.from({ length: matrixWidth * matrixWidth }).fill(false);
const matrix = Array.from({ length: matrixWidth * matrixWidth }).fill(0);
const qrWidth = qr.version * 4 + 17;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (
y >= margin &&
y < matrixWidth - margin &&
x >= margin &&
x < matrixWidth - margin
) {
matrix[y * matrixWidth + x] =
qr.matrix[(y - margin) * qrWidth + (x - margin)];
}
}
}
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = matrix[y * matrixWidth + x];
if (visited[y * matrixWidth + x]) continue;
let layer = "";
const on = module & Module.ON;
visited[y * matrixWidth + x] = true;
let width = 1;
let height = 1;
while (
x + width < matrixWidth &&
(matrix[y * matrixWidth + x + width] & Module.ON) === on &&
!visited[y * matrixWidth + x + width]
) {
visited[y * matrixWidth + x + width] = true;
width++;
}
outer: while (y + height < matrixWidth) {
for (let i = 0; i < width; i++) {
if ((matrix[(y + height) * matrixWidth + x + i] & Module.ON) !== on) {
break outer;
}
}
for (let i = 0; i < width; i++) {
visited[(y + height) * matrixWidth + x + i] = true;
}
height++;
}
const hSide = width * unit - gap;
const vSide = height * unit - gap;
layer += \`M\${x * unit + offset},\${y * unit + offset}h\${hSide}v\${vSide}h-\${hSide}z\`;
if (on) {
darkLayers[Math.floor(rand() * darkLayers.length)] += layer;
} else {
lightLayer += layer;
}
}
}
darkLayers.forEach((layer) => (svg += layer + \`"/>\`));
svg += lightLayer + \`"/>\`;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,328 +0,0 @@
export const Neon = `import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Foreground: {
type: "array",
props: {
type: "color",
},
resizable: true,
default: ["#fb51dd", "#f2cffa", "#aefdfd", "#54a9fe"],
},
Background: {
type: "color",
default: "#101529",
},
Margin: {
type: "number",
min: 0,
max: 10,
default: 4,
},
"Quiet zone": {
type: "select",
options: ["Minimal", "Full"],
},
Invert: {
type: "boolean",
},
"Line thickness": {
type: "number",
min: 1,
max: 4,
default: 2,
},
"Finder thickness": {
type: "number",
min: 1,
max: 4,
default: 4,
},
"Glow strength": {
type: "number",
min: 0,
max: 4,
step: 0.1,
default: 2,
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const margin = params["Margin"];
const colors = params["Foreground"];
const bg = params["Background"];
const qrWidth = qr.version * 4 + 17;
const matrixWidth = qrWidth + 2 * margin;
const newMatrix = Array(matrixWidth * matrixWidth).fill(0);
const visited = new Uint16Array(matrixWidth * matrixWidth);
// Copy qr to matrix with margin and randomly set pixels in margin
for (let y = 0; y < margin - 1; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
}
for (let y = margin - 1; y < margin + qrWidth + 1; y++) {
for (let x = 0; x < margin - 1; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
if (y >= margin && y < margin + qrWidth) {
for (let x = margin; x < matrixWidth - margin; x++) {
newMatrix[y * matrixWidth + x] =
qr.matrix[(y - margin) * qrWidth + x - margin];
}
}
for (let x = margin + qrWidth + 1; x < matrixWidth; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
}
for (let y = margin + qrWidth + 1; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (rand() > 0.5) newMatrix[y * matrixWidth + x] = Module.ON;
}
}
if (params["Quiet zone"] === "Minimal") {
for (let x = margin + 8; x < matrixWidth - margin - 8; x++) {
if (rand() > 0.5) newMatrix[(margin - 1) * matrixWidth + x] = Module.ON;
}
for (let y = margin + 8; y < matrixWidth - margin; y++) {
if (y < matrixWidth - margin - 8) {
if (rand() > 0.5) newMatrix[y * matrixWidth + margin - 1] = Module.ON;
}
if (rand() > 0.5)
newMatrix[y * matrixWidth + matrixWidth - margin] = Module.ON;
}
for (let x = margin + 8; x < matrixWidth - margin + 1; x++) {
if (rand() > 0.5)
newMatrix[(matrixWidth - margin) * matrixWidth + x] = Module.ON;
}
}
const unit = 4;
let thin = params["Line thickness"];
let offset = (unit - thin) / 2;
const size = matrixWidth * unit - 2 * offset;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${offset} \${offset} \${size} \${size}">\`;
svg += \`<filter id="glow">
<feGaussianBlur stdDeviation="\${params["Glow strength"]}"/>
<feComposite in2="SourceGraphic" operator="over"/>
</filter>\`;
svg += \`<rect x="\${offset}" y="\${offset}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
const xMax = matrixWidth - 1;
const yMax = matrixWidth - 1;
let baseX;
let baseY;
const on = params["Invert"]
? (x, y) => (newMatrix[y * matrixWidth + x] & Module.ON) === 0
: (x, y) => (newMatrix[y * matrixWidth + x] & Module.ON) !== 0;
function goRight(x, y, shape, cw) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = shape;
while (x < xMax) {
const right = on(x + 1, y);
const vertRight = y > 0 && on(x + 1, y - 1);
if (!right || vertRight) {
vert = right && vertRight;
break;
}
x++;
visited[y * matrixWidth + x] = shape;
}
if (vert) {
paths[shape] += \`h\${(x - sx + 1) * unit}v\${-2 * offset}\`;
goUp(x + 1, y - 1, shape, cw);
} else {
paths[shape] += \`h\${(x - sx) * unit + thin}\`;
goDown(x, y, shape, cw);
}
}
function goLeft(x, y, shape, cw) {
let sx = x;
let vert = false;
visited[y * matrixWidth + x] = shape;
while (x > 0) {
const left = on(x - 1, y);
const vertLeft = y < yMax && on(x - 1, y + 1);
if (!left || vertLeft) {
vert = left && vertLeft;
break;
}
x--;
visited[y * matrixWidth + x] = shape;
}
if (!cw && x === baseX && y === baseY) {
paths[shape] += "z";
return;
}
if (vert) {
paths[shape] += \`h\${(x - sx - 1) * unit}v\${2 * offset}\`;
goDown(x - 1, y + 1, shape, cw);
} else {
paths[shape] += \`h\${(x - sx) * unit - thin}\`;
goUp(x, y, shape, cw);
}
}
function goUp(x, y, shape, cw) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = shape;
while (y > 0) {
const up = on(x, y - 1);
const horzUp = x > 0 && on(x - 1, y - 1);
if (!up || horzUp) {
horz = up && horzUp;
break;
}
y--;
visited[y * matrixWidth + x] = shape;
}
if (cw && x === baseX && y === baseY) {
paths[shape] += "z";
return;
}
if (horz) {
paths[shape] += \`v\${(y - sy - 1) * unit}h\${-2 * offset}\`;
goLeft(x - 1, y - 1, shape, cw);
} else {
paths[shape] += \`v\${(y - sy) * unit - thin}\`;
goRight(x, y, shape, cw);
}
}
function goDown(x, y, shape, cw) {
let sy = y;
let horz = false;
visited[y * matrixWidth + x] = shape;
while (y < yMax) {
const down = on(x, y + 1);
const horzDown = x < xMax && on(x + 1, y + 1);
if (!down || horzDown) {
horz = down && horzDown;
break;
}
y++;
visited[y * matrixWidth + x] = shape;
}
if (horz) {
paths[shape] += \`v\${(y - sy + 1) * unit}h\${2 * offset}\`;
goRight(x + 1, y + 1, shape, cw);
} else {
paths[shape] += \`v\${(y - sy) * unit + thin}\`;
goLeft(x, y, shape, cw);
}
}
const stack = [];
for (let x = 0; x < matrixWidth; x++) {
if (!on(x, 0)) stack.push([x, 0]);
}
for (let y = 1; y < yMax; y++) {
if (!on(0, y)) stack.push([0, y]);
if (!on(xMax, y)) stack.push([xMax, y]);
}
for (let x = 0; x < matrixWidth; x++) {
if (!on(x, yMax)) stack.push([x, yMax]);
}
// recursion dfs limited to ~4000
// visit all whitespace connected to edges
function dfsOff() {
while (stack.length > 0) {
const [x, y] = stack.pop();
if (visited[y * matrixWidth + x]) continue;
visited[y * matrixWidth + x] = 1;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dy === 0 && dx === 0) continue;
let nx = x + dx;
let ny = y + dy;
if (nx < 0 || nx > xMax || ny < 0 || ny > yMax) continue;
if (on(nx, ny)) continue;
stack.push([nx, ny]);
}
}
}
}
dfsOff();
const paths = [""];
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
if (visited[y * matrixWidth + x]) continue;
if (newMatrix[y * matrixWidth + x] & Module.FINDER) {
thin = params["Finder thickness"];
offset = (unit - thin) / 2;
} else {
thin = params["Line thickness"];
offset = (unit - thin) / 2;
}
if (!on(x, y)) {
const shape = visited[y * matrixWidth + x - 1];
paths[shape] +=
\`M\${x * unit - offset},\${y * unit - offset}v\${2 * offset}\`;
baseY = y - 1;
baseX = x;
goDown(x - 1, y, shape, false);
stack.push([x, y]);
dfsOff();
continue;
}
if (y > 0 && on(x, y - 1) && visited[(y - 1) * matrixWidth + x]) {
visited[y * matrixWidth + x] = visited[(y - 1) * matrixWidth + x];
continue;
}
if (x > 0 && on(x - 1, y) && visited[y * matrixWidth + x - 1]) {
visited[y * matrixWidth + x] = visited[y * matrixWidth + x - 1];
continue;
}
const color = colors[Math.floor(rand() * colors.length)];
paths.push(
\`<path fill="\${color}" filter="url(#glow)" d="M\${x * unit + offset},\${y * unit + offset}\`
);
baseY = y;
baseX = x;
goRight(x, y, paths.length - 1, true);
}
}
paths.forEach((path, i) => {
if (i === 0) return;
svg += path;
svg += \`"/>\`;
});
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,180 +0,0 @@
export const Quantum = `// Based on QRBTF's A1P style
// https://github.com/CPunisher/react-qrbtf/blob/master/src/components/QRNormal.tsx
import { Module, getSeededRand } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Background: {
type: "color",
default: "#ffffff",
},
Foreground: {
type: "color",
default: "#000000",
},
"Finder pattern": {
type: "select",
options: ["Atom", "Planet"],
},
Particles: {
type: "boolean",
default: true,
},
Seed: {
type: "number",
min: 1,
max: 100,
default: 1,
},
};
export function renderSVG(qr, params) {
const rand = getSeededRand(params["Seed"]);
const range = (min, max) =>
Math.trunc(100 * (rand() * (max - min) + min)) / 100;
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const bg = params["Background"];
const fg = params["Foreground"];
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
for (const [x, y] of [
[0, 0],
[matrixWidth - 7, 0],
[0, matrixWidth - 7],
]) {
svg += \`<g fill="\${fg}">\`;
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 3.5}" r="1.5"/>\`;
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 0.5}" r="0.5"/>\`;
svg += \`<circle cx="\${x + 0.5}" cy="\${y + 3.5}" r="0.5"/>\`;
svg += \`<circle cx="\${x + 6.5}" cy="\${y + 3.5}" r="0.5"/>\`;
svg += \`<circle cx="\${x + 3.5}" cy="\${y + 6.5}" r="0.5"/>\`;
switch (params["Finder pattern"]) {
case "Atom":
let r1 = 0.98;
let r2 = 1.5;
const a = 0.87 * r2;
const b = 0.5 * r2;
svg += \`<path fill="none" stroke-width="0.1" stroke="\${fg}" d="\`;
svg += \`M\${x + 3.5 + 3 * a},\${y + 3.5 - 3 * b}a\${r1},\${r2} 60,0,1 \${-6 * a},\${6 * b}a\${r1},\${r2} 60,0,1 \${6 * a},\${-6 * b}\`;
svg += \`M\${x + 3.5 + 3 * a},\${y + 3.5 + 3 * b}a\${r2},\${r1} 30,0,1 \${-6 * a},\${-6 * b}a\${r2},\${r1} 30,0,1 \${6 * a},\${6 * b}\`;
svg += \`M\${x + 3.5},\${y + 3.5 - 3 * r2}a\${r1},\${r2} 0,0,1 0,\${6 * r2}a\${r1},\${r2} 0,0,1 0,\${-6 * r2}\`;
break;
case "Planet":
svg += \`<path fill="none" stroke-width="0.1" stroke="\${fg}" stroke-dasharray="0.5 0.65" d="\`;
svg += \`M\${x + 3.5},\${y + 0.5}a3,3 0,0,1 0,6a3,3 0,0,1 0-6\`;
break;
}
svg += \`"/></g>\`;
}
let linesLayer = \`<g stroke="\${fg}"><path fill="none" stroke-width="0.1" d="\`;
let dotsLayer = \`<g fill="\${fg}">\`;
function on(x, y) {
return (qr.matrix[y * matrixWidth + x] & Module.ON) !== 0;
}
const visitArray = Array.from({ length: matrixWidth * matrixWidth }).fill(
false
);
function visited(x, y) {
return visitArray[y * matrixWidth + x];
}
function visitCenter(x, y) {
visitArray[y * matrixWidth + x] = true;
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 0.5}" r="\${range(0.3, 0.5)}"/>\`;
}
function visit(x, y) {
visitArray[y * matrixWidth + x] = true;
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 0.5}" r="0.2"/>\`;
}
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = qr.matrix[y * matrixWidth + x];
if (module & Module.FINDER) continue;
if (params["Particles"] && y < matrixWidth - 2 && x < matrixWidth - 2) {
let xCross = false;
let tCross = false;
let a = range(-10, 10);
if (
on(x, y) &&
!visited(x, y) &&
on(x + 2, y) &&
!visited(x + 2, y) &&
on(x + 1, y + 1) &&
!visited(x + 1, y + 1) &&
on(x, y + 2) &&
!visited(x, y + 2) &&
on(x + 2, y + 2) &&
!visited(x + 2, y + 2)
) {
linesLayer += \`M\${x + 0.5},\${y + 0.5}a1.4,.35 \${45 + a},0,1 2,2a1.4,.35 \${45 + a},0,1 -2,-2\`;
linesLayer += \`M\${x + 2.5},\${y + 0.5}a.35,1.4 \${45 + a},0,1 -2,2a.35,1.4 \${45 + a},0,1 2,-2\`;
xCross = true;
}
if (
on(x + 1, y) &&
!visited(x + 1, y) &&
on(x, y + 1) &&
!visited(x, y + 1) &&
on(x + 1, y + 1) &&
!visited(x + 1, y + 1) &&
on(x + 2, y + 1) &&
!visited(x + 2, y + 1) &&
on(x + 1, y + 2) &&
!visited(x + 1, y + 2)
) {
linesLayer += \`M\${x},\${y + 1.55}a1,.35 \${a},0,1 3,0a1,.35 \${a},0,1 -3,0\`;
linesLayer += \`M\${x + 1.5},\${y}a.35,1 \${a},0,1 0,3a.35,1 \${a},0,1 0,-3\`;
tCross = true;
}
if (xCross) {
visit(x, y);
visit(x + 2, y);
visitCenter(x + 1, y + 1);
visit(x, y + 2);
visit(x + 2, y + 2);
}
if (tCross) {
visit(x + 1, y);
visit(x, y + 1);
visitCenter(x + 1, y + 1);
visit(x + 2, y + 1);
visit(x + 1, y + 2);
}
}
if (!visited(x, y) && on(x, y)) {
dotsLayer += \`<circle cx="\${x + 0.5}" cy="\${y + 0.5}" r="\${range(0.3, 0.5)}"/>\`;
}
}
}
linesLayer += \`"/></g>\`;
svg += linesLayer;
dotsLayer += \`</g>\`;
svg += dotsLayer;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,163 +0,0 @@
export const Tile = `import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 20,
default: 10,
},
"Inner square": {
type: "number",
min: 0,
max: 10,
default: 2,
},
"Outer square": {
type: "number",
min: 0,
max: 10,
default: 6,
},
Foreground: {
type: "color",
default: "#000000",
},
Background: {
type: "color",
default: "#ffffff",
},
Grout: {
type: "color",
default: "#b3b8fd",
},
};
export function renderSVG(qr, params) {
const unit = 16;
const gap = 2;
const offset = gap / 2;
const margin = params["Margin"];
const qrWidth = qr.version * 4 + 17;
const matrixWidth = qrWidth + 2 * margin;
const fg = params["Foreground"];
const bg = params["Background"];
const grout = params["Grout"];
const newMatrix = Array.from({ length: matrixWidth * matrixWidth }).fill(0);
const start = margin;
const end = matrixWidth - 1 - margin;
const inner = params["Inner square"];
const outer = params["Outer square"];
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
// outer square
if (y === start - outer && x >= start - outer && x <= end + outer) {
newMatrix[y * matrixWidth + x] = Module.ON;
} else if (
(x === start - outer || x === end + outer) &&
y >= start - outer + 1 &&
y <= end + outer - 1
) {
newMatrix[y * matrixWidth + x] = Module.ON;
newMatrix[y * matrixWidth + x] = Module.ON;
} else if (y === end + outer && x >= start - outer && x <= end + outer) {
newMatrix[y * matrixWidth + x] = Module.ON;
}
// inner square
else if (y === start - inner && x >= start - inner && x <= end + inner) {
newMatrix[y * matrixWidth + x] = Module.ON;
} else if (
(x === start - inner || x === end + inner) &&
y >= start - inner + 1 &&
y <= end + inner - 1
) {
newMatrix[y * matrixWidth + x] = Module.ON;
newMatrix[y * matrixWidth + x] = Module.ON;
} else if (y === end + inner && x >= start - inner && x <= end + inner) {
newMatrix[y * matrixWidth + x] = Module.ON;
}
// qr code w/ quiet zone
else if (
y >= margin - inner &&
y < matrixWidth - margin + inner &&
x >= margin - inner &&
x < matrixWidth - margin + inner
) {
if (
y >= margin &&
y < matrixWidth - margin &&
x >= margin &&
x < matrixWidth - margin
) {
newMatrix[y * matrixWidth + x] =
qr.matrix[(y - margin) * qrWidth + x - margin];
}
}
// between squares
else if (
y > start - outer &&
y < end + outer &&
x > start - outer &&
x < end + outer
) {
if ((x + y) & 1) {
newMatrix[y * matrixWidth + x] = Module.ON;
}
// outside squares
} else {
if (x % 4 && y % 4) {
if ((x % 8 < 4 && y % 8 < 4) || (x % 8 > 4 && y % 8 > 4)) {
newMatrix[y * matrixWidth + x] = Module.ON;
}
} else {
newMatrix[y * matrixWidth + x] = Module.ON;
}
}
}
}
const size = matrixWidth * unit;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 \${size} \${size}">\`;
svg += \`<rect width="\${size}" height="\${size}" fill="\${grout}"/>\`;
svg += \`<path fill="\${fg}" d="\`;
let layer = \`<path fill="\${bg}" d="\`;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = newMatrix[y * matrixWidth + x];
let tiles;
if (module & Module.ON) {
if (module & Module.FINDER) {
svg += \`M\${x * unit},\${y * unit}h\${unit}v\${unit}h-\${unit}z\`;
continue;
}
tiles = 1;
} else {
tiles = 2;
}
const tile = (unit - tiles * gap) / tiles;
for (let dy = 0; dy < tiles; dy++) {
const ny = y * unit + offset + dy * (tile + gap);
for (let dx = 0; dx < tiles; dx++) {
const nx = x * unit + offset + dx * (tile + gap);
if (module & Module.ON) {
svg += \`M\${nx},\${ny}h\${tile}v\${tile}h-\${tile}z\`;
} else {
layer += \`M\${nx},\${ny}h\${tile}v\${tile}h-\${tile}z\`;
}
}
}
}
}
svg += \`"/>\`;
svg += layer + \`"/>\`;
svg += \`</svg>\`;
return svg;
}
`

Wyświetl plik

@ -1,68 +0,0 @@
export const Tutorial = `import { Module } from "https://qrframe.kylezhe.ng/utils.js";
export const paramsSchema = {
Margin: {
type: "number",
min: 0,
max: 10,
step: 0.1,
default: 2,
},
Foreground: {
type: "color",
default: "#000000",
},
Background: {
type: "color",
default: "#ffffff",
},
};
export function renderSVG(qr, params) {
const matrixWidth = qr.version * 4 + 17;
const margin = params["Margin"];
const fg = params["Foreground"];
const bg = params["Background"];
const size = matrixWidth + 2 * margin;
let svg = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="\${-margin} \${-margin} \${size} \${size}">\`;
svg += \`<rect x="\${-margin}" y="\${-margin}" width="\${size}" height="\${size}" fill="\${bg}"/>\`;
svg += \`<path fill="\${fg}" d="\`;
for (let y = 0; y < matrixWidth; y++) {
for (let x = 0; x < matrixWidth; x++) {
const module = qr.matrix[y * matrixWidth + x];
if (module & Module.ON) {
svg += \`M\${x},\${y}h1v1h-1z\`;
}
}
}
svg += \`"/></svg>\`;
return svg;
}
// export function renderCanvas(qr, params, canvas) {
// const matrixWidth = qr.version * 4 + 17;
// const margin = params["Margin"];
// const fg = params["Foreground"];
// const bg = params["Background"];
// const unit = 10;
// const size = (matrixWidth + 2 * margin) * unit;
// canvas.width = size;
// canvas.height = size;
// const ctx = canvas.getContext("2d");
// ctx.fillStyle = bg;
// ctx.fillRect(0, 0, size, size)
// ctx.fillStyle = fg;
// for (let y = 0; y < matrixWidth; y++) {
// for (let x = 0; x < matrixWidth; x++) {
// const module = qr.matrix[y * matrixWidth + x];
// if (module & Module.ON) {
// ctx.fillRect((x + margin) * unit, (y + margin) * unit, unit, unit)
// }
// }
// }
// }
`

Wyświetl plik

@ -14,7 +14,7 @@
"types": ["vinxi/types/client"],
"isolatedModules": true,
"paths": {
"~/*": ["./src/*"]
"~/*": ["./src/*"],
}
}
}

Wyświetl plik

@ -1,41 +0,0 @@
import { promises as fs } from "fs";
import path from "path";
const dev = process.argv[2] === "dev";
const url = dev ? "http://localhost:3000" : "https://qrframe.kylezhe.ng";
async function convertTsToJs(inputDir, outputDir) {
try {
// Ensure output directory exists
await fs.mkdir(outputDir, { recursive: true });
const files = await fs.readdir(inputDir);
const jsFiles = files.filter((file) => file.endsWith(".js"));
for (const file of jsFiles) {
const inputPath = path.join(inputDir, file);
const outputPath = path.join(outputDir, file.slice(0, -3) + ".ts");
let code = await fs.readFile(inputPath, "utf-8");
code = code.replace("REPLACE_URL", url);
code = code.replaceAll("`", "\\`");
code = code.replaceAll("${", "\\${");
await fs.writeFile(
outputPath,
`export const ${file.slice(0, -3)} = \`${code.slice(0, -1)}\n\`\n`,
"utf-8"
);
if (!dev) console.log(`Converted and formatted: ${inputPath} -> ${outputPath}`);
}
if (!dev) console.log("Conversion and formatting completed successfully.");
} catch (error) {
console.error("An error occurred:", error);
}
}
const inputDir = "./presets";
const outputDir = "./src/lib/presets";
convertTsToJs(inputDir, outputDir);