kopia lustrzana https://github.com/zhengkyl/qrframe
remove presets build step w/ raw? + rewrite import in dev
rodzic
8a7ac66481
commit
e939aa76fc
13
README.md
13
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
BIN
examples/ui1.png
BIN
examples/ui1.png
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 87 KiB |
|
@ -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"
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from "REPLACE_URL/utils.js";
|
||||
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
|
||||
|
||||
export const paramsSchema = {
|
||||
Margin: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from "REPLACE_URL/utils.js";
|
||||
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
|
||||
|
||||
export const paramsSchema = {
|
||||
Margin: {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from "REPLACE_URL/utils.js";
|
||||
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
|
||||
|
||||
export const paramsSchema = {
|
||||
Margin: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Module } from "REPLACE_URL/utils.js";
|
||||
import { Module } from "https://qrframe.kylezhe.ng/utils.js";
|
||||
|
||||
export const paramsSchema = {
|
||||
Margin: {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
`
|
|
@ -14,7 +14,7 @@
|
|||
"types": ["vinxi/types/client"],
|
||||
"isolatedModules": true,
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
"~/*": ["./src/*"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
Ładowanie…
Reference in New Issue