Puzzle shapes
An investigation of puzzle shapes for "Jigsaw puzzle maths".
Click on the puzzle to regenerate the shape; click near the edges to toggle edge pieces; click on the statistics to toggle percentage mode.
function setup() {
let w = 500
createCanvas(w, w * 2.15);
noLoop()
rand = random(10000000)
}
let rand = 43
let regular = 0.9
let noiseScale = 0.5
let _pmouseX = 0
let _pmouseY = 0
let percents = false
let edges = false
let N = 15
let colors = {
regular: [255, 255, 255],
bend: [255, 150, 150],
sink: [255, 100, 150],
source: [255, 100, 150],
edge: [176, 176, 176]
}
function draw() {
let o = 20
background(220);
if (mouseIsPressed && mouseX > o && mouseX < width - o && mouseY > o && mouseY < width - o) {
mouseIsPressed = false
noLoop()
rand = random(10000000)
} else if (mouseIsPressed && mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < width) {
mouseIsPressed = false
noLoop()
edges = !edges
}
N = edges ? 17 : 15
let s = (width - 2 * o) / N
randomSeed(rand)
let o1 = random(5000)
let o2 = random(5000)
let mem = {}
function getTL(i, j) {
if (!mem[[i,j]]) {
mem[[i, j]] = [random([-1, 1]), random([-1, 1])]
if (random() < regular) {
let sgn = (i + j) % 2 ? -1 : 1
if (noise(i * noiseScale * 0.9 + o1, j * noiseScale * 0.9 + o2) < 0.5) {
sgn *= -1
}
mem[[i, j]] = [sgn, -sgn]
}
}
if (edges) {
return [j % N && mem[[i,j]][0], i % N && mem[[i,j]][1]]
} else {
return mem[[i,j]]
}
}
let counts = {}
let memTypes = {}
for (let i = 0; i < N; i ++) {
for (let j = 0; j < N; j ++) {
let [t, l] = getTL(i, j)
let [b, ] = getTL(i, j + 1)
let [, r] = getTL(i + 1, j)
let type = piece(i * s + o, j * s + o, s, s, t, l, b, r)
counts[type] = (counts[type] || 0) + 1
memTypes[[i,j]] = type
let typeT = memTypes[[i,j-1]]
let typeL = memTypes[[i-1,j]]
counts[[typeT, type]] = (counts[[typeT, type]] || 0) + 1
counts[[type, typeT]] = (counts[[type, typeT]] || 0) + 1
counts[[typeL, type]] = (counts[[typeL, type]] || 0) + 1
counts[[type, typeL]] = (counts[[type, typeL]] || 0) + 1
}
}
textSize(s * 0.6)
textAlign(LEFT, CENTER)
let tw = max(textWidth("Regularity:"), textWidth("Domain noise:"))
{
fill(0)
text("Regularity:", o, s * N + 4 * o)
regular += bar(tw + 2 * o, s * N + 4 * o, s * N - tw - o, s * 0.15, regular)
regular = constrain(regular, 0, 1)
}
{
fill(0)
text("Noise scale:", o, s * (N + 1) + 4 * o)
noiseScale += bar(tw + 2 * o, s * (N + 1) + 4 * o, s * N - tw - o, s * 0.15, noiseScale)
noiseScale = constrain(noiseScale, 0, 1)
}
let types = [
[0, "regular", [-1, 1, 1, -1]],
[1, "bend", [1, 1, 1, 1]],
[2, "sink", [1, 1, 1, -1]],
[3, "source", [-1, 1, 1, 1]],
]
if (edges) {
types.push([4, "edge", [0, 1, 1, 0]])
}
if (mouseIsPressed && mouseX > o && mouseX < width - o && mouseY > s * (N + 5)) {
mouseIsPressed = false
noLoop()
percents = !percents
}
textAlign(CENTER, CENTER)
for (let [i, k, [t,l,b,r]] of types) {
let x = i * (o * 2 + s * 2)
fill(0)
textSize(s * 0.9)
let txt = (counts[k] || 0)
if (percents) {
txt = floor(txt / N / N * 100) + '%'
}
text(txt, x + o + s * 0.5, s * (N + 2.5) + 5 * o)
piece(x + o * 2 + s * 1, s * (N + 2) + 5 * o, s, s, t,l,b,r)
let subsum = 0 // Approaches counts[k] * 4 minus the pieces around the edge
for (let [j, k2, [l2,t2,r2,b2]] of types) {
subsum += (counts[[k,k2]] || 0)
}
for (let [j, k2, [l2,t2,r2,b2]] of types) {
let y = j * (o * 1.5 + s * 2)
fill(0)
textSize(s * 0.7)
let txt = (counts[[k,k2]] || 0)
if (percents) {
txt = floor(txt / subsum * 100) + '%'
}
text(txt, x + o + s * 0.5, y + s * (N + 5) + 5 * o)
piece(x + o * 2 + s * 1, y + s * (N + 4) + 5 * o, s, s, t,l,b,r)
piece(x + o * 2 + s * 1, y + s * (N + 5) + 5 * o, s, s, t2,l2,b2,r2)
}
}
_pmouseX = mouseX
_pmouseY = mouseY
}
function noiseVertex(x,y) {
splineVertex(
x + (noise(x * 0.012 + 400, y * 0.043 + 302) - 0.5) * width / N * 0.1,
y + (noise(x * 0.012 + 231, y * 0.043 + 542) - 0.5) * width / N * 0.1
)
}
function bar(x,y,w,h,p) {
fill(128)
beginShape()
for (let i = 0; i <= N * 3; i ++) {
noiseVertex(x + w / N / 3 * i, y)
}
for (let i = N * 3; i >= 0; i --) {
noiseVertex(x + w / N / 3 * i, y + h)
}
endShape(CLOSE)
fill(255, 128, 128)
beginShape()
for (let i = 0; i <= floor(N * 3 * p); i ++) {
noiseVertex(x + w / N / 3 * i, y)
}
for (let i = floor(N * 3 * p); i >= 0; i --) {
noiseVertex(x + w / N / 3 * i, y + h)
}
endShape(CLOSE)
fill(255)
let motion = 0
if (dist(_pmouseX, _pmouseY, x + w * p, y + h * 0.5) < h * 5 / 2) {
if (mouseIsPressed) {
motion = (mouseX - _pmouseX) / w
}
}
circle(x + w * p, y + h * 0.5, h * 5)
return motion
}
function mousePressed() {
_pmouseX = mouseX
_pmouseY = mouseY
loop()
}
function mouseReleased() {
noLoop()
}
function piece(x,y,w,h, t,l,b,r) {
let type
if (t * b * r * l == 0) {
type = "edge"
} else if (t * b < 0 && r * l < 0 && t * l < 0) {
type = "regular"
} else if ((t < 0) + (l < 0) + (b > 0) + (r > 0) >= 3) {
type = "source"
} else if ((t < 0) + (l < 0) + (b > 0) + (r > 0) <= 1) {
type = "sink"
} else {
type = "bend"
}
fill(...colors[type])
beginShape()
noiseVertex(x + w * 0, y + h * 0)
noiseVertex(x + w * 0.3, y + h * 0)
noiseVertex(x + w * 0.3, y + h * 0.2 * t)
noiseVertex(x + w * 0.7, y + h * 0.2 * t)
noiseVertex(x + w * 0.7, y + h * 0)
noiseVertex(x + w * 1, y + h * 0)
noiseVertex(x + w * 1, y + h * 0.3)
noiseVertex(x + w * (1 + 0.2 * r), y + h * 0.3)
noiseVertex(x + w * (1 + 0.2 * r), y + h * 0.7)
noiseVertex(x + w * 1, y + h * 0.7)
noiseVertex(x + w * 1, y + h * 1)
noiseVertex(x + w * 0.7, y + h * 1)
noiseVertex(x + w * 0.7, y + h * (1 + 0.2 * b))
noiseVertex(x + w * 0.3, y + h * (1 + 0.2 * b))
noiseVertex(x + w * 0.3, y + h * 1)
noiseVertex(x + w * 0, y + h * 1)
noiseVertex(x + w * 0, y + h * 0.7)
noiseVertex(x + w * 0.2 * l, y + h * 0.7)
noiseVertex(x + w * 0.2 * l, y + h * 0.3)
noiseVertex(x + w * 0, y + h * 0.3)
noiseVertex(x + w * 0, y + h * 0)
endShape(CLOSE)
return type
}(Originally seen at https://editor.p5js.org/bojidar-bg/sketches/I4XCD6cIa)
Browse more articles?
← Gray spiral Experiments tagged p5 (84/85) Cursor survival →
← Particle swarm Experiments tagged interactive (26/26) →|
← Gray spiral Experiments on this site (84/85) Cursor survival →