/* eslint-disable @typescript-eslint/unbound-method */ import dotTypes from '../constants/dotTypes' import { DotType } from '../types' type GetNeighbor = (x: number, y: number) => boolean; type DrawArgs = { x: number; y: number; size: number; context: CanvasRenderingContext2D; getNeighbor: GetNeighbor; }; type BasicFigureDrawArgs = { x: number; y: number; size: number; context: CanvasRenderingContext2D; rotation: number; }; type RotateFigureArgs = { x: number; y: number; size: number; context: CanvasRenderingContext2D; rotation: number; draw: () => void; }; export default class QRDot { _context: CanvasRenderingContext2D; _type: DotType; constructor ({ context, type }: { context: CanvasRenderingContext2D; type: DotType }) { this._context = context this._type = type } draw (x: number, y: number, size: number, getNeighbor: GetNeighbor): void { const context = this._context const type = this._type let drawFunction switch (type) { case dotTypes.dots: drawFunction = this._drawDot break case dotTypes.classy: drawFunction = this._drawClassy break case dotTypes.classyRounded: drawFunction = this._drawClassyRounded break case dotTypes.rounded: drawFunction = this._drawRounded break case dotTypes.extraRounded: drawFunction = this._drawExtraRounded break case dotTypes.square: default: drawFunction = this._drawSquare } drawFunction.call(this, { x, y, size, context, getNeighbor }) } _rotateFigure ({ x, y, size, context, rotation, draw }: RotateFigureArgs): void { const cx = x + size / 2 const cy = y + size / 2 context.translate(cx, cy) rotation && context.rotate(rotation) draw() context.closePath() rotation && context.rotate(-rotation) context.translate(-cx, -cy) } _basicDot (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.arc(0, 0, size / 2, 0, Math.PI * 2) } }) } _basicSquare (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.rect(-size / 2, -size / 2, size, size) } }) } // if rotation === 0 - right side is rounded _basicSideRounded (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.arc(0, 0, size / 2, -Math.PI / 2, Math.PI / 2) context.lineTo(-size / 2, size / 2) context.lineTo(-size / 2, -size / 2) context.lineTo(0, -size / 2) } }) } // if rotation === 0 - top right corner is rounded _basicCornerRounded (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.arc(0, 0, size / 2, -Math.PI / 2, 0) context.lineTo(size / 2, size / 2) context.lineTo(-size / 2, size / 2) context.lineTo(-size / 2, -size / 2) context.lineTo(0, -size / 2) } }) } // if rotation === 0 - top right corner is rounded _basicCornerExtraRounded (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.arc(-size / 2, size / 2, size, -Math.PI / 2, 0) context.lineTo(-size / 2, size / 2) context.lineTo(-size / 2, -size / 2) } }) } _basicCornersRounded (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.arc(0, 0, size / 2, -Math.PI / 2, 0) context.lineTo(size / 2, size / 2) context.lineTo(0, size / 2) context.arc(0, 0, size / 2, Math.PI / 2, Math.PI) context.lineTo(-size / 2, -size / 2) context.lineTo(0, -size / 2) } }) } _basicCornersExtraRounded (args: BasicFigureDrawArgs): void { const { size, context } = args this._rotateFigure({ ...args, draw: () => { context.arc(-size / 2, size / 2, size, -Math.PI / 2, 0) context.arc(size / 2, -size / 2, size, Math.PI / 2, Math.PI) } }) } _drawDot ({ x, y, size, context }: DrawArgs): void { this._basicDot({ x, y, size, context, rotation: 0 }) } _drawSquare ({ x, y, size, context }: DrawArgs): void { this._basicSquare({ x, y, size, context, rotation: 0 }) } _drawRounded ({ x, y, size, context, getNeighbor }: DrawArgs): void { const leftNeighbor = +getNeighbor(-1, 0) const rightNeighbor = +getNeighbor(1, 0) const topNeighbor = +getNeighbor(0, -1) const bottomNeighbor = +getNeighbor(0, 1) const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor if (neighborsCount === 0) { this._basicDot({ x, y, size, context, rotation: 0 }) return } if (neighborsCount > 2 || (leftNeighbor && rightNeighbor) || (topNeighbor && bottomNeighbor)) { this._basicSquare({ x, y, size, context, rotation: 0 }) return } if (neighborsCount === 2) { let rotation = 0 if (leftNeighbor && topNeighbor) { rotation = Math.PI / 2 } else if (topNeighbor && rightNeighbor) { rotation = Math.PI } else if (rightNeighbor && bottomNeighbor) { rotation = -Math.PI / 2 } this._basicCornerRounded({ x, y, size, context, rotation }) return } if (neighborsCount === 1) { let rotation = 0 if (topNeighbor) { rotation = Math.PI / 2 } else if (rightNeighbor) { rotation = Math.PI } else if (bottomNeighbor) { rotation = -Math.PI / 2 } this._basicSideRounded({ x, y, size, context, rotation }) } } _drawExtraRounded ({ x, y, size, context, getNeighbor }: DrawArgs): void { const leftNeighbor = +getNeighbor(-1, 0) const rightNeighbor = +getNeighbor(1, 0) const topNeighbor = +getNeighbor(0, -1) const bottomNeighbor = +getNeighbor(0, 1) const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor if (neighborsCount === 0) { this._basicDot({ x, y, size, context, rotation: 0 }) return } if (neighborsCount > 2 || (leftNeighbor && rightNeighbor) || (topNeighbor && bottomNeighbor)) { this._basicSquare({ x, y, size, context, rotation: 0 }) return } if (neighborsCount === 2) { let rotation = 0 if (leftNeighbor && topNeighbor) { rotation = Math.PI / 2 } else if (topNeighbor && rightNeighbor) { rotation = Math.PI } else if (rightNeighbor && bottomNeighbor) { rotation = -Math.PI / 2 } this._basicCornerExtraRounded({ x, y, size, context, rotation }) return } if (neighborsCount === 1) { let rotation = 0 if (topNeighbor) { rotation = Math.PI / 2 } else if (rightNeighbor) { rotation = Math.PI } else if (bottomNeighbor) { rotation = -Math.PI / 2 } this._basicSideRounded({ x, y, size, context, rotation }) } } _drawClassy ({ x, y, size, context, getNeighbor }: DrawArgs): void { const leftNeighbor = +getNeighbor(-1, 0) const rightNeighbor = +getNeighbor(1, 0) const topNeighbor = +getNeighbor(0, -1) const bottomNeighbor = +getNeighbor(0, 1) const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor if (neighborsCount === 0) { this._basicCornersRounded({ x, y, size, context, rotation: Math.PI / 2 }) return } if (!leftNeighbor && !topNeighbor) { this._basicCornerRounded({ x, y, size, context, rotation: -Math.PI / 2 }) return } if (!rightNeighbor && !bottomNeighbor) { this._basicCornerRounded({ x, y, size, context, rotation: Math.PI / 2 }) return } this._basicSquare({ x, y, size, context, rotation: 0 }) } _drawClassyRounded ({ x, y, size, context, getNeighbor }: DrawArgs): void { const leftNeighbor = +getNeighbor(-1, 0) const rightNeighbor = +getNeighbor(1, 0) const topNeighbor = +getNeighbor(0, -1) const bottomNeighbor = +getNeighbor(0, 1) const neighborsCount = leftNeighbor + rightNeighbor + topNeighbor + bottomNeighbor if (neighborsCount === 0) { this._basicCornersRounded({ x, y, size, context, rotation: Math.PI / 2 }) return } if (!leftNeighbor && !topNeighbor) { this._basicCornerExtraRounded({ x, y, size, context, rotation: -Math.PI / 2 }) return } if (!rightNeighbor && !bottomNeighbor) { this._basicCornerExtraRounded({ x, y, size, context, rotation: Math.PI / 2 }) return } this._basicSquare({ x, y, size, context, rotation: 0 }) } }