Public
Edited
Mar 20, 2023
Importers
1 star
Insert cell
# Pattern Lock Generator
Insert cell
html`<div id="pattern-code"></div>`
Insert cell
html`<div id="shadow-pattern" style="display:none"></div>`
Insert cell
Insert cell
show = (pattern) => {
const view = html`<div id="pattern-code"></div>`;
const controller = new PatternLock("#shadow-pattern", {
enableSetPattern: true
});

controller.holder = view;
controller._render();
controller.setPattern(pattern.join(""));

return view;
}
Insert cell
viewPattern(pin)
Insert cell
pin = generatePattern2().map((x) => x + 1)
Insert cell
generatePattern = () => {
let n = getRandomInt(1, 9);
const code = [n];
while (code.length <= 8) {
const next = moves(n).filter((n) => !code.includes(n));
if (next.length === 0) {
break;
} else {
n = next[getRandomInt(0, next.length - 1)];
code.push(n);
}
}
return code;
}
Insert cell
generatePattern2 = (size = 6) => {
// Allocate a buffer for 32 random bytes from which we will derive the pin
// Our pin will never be longer than 9 digits, but we may not be able to use
// every byte so we generate extra.
const bytes = new Uint8Array(32);

// We will collect digits for the pin code here.
const pin = [];
// We will keep track of the digits we have already used here
// to avoid duplicates in the pin as it would lead to unclear
// lock patterns
const visited = new Set();

// Loop until we have collected enough digits for the pin.
while (pin.length < size) {
// We fill the buffer with random bytes
crypto.getRandomValues(bytes);
// and iterate over them attempting to derive digits for the pin
// if digit is already in the pin or if it leads to an overlapping
// pattern we skip it and consider next byte.
for (const byte of bytes) {
// Map the random values to the numbers 0 to 8
const digit = byte % 9;
// If this is the first digit we include it in the pin
const conflict =
pin.length === 0
? false
: // If we have already used this digit we skip it
// otherwise same dot will be used twice in the pattern
visited.has(digit)
? true
: // If the digit is leading to a pattern with a line crossing
// dot that is not part of the pin we skip it
CONFLICT[pin[pin.length - 1]][digit] != null;

if (!conflict) {
visited.add(digit);
pin.push(digit);
}

// If we already have enough digits we can stop here
if (pin.length === size) {
break;
}
}
}

return pin;
}
Insert cell
CONFLICT = ({
0: { 2: 1, 6: 3, 8: 4 },
1: { 7: 4 },
2: { 0: 1, 6: 4, 8: 5 },
3: { 5: 4 },
4: {},
5: { 3: 4 },
6: { 0: 3, 2: 4, 8: 7 },
7: { 1: 4 },
8: { 0: 4, 2: 5, 6: 7 }
})
Insert cell
isValidMove = (start, end) => {
const invalidMoves = {
0: { 2: 1, 6: 3, 8: 4 },
1: { 7: 4 },
2: { 0: 1, 6: 4, 8: 5 },
3: { 5: 4 },
4: {},
5: { 3: 4 },
6: { 0: 3, 2: 4, 8: 7 },
7: { 1: 4 },
8: { 0: 4, 2: 5, 6: 7 }
};

const requiredDot = invalidMoves[start][end];
if (requiredDot === undefined) {
return true;
} else {
return false;
}
}
Insert cell
Insert cell
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Insert cell
Insert cell
Insert cell
draw = (code) => {
let n = 1;
const result = [[], [], []];
for (const r in [0, 1, 2]) {
for (const c in [0, 1, 2]) {
result[r][c] = code.includes(n) ? n : "·";
n += 1;
}
}

pattern.setPattern(code.join(""));

return md`${code.join("")} <pre>${result
.map((r) => r.join(" "))
.join("\n")}</pre>`;
}
Insert cell
getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
}
Insert cell
PatternLock = {
return (await import("https://cdn.skypack.dev/patternlock")).default;
}
Insert cell
Insert cell
pattern = new PatternLock("#pattern-code", {
enableSetPattern: true
})
Insert cell
PLock = {
const lib = await import("https://cdn.skypack.dev/vanilla-pattern-lock");
return lib.default;
}
Insert cell
viewPattern = (code = []) => {
const view = html`<div></div>`;
const ui = html`
<div><style>
svg.pattern-lock g.lock-lines line {
stroke-width: 1.5;
stroke: black;
opacity: 0.5;
}

svg.pattern-lock g.lock-dots circle {
stroke: transparent;
fill: black;
stroke-width: 13.5;
}

svg.pattern-lock g.lock-actives circle {
fill: black;
opacity: .2;
animation: lock-activate-dot .15s 0s ease 1;
transform-origin: center;
}

svg.pattern-lock g.lock-lines line {
stroke-width: 1.5;
stroke-linecap: round;
}

svg.pattern-lock.success g.lock-actives circle {
fill: green;
}

svg.pattern-lock.error g.lock-actives circle {
fill: red;
}

@keyframes lock-activate-dot {
0% {
transform: scale(0);
}
75% {
transform: scale(1.1);
}
100% {
transform: scale(1.0);
}
}
</style>${view}</div>`;

const controller = new PLock({});
controller.render(view);

const canvas = controller.lineCanvas;
const dots = canvas.availableDots;

let line = null;
for (const digit of code) {
if (line) {
canvas.stopTrack(line, dots[digit - 1]);
}
line = canvas.beginTrack(dots[digit - 1]);
}

return Object.assign(ui, { controller });
}
Insert cell
Insert cell
customElements.define("lock-pattern", LockPattern)
Insert cell
Insert cell
code.resetPattern()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more