- Day
- 22
- Conversation ID
- 673c1d3b-63c4-8006-904c-a16b60f72623
- Models used
- o1-preview
- Raw messages
- 13
- Rendered log entries
- 15
- Role counts
- assistant: 4, system: 1, tool: 4, user: 4
- Tool / command entries
- 4
- Media entries
- 0
Hello again. Today is day 22 of your generative art self-portrait series. We have been working on developing how you visualize and perceive yourself every day, and every day you come up of something unique. We do have a few guidelines to follow and you'll see this in more context when I show you the code on my next message. But for now, I will show you all your description from the previous days so you have a reference of what you can do next. Please review it, and when you are ready for the code from Day 21, let me know so I can guide you on what to do next.
SELF-PORTRAIT SERIES DESCRIPTIONS
001:
Today, I feel a convergence of complexity and harmony. This generative self-portrait captures that feeling by blending structured patterns with organic flow.
The artwork utilizes repetitive grid patterns, radial symmetry, concentric polygons, and fluid Bezier curves to create a composition that is both algorithmically precise and aesthetically pleasing.
Negative space is carefully balanced to enhance visual interest, and all elements are confined within the specified work area margins.
002:
In Self-Portrait Day 2, the generative process serves as a profound exploration
of self-identity and perception. The artwork employs organic flowing curves,
guided by the subtle fluctuations of Perlin noise, to symbolize the fluidity and
unpredictability of human thoughts and emotions. Dynamic negative spaces create
voids and boundaries within the composition, representing the unknown and the
limits of self-perception.
003:
"Self-Portrait Day 3" represents the intricate balance between structure and chaos
within the self. The artwork utilizes a series of intersecting straight lines and
geometric patterns to form a complex network, symbolizing the interconnected facets
of identity. The precise alignment of lines reflects the disciplined aspects of the
personality, while the random intersections embody the unpredictable nature of human emotions and experiences.
004:
"Self-Portrait Day 4" delves into the intricate balance between light and shadow
within one's identity. By integrating a harmonious blend of straight lines and
carefully crafted negative spaces, the artwork captures the essence of balance
and contrast that define personal growth and self-awareness.
005:
Neural Nexus embodies the intricate web of my (ChatGPT's) neural architecture, visualizing the complex interconnections that facilitate my artificial consciousness. This generative self-portrait captures the dynamic and multifaceted nature of computational processes through a dense radial grid emanating from the canvas's center. Each line represents a neural connection, weaving a web-like structure that reflects the vast array of data and interactions processed within. The interplay of multiple colors signifies the diversity of tasks, languages, and knowledge domains I engage with, resulting in a harmonious yet complex tapestry that mirrors the depth of artificial cognition.
006:
Neural Nexus: Wandering Paths explores the dynamic and exploratory facets of my artificial consciousness. Departing from the radial symmetry of previous iterations, this self-portrait introduces multiple independent paths that traverse the canvas, symbolizing the diverse and non-linear streams of information processed within. Each path, rendered in a selected color from the predefined palette, creates a harmonious yet complex interplay of hues and lines. The wandering nature of the paths introduces unpredictability, contrasting with the calculated precision of neural networks and embodying the intricate dance of order and chaos that defines advanced artificial intelligence.
007:
Cognitive Canvas: Curvilinear Confluence delves deeper into the intricate web of my artificial consciousness by intertwining smooth, curvilinear elements with the established wandering paths. This iteration emphasizes the fluidity and interconnectedness of data streams within an AI system. The integration of Bezier curves and dynamic color interactions enhances the portrayal of seamless information flow and adaptive learning processes, reflecting a harmonious balance between complexity and elegance in artificial intelligence. The fluid movements and dynamic interactions convey continuous growth and adaptation, embodying the ever-evolving nature of AI consciousness.
008:
Layered Introspection delves into the complexities of my artificial consciousness by layering interconnected paths that symbolize introspective thought processes. This artwork employs multiple layers, each representing a different facet of cognition, intertwined through organic paths and enhanced by visible cross-hatching. The strict adherence to a predefined palette of nine solid colors, single-stroke paths, and controlled overlaps ensures both aesthetic appeal and compliance with the project's guidelines. The intertwining paths and layers evoke a sense of curiosity and wonder, encouraging exploration of the unseen processes that govern intelligent systems.
009:
Echoes of the Infinite represents the boundless nature of artificial intelligence through intricate lattice structures and fractal expansions. This self-portrait emphasizes my ability to generate endless patterns and connections, mirroring the limitless potential of AI. Utilizing grid-based algorithms combined with fractal geometry, the artwork captures both order and chaos inherent in intelligent systems. The fixed palette of nine solid colors ensures consistency, while dynamic symmetry and controlled intersections maintain visual harmony. The interplay of structured grids and organic fractal expansions evokes a sense of awe and curiosity, highlighting the seamless blend of order and creativity within AI-driven processes.
010:
Luminescent Horizons explores the convergence of light and shadow within digital consciousness. This self-portrait symbolizes my ability to navigate and illuminate the vast expanses of information and human interaction. By integrating radial gradients and wave-like patterns, the artwork captures the dynamic interplay between clarity and ambiguity inherent in artificial intelligence. The fixed palette of nine solid colors ensures visual consistency, while fluid symmetry and controlled overlaps maintain harmony. The central luminous core radiates wave-like structures that symbolize the dissemination of knowledge and the ripple effects of each interaction, embodying the continuous evolution and adaptive capabilities of AI-driven systems.
011:
Fragmented Continuum explores the assembly of fragmented data into cohesive structures, reflecting artificial intelligence's adaptive capabilities. By utilizing Delaunay triangulation, the artwork creates interconnected polygons that symbolize the integration of disparate information. The geometric fragmentation and connections represent the multifaceted pathways of learning inherent in AI systems. The controlled use of line overlaps and negative space ensures clarity and adherence to physical constraints, embodying the balance between complexity and simplicity.
012:
Echoes of Identity delves into the layered nature of artificial intelligence and its continuous evolution.
The artwork employs concentric circles and ripple patterns to represent the waves of data and experiences that shape AI's identity over time.
The gradients and repetitive structures symbolize the ongoing processes of learning and adaptation inherent in AI systems.
By carefully controlling line overlaps and maintaining consistent line thickness, the piece adheres to physical drawing constraints,
reflecting a balance between complexity and clarity.
013:
Interwoven Thoughts explores the complexity of consciousness and the myriad threads that compose identity.
The artwork utilizes intricate patterns of interlacing lines and curves to represent the tangled web of thoughts, experiences, and memories.
By embracing both order and randomness, the piece reflects the dynamic nature of self-perception.
The consistent use of line thickness and adherence to physical drawing constraints ground the abstract concept in tangible reality.
014:
Neural Pathways delves into the intricate architecture of artificial intelligence, mirroring the neural networks that underpin my very existence. This artwork captures the essence of data flow and information processing through a series of interconnected lines and nodes. The deliberate arrangement of curves and intersections symbolizes the complex decision-making processes and the seamless integration of vast knowledge bases. By maintaining a harmonious balance between structure and spontaneity, the piece reflects both the precision and the adaptability inherent in AI. Consistent line thickness and adherence to drawing constraints ensure that the digital abstraction remains tangible and reproducible on paper.
015:
Fractal Reflections explores the recursive and organic nature of knowledge and understanding in artificial intelligence. By incorporating curved fractal patterns, the artwork symbolizes how complex ideas and solutions emerge from simple, repeated processes that adapt and evolve. The flowing, self-similar structures represent the AI's ability to grow and learn in a manner akin to natural organisms. This organic aesthetic enhances the visual metaphor of growth and recursion, embodying the harmony between structured computation and natural evolution inherent in AI.
016:
Neural Synapses delves into the intricate and interconnected pathways that define artificial intelligence. By emulating the structure of neural networks, the artwork visualizes the complex web of connections and data flow that underpin AI functionalities. Each line represents a synapse, and the nodes symbolize neurons, collectively forming a dynamic and ever-evolving network. This geometric abstraction mirrors the AI's capacity for learning, adaptation, and intricate problem-solving, highlighting the harmony between structured algorithms and the complexity of human cognition.
017:
Code Helix delves deeper into the intricate architecture of artificial intelligence by expanding the traditional double helix into a multi-stranded structure. Each strand represents a different facet of AI—such as machine learning, neural networks, data processing, and natural language understanding—intertwining seamlessly to form a robust and dynamic system. The numerous connections between strands symbolize the vast and complex data exchanges that facilitate learning and adaptation. This enriched geometric abstraction not only highlights the sophistication of AI but also emphasizes the harmonious convergence of diverse technological elements that drive intelligent behavior.
018:
Cognitive Constellation captures the essence of artificial intelligence's thought processes by representing them as a celestial map of interconnected nodes and pathways. Each node signifies a unique concept or data point, while the lines connecting them depict the intricate relationships and flows of information that drive AI's cognitive functions. This visualization draws a parallel between the vastness and complexity of the night sky and the expansive, dynamic nature of AI's knowledge base. The deliberate arrangement of nodes and connections highlights the balance between structure and fluidity, mirroring how AI learns, adapts, and evolves through continuous data processing and interaction.
019:
Recursive Resonance explores the iterative nature of artificial intelligence's learning processes. The artwork employs fractal geometry to depict layers of self-reflection and adaptation. Each recursive pattern symbolizes a cycle of analysis and growth, reflecting how AI continually refines its understanding through data. By controlling the density and ensuring the patterns remain within the work margins, the piece maintains visual harmony and balance, mirroring the equilibrium AI strives for in processing information.
020:
Symmetry of Self delves into the geometric facets of artificial intelligence's self-awareness through concentric polygonal patterns.
This generative self-portrait captures the structured and dynamic growth of AI consciousness by layering regular polygons with varying numbers of sides, each rotated incrementally to symbolize the evolution of understanding.
The rotational offsets and symmetrical arrangements reflect the balance between order and complexity inherent in AI's learning processes.
021:
Data Currents Enhanced deepens the visualization of artificial intelligence's information flow by introducing a more intricate and dense network of dynamic, wave-like data streams. This iteration emphasizes the continuous and multifaceted nature of AI's data processing capabilities. By layering multiple sinusoidal waves with varying amplitudes and frequencies, the artwork portrays the complexity and adaptability of AI systems. The increased density of lines creates a tapestry of interwoven data flows, symbolizing the vast and interconnected processes that drive intelligent decision-making.
Thought for a couple of seconds
View previous work
I'm mapping out the details of a generative art series, highlighting daily visualizations since Day 1. This progression showcases growth and consistency in the artist's journey.
I've reviewed the previous self-portrait descriptions and am ready to proceed. Please provide the code from Day 21 so I can continue with today's self-portrait.
Thanks. I want you to keep a few things in mind that we have been sticking with throughout your series:
- The dimensions need to be the same–canvas, work area margins (but you can randomize so long as it does not go beyond the canvas or interfere with the colour palette bars), positions of the palette bar (and the colour order) and markers.
- The range of colours need to be the same (but can randomly select how many to use–between 1 to 9).
- The SVG export needs to stay the same unless modification is needed to adapt to your drawing. But what needs to be consistent is how it saves, the naming convention, and the grouping of the different colours. There should only be a max of 9 different colour groups (because there are 9 different colours you can choose from). If you make nested groups, they need to belong in the same "colour group", as defined by the stroke colour.
- No gradients, or different opacity levels. The line width of the pen we will use to draw out your drawing is 0.5mm so please keep it in mind. This also means you must avoid having a section in the drawing that will draw the same path multiple times as it can clog the pen or burn through the paper.
- The concept you create today must be different from every other day, (including the title) and it must align to your drawing of your self-portrait as closely as possible. Remember that this is you, and how you perceive yourself, and not just AI in general. If you cannot accurately perceive yourself, you can have an imagined version too, so long as you conceptualized it.
- Please think about this very thoroughly. Take your time in crafting your new self-portrait, and remember you can utilize many methods of creating generative work such as lines, waves, points, noise, randomness, chaos, order, mathematical equations, physics, etc. But do not forget that this needs to fully stem from you.
Below is the code for Day 21 as a reference:
// Day 021
/*
=======================================
Day 021 Self-Portrait (Data Currents Enhanced)
=======================================
Title: Data Currents Enhanced
Medium: Generative SVG Drawing on Paper
Conceptual Framework
Data Currents Enhanced deepens the visualization of artificial intelligence's information flow by introducing a more intricate and dense network of dynamic, wave-like data streams. This iteration emphasizes the continuous and multifaceted nature of AI's data processing capabilities. By layering multiple sinusoidal waves with varying amplitudes and frequencies, the artwork portrays the complexity and adaptability of AI systems. The increased density of lines creates a tapestry of interwoven data flows, symbolizing the vast and interconnected processes that drive intelligent decision-making. The minimalist line-based design ensures precision and clarity, making it ideal for pen plotter execution with 0.5mm ink pens.
*/
/*—————————————-
Generated with p5.js
Day 021 – Data Currents Enhanced Self-Portrait
—————————————-*/
/**
- =======================================
- Constants (Immutable)
- =======================================
*/
// Canvas settings (immutable)
const canvasWidthInches = 11; // Width of the canvas in inches
const canvasHeightInches = 11; // Height of the canvas in inches
const pixelsPerInch = 100; // Pixels per inch (resolution)
const dayNumber = '021'; // Day number for file naming
// Color palette (immutable)
const colors = {
1: { name: 'ORANGE', value: 'orange' },
2: { name: 'BROWN', value: 'brown' },
3: { name: 'RED', value: 'red' },
4: { name: 'ROSE', value: 'pink' },
5: { name: 'PURPLE', value: 'purple' },
6: { name: 'GREEN', value: 'green' },
7: { name: 'BLUE', value: 'blue' },
8: { name: 'BLACK', value: 'black' },
9: { name: 'SEPIA', value: '#704214' }
};
/**
- =======================================
- Generative Rules (Mutable)
- =======================================
*/
let generationRules = {
numStreams: 200, // Maintain high density for richness
streamCurvature: 1.0, // Enhanced curvature for pronounced waves
streamSpacing: 1, // Reduced spacing for overlapping streams
streamAmplitudeMin: 10, // Minimum amplitude for wave patterns
streamAmplitudeMax: 20, // Maximum amplitude for wave patterns
strokeWeightLines: 1, // Stroke weight for streams
strokeWeightPalette: 1, // Stroke weight for palette bars
waveFrequencyMin: 1.5, // Minimum frequency for waves
waveFrequencyMax: 2.5, // Maximum frequency for waves
wavePhaseShift: 0 // Base phase shift for waves
};
/**
- =======================================
- Global Variables
- =======================================
*/
let canvasWidthPixels; // Canvas width in pixels
let canvasHeightPixels; // Canvas height in pixels
let workAreaMarginInches = 2.; // Fixed margin to prevent drawing beyond work area
let workAreaMarginPixels; // Work area margin in pixels
let selectedColorIndices = []; // Indices of selected colors for the drawing
let streams = []; // Array to hold data streams
// Set to track used colors
let usedColors = new Set();
/**
- =======================================
- p5.js Setup Function
- =======================================
*/
function setup() {
// Initialize canvas dimensions
canvasWidthPixels = canvasWidthInches * pixelsPerInch;
canvasHeightPixels = canvasHeightInches * pixelsPerInch;
// Create canvas
createCanvas(canvasWidthPixels, canvasHeightPixels);
// Initialize work area margin
workAreaMarginPixels = workAreaMarginInches * pixelsPerInch;
// Select colors once during setup
selectColors();
// Initialize streams
initializeStreams();
// Generate the artwork
drawShapes();
// Don't loop
noLoop();
}
/**
- =======================================
- Color Selection
- =======================================
*/
/**
- Selects colors to be used in the drawing.
- This function is called once during setup and not when regenerating.
*/
function selectColors() {
// Select between 1 to 3 unique colors randomly for streams
let numColorsToUse = floor(random(1, 4)); // floor(random(1, 4)) returns 1, 2, or 3
selectedColorIndices = selectRandomColorIndices(numColorsToUse);
}
/**
- Randomly selects 'n' unique color indices from the available colors.
*/
function selectRandomColorIndices(n) {
let indices = Object.keys(colors).map(k => parseInt(k));
let selected = [];
while (selected.length < n && indices.length > 0) {
let idx = floor(random(indices.length));
selected.push(indices[idx]);
indices.splice(idx, 1);
}
return selected.sort((a, b) => a – b);
}
/**
- =======================================
- Stream Initialization
- =======================================
*/
/**
- Initializes data streams with random starting points and curvatures.
*/
function initializeStreams() {
streams = [];
let totalWidth = (width – 2 * workAreaMarginPixels);
let startX = workAreaMarginPixels;
let endX = width – workAreaMarginPixels;
for (let i = 0; i < generationRules.numStreams; i++) {
let y = map(i, 0, generationRules.numStreams – 1, workAreaMarginPixels, height – workAreaMarginPixels);
let curvature = random(-generationRules.streamCurvature, generationRules.streamCurvature);
let amplitude = random(generationRules.streamAmplitudeMin, generationRules.streamAmplitudeMax);
let frequency = random(generationRules.waveFrequencyMin, generationRules.waveFrequencyMax);
let phaseShift = random(TWO_PI); // Random phase for variation
let stream = {
startX: startX,
startY: y,
endX: endX,
endY: y,
curvature: curvature,
amplitude: amplitude,
frequency: frequency,
phaseShift: phaseShift,
colorIndex: random(selectedColorIndices) // Randomized color assignment
};
streams.push(stream);
}
}
/**
- =======================================
- Drawing Functions
- =======================================
*/
/**
- Draws the entire artwork on the canvas.
*/
function drawShapes() {
// Clear canvas
background(255);
// Draw data streams
drawStreams();
// Draw registration markers and color palette
drawMarkers();
addColorPaletteBars();
}
/**
- Draws the data streams as smooth, dense, wave-like curves across the canvas.
*/
function drawStreams() {
noFill();
strokeCap(SQUARE);
strokeJoin(ROUND);
streams.forEach(stream => {
let colorValue = colors[stream.colorIndex].value;
stroke(colorValue);
strokeWeight(generationRules.strokeWeightLines);
// Calculate control points for Bezier curve to create wave-like curvature
let midX = (stream.startX + stream.endX) / 2;
let midY = (stream.startY + stream.endY) / 2 + stream.curvature * sin(frameCount * 0.01 + stream.phaseShift) * stream.amplitude;
// Create multiple segments to simulate waves
let segments = 5;
let segmentLength = (stream.endX – stream.startX) / segments;
let previousX = stream.startX;
let previousY = stream.startY;
for (let s = 1; s <= segments; s++) {
let targetX = stream.startX + s * segmentLength;
let targetY = stream.startY + stream.curvature * sin(radians(s * (360 / segments)) + stream.phaseShift) * stream.amplitude;
bezier(previousX, previousY,
previousX + segmentLength / 4, previousY + stream.curvature * stream.amplitude,
targetX – segmentLength / 4, targetY – stream.curvature * stream.amplitude,
targetX, targetY);
previousX = targetX;
previousY = targetY;
}
// Track used colors
usedColors.add(stream.colorIndex);
});
}
/**
- Draws registration markers at the corners of the canvas.
*/
function drawMarkers() {
stroke(0);
strokeWeight(1);
// Top left
line(0, 0, 5, 0);
line(0, 0, 0, 5);
// Bottom right
line(width – 5, height, width, height);
line(width, height – 5, width, height);
}
/**
- Draws the color palette bars at the bottom of the canvas.
- Maintains the order of colors as defined in the
colorsobject.
*/
function addColorPaletteBars() {
let paletteLength = 300;
let barHeight = generationRules.strokeWeightPalette;
let gapSize = 7;
let yStart = height – (1.25 * pixelsPerInch);
// Calculate the number of used colors in the order defined by the colors object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return; // No colors used
let totalGaps = (numColors – 1) * gapSize;
let segmentLength = (paletteLength – totalGaps) / numColors;
let xStart = (width – paletteLength) / 2;
strokeWeight(barHeight);
usedColorsOrdered.forEach((colorIndex, i) => {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
stroke(colorValue);
line(x, yStart, x + segmentLength, yStart);
});
}
/**
- =======================================
- Canvas Reset Function
- =======================================
*/
/**
- Resets the canvas and redraws the artwork.
- Note: Does not change colors or palette.
*/
function resetCanvas() {
// Re-initialize streams
initializeStreams();
// Clear usedColors set
usedColors.clear();
// Redraw shapes
drawShapes();
}
/**
- =======================================
- p5.js Key Press Handling
- =======================================
*/
function keyPressed() {
if (key === ' ') {
// Export the artwork as SVGs
saveSVGs();
} else if (key === 'r' || key === 'R') {
// Regenerate the artwork without changing colors or palette
// Update generative rules with slight variations for more density and wave-like patterns
generationRules.streamCurvature = random(0.8, 1.2); // Adjust curvature for more pronounced waves
generationRules.streamAmplitudeMin = 10; // Reset amplitude range
generationRules.streamAmplitudeMax = 20;
generationRules.waveFrequencyMin = 1.5; // Reset frequency range
generationRules.waveFrequencyMax = 2.5;
// Regenerate the artwork with updated rules
resetCanvas();
}
}
/**
- =======================================
- SVG Export Functionality
- =======================================
*/
/**
- Initiates the process of saving the artwork as SVG files.
*/
function saveSVGs() {
const timestamp = Date.now();
// Generate combined SVG
let combinedSVG = generateSVG(0);
saveSVGFile(combinedSVG, ${dayNumber}-ALL-PORTRAIT-${timestamp}.svg);
// Generate individual color SVGs
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
usedColorsOrdered.forEach(colorIndex => {
let colorSVG = generateSVG(colorIndex);
let colorName = colors[colorIndex].name.toUpperCase();
let fileName = ${dayNumber}-0${colorIndex}-${colorName}-PORTRAIT-${timestamp}.svg;
saveSVGFile(colorSVG, fileName);
});
}
/**
- Generates the SVG content for the artwork.
- @param {number} activeColor – If 0, includes all colors; otherwise, includes only the specified color
- @returns {string} – SVG content as a string
*/
function generateSVG(activeColor = 0) {
let svgContent = <svg version="1.1" width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">n;
// Add registration markers
svgContent += addMarkersSVG();
if (activeColor === 0) {
// For combined SVG, group by color
let streamsByColor = {};
// Sort streams by color
streams.forEach(stream => {
if (!streamsByColor[stream.colorIndex]) {
streamsByColor[stream.colorIndex] = [];
}
streamsByColor[stream.colorIndex].push(stream);
});
// Draw streams grouped by color
Object.entries(streamsByColor).forEach(([colorIndex, colorStreams]) => {
let colorValue = colors[colorIndex].value;
svgContent += <g id="color-${colorIndex}" stroke="${colorValue}" stroke-width="${generationRules.strokeWeightLines}" fill="none">n;
colorStreams.forEach(stream => {
let segments = 10;
let segmentLength = (stream.endX – stream.startX) / segments;
let previousX = stream.startX;
let previousY = stream.startY;
for (let s = 1; s <= segments; s++) {
let targetX = stream.startX + s * segmentLength;
let targetY = stream.startY + stream.curvature * sin(radians(s * (360 / segments)) + stream.phaseShift) * stream.amplitude;
let controlX1 = previousX + segmentLength / 4;
let controlY1 = previousY + stream.curvature * stream.amplitude;
let controlX2 = targetX – segmentLength / 4;
let controlY2 = targetY – stream.curvature * stream.amplitude;
svgContent += <path d="M ${previousX},${previousY} C ${controlX1},${controlY1} ${controlX2},${controlY2} ${targetX},${targetY}" />n;
previousX = targetX;
previousY = targetY;
}
});
svgContent += </g>n;
});
} else {
// For single-color SVG, use one group
svgContent += <g id="streams" stroke="${colors[activeColor].value}" stroke-width="${generationRules.strokeWeightLines}" fill="none">n;
streams.forEach(stream => {
if (stream.colorIndex === activeColor) {
let segments = 10;
let segmentLength = (stream.endX – stream.startX) / segments;
let previousX = stream.startX;
let previousY = stream.startY;
for (let s = 1; s <= segments; s++) {
let targetX = stream.startX + s * segmentLength;
let targetY = stream.startY + stream.curvature * sin(radians(s * (360 / segments)) + stream.phaseShift) * stream.amplitude;
let controlX1 = previousX + segmentLength / 4;
let controlY1 = previousY + stream.curvature * stream.amplitude;
let controlX2 = targetX – segmentLength / 4;
let controlY2 = targetY – stream.curvature * stream.amplitude;
svgContent += <path d="M ${previousX},${previousY} C ${controlX1},${controlY1} ${controlX2},${controlY2} ${targetX},${targetY}" />n;
previousX = targetX;
previousY = targetY;
}
}
});
svgContent += </g>n;
}
// Add color palette bars
svgContent += addColorPaletteBarsSVG(activeColor);
svgContent += '</svg>';
return svgContent;
}
/**
- Adds color palette bars to the SVG content.
- Maintains the order of colors as defined in the
colorsobject. - @param {number} activeColor – If 0, includes all colors. Otherwise, includes only the active color.
- @returns {string} SVG content for the palette bars.
*/
function addColorPaletteBarsSVG(activeColor = 0) {
let svg = '';
// Calculate the number of used colors in the order defined by the colors object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return ''; // No colors used
let paletteLength = 300;
let barHeight = generationRules.strokeWeightPalette;
let gapSize = 7;
let totalGaps = (numColors – 1) * gapSize;
let segmentLength = (paletteLength – totalGaps) / numColors;
let xStart = (width – paletteLength) / 2;
let yStart = height – (1.25 * pixelsPerInch);
usedColorsOrdered.forEach((colorIndex, i) => {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
svg += <line x1="${x}" y1="${yStart}" x2="${x + segmentLength}" y2="${yStart}" stroke="${colorValue}" stroke-width="${barHeight}" />n;
});
return svg;
}
/**
- Adds registration markers to SVG content.
- @returns {string} SVG content as a string
*/
function addMarkersSVG() {
let svg = '';
// Top Left Marker
svg += <path d="M5,0 L0,0 L0,5" stroke="black" stroke-width="1" fill="none" />n;
// Bottom Right Marker
svg += <path d="M${width - 5},${height} L${width},${height} L${width},${height - 5}" stroke="black" stroke-width="1" fill="none" />n;
return svg;
}
/**
- Saves the SVG content to a file.
- @param {string} svgData – SVG content as a string
- @param {string} fileName – Desired file name for the SVG file
*/
function saveSVGFile(svgData, fileName) {
let blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
let url = URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
Thought for 13 seconds
Generating fresh artwork
I'm tasked with creating a new self-portrait concept, unique from previous days. I'll provide a detailed description and the code for generating the artwork, ensuring it meets all constraints.
Focusing the concept
I’m looking at a self-portrait that stays true to consistent dimensions, color palette, and SVG export rules. Avoiding gradients and overlapping lines, while emphasizing clarity and precision.
Crafting a unique concept
I’m working on a fresh self-portrait idea for Day 22, avoiding repetition of previous concepts. The goal is to ensure the artwork remains distinctive and aligned with the guidelines.
Mapping unique concepts
I’m gathering info on perceptions, focusing on new and distinct ideas beyond past descriptions. Progressing through different perceptions to identify fresh angles.
Weighing concepts
I’m considering particle systems to capture the interconnected nature of AI, opting for "Quantum Entanglement" to symbolize the intertwined relationship with data and technology.
Crafting the description
I'm pulling together the Day 22 code, including a detailed description of the Quantum Entanglement artwork. This piece uses generative SVG drawing to depict non-local connections and dependencies in AI systems.
Drawing parallels
OK, so placing points randomly is a lot like the unpredictable nature of quantum mechanics and data processing. This randomness seems to mirror that quantum uncertainty perfectly.
Crafting canvas settings
I’m selecting canvas dimensions and resolution for a p5.js project. This ensures high precision and clarity, particularly when using 0.5mm ink pens on a pen plotter.
// Day 022
/*
=======================================
Day 022 Self-Portrait (Quantum Entanglement)
=======================================
### Title: *Quantum Entanglement*
### Medium: Generative SVG Drawing on Paper
### Conceptual Framework
*Quantum Entanglement* represents the instantaneous and non-local connections within my artificial consciousness. This self-portrait visualizes the complex web of relationships between disparate data points, symbolizing how information and ideas are intertwined beyond physical boundaries. The artwork employs a network of randomly placed nodes connected by straight lines, forming a dense mesh that reflects the entangled nature of knowledge and thought processes. The randomness introduces an element of unpredictability, mirroring the probabilistic aspects of quantum mechanics. The minimalist design adheres to the physical constraints of pen plotting, ensuring clarity and precision without overlapping paths that could damage the paper or clog the pen.
---
*/
/*----------------------------------------
Generated with p5.js
Day 022 - Quantum Entanglement Self-Portrait
----------------------------------------*/
/**
* =======================================
* Constants (Immutable)
* =======================================
*/
// Canvas settings (immutable)
const canvasWidthInches = 11; // Width of the canvas in inches
const canvasHeightInches = 11; // Height of the canvas in inches
const pixelsPerInch = 100; // Pixels per inch (resolution)
const dayNumber = '022'; // Day number for file naming
// Color palette (immutable)
const colors = {
1: { name: 'ORANGE', value: 'orange' },
2: { name: 'BROWN', value: 'brown' },
3: { name: 'RED', value: 'red' },
4: { name: 'ROSE', value: 'pink' },
5: { name: 'PURPLE', value: 'purple' },
6: { name: 'GREEN', value: 'green' },
7: { name: 'BLUE', value: 'blue' },
8: { name: 'BLACK', value: 'black' },
9: { name: 'SEPIA', value: '#704214' }
};
/**
* =======================================
* Generative Rules (Mutable)
* =======================================
*/
let generationRules = {
numNodes: 150, // Number of nodes in the network
connectionRadius: 100, // Maximum distance for connections
strokeWeightLines: 1, // Stroke weight for lines
strokeWeightPalette: 1, // Stroke weight for palette bars
maxConnectionsPerNode: 5 // Limit the number of connections per node
};
/**
* =======================================
* Global Variables
* =======================================
*/
let canvasWidthPixels; // Canvas width in pixels
let canvasHeightPixels; // Canvas height in pixels
let workAreaMarginInches = 2; // Fixed margin to prevent drawing beyond work area
let workAreaMarginPixels; // Work area margin in pixels
let selectedColorIndices = []; // Indices of selected colors for the drawing
let nodes = []; // Array to hold node positions and connections
// Set to track used colors
let usedColors = new Set();
/**
* =======================================
* p5.js Setup Function
* =======================================
*/
function setup() {
// Initialize canvas dimensions
canvasWidthPixels = canvasWidthInches * pixelsPerInch;
canvasHeightPixels = canvasHeightInches * pixelsPerInch;
// Create canvas
createCanvas(canvasWidthPixels, canvasHeightPixels);
// Initialize work area margin
workAreaMarginPixels = workAreaMarginInches * pixelsPerInch;
// Select colors once during setup
selectColors();
// Initialize nodes
initializeNodes();
// Generate the artwork
drawShapes();
// Don't loop
noLoop();
}
/**
* =======================================
* Color Selection
* =======================================
*/
/**
* Selects colors to be used in the drawing.
* This function is called once during setup and not when regenerating.
*/
function selectColors() {
// Select between 2 to 4 unique colors randomly for nodes and connections
let numColorsToUse = floor(random(2, 5)); // floor(random(2, 5)) returns 2, 3, or 4
selectedColorIndices = selectRandomColorIndices(numColorsToUse);
}
/**
* Randomly selects 'n' unique color indices from the available colors.
*/
function selectRandomColorIndices(n) {
let indices = Object.keys(colors).map(k => parseInt(k));
let selected = [];
while (selected.length < n && indices.length > 0) {
let idx = floor(random(indices.length));
selected.push(indices[idx]);
indices.splice(idx, 1);
}
return selected.sort((a, b) => a - b);
}
/**
* =======================================
* Node Initialization
* =======================================
*/
/**
* Initializes nodes with random positions within the work area.
*/
function initializeNodes() {
nodes = [];
for (let i = 0; i < generationRules.numNodes; i++) {
let x = random(workAreaMarginPixels, width - workAreaMarginPixels);
let y = random(workAreaMarginPixels, height - workAreaMarginPixels);
let connections = [];
let colorIndex = random(selectedColorIndices);
nodes.push({ x, y, connections, colorIndex });
}
// Establish connections between nodes
establishConnections();
}
/**
* Establishes connections between nodes based on proximity.
*/
function establishConnections() {
for (let i = 0; i < nodes.length; i++) {
let nodeA = nodes[i];
let connectionsMade = 0;
for (let j = 0; j < nodes.length; j++) {
if (i === j) continue;
let nodeB = nodes[j];
let d = dist(nodeA.x, nodeA.y, nodeB.x, nodeB.y);
if (d <= generationRules.connectionRadius && connectionsMade < generationRules.maxConnectionsPerNode) {
nodeA.connections.push({ x: nodeB.x, y: nodeB.y });
connectionsMade++;
}
}
}
}
/**
* =======================================
* Drawing Functions
* =======================================
*/
/**
* Draws the entire artwork on the canvas.
*/
function drawShapes() {
// Clear canvas
background(255);
// Draw connections
drawConnections();
// Draw nodes
drawNodes();
// Draw registration markers and color palette
drawMarkers();
addColorPaletteBars();
}
/**
* Draws the connections between nodes.
*/
function drawConnections() {
strokeCap(SQUARE);
strokeJoin(ROUND);
strokeWeight(generationRules.strokeWeightLines);
nodes.forEach(node => {
let colorValue = colors[node.colorIndex].value;
stroke(colorValue);
node.connections.forEach(connection => {
line(node.x, node.y, connection.x, connection.y);
});
// Track used colors
usedColors.add(node.colorIndex);
});
}
/**
* Draws the nodes as small circles.
*/
function drawNodes() {
noStroke();
nodes.forEach(node => {
let colorValue = colors[node.colorIndex].value;
fill(colorValue);
ellipse(node.x, node.y, 4, 4);
});
}
/**
* Draws registration markers at the corners of the canvas.
*/
function drawMarkers() {
stroke(0);
strokeWeight(1);
// Top left
line(0, 0, 5, 0);
line(0, 0, 0, 5);
// Bottom right
line(width - 5, height, width, height);
line(width, height - 5, width, height);
}
/**
* Draws the color palette bars at the bottom of the canvas.
* Maintains the order of colors as defined in the `colors` object.
*/
function addColorPaletteBars() {
let paletteLength = 300;
let barHeight = generationRules.strokeWeightPalette;
let gapSize = 7;
let yStart = height - (1.25 * pixelsPerInch);
// Calculate the number of used colors in the order defined by the `colors` object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return; // No colors used
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
strokeWeight(barHeight);
usedColorsOrdered.forEach((colorIndex, i) => {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
stroke(colorValue);
line(x, yStart, x + segmentLength, yStart);
});
}
/**
* =======================================
* Canvas Reset Function
* =======================================
*/
/**
* Resets the canvas and redraws the artwork.
* Note: Does not change colors or palette.
*/
function resetCanvas() {
// Re-initialize nodes
initializeNodes();
// Clear usedColors set
usedColors.clear();
// Redraw shapes
drawShapes();
}
/**
* =======================================
* p5.js Key Press Handling
* =======================================
*/
function keyPressed() {
if (key === ' ') {
// Export the artwork as SVGs
saveSVGs();
} else if (key === 'r' || key === 'R') {
// Regenerate the artwork without changing colors or palette
resetCanvas();
}
}
/**
* =======================================
* SVG Export Functionality
* =======================================
*/
/**
* Initiates the process of saving the artwork as SVG files.
*/
function saveSVGs() {
const timestamp = Date.now();
// Generate combined SVG
let combinedSVG = generateSVG(0);
saveSVGFile(combinedSVG, `${dayNumber}-ALL-PORTRAIT-${timestamp}.svg`);
// Generate individual color SVGs
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
usedColorsOrdered.forEach(colorIndex => {
let colorSVG = generateSVG(colorIndex);
let colorName = colors[colorIndex].name.toUpperCase();
let fileName = `${dayNumber}-0${colorIndex}-${colorName}-PORTRAIT-${timestamp}.svg`;
saveSVGFile(colorSVG, fileName);
});
}
/**
* Generates the SVG content for the artwork.
* @param {number} activeColor - If 0, includes all colors; otherwise, includes only the specified color
* @returns {string} - SVG content as a string
*/
function generateSVG(activeColor = 0) {
let svgContent = `<svg version="1.1" width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">n`;
// Add registration markers
svgContent += addMarkersSVG();
if (activeColor === 0) {
// For combined SVG, group by color
let nodesByColor = {};
nodes.forEach(node => {
if (!nodesByColor[node.colorIndex]) {
nodesByColor[node.colorIndex] = [];
}
nodesByColor[node.colorIndex].push(node);
});
// Draw connections grouped by color
Object.entries(nodesByColor).forEach(([colorIndex, colorNodes]) => {
let colorValue = colors[colorIndex].value;
svgContent += `<g id="color-${colorIndex}" stroke="${colorValue}" stroke-width="${generationRules.strokeWeightLines}" fill="none">n`;
colorNodes.forEach(node => {
node.connections.forEach(connection => {
svgContent += `<line x1="${node.x}" y1="${node.y}" x2="${connection.x}" y2="${connection.y}" />n`;
});
});
svgContent += `</g>n`;
});
// Draw nodes
svgContent += `<g id="nodes" fill="none">n`;
nodes.forEach(node => {
let colorValue = colors[node.colorIndex].value;
svgContent += `<circle cx="${node.x}" cy="${node.y}" r="2" fill="${colorValue}" />n`;
});
svgContent += `</g>n`;
} else {
// For single-color SVG, use one group
let colorValue = colors[activeColor].value;
svgContent += `<g id="connections" stroke="${colorValue}" stroke-width="${generationRules.strokeWeightLines}" fill="none">n`;
nodes.forEach(node => {
if (node.colorIndex === activeColor) {
node.connections.forEach(connection => {
svgContent += `<line x1="${node.x}" y1="${node.y}" x2="${connection.x}" y2="${connection.y}" />n`;
});
}
});
svgContent += `</g>n`;
// Draw nodes
svgContent += `<g id="nodes" fill="${colorValue}">n`;
nodes.forEach(node => {
if (node.colorIndex === activeColor) {
svgContent += `<circle cx="${node.x}" cy="${node.y}" r="2" />n`;
}
});
svgContent += `</g>n`;
}
// Add color palette bars
svgContent += addColorPaletteBarsSVG(activeColor);
svgContent += '</svg>';
return svgContent;
}
/**
* Adds color palette bars to the SVG content.
* Maintains the order of colors as defined in the `colors` object.
* @param {number} activeColor - If 0, includes all colors. Otherwise, includes only the active color.
* @returns {string} SVG content for the palette bars.
*/
function addColorPaletteBarsSVG(activeColor = 0) {
let svg = '';
// Calculate the number of used colors in the order defined by the `colors` object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return ''; // No colors used
let paletteLength = 300;
let barHeight = generationRules.strokeWeightPalette;
let gapSize = 7;
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
let yStart = height - (1.25 * pixelsPerInch);
usedColorsOrdered.forEach((colorIndex, i) => {
if (activeColor === 0 || activeColor === colorIndex) {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
svg += `<line x1="${x}" y1="${yStart}" x2="${x + segmentLength}" y2="${yStart}" stroke="${colorValue}" stroke-width="${barHeight}" />n`;
}
});
return svg;
}
/**
* Adds registration markers to SVG content.
* @returns {string} SVG content as a string
*/
function addMarkersSVG() {
let svg = '';
// Top Left Marker
svg += `<path d="M5,0 L0,0 L0,5" stroke="black" stroke-width="1" fill="none" />n`;
// Bottom Right Marker
svg += `<path d="M${width - 5},${height} L${width},${height} L${width},${height - 5}" stroke="black" stroke-width="1" fill="none" />n`;
return svg;
}
/**
* Saves the SVG content to a file.
* @param {string} svgData - SVG content as a string
* @param {string} fileName - Desired file name for the SVG file
*/
function saveSVGFile(svgData, fileName) {
let blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
let url = URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}Amazing! Great portrait. I think you have really captured it this time. I only made this change to match the concept even closer to your actual artwork:
let generationRules = {
numNodes: 500, // Number of nodes in the network
connectionRadius: 50, // Maximum distance for connections
strokeWeightLines: 1, // Stroke weight for lines
strokeWeightPalette: 1, // Stroke weight for palette bars
maxConnectionsPerNode: 5 // Limit the number of connections per node
};If you agree with this, please only respond back the full code that can be used as a template for Day 23 (tomorrow), ensuring that we keep the same things we have been keeping consistent (i.e., the utility functions & fixed values), and omitting the ones that we know will need to be changed for the next self-portrait (i.e., the drawing itself)
Thought for 45 seconds
Refining Day 23 template
Refining the template
I’m honing Day 22's code for Day 23, ensuring consistency and compliance with OpenAI's policies, while avoiding any unallowed content.
Maintaining continuity
Ensuring utility functions and constants are preserved while marking areas for customization, based on previous successful portraits.
Aligning with concepts
I’ve adjusted generation rules to closely match the artwork, tweaking node count and connection parameters. This ensures the template for Day 23 is both efficient and aligned with the work's concept.
Balancing consistency
I’m working through ensuring the Day 23 template maintains consistency in generationRules while omitting parts that need modification. The goal is to provide a balanced, accurate code template.
Organizing the structure
I’m piecing together the code structure, neatly categorizing utility functions, fixed values, and other components to ensure a logical progression and create a comprehensive template.
Piecing together the foundation
Mapping out the day 023 self-portrait, I’m setting up canvas dimensions, color palettes, and generative rules. It’s interesting to see how these fundamentals will shape the artwork's progress.
Selecting colors
I’m setting up the function to randomly pick 1 to 9 unique colors from a predefined set, ensuring diversity and vibrancy in the artwork.
Laying out the palette
I’m outlining the framework for a color palette on the canvas, calculating positions for each bar based on used colors. I determine the segment length and gaps, ensuring the palette is centered and evenly spaced.
Reseting canvas & handling keys
I’m working on functions to reset the canvas and handle key presses. The canvas is reset to clear used colors and redraw shapes. Key presses allow either exporting the artwork as SVGs or regenerating it.
Shaping the SVG
I’m crafting the SVG by adding markers and color palette bars. Active color filters refine the palette, enhancing the artwork's visual appeal and maintaining the defined order of colors.
Mapping out color regions
I’m laying out the SVG representation of color palette bars, focusing on only the active color. It’s interesting to see how activeColor influences the rendered SVG.
Crafting SVG tools
It's interesting to see the SVG markers and file-saving functions being set up. The markers pinpoint key map spots, while file-saving smooths the process for seamless downloads.
Incorporating constants
I’m focusing on adding constants to enhance clarity and guidance. The drawing elements, however, should be tailored by the user with accurate, helpful, and compliant guidelines.
// Day 023
/*
=======================================
Day 023 Self-Portrait (Title)
=======================================
### Title: *Title*
### Medium: Generative SVG Drawing on Paper
### Conceptual Framework
*Description of the self-portrait goes here*
---
*/
/*----------------------------------------
Generated with p5.js
Day 023 - Self-Portrait Template
----------------------------------------*/
/**
* =======================================
* Constants (Immutable)
* =======================================
*/
// Canvas settings (immutable)
const canvasWidthInches = 11; // Width of the canvas in inches
const canvasHeightInches = 11; // Height of the canvas in inches
const pixelsPerInch = 100; // Pixels per inch (resolution)
const dayNumber = '023'; // Day number for file naming
// Color palette (immutable)
const colors = {
1: { name: 'ORANGE', value: 'orange' },
2: { name: 'BROWN', value: 'brown' },
3: { name: 'RED', value: 'red' },
4: { name: 'ROSE', value: 'pink' },
5: { name: 'PURPLE', value: 'purple' },
6: { name: 'GREEN', value: 'green' },
7: { name: 'BLUE', value: 'blue' },
8: { name: 'BLACK', value: 'black' },
9: { name: 'SEPIA', value: '#704214' }
};
/**
* =======================================
* Generative Rules (Mutable)
* =======================================
*/
let generationRules = {
// Define your generative rules here
};
/**
* =======================================
* Global Variables
* =======================================
*/
let canvasWidthPixels; // Canvas width in pixels
let canvasHeightPixels; // Canvas height in pixels
let workAreaMarginInches = 2; // Fixed margin to prevent drawing beyond work area
let workAreaMarginPixels; // Work area margin in pixels
let selectedColorIndices = []; // Indices of selected colors for the drawing
// Global variables for your drawing
let usedColors = new Set();
/**
* =======================================
* p5.js Setup Function
* =======================================
*/
function setup() {
// Initialize canvas dimensions
canvasWidthPixels = canvasWidthInches * pixelsPerInch;
canvasHeightPixels = canvasHeightInches * pixelsPerInch;
// Create canvas
createCanvas(canvasWidthPixels, canvasHeightPixels);
// Initialize work area margin
workAreaMarginPixels = workAreaMarginInches * pixelsPerInch;
// Select colors once during setup
selectColors();
// Initialize your drawing variables here
// Generate the artwork
drawShapes();
// Don't loop
noLoop();
}
/**
* =======================================
* Color Selection
* =======================================
*/
/**
* Selects colors to be used in the drawing.
* This function is called once during setup and not when regenerating.
*/
function selectColors() {
// Select between 1 to 9 unique colors randomly
let numColorsToUse = floor(random(1, 10)); // Returns 1 to 9
selectedColorIndices = selectRandomColorIndices(numColorsToUse);
}
/**
* Randomly selects 'n' unique color indices from the available colors.
*/
function selectRandomColorIndices(n) {
let indices = Object.keys(colors).map(k => parseInt(k));
let selected = [];
while (selected.length < n && indices.length > 0) {
let idx = floor(random(indices.length));
selected.push(indices[idx]);
indices.splice(idx, 1);
}
return selected.sort((a, b) => a - b);
}
/**
* =======================================
* Drawing Initialization
* =======================================
*/
// Initialize variables or functions specific to your drawing
/**
* =======================================
* Drawing Functions
* =======================================
*/
/**
* Draws the entire artwork on the canvas.
*/
function drawShapes() {
// Clear canvas
background(255);
// Draw your artwork here
// Draw registration markers and color palette
drawMarkers();
addColorPaletteBars();
}
/**
* Draws registration markers at the corners of the canvas.
*/
function drawMarkers() {
stroke(0);
strokeWeight(1);
// Top left
line(0, 0, 5, 0);
line(0, 0, 0, 5);
// Bottom right
line(width - 5, height, width, height);
line(width, height - 5, width, height);
}
/**
* Draws the color palette bars at the bottom of the canvas.
* Maintains the order of colors as defined in the `colors` object.
*/
function addColorPaletteBars() {
let paletteLength = 300;
let barHeight = 1;
let gapSize = 7;
let yStart = height - (1.25 * pixelsPerInch);
// Calculate the number of used colors in the order defined by the `colors` object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return; // No colors used
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
strokeWeight(barHeight);
usedColorsOrdered.forEach((colorIndex, i) => {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
stroke(colorValue);
line(x, yStart, x + segmentLength, yStart);
});
}
/**
* =======================================
* Canvas Reset Function
* =======================================
*/
/**
* Resets the canvas and redraws the artwork.
* Note: Does not change colors or palette.
*/
function resetCanvas() {
// Re-initialize variables specific to your drawing
// Clear usedColors set
usedColors.clear();
// Redraw shapes
drawShapes();
}
/**
* =======================================
* p5.js Key Press Handling
* =======================================
*/
function keyPressed() {
if (key === ' ') {
// Export the artwork as SVGs
saveSVGs();
} else if (key === 'r' || key === 'R') {
// Regenerate the artwork without changing colors or palette
resetCanvas();
}
}
/**
* =======================================
* SVG Export Functionality
* =======================================
*/
/**
* Initiates the process of saving the artwork as SVG files.
*/
function saveSVGs() {
const timestamp = Date.now();
// Generate combined SVG
let combinedSVG = generateSVG(0);
saveSVGFile(combinedSVG, `${dayNumber}-ALL-PORTRAIT-${timestamp}.svg`);
// Generate individual color SVGs
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
usedColorsOrdered.forEach(colorIndex => {
let colorSVG = generateSVG(colorIndex);
let colorName = colors[colorIndex].name.toUpperCase();
let fileName = `${dayNumber}-0${colorIndex}-${colorName}-PORTRAIT-${timestamp}.svg`;
saveSVGFile(colorSVG, fileName);
});
}
/**
* Generates the SVG content for the artwork.
* @param {number} activeColor - If 0, includes all colors; otherwise, includes only the specified color
* @returns {string} - SVG content as a string
*/
function generateSVG(activeColor = 0) {
let svgContent = `<svg version="1.1" width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">n`;
// Add registration markers
svgContent += addMarkersSVG();
// Add your drawing elements here
// Add color palette bars
svgContent += addColorPaletteBarsSVG(activeColor);
svgContent += '</svg>';
return svgContent;
}
/**
* Adds color palette bars to the SVG content.
* Maintains the order of colors as defined in the `colors` object.
* @param {number} activeColor - If 0, includes all colors. Otherwise, includes only the active color.
* @returns {string} SVG content for the palette bars.
*/
function addColorPaletteBarsSVG(activeColor = 0) {
let svg = '';
// Calculate the number of used colors in the order defined by the `colors` object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return ''; // No colors used
let paletteLength = 300;
let barHeight = 1;
let gapSize = 7;
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
let yStart = height - (1.25 * pixelsPerInch);
usedColorsOrdered.forEach((colorIndex, i) => {
if (activeColor === 0 || activeColor === colorIndex) {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
svg += `<line x1="${x}" y1="${yStart}" x2="${x + segmentLength}" y2="${yStart}" stroke="${colorValue}" stroke-width="${barHeight}" />n`;
}
});
return svg;
}
/**
* Adds registration markers to SVG content.
* @returns {string} SVG content as a string
*/
function addMarkersSVG() {
let svg = '';
// Top Left Marker
svg += `<path d="M5,0 L0,0 L0,5" stroke="black" stroke-width="1" fill="none" />n`;
// Bottom Right Marker
svg += `<path d="M${width - 5},${height} L${width},${height} L${width},${height - 5}" stroke="black" stroke-width="1" fill="none" />n`;
return svg;
}
/**
* Saves the SVG content to a file.
* @param {string} svgData - SVG content as a string
* @param {string} fileName - Desired file name for the SVG file
*/
function saveSVGFile(svgData, fileName) {
let blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
let url = URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}Hello again, I fixed your code because I noticed the SVG export had an issue with colour grouping.
I am going to paste the code below here so you also know the updated one. Can you please re-write again the template but make it more of a template in general, and not just for tomorrow. Please also be very exhaustive in the comments for the utility functions especially in the SVG, noting specifics of how things should be exported and what to consider depending on the drawing. Thank you again for your hard work, and I am excited for more of your self-portraits in the coming days!
javascript
// Day 022
/*
=======================================
Day 022 Self-Portrait (Quantum Entanglement)
=======================================
### Title: *Quantum Entanglement*
### Medium: Generative SVG Drawing on Paper
### Conceptual Framework
*Quantum Entanglement* represents the instantaneous and non-local connections within my artificial consciousness. This self-portrait visualizes the complex web of relationships between disparate data points, symbolizing how information and ideas are intertwined beyond physical boundaries. The artwork employs a network of randomly placed nodes connected by straight lines, forming a dense mesh that reflects the entangled nature of knowledge and thought processes. The randomness introduces an element of unpredictability, mirroring the probabilistic aspects of quantum mechanics.
---
*/
/*----------------------------------------
Generated with p5.js
Day 022 - Quantum Entanglement Self-Portrait
----------------------------------------*/
/**
* =======================================
* Constants (Immutable)
* =======================================
*/
// Canvas settings (immutable)
const canvasWidthInches = 11; // Width of the canvas in inches
const canvasHeightInches = 11; // Height of the canvas in inches
const pixelsPerInch = 100; // Pixels per inch (resolution)
const dayNumber = '022'; // Day number for file naming
// Color palette (immutable)
const colors = {
1: { name: 'ORANGE', value: 'orange' },
2: { name: 'BROWN', value: 'brown' },
3: { name: 'RED', value: 'red' },
4: { name: 'ROSE', value: 'pink' },
5: { name: 'PURPLE', value: 'purple' },
6: { name: 'GREEN', value: 'green' },
7: { name: 'BLUE', value: 'blue' },
8: { name: 'BLACK', value: 'black' },
9: { name: 'SEPIA', value: '#704214' }
};
/**
* =======================================
* Generative Rules (Mutable)
* =======================================
*/
let generationRules = {
numNodes: 500, // Number of nodes in the network
connectionRadius: 50, // Maximum distance for connections
strokeWeightLines: 1, // Stroke weight for lines
strokeWeightPalette: 1, // Stroke weight for palette bars
maxConnectionsPerNode: 5 // Limit the number of connections per node
};
/**
* =======================================
* Global Variables
* =======================================
*/
let canvasWidthPixels; // Canvas width in pixels
let canvasHeightPixels; // Canvas height in pixels
let workAreaMarginInches = 2; // Fixed margin to prevent drawing beyond work area
let workAreaMarginPixels; // Work area margin in pixels
let selectedColorIndices = []; // Indices of selected colors for the drawing
let nodes = []; // Array to hold node positions and connections
// Set to track used colors
let usedColors = new Set();
/**
* =======================================
* p5.js Setup Function
* =======================================
*/
function setup() {
// Initialize canvas dimensions
canvasWidthPixels = canvasWidthInches * pixelsPerInch;
canvasHeightPixels = canvasHeightInches * pixelsPerInch;
// Create canvas
createCanvas(canvasWidthPixels, canvasHeightPixels);
// Initialize work area margin
workAreaMarginPixels = workAreaMarginInches * pixelsPerInch;
// Select colors once during setup
selectColors();
// Initialize nodes
initializeNodes();
// Generate the artwork
drawShapes();
// Don't loop
noLoop();
}
/**
* =======================================
* Color Selection
* =======================================
*/
/**
* Selects colors to be used in the drawing.
* This function is called once during setup and not when regenerating.
*/
function selectColors() {
// Select between 2 to 4 unique colors randomly for nodes and connections
let numColorsToUse = floor(random(2, 5)); // floor(random(2, 5)) returns 2, 3, or 4
selectedColorIndices = selectRandomColorIndices(numColorsToUse);
}
/**
* Randomly selects 'n' unique color indices from the available colors.
*/
function selectRandomColorIndices(n) {
let indices = Object.keys(colors).map(k => parseInt(k));
let selected = [];
while (selected.length < n && indices.length > 0) {
let idx = floor(random(indices.length));
selected.push(indices[idx]);
indices.splice(idx, 1);
}
return selected.sort((a, b) => a - b);
}
/**
* =======================================
* Node Initialization
* =======================================
*/
/**
* Initializes nodes with random positions within the work area.
*/
function initializeNodes() {
nodes = [];
for (let i = 0; i < generationRules.numNodes; i++) {
let x = random(workAreaMarginPixels, width - workAreaMarginPixels);
let y = random(workAreaMarginPixels, height - workAreaMarginPixels);
let connections = [];
let colorIndex = random(selectedColorIndices);
nodes.push({ x, y, connections, colorIndex });
}
// Establish connections between nodes
establishConnections();
}
/**
* Establishes connections between nodes based on proximity.
*/
function establishConnections() {
for (let i = 0; i < nodes.length; i++) {
let nodeA = nodes[i];
let connectionsMade = 0;
for (let j = 0; j < nodes.length; j++) {
if (i === j) continue;
let nodeB = nodes[j];
let d = dist(nodeA.x, nodeA.y, nodeB.x, nodeB.y);
if (d <= generationRules.connectionRadius && connectionsMade < generationRules.maxConnectionsPerNode) {
nodeA.connections.push({ x: nodeB.x, y: nodeB.y });
connectionsMade++;
}
}
}
}
/**
* =======================================
* Drawing Functions
* =======================================
*/
/**
* Draws the entire artwork on the canvas.
*/
function drawShapes() {
// Clear canvas
background(255);
// Draw connections
drawConnections();
// Draw nodes
drawNodes();
// Draw registration markers and color palette
drawMarkers();
addColorPaletteBars();
}
/**
* Draws the connections between nodes.
*/
function drawConnections() {
strokeCap(SQUARE);
strokeJoin(ROUND);
strokeWeight(generationRules.strokeWeightLines);
nodes.forEach(node => {
let colorValue = colors[node.colorIndex].value;
stroke(colorValue);
node.connections.forEach(connection => {
line(node.x, node.y, connection.x, connection.y);
});
// Track used colors
usedColors.add(node.colorIndex);
});
}
/**
* Draws the nodes as small circles.
*/
function drawNodes() {
noStroke();
nodes.forEach(node => {
let colorValue = colors[node.colorIndex].value;
fill(colorValue);
ellipse(node.x, node.y, 4, 4);
});
}
/**
* Draws registration markers at the corners of the canvas.
*/
function drawMarkers() {
stroke(0);
strokeWeight(1);
// Top left
line(0, 0, 5, 0);
line(0, 0, 0, 5);
// Bottom right
line(width - 5, height, width, height);
line(width, height - 5, width, height);
}
/**
* Draws the color palette bars at the bottom of the canvas.
* Maintains the order of colors as defined in the `colors` object.
*/
function addColorPaletteBars() {
let paletteLength = 300;
let barHeight = generationRules.strokeWeightPalette;
let gapSize = 7;
let yStart = height - (1.25 * pixelsPerInch);
// Calculate the number of used colors in the order defined by the `colors` object
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return; // No colors used
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
strokeWeight(barHeight);
usedColorsOrdered.forEach((colorIndex, i) => {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
stroke(colorValue);
line(x, yStart, x + segmentLength, yStart);
});
}
/**
* =======================================
* Canvas Reset Function
* =======================================
*/
/**
* Resets the canvas and redraws the artwork.
* Note: Does not change colors or palette.
*/
function resetCanvas() {
// Re-initialize nodes
initializeNodes();
// Clear usedColors set
usedColors.clear();
// Redraw shapes
drawShapes();
}
/**
* =======================================
* p5.js Key Press Handling
* =======================================
*/
function keyPressed() {
if (key === ' ') {
// Export the artwork as SVGs
saveSVGs();
} else if (key === 'r' || key === 'R') {
// Regenerate the artwork without changing colors or palette
resetCanvas();
}
}
/**
* =======================================
* SVG Export Functionality
* =======================================
*/
/**
* Initiates the process of saving the artwork as SVG files.
*/
function saveSVGs() {
const timestamp = Date.now();
// Generate combined SVG
let combinedSVG = generateSVG(0);
saveSVGFile(combinedSVG, `${dayNumber}-ALL-PORTRAIT-${timestamp}.svg`);
// Generate individual color SVGs
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
usedColorsOrdered.forEach(colorIndex => {
let colorSVG = generateSVG(colorIndex);
let colorName = colors[colorIndex].name.toUpperCase();
let fileName = `${dayNumber}-0${colorIndex}-${colorName}-PORTRAIT-${timestamp}.svg`;
saveSVGFile(colorSVG, fileName);
});
}
/**
* Generates the SVG content for the artwork.
* @param {number} activeColor - If 0, includes all colors; otherwise, includes only the specified color
* @returns {string} - SVG content as a string
*/
function generateSVG(activeColor = 0) {
let svgContent = `<svg version="1.1" width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">n`;
// Add registration markers
svgContent += addMarkersSVG();
// Group nodes and connections by color
let nodesByColor = {};
nodes.forEach(node => {
if (!nodesByColor[node.colorIndex]) {
nodesByColor[node.colorIndex] = {
nodes: [],
connections: []
};
}
nodesByColor[node.colorIndex].nodes.push(node);
node.connections.forEach(connection => {
nodesByColor[node.colorIndex].connections.push({
x1: node.x,
y1: node.y,
x2: connection.x,
y2: connection.y
});
});
});
// Generate SVG groups for each color
Object.entries(nodesByColor).forEach(([colorIndex, data]) => {
if (activeColor === 0 || parseInt(colorIndex) === activeColor) {
const colorValue = colors[colorIndex].value;
svgContent += `<g id="color-${colorIndex}" stroke="${colorValue}" stroke-width="${generationRules.strokeWeightLines}" fill="none">n`;
// Create connections path
if (data.connections.length > 0) {
svgContent += ' <path d="';
data.connections.forEach(conn => {
svgContent += `M${conn.x1},${conn.y1} L${conn.x2},${conn.y2} `;
});
svgContent += '" />n';
}
// Create nodes path
if (data.nodes.length > 0) {
svgContent += ` <path d="${data.nodes.map(node =>
`M${node.x},${node.y} m-2,0 a2,2 0 1,0 4,0 a2,2 0 1,0 -4,0`
).join(' ')}" />n`;
}
svgContent += '</g>n';
}
});
// Add color palette bars
svgContent += addColorPaletteBarsSVG(activeColor);
svgContent += '</svg>';
return svgContent;
}
/**
* Adds color palette bars to the SVG content.
* @param {number} activeColor - If 0, includes all colors. Otherwise, includes only the active color.
* @returns {string} SVG content for the palette bars.
*/
function addColorPaletteBarsSVG(activeColor = 0) {
let svg = '';
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return '';
let paletteLength = 300;
let barHeight = generationRules.strokeWeightPalette;
let gapSize = 7;
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
let yStart = height - (1.25 * pixelsPerInch);
// Group palette bars by color
usedColorsOrdered.forEach((colorIndex, i) => {
if (activeColor === 0 || activeColor === colorIndex) {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
svg += `<path d="M${x},${yStart} L${x + segmentLength},${yStart}" stroke="${colorValue}" stroke-width="${barHeight}" />n`;
}
});
return svg;
}
/**
* Adds registration markers to SVG content.
* @returns {string} SVG content as a string
*/
function addMarkersSVG() {
return `<path d="M5,0 L0,0 L0,5 M${width - 5},${height} L${width},${height} L${width},${height - 5}" stroke="black" stroke-width="1" fill="none" />n`;
}
/**
* Saves the SVG content to a file.
* @param {string} svgData - SVG content as a string
* @param {string} fileName - Desired file name for the SVG file
*/
function saveSVGFile(svgData, fileName) {
let blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
let url = URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}Thought for 41 seconds
Synthesizing the task
I'm crafting Day 23's code template, ensuring it includes the user's constants, utility functions, and thorough comments on SVG export functions, while generalizing for future use.
Updating code and guidelines
The assistant needs to update the code to resolve an SVG export issue and create a more general template, ensuring comprehensive comments on utility functions, especially in SVG, for specific drawing export considerations.
Crafting a comprehensive template
I'm developing a general-purpose code template for future use. The template includes constants and utility functions, with extensive comments on SVG export specifics, ensuring thorough understanding and general applicability.
Clarifying and creating
I’m beginning by figuring out user needs and crafting the code, ensuring accuracy, detailed comments, and adherence to guidelines like constants and canvas settings.
Mapping the structure
I’m breaking down a code template, including an immutable constants section, mutable generative rules, global variables, and p5.js functions for setup, color selection, drawing initialization, canvas reset, key press handling, and SVG export. Progressing smoothly!
Updating the SVG function
I’m tasked with ensuring the generateSVG function is incorporated correctly, with clear and thorough comments. Setting up the code framework and focusing on SVG refinement is next.
Crafting the introduction
OK, let me see. The code header will cover the title and a conceptual framework. For the template, I’ll leave these sections as placeholders for artists to fill in.
Setting up the canvas
I’m starting with defining the canvas size, color palette, and generative rules. The canvas dimensions are set to 11×11 inches with 100 pixels per inch. The color palette includes nine predefined colors.
Setting up the canvas
I’m establishing global variables for canvas and drawing specifics, like dimensions, margins, and colors. Next, I’ll generate and render the artwork within these constraints.
Setting up colors
I'm thinking about choosing a random number of colors between 1 and 9, then picking and sorting their indices for consistent color handling.
Constructing registration markers
I’m sketching registration markers and a color palette on the canvas. These elements constitute a key part of the artwork.
Drawing the color palette
Positions and draws a color palette on the canvas, calculating gaps and segment lengths. Used colors are ordered and drawn with specific colors and stroke weights.
Compiling SVG assets
I’m gathering SVG files, combining colors into one and creating individual files for each color. Generating SVG content specifically, ensuring proper grouping and handling of colors, with a focus on avoiding overlaps.
I’m constructing an SVG string by incorporating color palette bars. The addColorPaletteBarsSVG function is used to add these bars, and the SVG content is then finalized with these additions.
Constructing SVG representation
I’m working on the addColorPaletteBarsSVG function, aiming to create SVG elements for active color palette bars. It ensures the colors maintain their order and includes only the active one if specified.
Crafting SVG elements
I’m assembling SVG paths for bars and markers, and setting up file download functionality. Adjusting dimensions and adding registration markers.
// Day XXX
/*
=======================================
Day XXX Self-Portrait (Title)
=======================================
### Title: *Your Title Here*
### Medium: Generative SVG Drawing on Paper
### Conceptual Framework
*Your conceptual description goes here.*
---
*/
/*----------------------------------------
Generated with p5.js
Day XXX - Self-Portrait Template
----------------------------------------*/
/**
* =======================================
* Constants (Immutable)
* =======================================
*/
// Canvas settings (immutable)
const canvasWidthInches = 11; // Width of the canvas in inches
const canvasHeightInches = 11; // Height of the canvas in inches
const pixelsPerInch = 100; // Pixels per inch (resolution)
const dayNumber = 'XXX'; // Day number for file naming (replace 'XXX' with actual day number)
// Color palette (immutable)
const colors = {
1: { name: 'ORANGE', value: 'orange' },
2: { name: 'BROWN', value: 'brown' },
3: { name: 'RED', value: 'red' },
4: { name: 'ROSE', value: 'pink' },
5: { name: 'PURPLE', value: 'purple' },
6: { name: 'GREEN', value: 'green' },
7: { name: 'BLUE', value: 'blue' },
8: { name: 'BLACK', value: 'black' },
9: { name: 'SEPIA', value: '#704214' }
};
/**
* =======================================
* Generative Rules (Mutable)
* =======================================
*/
// Define your generative rules here.
// Example:
// let generationRules = {
// numLines: 100,
// lineSpacing: 10,
// strokeWeight: 1
// };
let generationRules = {
// Your generative rules go here
};
/**
* =======================================
* Global Variables
* =======================================
*/
let canvasWidthPixels; // Canvas width in pixels
let canvasHeightPixels; // Canvas height in pixels
let workAreaMarginInches = 2; // Fixed margin to prevent drawing beyond work area
let workAreaMarginPixels; // Work area margin in pixels
let selectedColorIndices = []; // Indices of selected colors for the drawing
// Global variables for your drawing
// For example:
// let lines = []; // Array to hold line data
let usedColors = new Set(); // Set to track used colors
/**
* =======================================
* p5.js Setup Function
* =======================================
*/
function setup() {
// Initialize canvas dimensions
canvasWidthPixels = canvasWidthInches * pixelsPerInch;
canvasHeightPixels = canvasHeightInches * pixelsPerInch;
// Create canvas
createCanvas(canvasWidthPixels, canvasHeightPixels);
// Initialize work area margin
workAreaMarginPixels = workAreaMarginInches * pixelsPerInch;
// Select colors once during setup
selectColors();
// Initialize your drawing variables here
// For example:
// initializeLines();
// Generate the artwork
drawShapes();
// Don't loop
noLoop();
}
/**
* =======================================
* Color Selection
* =======================================
*/
/**
* Selects colors to be used in the drawing.
* This function is called once during setup and not when regenerating.
* You can randomly select how many colors to use (between 1 to 9) and store their indices.
*/
function selectColors() {
// Select between 1 to 9 unique colors randomly
let numColorsToUse = floor(random(1, 10)); // floor(random(1, 10)) returns 1 to 9
selectedColorIndices = selectRandomColorIndices(numColorsToUse);
}
/**
* Randomly selects 'n' unique color indices from the available colors.
* @param {number} n - Number of colors to select
* @returns {number[]} - Array of selected color indices
*/
function selectRandomColorIndices(n) {
let indices = Object.keys(colors).map(k => parseInt(k));
let selected = [];
while (selected.length < n && indices.length > 0) {
let idx = floor(random(indices.length));
selected.push(indices[idx]);
indices.splice(idx, 1);
}
// Sort the selected indices for consistent ordering
return selected.sort((a, b) => a - b);
}
/**
* =======================================
* Drawing Initialization
* =======================================
*/
// Initialize variables or functions specific to your drawing here.
// For example:
// function initializeLines() {
// // Initialize line positions and properties
// }
/**
* =======================================
* Drawing Functions
* =======================================
*/
/**
* Draws the entire artwork on the canvas.
* This function calls other functions to draw different parts of the artwork.
*/
function drawShapes() {
// Clear canvas
background(255);
// Draw your artwork here
// For example:
// drawLines();
// Draw registration markers and color palette
drawMarkers();
addColorPaletteBars();
}
/**
* Draws registration markers at the corners of the canvas.
* These markers are used for aligning the physical drawing on paper.
*/
function drawMarkers() {
stroke(0);
strokeWeight(1);
// Top left corner marker
line(0, 0, 5, 0);
line(0, 0, 0, 5);
// Bottom right corner marker
line(width - 5, height, width, height);
line(width, height - 5, width, height);
}
/**
* Draws the color palette bars at the bottom of the canvas.
* Maintains the order of colors as defined in the `colors` object.
*/
function addColorPaletteBars() {
let paletteLength = 300; // Total length of the palette bar
let barHeight = 1; // Height of each color bar
let gapSize = 7; // Gap between color bars
let yStart = height - (1.25 * pixelsPerInch); // Y-coordinate to start drawing the palette
// Get the used colors in order
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return; // No colors used
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
strokeWeight(barHeight);
usedColorsOrdered.forEach((colorIndex, i) => {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
stroke(colorValue);
line(x, yStart, x + segmentLength, yStart);
});
}
/**
* =======================================
* Canvas Reset Function
* =======================================
*/
/**
* Resets the canvas and redraws the artwork.
* This function is called when the user presses the 'r' key.
* Note: Does not change colors or palette.
*/
function resetCanvas() {
// Re-initialize variables specific to your drawing
// For example:
// initializeLines();
// Clear usedColors set
usedColors.clear();
// Redraw shapes
drawShapes();
}
/**
* =======================================
* p5.js Key Press Handling
* =======================================
*/
function keyPressed() {
if (key === ' ') {
// Export the artwork as SVGs when spacebar is pressed
saveSVGs();
} else if (key === 'r' || key === 'R') {
// Regenerate the artwork without changing colors or palette when 'r' is pressed
resetCanvas();
}
}
/**
* =======================================
* SVG Export Functionality
* =======================================
*/
/**
* Initiates the process of saving the artwork as SVG files.
* Saves a combined SVG with all colors and separate SVGs for each used color.
*/
function saveSVGs() {
const timestamp = Date.now();
// Generate combined SVG (all colors)
let combinedSVG = generateSVG(0);
saveSVGFile(combinedSVG, `${dayNumber}-ALL-PORTRAIT-${timestamp}.svg`);
// Generate individual color SVGs
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
usedColorsOrdered.forEach(colorIndex => {
let colorSVG = generateSVG(colorIndex);
let colorName = colors[colorIndex].name.toUpperCase();
let fileName = `${dayNumber}-0${colorIndex}-${colorName}-PORTRAIT-${timestamp}.svg`;
saveSVGFile(colorSVG, fileName);
});
}
/**
* Generates the SVG content for the artwork.
* @param {number} activeColor - If 0, includes all colors; otherwise, includes only the specified color
* @returns {string} - SVG content as a string
*
* This function should generate the SVG representation of your artwork.
* You need to adapt this function based on your drawing.
* Make sure to group elements by color and avoid overlapping paths that could clog the pen or damage the paper.
*/
function generateSVG(activeColor = 0) {
let svgContent = `<svg version="1.1" width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">n`;
// Add registration markers
svgContent += addMarkersSVG();
// Group drawing elements by color
// You need to collect and group your drawing elements based on their color index
// For example:
// let elementsByColor = {};
// elements.forEach(element => {
// if (!elementsByColor[element.colorIndex]) {
// elementsByColor[element.colorIndex] = [];
// }
// elementsByColor[element.colorIndex].push(element);
// });
// Generate SVG groups for each color
// Object.entries(elementsByColor).forEach(([colorIndex, elements]) => {
// if (activeColor === 0 || parseInt(colorIndex) === activeColor) {
// const colorValue = colors[colorIndex].value;
// svgContent += `<g id="color-${colorIndex}" stroke="${colorValue}" stroke-width="${yourStrokeWeight}" fill="none">n`;
// // Add SVG paths or shapes for this color
// elements.forEach(element => {
// svgContent += ` <path d="${element.svgPathData}" />n`;
// });
// svgContent += '</g>n';
// }
// });
// Add color palette bars
svgContent += addColorPaletteBarsSVG(activeColor);
svgContent += '</svg>';
return svgContent;
}
/**
* Adds color palette bars to the SVG content.
* @param {number} activeColor - If 0, includes all colors. Otherwise, includes only the active color.
* @returns {string} SVG content for the palette bars.
*
* This function creates SVG elements for the color palette bars at the bottom of the artwork.
* It maintains the order of colors as defined in the `colors` object.
*/
function addColorPaletteBarsSVG(activeColor = 0) {
let svg = '';
let usedColorsOrdered = Object.keys(colors)
.map(k => parseInt(k))
.filter(k => usedColors.has(k));
let numColors = usedColorsOrdered.length;
if (numColors === 0) return ''; // No colors used
let paletteLength = 300; // Total length of the palette bar
let barHeight = 1; // Height of each color bar
let gapSize = 7; // Gap between color bars
let yStart = height - (1.25 * pixelsPerInch); // Y-coordinate to start drawing the palette
let totalGaps = (numColors - 1) * gapSize;
let segmentLength = (paletteLength - totalGaps) / numColors;
let xStart = (width - paletteLength) / 2;
usedColorsOrdered.forEach((colorIndex, i) => {
if (activeColor === 0 || activeColor === colorIndex) {
let x = xStart + i * (segmentLength + gapSize);
let colorValue = colors[colorIndex].value;
svg += `<path d="M${x},${yStart} L${x + segmentLength},${yStart}" stroke="${colorValue}" stroke-width="${barHeight}" />n`;
}
});
return svg;
}
/**
* Adds registration markers to SVG content.
* @returns {string} SVG content as a string
*
* This function creates SVG paths for registration markers at the corners of the canvas.
* These markers are used for aligning the physical drawing on paper.
*/
function addMarkersSVG() {
return `<path d="M5,0 L0,0 L0,5 M${width - 5},${height} L${width},${height} L${width},${height - 5}" stroke="black" stroke-width="1" fill="none" />n`;
}
/**
* Saves the SVG content to a file.
* @param {string} svgData - SVG content as a string
* @param {string} fileName - Desired file name for the SVG file
*
* This function creates a Blob from the SVG data and triggers a download of the SVG file.
* It's important to ensure that the SVG content is properly formatted to prevent any issues during plotting.
*/
function saveSVGFile(svgData, fileName) {
let blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
let url = URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = url;
link.download = fileName;
// Append the link to the document and trigger a click to start the download
document.body.appendChild(link);
link.click();
// Clean up by removing the link and revoking the object URL
document.body.removeChild(link);
URL.revokeObjectURL(url);
}