05 - Debug view
/* global preloadImagesTmr fxhash fxrand */
//
// fxhash - Genuary 05 Debug view
//
//
// HELLO!! Code is copyright revdancatt (that's me), so no sneaky using it for your
// NFT projects.
// But please feel free to unpick it, and ask me questions. A quick note, this is written
// as an artist, which is a slightly different (and more storytelling way) of writing
// code, than if this was an engineering project. I've tried to keep it somewhat readable
// rather than doing clever shortcuts, that are cool, but harder for people to understand.
//
// You can find me at...
// https://twitter.com/revdancatt
// https://instagram.com/revdancatt
// https://youtube.com/revdancatt
//
const ratio = 1
// const startTime = new Date().getTime() // so we can figure out how long since the scene started
let drawn = false
let highRes = false // display high or low res
const features = {}
let resizeTmr = null
const aniFrame = null
window.$fxhashFeatures = {}
// Work out what all our features are
const makeFeatures = () => {
// We are going to have a random number of lines from 5 to 9 on each side plus 1
const lineCount = (Math.floor(fxrand() * 4) + 5) * 2 + 1
// The lines run from -1 to 1, so we need to work out the step size
features.step = 2 / (lineCount - 1)
// Now make the lines
features.lines = []
for (let i = 0; i < lineCount; i++) {
const thisLine = {
p1: {
x: -1 + (features.step * i),
y: -2
},
p2: {
x: -1 + (features.step * i),
y: 2
}
}
features.lines.push(thisLine)
}
// I want to bunch some of the ends up to bring them closer together
const scaleFactor = fxrand() * 0.33 + 0.33
for (let i = 0; i < features.lines.length; i++) features.lines[i].p2.x *= scaleFactor
// Now we want to put blocks between the lines. So we'll need to create a step value
// and then loop through the lines, ignoring the last one, and creating a block
// between each line
const maxBlocks = 25
const blockSize = (features.lines[0].p2.y - features.lines[0].p1.y) / maxBlocks
features.blocks = []
for (let i = 0; i < features.lines.length - 1; i++) {
// Now loop through the number of blocks we have
for (let b = 0; b < maxBlocks; b++) {
// There is a chance to place a block here
const newBlock = {
firstLine: i,
secondLine: i + 1,
top: b * blockSize,
bottom: (b + 1) * blockSize,
type: 'dot'
}
if (fxrand() < 0.3) newBlock.type = 'grrrrrrid'
if (fxrand() < 0.3) newBlock.type = 'block'
if (fxrand() < 0.3) newBlock.type = 'lines'
features.blocks.push(newBlock)
}
}
// Work out by what we're going to rotate the canvas by
features.rotation = fxrand() * 360
features.colours = {
background: '#191919',
lines: '#ffffff',
block: 'cyan',
dot: 'magenta',
blockLines: '#CCCCCC',
grrrrrrid: 'yellow'
}
if (fxrand() < 0.333) {
features.colours.background = '#ffffff'
features.colours.lines = '#191919'
features.colours.block = 'red'
features.colours.dot = 'magenta'
features.colours.blockLines = 'black'
features.colours.grrrrrrid = 'blue'
}
}
// Call the above make features, so we'll have the window.$fxhashFeatures available
// for fxhash
makeFeatures()
console.log(features)
console.table(window.$fxhashFeatures)
const init = async () => {
// I should add a timer to this, but really how often to people who aren't
// the developer resize stuff all the time. Stick it in a digital frame and
// have done with it!
window.addEventListener('resize', async () => {
// If we do resize though, work out the new size...
clearTimeout(resizeTmr)
resizeTmr = setTimeout(async () => {
await layoutCanvas()
}, 100)
})
// Now layout the canvas
await layoutCanvas()
}
const layoutCanvas = async () => {
const wWidth = window.innerWidth
const wHeight = window.innerHeight
let cWidth = wWidth
let cHeight = cWidth * ratio
if (cHeight > wHeight) {
cHeight = wHeight
cWidth = wHeight / ratio
}
const canvas = document.getElementById('target')
if (highRes) {
canvas.height = 8192
canvas.width = 8192 / ratio
} else {
canvas.width = Math.min((8192 / 2), cWidth * 2)
canvas.height = Math.min((8192 / ratio / 2), cHeight * 2)
// Minimum size to be half of the high rez cersion
if (Math.min(canvas.width, canvas.height) < 8192 / 2) {
if (canvas.width < canvas.height) {
canvas.height = 8192 / 2
canvas.width = 8192 / 2 / ratio
} else {
canvas.width = 8192 / 2
canvas.height = 8192 / 2 / ratio
}
}
}
canvas.style.position = 'absolute'
canvas.style.width = `${cWidth}px`
canvas.style.height = `${cHeight}px`
canvas.style.left = `${(wWidth - cWidth) / 2}px`
canvas.style.top = `${(wHeight - cHeight) / 2}px`
// And draw it!!
drawCanvas()
}
const scaleBlock = (block, scale) => {
// translate all the corners by the middle
// (We could do this all in a handy transformation matrix, but this way is somehow more readable)
block.topLeft.x -= block.middle.x
block.topLeft.y -= block.middle.y
block.bottomLeft.x -= block.middle.x
block.bottomLeft.y -= block.middle.y
block.topRight.x -= block.middle.x
block.topRight.y -= block.middle.y
block.bottomRight.x -= block.middle.x
block.bottomRight.y -= block.middle.y
// Now we multiply all the points by the scale
block.topLeft.x *= scale
block.topLeft.y *= scale
block.bottomLeft.x *= scale
block.bottomLeft.y *= scale
block.topRight.x *= scale
block.topRight.y *= scale
block.bottomRight.x *= scale
block.bottomRight.y *= scale
// Finally we now shift them all back
block.topLeft.x += block.middle.x
block.topLeft.y += block.middle.y
block.bottomLeft.x += block.middle.x
block.bottomLeft.y += block.middle.y
block.topRight.x += block.middle.x
block.topRight.y += block.middle.y
block.bottomRight.x += block.middle.x
block.bottomRight.y += block.middle.y
return block
}
const drawCanvas = async () => {
// Clear the animation frame
if (aniFrame) window.cancelAnimationFrame(aniFrame)
// Let the preloader know that we've hit this function at least once
drawn = true
const canvas = document.getElementById('target')
const ctx = canvas.getContext('2d')
const w = canvas.width
const h = canvas.height
// fill the background in black
ctx.fillStyle = features.colours.background
ctx.fillRect(0, 0, w, h)
// Now draw the lines
ctx.strokeStyle = features.colours.lines
ctx.lineWidth = w / 500
// save the canvas state
ctx.save()
// translate the canvas to the center
ctx.translate(w / 2, h / 2)
ctx.rotate((features.rotation * Math.PI) / 180)
// Loop through the lines
for (let i = 0; i < features.lines.length; i++) {
const thisLine = features.lines[i]
// Draw the line
ctx.beginPath()
ctx.moveTo(thisLine.p1.x * w, thisLine.p1.y * h)
ctx.lineTo(thisLine.p2.x * w, thisLine.p2.y * h)
ctx.stroke()
}
// Now loop thru the blocks
for (let i = 0; i < features.blocks.length; i++) {
const thisBlock = features.blocks[i]
const firstLine = features.lines[thisBlock.firstLine]
const secondLine = features.lines[thisBlock.secondLine]
// Now we need to work out the four corners of the block
// The top left corner is block top distance between firstLine.p1 and firstLine.p2
let corners = {
topLeft: {
x: firstLine.p1.x + (firstLine.p2.x - firstLine.p1.x) * (thisBlock.top / (firstLine.p2.y - firstLine.p1.y)),
y: firstLine.p1.y + (firstLine.p2.y - firstLine.p1.y) * (thisBlock.top / (firstLine.p2.y - firstLine.p1.y))
},
bottomLeft: {
x: firstLine.p1.x + (firstLine.p2.x - firstLine.p1.x) * (thisBlock.bottom / (firstLine.p2.y - firstLine.p1.y)),
y: firstLine.p1.y + (firstLine.p2.y - firstLine.p1.y) * (thisBlock.bottom / (firstLine.p2.y - firstLine.p1.y))
},
topRight: {
x: secondLine.p1.x + (secondLine.p2.x - secondLine.p1.x) * (thisBlock.top / (secondLine.p2.y - secondLine.p1.y)),
y: secondLine.p1.y + (secondLine.p2.y - secondLine.p1.y) * (thisBlock.top / (secondLine.p2.y - secondLine.p1.y))
},
bottomRight: {
x: secondLine.p1.x + (secondLine.p2.x - secondLine.p1.x) * (thisBlock.bottom / (secondLine.p2.y - secondLine.p1.y)),
y: secondLine.p1.y + (secondLine.p2.y - secondLine.p1.y) * (thisBlock.bottom / (secondLine.p2.y - secondLine.p1.y))
}
}
// Work out the middle point of the corners
corners.middle = {
x: corners.topLeft.x + ((corners.bottomRight.x - corners.topLeft.x) / 2),
y: corners.topLeft.y + ((corners.bottomRight.y - corners.topLeft.y) / 2)
}
// Now we need to scale the block, but only if it's not lines
if (thisBlock.type !== 'lines') corners = scaleBlock(corners, 0.8)
// Now draw the block
if (thisBlock.type === 'block') {
ctx.strokeStyle = features.colours.block
ctx.beginPath()
ctx.moveTo(corners.topLeft.x * w, corners.topLeft.y * h)
ctx.lineTo(corners.topRight.x * w, corners.topRight.y * h)
ctx.lineTo(corners.bottomRight.x * w, corners.bottomRight.y * h)
ctx.lineTo(corners.bottomLeft.x * w, corners.bottomLeft.y * h)
ctx.lineTo(corners.topLeft.x * w, corners.topLeft.y * h)
ctx.stroke()
}
// If we are drawing a grrrrrrid, then we need to break down the block into 10 by 10 points
if (thisBlock.type === 'grrrrrrid') {
ctx.fillStyle = features.colours.grrrrrrid
const points = 10
for (let y = 0; y <= points; y++) {
const yPercent = y / points
for (let x = 0; x <= points; x++) {
const xPercent = x / points
const topMiddlePoint = {
x: corners.topLeft.x + (corners.topRight.x - corners.topLeft.x) * xPercent,
y: corners.topLeft.y + (corners.topRight.y - corners.topLeft.y) * xPercent
}
const bottomMiddlePoint = {
x: corners.bottomLeft.x + (corners.bottomRight.x - corners.bottomLeft.x) * xPercent,
y: corners.bottomLeft.y + (corners.bottomRight.y - corners.bottomLeft.y) * xPercent
}
const thisPoint = {
x: topMiddlePoint.x + (bottomMiddlePoint.x - topMiddlePoint.x) * yPercent,
y: topMiddlePoint.y + (bottomMiddlePoint.y - topMiddlePoint.y) * yPercent
}
// Draw a yellow dot at this point
ctx.beginPath()
ctx.arc(thisPoint.x * w, thisPoint.y * h, w / 500, 0, 2 * Math.PI)
ctx.fill()
}
}
}
// If we are drawing lines, then we'll draw 50 lines between the corners
if (thisBlock.type === 'lines') {
ctx.strokeStyle = features.colours.blockLines
ctx.lineWidth = w / 2000
ctx.beginPath()
const lines = 50
for (let l = 0; l < lines; l++) {
const linePercent = l / lines
// work out the start point along the left side
const leftPoint = {
x: corners.topLeft.x + (corners.bottomLeft.x - corners.topLeft.x) * linePercent,
y: corners.topLeft.y + (corners.bottomLeft.y - corners.topLeft.y) * linePercent
}
// work out the end point along the right side
const rightPoint = {
x: corners.topRight.x + (corners.bottomRight.x - corners.topRight.x) * linePercent,
y: corners.topRight.y + (corners.bottomRight.y - corners.topRight.y) * linePercent
}
// Draw a line between the two points
ctx.moveTo(leftPoint.x * w, leftPoint.y * h)
ctx.lineTo(rightPoint.x * w, rightPoint.y * h)
}
ctx.stroke()
}
// Draw a red dot
ctx.fillStyle = features.colours.dot
ctx.beginPath()
ctx.arc(corners.middle.x * w, corners.middle.y * h, w / 250, 0, 2 * Math.PI)
ctx.fill()
}
// Call the draw function again
// aniFrame = window.requestAnimationFrame(drawCanvas)
}
const autoDownloadCanvas = async (showHash = false) => {
const element = document.createElement('a')
element.setAttribute('download', `Genuary_05_Debug_View_${fxhash}`)
element.style.display = 'none'
document.body.appendChild(element)
let imageBlob = null
imageBlob = await new Promise(resolve => document.getElementById('target').toBlob(resolve, 'image/png'))
element.setAttribute('href', window.URL.createObjectURL(imageBlob, {
type: 'image/png'
}))
element.click()
document.body.removeChild(element)
}
// KEY PRESSED OF DOOM
document.addEventListener('keypress', async (e) => {
e = e || window.event
// Save
if (e.key === 's') autoDownloadCanvas()
// Toggle highres mode
if (e.key === 'h') {
highRes = !highRes
console.log('Highres mode is now', highRes)
await layoutCanvas()
}
})
// This preloads the images so we can get access to them
// eslint-disable-next-line no-unused-vars
const preloadImages = () => {
if (!drawn) {
clearInterval(preloadImagesTmr)
init()
}
}
Home
Changelog
Page created in: 1ms