xxxxxxxxxx
333
let W = 650;
let H = 450;
let timeSlider; // Slider to control day and night transition
let shiftButton; // Button to shift trees and mountain layout
let saveButton; // Button to save the current drawing as an SVG
let mountainsButton; // Button to toggle mountains on/off
let addTreesButton; // Button to toggle trees on/off
let showMountains = false; // Boolean to track if mountains are visible
let showTrees = false; // Boolean to track if trees are visible
let treeShapeSeed = 0; // Seed value to control tree randomness
let mountainShapeSeed = 0; // Seed value to control mountain randomness
let sunX; // X position of the sun (controlled by slider)
let sunY; // Y position of the sun (controlled by slider)
let moonX; // X position of the moon (controlled by slider)
let moonY; // Y position of the moon (controlled by slider)
let mountainLayers = 2; // Number of mountain layers to draw
let treeCount = 8; // Number of background trees to draw
const Y_AXIS = 1; // Constant for vertical gradient drawing
let groundLevel = H - 50; // The ground level (50 pixels above the canvas bottom)
function setup() {
createCanvas(W, H);
background(135, 206, 235);
// Creates the time slider that controls day-night transitions
timeSlider = createSlider(0, 100, 50);
timeSlider.position(200, 460);
timeSlider.size(250);
timeSlider.input(updateCanvasWithSlider); // Calls function to update canvas when slider moves
// Creates button to toggle mountains on/off
mountainsButton = createButton('Mountains');
mountainsButton.position(100, 460);
mountainsButton.mousePressed(() => {
toggleMountains(); // Toggle the visibility of mountains
redrawCanvas(); // Redraws the canvas after the toggle
});
// Creates a button to toggle trees on/off
addTreesButton = createButton('Add Trees');
addTreesButton.position(10, 460);
addTreesButton.mousePressed(() => {
toggleTrees(); // Toggles the visibility of trees
redrawCanvas(); // Redraws the canvas after the toggle
});
// Creates a button to shift trees and mountain layout (randomize positions and shapes)
shiftButton = createButton('Shift');
shiftButton.position(490, 460);
shiftButton.mousePressed(() => {
changeTreeCountAndShape(); // Randomize the trees and mountain layout
redrawCanvas(); // Redraws the canvas after the layout shift
});
// Creates a button to save the canvas as an SVG file
saveButton = createButton('Save as SVG');
saveButton.position(550, 460);
saveButton.mousePressed(saveCanvasAsSVG); // Save the canvas as an SVG when clicked
noLoop(); // Prevents continuous looping of the draw function, manual updates only
redrawCanvas(); // Perform the initial canvas draw
}
// This is called whenever the slider is moved; to update the canvas to reflect the new slider value
function updateCanvasWithSlider() {
redrawCanvas(); // Redraws the canvas to update the time of day
}
// Saves the current canvas as an SVG
function saveCanvasAsSVG() {
let svgCanvas = createGraphics(W, H, SVG); // Creates a new graphics context for SVG rendering
redrawCanvas(svgCanvas); // Redraws everything onto the SVG canvas
save(svgCanvas, "myLandscape.svg"); // Saves the SVG with a custom filename
svgCanvas.remove(); // Removes the SVG canvas once it's saved
}
// Main function to redraw the entire canvas (or an SVG) when something changes
function redrawCanvas(renderer = this) {
// Clears the canvas or the SVG renderer
if (renderer === this) {
background(135, 206, 235); // Light blue background for daytime sky
} else {
renderer.background(135, 206, 235); // Same background for SVG output
}
// Get the slider value to adjust the time of day (0 to 100)
let timeValue = timeSlider.value();
// Updates the background gradient and sun/moon positions based on the time of day
updateBackground(timeValue, renderer);
updateSunAndMoon(timeValue, renderer);
// Checks if the mountains should be drawn, and draws them if necessary
if (showMountains) {
randomSeed(mountainShapeSeed); // Ensures randomness is consistent with each draw
drawLayeredMountains(renderer); // Draws the mountain layers
}
// Draws the simple green ground at the bottom of the canvas
drawSimpleGreenGround(renderer);
// Checks if trees should be drawn, and draws them if necessary
if (showTrees) {
drawBackgroundTrees(renderer); // Draws background trees
}
// Draws the main tree in the middle of the canvas
if (renderer === this) {
// Draws the main tree on the canvas itself
push();
translate(W / 2, H - 50); // Positions the tree in the center at the ground level
randomSeed(treeShapeSeed); // Controls randomness for consistent tree shapes
drawTree(0, renderer); // Draws the tree
pop();
// Calculates and draw the shadow of the tree based on the sun or moon position
let shadowDirection = sunX ? sunX : moonX; // Uses the sunX if it's daytime, otherwise use moonX
let shadowAngle = map(shadowDirection, 0, width, -PI / 4, PI / 4); // Adjusts shadow direction
push();
translate(W / 2, H - 50); // Translates to the same position as the tree
rotate(shadowAngle); // Rotates the shadow based on the angle
scale(0.5, -1.5); // Flips and scales down the shadow so it doens't pop outside the green grass and shows up in the sky
drawTreeShadow(0, renderer); // Draws the tree shadow
pop();
} else {
// Draws the main tree in an SVG without shadow because it doesn't look nice
renderer.push();
renderer.translate(W / 2, H - 50); // Positions for SVG
randomSeed(treeShapeSeed);
drawTree(0, renderer); // Draws the tree in SVG mode
renderer.pop();
}
}
// Toggles mountains visibility on or off
function toggleMountains() {
showMountains = !showMountains; // Flips the boolean to show/hide mountains
redrawCanvas(); // Redraws the canvas to reflect the change
}
// Toggles trees visibility on or off
function toggleTrees() {
showTrees = !showTrees; // Flips the boolean to show/hide trees
redrawCanvas(); // Redraws the canvas to reflect the change
}
// Draws the tree shadow based on depth, used to create a shadow effect for the main tree
function drawTreeShadow(depth, renderer = this) {
renderer.stroke(0, 0, 0, 80); // Semi-transparent shadow
renderer.strokeWeight(5 - depth); // Adjust shadow thickness based on depth
branch(depth, renderer); // Recursively draw branches as shadows
}
// Randomizes tree and mountain layouts by changing seeds and tree count
function changeTreeCountAndShape() {
treeShapeSeed = millis(); // Updates the tree shape randomness seed
mountainShapeSeed = millis(); // Updates the mountain shape randomness seed
treeCount = random([8, 16]); // Randomly selects between 8 or 16 trees
mountainLayers = random([1, 2]); // Randomly select 1 or 2 mountain layers
redrawCanvas(); // Redraw the canvas with the new layout
}
// Draws multiple layers of mountains based on the chosen number of layers
function drawLayeredMountains(renderer = this) {
if (mountainLayers === 2) {
let layers = [
{ oy: 200, epsilon: 0.02, startColor: '#888888', endColor: '#666666', reductionScaler: 100 },
{ oy: 270, epsilon: 0.015, startColor: '#777777', endColor: '#555555', reductionScaler: 80 }
];
// Draw two layers of mountains, with different heights and colors
layers.forEach(layer => {
drawMountainLayer(layer.oy, layer.epsilon, layer.startColor, layer.endColor, layer.reductionScaler, renderer);
});
} else if (mountainLayers === 1) {
let layers = [
{ oy: 250, epsilon: 0.02, startColor: '#777777', endColor: '#555555', reductionScaler: 80 }
];
// Draw a single layer of mountains
layers.forEach(layer => {
drawMountainLayer(layer.oy, layer.epsilon, layer.startColor, layer.endColor, layer.reductionScaler, renderer);
});
}
}
// Draws a single layer of mountains
function drawMountainLayer(oy, epsilon, startColor, endColor, reductionScaler, renderer = this) {
let col1 = color(startColor); // Starts the color for the gradient
let col2 = color(endColor); // Ends the color for the gradient
renderer.noStroke();
for (let x = 0; x < width; x++) {
// Generates random mountain height based on noise and epsilon value
let y = oy + noise(x * epsilon) * reductionScaler;
let col = lerpColor(col1, col2, map(y, oy, oy + reductionScaler, 0, 1)); // Creates color gradient
renderer.fill(col); // Fills mountain with the gradient color
renderer.rect(x, y, 1, height - y); // Draws vertical rectangles to represent mountain peaks
}
}
// Draws trees in the background based on the current tree count
function drawBackgroundTrees(renderer = this) {
let positions = []; // Array to store the X positions of the trees
for (let i = 0; i < treeCount; i++) {
let x = (i + 1) * W / (treeCount + 1); // Spaces the trees evenly across the canvas
positions.push(x);
}
// Draws each tree at the calculated X positions
positions.forEach(posX => {
renderer.push();
renderer.translate(posX, groundLevel); // Positions trees on the ground
renderer.scale(0.3); // Scales down the background trees
randomSeed(treeShapeSeed + posX); // Uses a random seed to vary tree shapes
drawTree(0, renderer); // Draws each tree
renderer.pop();
});
}
// Updates the background gradient based on the time of day (slider value)
function updateBackground(timeValue, renderer = this) {
let sunriseColor = color(255, 102, 51);
let sunsetColor = color(30, 144, 255);
let nightColor = color(25, 25, 112);
// Lerp between sunrise and sunset based on time slider
let transitionColor = lerpColor(sunriseColor, sunsetColor, timeValue / 100);
// If the time is past halfway (i.e., after sunset), lerp to nighttime
if (timeValue > 50) {
transitionColor = lerpColor(sunsetColor, nightColor, (timeValue - 50) / 50);
c2 = lerpColor(color(255, 127, 80), nightColor, (timeValue - 50) / 50);
} else {
c2 = color(255, 127, 80); // Defaults to an orange hue for sunrise/sunset
}
setGradient(0, 0, W, H, transitionColor, c2, Y_AXIS, renderer); // Apply gradient to the background
}
// Updates the position of the sun and moon based on the time of day (slider value)
function updateSunAndMoon(timeValue, renderer = this) {
// Update sun position during daytime
if (timeValue <= 50) {
sunX = map(timeValue, 0, 50, -50, width + 50); // Sun moves across the sky
sunY = height * 0.8 - sin(map(sunX, -50, width + 50, 0, PI)) * height * 0.5;
renderer.noStroke();
renderer.fill(255, 200, 0); // Yellow sun
renderer.ellipse(sunX, sunY, 70, 70); // Draws the sun as a large circle
}
// Updates moon position during nighttime
if (timeValue > 50) {
moonX = map(timeValue, 50, 100, -50, width + 50); // Moon moves across the sky
moonY = height * 0.8 - sin(map(moonX, -50, width + 50, 0, PI)) * height * 0.5;
renderer.noStroke();
renderer.fill(200); // Light gray moon
renderer.ellipse(moonX, moonY, 60, 60); // Draw the moon as a smaller circle
}
}
// Creates a vertical gradient for the background sky
function setGradient(x, y, w, h, c1, c2, axis, renderer = this) {
renderer.noFill(); // Ensures no fill is applied
if (axis === Y_AXIS) {
// Loops through each horizontal line and apply gradient colors
for (let i = y; i <= y + h; i++) {
let inter = map(i, y, y + h, 0, 1); // Interpolation factor
let c = lerpColor(c1, c2, inter); // Lerp between the two colors
renderer.stroke(c); // Sets the stroke to the interpolated color
renderer.line(x, i, x + w, i); // Draws the gradient line by line
}
}
}
// Draws the green ground at the bottom of the canvas
function drawSimpleGreenGround(renderer = this) {
renderer.fill(34, 139, 34); // Set fill to green
renderer.rect(0, H - 50, W, 50); // Draws a green rectangle as the ground
}
// Draws a main tree in the center of the canvas
function drawTree(depth, renderer = this) {
renderer.stroke(139, 69, 19); // Sets the stroke to brown for the tree trunk
renderer.strokeWeight(3 - depth); // Adjusts the stroke weight for consistency
branch(depth, renderer); // Draws the branches recursively
}
// Draws tree branches recursively
function branch(depth, renderer = this) {
if (depth < 10) { // Limits the depth of recursion
renderer.line(0, 0, 0, -H / 15); // Draws a vertical line for the branch
renderer.translate(0, -H / 15); // Moves up along the branch
renderer.rotate(random(-0.05, 0.05)); // Slightly randomize branch angle
if (random(1.0) < 0.7) {
// Draws two branches at slightly different angles
renderer.rotate(0.3); // Rotates clockwise
renderer.scale(0.8); // Scales down the branch
renderer.push(); // Saves the current state
branch(depth + 1, renderer); // Recursively draws the next branch
renderer.pop(); // Restores the previous state
renderer.rotate(-0.6); // Rotates counterclockwise for the second branch
renderer.push();
branch(depth + 1, renderer); // Recursively draws the next branch
renderer.pop();
} else {
branch(depth, renderer); // Continues the drawing of the same branch
}
} else {
drawLeaf(renderer); // Once depth limit is reached, it draws leaves
}
}
// Draws leaves on the branches
function drawLeaf(renderer = this) {
renderer.fill(34, 139, 34); // Set fill to green for leaves
renderer.noStroke();
for (let i = 0; i < random(3, 6); i++) {
renderer.ellipse(random(-10, 10), random(-10, 10), 12, 24); // Draws leaves as ellipses
}
}