diff --git a/src/components.d.ts b/src/components.d.ts
index dbd037f..d1d1722 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -39,8 +39,11 @@ declare global {
'getModuleCount': () => number;
'maskXToYRatio': number;
'moduleColor': string;
+ 'moduleRoundness': number;
'positionCenterColor': string;
+ 'positionCenterRoundness': number;
'positionRingColor': string;
+ 'positionRingRoundness': number;
'protocol': string;
'squares': boolean;
}
@@ -68,9 +71,12 @@ declare global {
'contents'?: string;
'maskXToYRatio'?: number;
'moduleColor'?: string;
+ 'moduleRoundness'?: number;
'onCodeRendered'?: (event: CustomEvent) => void;
'positionCenterColor'?: string;
+ 'positionCenterRoundness'?: number;
'positionRingColor'?: string;
+ 'positionRingRoundness'?: number;
'protocol'?: string;
'squares'?: boolean;
}
diff --git a/src/components/qr-code/qr-code.tsx b/src/components/qr-code/qr-code.tsx
index b8131d7..e0777a4 100644
--- a/src/components/qr-code/qr-code.tsx
+++ b/src/components/qr-code/qr-code.tsx
@@ -39,6 +39,9 @@ export class BpQRCode {
@Prop() positionCenterColor: string = '#000';
@Prop() maskXToYRatio: number = 1;
@Prop() squares: boolean = false;
+ @Prop() moduleRoundness: number = 0.5;
+ @Prop() positionRingRoundness: number = 0.5;
+ @Prop() positionCenterRoundness: number = 0.5;
@State() data: string;
@State() moduleCount: number;
@@ -187,8 +190,10 @@ export class BpQRCode {
margin,
this.positionRingColor,
this.positionCenterColor,
- coordinateShift
- )
+ coordinateShift,
+ this.positionRingRoundness,
+ this.positionCenterRoundness
+ )
}
${renderQRModulesSVG(
qr,
@@ -198,7 +203,8 @@ export class BpQRCode {
this.maskXToYRatio,
this.squares,
this.moduleColor,
- coordinateShift
+ coordinateShift,
+ this.moduleRoundness
)}
`;
@@ -207,7 +213,9 @@ export class BpQRCode {
margin: number,
ringFill: string,
centerFill: string,
- coordinateShift: number
+ coordinateShift: number,
+ positionRingRoundness: number,
+ positionCenterRoundness: number
) {
return `
${renderQRPositionDetectionPattern(
@@ -216,7 +224,9 @@ export class BpQRCode {
margin,
ringFill,
centerFill,
- coordinateShift
+ coordinateShift,
+ positionRingRoundness,
+ positionCenterRoundness
)}
${renderQRPositionDetectionPattern(
count - 7 + margin,
@@ -224,7 +234,9 @@ export class BpQRCode {
margin,
ringFill,
centerFill,
- coordinateShift
+ coordinateShift,
+ positionRingRoundness,
+ positionCenterRoundness
)}
${renderQRPositionDetectionPattern(
margin,
@@ -232,67 +244,127 @@ export class BpQRCode {
margin,
ringFill,
centerFill,
- coordinateShift
+ coordinateShift,
+ positionRingRoundness,
+ positionCenterRoundness
)}
`;
}
- function renderQRPositionDetectionPattern(
- x: number,
- y: number,
- margin: number,
- ringFill: string,
- centerFill: string,
- coordinateShift: number
- ) {
- return `
-
-
- `;
- }
+ function renderQRPositionDetectionPattern(
+ x: number,
+ y: number,
+ margin: number,
+ ringFill: string,
+ centerFill: string,
+ coordinateShift: number,
+ positionRingRoundness: number = 0,
+ positionCenterRoundness: number = 0
+ ) {
+ // Clamp roundness values between 0 (square) and 1 (circle)
+ positionRingRoundness = Math.max(0, Math.min(1, positionRingRoundness));
+ positionCenterRoundness = Math.max(0, Math.min(1, positionCenterRoundness));
- function renderQRModulesSVG(
- qr: QRCode,
- count: number,
- margin: number,
- maskCenter: boolean,
- maskXToYRatio: number,
- squares: boolean,
- moduleFill: string,
- coordinateShift: number
- ) {
- let svg = '';
- for (let column = 0; column < count; column += 1) {
- const positionX = column + margin;
- for (let row = 0; row < count; row += 1) {
- if (
- qr.isDark(column, row) &&
- (squares ||
- (!isPositioningElement(row, column, count) &&
- !isRemovableCenter(
- row,
- column,
- count,
- maskCenter,
- maskXToYRatio
- )))
- ) {
- const positionY = row + margin;
- svg += squares
- ? `
+ // Outer ring (7x7)
+ const outerSide = 7;
+ const outerCornerRadius = positionRingRoundness * (outerSide / 2);
+ const outerStraight = outerSide - 2 * outerCornerRadius;
+ const outerPath = `
+ M${x - coordinateShift + outerCornerRadius - 0.5} ${y - 0.5 - coordinateShift}
+ h${outerStraight}
+ ${outerCornerRadius > 0 ? `a${outerCornerRadius},${outerCornerRadius} 0 0 1 ${outerCornerRadius},${outerCornerRadius}` : ''}
+ v${outerStraight}
+ ${outerCornerRadius > 0 ? `a${outerCornerRadius},${outerCornerRadius} 0 0 1 -${outerCornerRadius},${outerCornerRadius}` : ''}
+ h-${outerStraight}
+ ${outerCornerRadius > 0 ? `a${outerCornerRadius},${outerCornerRadius} 0 0 1 -${outerCornerRadius},-${outerCornerRadius}` : ''}
+ v-${outerStraight}
+ ${outerCornerRadius > 0 ? `a${outerCornerRadius},${outerCornerRadius} 0 0 1 ${outerCornerRadius},-${outerCornerRadius}` : ''}
+ z
+ `;
+
+ // Inner ring hole (5x5)
+ const innerSide = 5;
+ const innerCornerRadius = positionRingRoundness * (innerSide / 2);
+ const innerStraight = innerSide - 2 * innerCornerRadius;
+ const innerOffset = 1;
+ const innerPath = `
+ M${x - coordinateShift + innerOffset + innerCornerRadius - 0.5} ${y - 0.5 - coordinateShift + innerOffset}
+ h${innerStraight}
+ ${innerCornerRadius > 0 ? `a${innerCornerRadius},${innerCornerRadius} 0 0 1 ${innerCornerRadius},${innerCornerRadius}` : ''}
+ v${innerStraight}
+ ${innerCornerRadius > 0 ? `a${innerCornerRadius},${innerCornerRadius} 0 0 1 -${innerCornerRadius},${innerCornerRadius}` : ''}
+ h-${innerStraight}
+ ${innerCornerRadius > 0 ? `a${innerCornerRadius},${innerCornerRadius} 0 0 1 -${innerCornerRadius},-${innerCornerRadius}` : ''}
+ v-${innerStraight}
+ ${innerCornerRadius > 0 ? `a${innerCornerRadius},${innerCornerRadius} 0 0 1 ${innerCornerRadius},-${innerCornerRadius}` : ''}
+ z
+ `;
+
+ // Center (3x3)
+ const centerSide = 3;
+ const centerCornerRadius = positionCenterRoundness * (centerSide / 2);
+ const centerStraight = centerSide - 2 * centerCornerRadius;
+ const centerOffset = 2;
+ const centerPath = `
+ M${x - coordinateShift + centerOffset + centerCornerRadius - 0.5} ${y - 0.5 - coordinateShift + centerOffset}
+ h${centerStraight}
+ ${centerCornerRadius > 0 ? `a${centerCornerRadius},${centerCornerRadius} 0 0 1 ${centerCornerRadius},${centerCornerRadius}` : ''}
+ v${centerStraight}
+ ${centerCornerRadius > 0 ? `a${centerCornerRadius},${centerCornerRadius} 0 0 1 -${centerCornerRadius},${centerCornerRadius}` : ''}
+ h-${centerStraight}
+ ${centerCornerRadius > 0 ? `a${centerCornerRadius},${centerCornerRadius} 0 0 1 -${centerCornerRadius},-${centerCornerRadius}` : ''}
+ v-${centerStraight}
+ ${centerCornerRadius > 0 ? `a${centerCornerRadius},${centerCornerRadius} 0 0 1 ${centerCornerRadius},-${centerCornerRadius}` : ''}
+ z
+ `;
+
+ return `
+
+
+ `;
+ }
+
+ function renderQRModulesSVG(
+ qr: QRCode,
+ count: number,
+ margin: number,
+ maskCenter: boolean,
+ maskXToYRatio: number,
+ squares: boolean,
+ moduleFill: string,
+ coordinateShift: number,
+ moduleRoundness: number = 1
+ ) {
+ let svg = '';
+ for (let column = 0; column < count; column += 1) {
+ const positionX = column + margin;
+ for (let row = 0; row < count; row += 1) {
+ if (
+ qr.isDark(column, row) &&
+ (squares ||
+ (!isPositioningElement(row, column, count) &&
+ !isRemovableCenter(
+ row,
+ column,
+ count,
+ maskCenter,
+ maskXToYRatio
+ )))
+ ) {
+ const positionY = row + margin;
+
+ if (squares) {
+ // Keep existing square logic unchanged
+ svg += `
- `
- : `
+ positionY - 0.5 - coordinateShift
+ }" width="1" height="1" />
+ `;
+ } else {
+ // New rounded rectangle logic
+ if (moduleRoundness === 1) {
+ // Full circle (moduleRoundness = 1)
+ svg += `
`;
+ } else {
+ // Rounded rectangle (moduleRoundness 0-0.99)
+ const radius = moduleRoundness * 0.5; // Scale roundness to module size
+ svg += `
+ `;
+ }
+ }
+ }
+ }
}
- }
+ return svg;
}
- return svg;
- }
function isPositioningElement(row: number, column: number, count: number) {
const elemWidth = 7;
diff --git a/src/index.html b/src/index.html
index b2244b6..5ababff 100644
--- a/src/index.html
+++ b/src/index.html
@@ -87,6 +87,9 @@
bitjson/qr-code
module-color="#1c7d43"
position-ring-color="#13532d"
position-center-color="#70C559"
+ module-roundness="1"
+ position-ring-roundness="0.5"
+ position-center-roundness="0.5"
style="width: 60vw; height: 60vw; background-color: #fff"
onclick="play()"
>