Skip to content

Instantly share code, notes, and snippets.

@scriptype
Last active March 12, 2018 21:49
Show Gist options
  • Save scriptype/edd9988ad3a78e1ad6432eb32d19547d to your computer and use it in GitHub Desktop.
Save scriptype/edd9988ad3a78e1ad6432eb32d19547d to your computer and use it in GitHub Desktop.
vREqpz
<canvas id="canvas"></canvas>
(() => {
function isTileHere(x, y, tile) {
const tileTop = tile.y
const tileRight = tile.x + tile.width
const tileBottom = tile.y + tile.height
const tileLeft = tile.x
return (
(x >= tileLeft && x <= tileRight) &&
(y >= tileTop && y <= tileBottom)
)
}
function getEmptySpot(width = 1, height = 1, tiles = []) {
let x = 0
let y = 0
let limit = width * height
if (!tiles.length) {
return [x, y]
}
while (limit > 0) {
let tileIndex = 0
let isBlocked = false
o: while (tileIndex < tiles.length) {
let tile = tiles[tileIndex]
if (isTileHere(x, y, tile)) {
isBlocked = true
break o
}
tileIndex++
}
if (!isBlocked) {
return [x, y]
}
limit--
if (x < width) {
x++
} else {
x = 0
y++
}
}
return null
}
function getLayout(options = {}) {
const {
variationFactor = .5,
totalWidth = 512,
totalHeight = 512,
baseTileSize = 128
} = options
const baseSize = baseTileSize
const variation = baseSize * variationFactor
const minSize = baseSize - variation
const maxSize = baseSize + variation
const tiles = []
let limit = 100
while (true) {
if (limit-- < 0) {
alert('something went wrong')
break
}
const emptySpot = getEmptySpot(
totalWidth,
totalHeight,
tiles
)
if (!emptySpot) {
break
}
const [ x, y ] = emptySpot
const varyWidth = Math.random() >= .8 ? minSize :
Math.random() >= .66 ? maxSize : baseSize
let width = varyWidth
let tileEndX = x + width
if (tileEndX > totalWidth) {
if (x + baseSize <= totalWidth) {
width = x + baseSize
} else if (x + minSize <= totalWidth) {
width = x + minSize
} else {
width -= (tileEndX - totalWidth)
}
}
if (totalWidth - tileEndX < minSize) {
const gap = totalWidth - tileEndX
width += gap
}
let neighbourIndex = 0
let lim = 1000
o: while (lim-- > 0 && neighbourIndex < tiles.length) {
let neighbour = tiles[neighbourIndex]
if (isTileHere(tileEndX, y, neighbour)) {
width = neighbour.x - 1 - x
break o
}
neighbourIndex++
}
const varyHeight = Math.random() >= .8 ? minSize :
Math.random() >= .75 ? maxSize : baseSize
let height = varyHeight
let tileEndY = y + height
const gap = totalHeight - tileEndY
if (gap > 0 && gap < minSize) {
height = gap
}
if (tileEndY > totalHeight) {
const overflow = tileEndY - totalHeight
height -= overflow
}
if (width < minSize) {
height = Math.min(width, height)
}
let lastTile = tiles[tiles.length - 1]
if (lastTile && y < lastTile.y && tileEndY > lastTile.y) {
height = lastTile.y - y
}
tiles.push({
x,
y,
width,
height
})
}
return tiles
}
const width = 480
const height = 360
const baseTileSize = 96
const canvas = document.getElementById('canvas')
canvas.width = width
canvas.height = height
canvas.addEventListener('click', generate)
generate()
function generate() {
const ctx = canvas.getContext('2d')
const tileOptions = {
base: {
color: '#575935'
},
shade: {
color: '#484024',
width: 5
},
darkerShade: {
color: '#2F2419',
width: 3
},
tint: {
color: '#70744C',
width: 5
}
}
const layoutOptions = {
totalWidth: width,
totalHeight: height,
baseTileSize,
variationFactor: .25
}
const tiles = getLayout(layoutOptions)
tiles.forEach((tile, index) => {
// Base
ctx.fillStyle = tileOptions.base.color
ctx.fillRect(tile.x, tile.y, tile.width, tile.height)
// Darker Shade
ctx.beginPath()
ctx.strokeStyle = tileOptions.darkerShade.color
ctx.lineWidth = tileOptions.darkerShade.width
ctx.moveTo(
tile.x,
tile.y
)
ctx.lineTo(
tile.x,
tile.y + tile.height
)
ctx.lineTo(
tile.x + tile.width,
tile.y + tile.height
)
ctx.stroke()
let shadeWidth, tintWidth
// Shade
shadeWidth = tileOptions.shade.width
ctx.beginPath()
ctx.strokeStyle = tileOptions.shade.color
ctx.lineWidth = tileOptions.shade.width
ctx.moveTo(
tile.x + shadeWidth,
tile.y
)
ctx.lineTo(
tile.x + shadeWidth,
tile.y + tile.height - shadeWidth
)
ctx.lineTo(
tile.x + tile.width,
tile.y + tile.height - shadeWidth
)
ctx.stroke()
// Lighting
shadeWidth = shadeWidth + tileOptions.tint.width / 2
tintWidth = tileOptions.tint.width / 2
ctx.beginPath()
ctx.strokeStyle = tileOptions.tint.color
ctx.lineWidth = tileOptions.tint.width
ctx.moveTo(
tile.x + shadeWidth,
tile.y + tintWidth
)
ctx.lineTo(
tile.x + tile.width - tintWidth,
tile.y + tintWidth
)
ctx.lineTo(
tile.x + tile.width - tintWidth,
tile.y + tile.height - shadeWidth
)
ctx.stroke()
/*
ctx.font = '20px serif'
ctx.fillStyle = 'white'
ctx.fillText(
`${index}`,
tile.x + tile.width / 2 - 10,
tile.y + tile.height / 2 + 10)
*/
// Shade vertical
noise.seed(Math.random())
var noiseX = 0
var noiseY = 500 + Math.round(Math.random() * 20500)
var noiseYCopy = noiseY
var distortionX = tile.x
var distortionY = tile.y
while (distortionY < tile.y + tile.height) {
distortionY += 1
distortionX = tile.x + tileOptions.shade.width - noise.simplex2(noiseX += .05, noiseY += .05) * tileOptions.shade.width
if (Math.random() >= .5) {
ctx.fillStyle = tileOptions.shade.color
ctx.fillRect(
distortionX,
distortionY,
tileOptions.shade.width / 1.25,
tileOptions.shade.width / 1.25
)
}
}
// Darkershade vertical
var noiseY = noiseYCopy
var distortionX = tile.x
var distortionY = tile.y
while (distortionY < tile.y + tile.height) {
distortionY += tileOptions.darkerShade.width / 2
distortionX = tile.x + (tileOptions.darkerShade.width / 2) - noise.simplex2(noiseX += .05, noiseY += .05) * tileOptions.darkerShade.width / 2
if (Math.random() >= .125) {
if (Math.random() >= .5) {
ctx.fillStyle = tileOptions.darkerShade.color
ctx.fillRect(
tile.x,
distortionY,
distortionX - tile.x,
tileOptions.darkerShade.width
)
} else {
ctx.fillStyle = tileOptions.shade.color
}
ctx.fillRect(
distortionX,
distortionY,
tileOptions.darkerShade.width / 1.25,
tileOptions.darkerShade.width / 1.25
)
}
}
// Tint horizontal
noise.seed(Math.random())
var noiseX = 0
var noiseY = 500 + Math.round(Math.random() * 25000)
var distortionX = tile.x
var distortionY = tile.y
while (distortionX < tile.x + tile.width) {
distortionX += 1
distortionY = tile.y + (tileOptions.tint.width / 2) - noise.simplex2(noiseX += .05, noiseY += .05) * tileOptions.tint.width
if (Math.random() >= .5) {
if (Math.random() >= .5) {
ctx.fillStyle = tileOptions.tint.color
ctx.fillRect(
distortionX,
distortionY,
tileOptions.tint.width / 1.5,
tileOptions.tint.width / 1.5
)
} else {
ctx.fillStyle = tileOptions.base.color
ctx.fillRect(
distortionX,
distortionY,
tileOptions.base.width / 1.75,
tileOptions.base.width / 1.75
)
}
}
}
// Tint vertical
noise.seed(Math.random())
var noiseX = 0
var noiseY = 500 + Math.round(Math.random() * 20500)
var distortionX = tile.x + tile.width
var distortionY = tile.y
while (distortionY < tile.y + tile.height) {
distortionY += 1
distortionX = tile.x + tile.width - tileOptions.tint.width - noise.simplex2(noiseX += .05, noiseY += .05) * tileOptions.tint.width
if (Math.random() >= .5) {
ctx.fillStyle = tileOptions.tint.color
ctx.fillRect(
distortionX,
distortionY,
tileOptions.tint.width / 1.5,
tileOptions.tint.width / 1.5
)
}
}
// Shade horizontal
noise.seed(Math.random())
var noiseX = 0
var noiseY = 500 + Math.round(Math.random() * 25000)
var noiseYCopy = noiseY
var distortionX = tile.x
var distortionY = tile.y + tile.height
while (distortionX < tile.x + tile.width) {
distortionX += 1
distortionY = tile.y + tile.height - (tileOptions.shade.width * 2) - noise.simplex2(noiseX += .05, noiseY += .05) * tileOptions.shade.width
if (Math.random() >= .5) {
if (Math.random() >= .5) {
ctx.fillStyle = tileOptions.shade.color
ctx.fillRect(
distortionX,
distortionY,
tileOptions.shade.width / 1.25,
tileOptions.shade.width / 1.25
)
} else {
ctx.fillStyle = tileOptions.base.color
ctx.fillRect(
distortionX,
distortionY,
tileOptions.shade.width / 1.5,
tileOptions.shade.width / 1.5
)
}
}
}
// Darkershade horizontal
var noiseY = noiseYCopy
var distortionX = tile.x
var distortionY = tile.y + tile.height
while (distortionX < tile.x + tile.width) {
distortionX += tileOptions.darkerShade.width / 1.25
distortionY = tile.y + tile.height - tileOptions.darkerShade.width - noise.simplex2(noiseX += .01, noiseY += .01) * tileOptions.darkerShade.width
if (Math.random() >= .25) {
if (Math.random() >= .4) {
ctx.fillStyle = tileOptions.darkerShade.color
if (Math.random() >= .5) {
ctx.fillRect(
distortionX,
distortionY,
tileOptions.darkerShade.width / 1.5,
tile.y + tile.height - distortionY
)
}
} else {
ctx.fillStyle = tileOptions.shade.color
}
ctx.fillRect(
distortionX,
distortionY,
tileOptions.darkerShade.width / 1.25,
tileOptions.darkerShade.width / 1.25
)
}
}
})
}
})()
<script src="https://cdn.rawgit.com/josephg/noisejs/master/perlin.js"></script>
body {
height: 100vmin;
display: flex;
justify-content: center;
align-items: center;
}
canvas {
cursor: pointer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment