visual gen with overflow bugs
This commit is contained in:
parent
824e5d9533
commit
088953b8fd
10 changed files with 2715 additions and 18976 deletions
16684
.pnp.cjs
generated
16684
.pnp.cjs
generated
File diff suppressed because one or more lines are too long
2040
.pnp.loader.mjs
generated
2040
.pnp.loader.mjs
generated
File diff suppressed because it is too large
Load diff
|
@ -1 +1,3 @@
|
|||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.3.0.cjs
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({});
|
||||
import compress from "astro-compress";
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [compress()]
|
||||
});
|
1531
package-lock.json
generated
1531
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,13 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^1.6.0"
|
||||
"@svgdotjs/svg.js": "^3.1.2",
|
||||
"@tweakpane/core": "^1.1.0",
|
||||
"astro": "^1.6.0",
|
||||
"astro-compress": "^1.1.7",
|
||||
"colord": "^2.9.3",
|
||||
"random-seed": "^0.3.0",
|
||||
"tweakpane": "^3.1.0"
|
||||
},
|
||||
"packageManager": "yarn@3.3.0",
|
||||
"devDependencies": {
|
||||
|
|
226
src/pages/visual-gen/gen.ts
Normal file
226
src/pages/visual-gen/gen.ts
Normal file
|
@ -0,0 +1,226 @@
|
|||
import { SVG, on as SVGOn, Svg } from "@svgdotjs/svg.js"
|
||||
import { colord } from "colord"
|
||||
import { Pane } from 'tweakpane';
|
||||
import RandomGen from "random-seed"
|
||||
|
||||
interface Config {
|
||||
canvas?: Svg;
|
||||
seed?: string;
|
||||
keepSeed: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
circleSize: number;
|
||||
padding: number;
|
||||
stepSize: number;
|
||||
lineChance: number;
|
||||
colorsPerLine: number;
|
||||
limitLinesToGrid?: boolean;
|
||||
lineOverlap?: boolean;
|
||||
coloredDots?: boolean;
|
||||
maxLineLength?: number;
|
||||
whiteBackground?: boolean;
|
||||
overrideXCount?: number;
|
||||
overrideYCount?: number;
|
||||
animate?: boolean;
|
||||
}
|
||||
|
||||
let config: Config = {
|
||||
seed: "",
|
||||
keepSeed: false,
|
||||
width: 800,
|
||||
height: 800,
|
||||
padding: 100,
|
||||
circleSize: 10,
|
||||
stepSize: 50,
|
||||
lineChance: .2,
|
||||
whiteBackground: false,
|
||||
limitLinesToGrid: true,
|
||||
colorsPerLine: 3,
|
||||
coloredDots: true,
|
||||
overrideXCount: 0,
|
||||
overrideYCount: 0,
|
||||
animate: true
|
||||
}
|
||||
|
||||
const configLimits = {
|
||||
width: { min: 500, max: 5000, step: 50 },
|
||||
height: { min: 500, max: 5000, step: 50 },
|
||||
circleSize: { min: 1, max: 40, step: 1 },
|
||||
padding: { min: 0, max: 500, step: 10 },
|
||||
stepSize: { min: 5, max: 500, step: 5 },
|
||||
lineChance: { min: 0, max: 1, step: 0.05 },
|
||||
colorsPerLine: { min: 1, max: 25, step: 1 },
|
||||
maxLineLength: { min: 2, max: 20, step: 1 },
|
||||
overrideXCount: { max: 100, step: 1 },
|
||||
overrideYCount: { max: 100, step: 1 },
|
||||
}
|
||||
|
||||
const colors = [
|
||||
"#FED61E",
|
||||
"#44B4E9",
|
||||
"#008317",
|
||||
"#002E5C",
|
||||
"#003A3E",
|
||||
"#6C2400",
|
||||
"#FF9900",
|
||||
]
|
||||
|
||||
// calculate absolute pposition with padding
|
||||
function calcPosition({ x, y, padding, numElements, reducedDims, subtractHalfCircle = true }) {
|
||||
const pos = {
|
||||
posX: x / numElements.x * reducedDims.width + padding,
|
||||
posY: y / numElements.y * reducedDims.height + padding
|
||||
}
|
||||
if (subtractHalfCircle) {
|
||||
for (const key in pos) {
|
||||
pos[key] -= reducedDims.halfCircle
|
||||
}
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
function drawElement({ x, y, canvas, occupationMap, random, lineChance, lineLengthLimit, numElements, padding, circleSize, limitLinesToGrid, lineOverlap, colorsPerLine, coloredDots, reducedDims }: Config & { x: number, y: number, occupationMap: number[][], random: RandomGen, lineLengthLimit: number, numElements: { x: number, y: number }, reducedDims: { width: number, height: number, halfCircle: number } }) {
|
||||
|
||||
if (occupationMap[x].includes(y) || !canvas) return
|
||||
if (random.random() < lineChance! && x != numElements.x && y != 0) {
|
||||
// Line
|
||||
let length = random.intBetween(2, lineLengthLimit);
|
||||
const { posX, posY } = calcPosition({ x, y, subtractHalfCircle: false, padding, numElements, reducedDims })
|
||||
|
||||
if (limitLinesToGrid) {
|
||||
const overshootX = x + length - numElements.x;
|
||||
const overshootY = (y - length) * -1;
|
||||
const overshoot = Math.max(overshootX, overshootY)
|
||||
if (overshoot > 0) length -= overshoot
|
||||
}
|
||||
|
||||
const { posX: endX, posY: endY } = calcPosition({
|
||||
x: x + length,
|
||||
y: y - length,
|
||||
subtractHalfCircle: false,
|
||||
padding,
|
||||
numElements,
|
||||
reducedDims
|
||||
})
|
||||
|
||||
const gradient = canvas!.gradient("linear", (add) => {
|
||||
for (let i = 0; i < colorsPerLine; i++) {
|
||||
add.stop({ offset: i / (colorsPerLine - 1), color: colors[random(colors.length)] })
|
||||
}
|
||||
}).transform({
|
||||
rotate: -45,
|
||||
origin: { x: .5, y: .5 }
|
||||
})
|
||||
canvas.line(posX, posY, endX, endY).stroke({
|
||||
width: circleSize,
|
||||
linecap: "round"
|
||||
}).attr({ "stroke": gradient })
|
||||
|
||||
if (!lineOverlap) {
|
||||
for (let i = 0; i <= length; i++) {
|
||||
if (x + i <= numElements.x) occupationMap[x + i].push(y - i)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Circle
|
||||
const { posX, posY } = calcPosition({ x, y, padding, numElements, reducedDims })
|
||||
canvas
|
||||
.circle(circleSize)
|
||||
.fill(coloredDots ? colors[random(colors.length)] : "#333")
|
||||
.move(posX, posY)
|
||||
occupationMap[x].push(y)
|
||||
}
|
||||
}
|
||||
|
||||
function render(configParams: Config) {
|
||||
config.canvas?.remove()
|
||||
|
||||
if (config.seed === "") config.seed = RandomGen.create().string(16)
|
||||
|
||||
const { seed, width, height, padding, circleSize, stepSize, overrideXCount, overrideYCount, maxLineLength, whiteBackground } = configParams
|
||||
|
||||
const random = RandomGen.create(seed)
|
||||
|
||||
const reducedDims = {
|
||||
width: width - 2 * padding,
|
||||
height: height - 2 * padding,
|
||||
halfCircle: circleSize / 2
|
||||
}
|
||||
|
||||
const numElements = {
|
||||
x: overrideXCount && overrideXCount > 0 ? overrideXCount : reducedDims.width / stepSize,
|
||||
y: overrideYCount && overrideYCount > 0 ? overrideYCount : reducedDims.height / stepSize
|
||||
}
|
||||
|
||||
const lineLengthLimit = maxLineLength ?? Math.floor(Math.min(numElements.x, numElements.y)) / 2;
|
||||
|
||||
config.canvas = SVG().viewbox(0, 0, width, height).size(width, height).addTo('#canvas')
|
||||
|
||||
config.canvas.rect(width, height).fill(whiteBackground ? "#ffffff" : "#000000")
|
||||
// Create 2D array of points in grid
|
||||
let occupationMap: number[][] = Array.from({ length: numElements.x + 1 }, x => [])
|
||||
|
||||
|
||||
for (let x = 0; x <= numElements.x; x++) {
|
||||
for (let y = 0; y <= numElements.y; y++) {
|
||||
drawElement({ x, y, occupationMap, random, numElements, lineLengthLimit, reducedDims, ...config })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pane = new Pane()
|
||||
for (const key in config) {
|
||||
pane.addInput(config, key as keyof Config, configLimits[key] ?? {})
|
||||
}
|
||||
|
||||
pane.on('change', () => {
|
||||
pane.refresh()
|
||||
render(config)
|
||||
})
|
||||
|
||||
const regenButton = pane.addButton({ title: "Regenerate" })
|
||||
regenButton.on("click", () => {
|
||||
if (!config.keepSeed) config.seed = RandomGen.create().string(16)
|
||||
render(config)
|
||||
pane.refresh()
|
||||
})
|
||||
|
||||
|
||||
const exportButton = pane.addButton({ title: "Export Settings" })
|
||||
exportButton.on('click', () => {
|
||||
const settings = pane.exportPreset()
|
||||
const settingsString = JSON.stringify(settings, null, 2)
|
||||
const blob = new Blob([settingsString], { type: "application/json" })
|
||||
const link = document.createElement("a")
|
||||
const url = URL.createObjectURL(blob)
|
||||
link.href = url
|
||||
link.download = "GraphicsGenConfig.json"
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 0)
|
||||
})
|
||||
|
||||
const saveButton = pane.addButton({ title: "Save SVG" })
|
||||
saveButton.on('click', () => {
|
||||
var url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(config.canvas?.svg()!);
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = `HiP-Visual-${config.seed}.svg`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 0)
|
||||
})
|
||||
|
||||
SVGOn(document, "DOMContentLoaded", function () {
|
||||
render(config)
|
||||
pane.refresh()
|
||||
})
|
||||
|
||||
if (config.animate) {
|
||||
}
|
36
src/pages/visual-gen/index.astro
Normal file
36
src/pages/visual-gen/index.astro
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
---
|
||||
|
||||
<Layout title='Visual Gen'>
|
||||
<div class='container'>
|
||||
<div id='canvas'></div>
|
||||
</div>
|
||||
</Layout>
|
||||
<style lang='scss'>
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
}
|
||||
#canvas {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
max-height: 80vh;
|
||||
object-fit: contain;
|
||||
> * {
|
||||
object-fit: contain;
|
||||
border: 3px solid #333;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import './gen'
|
||||
</script>
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict"
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue