93
votes
\$\begingroup\$

Your challenge: Make a Christmas tree. The size must be choosable by some input method, but doesn't have to be directly related to any part of the tree; however, larger inputs should produce a larger tree.

How can you make it? You can make the tree any way you like, other than by printing the unicode character for the tree, such as ouputting an image, ascii art, with other aspects, etc. Whatever you do, remember that this is a , so be creative.

The answer with the most upvotes by the end of December wins, but I'll accept another if it gets higher

\$\endgroup\$
10
  • 1
    \$\begingroup\$ Nice question ;) \$\endgroup\$
    – Timtech
    Dec 12, 2013 at 22:34
  • 1
    \$\begingroup\$ A pity decorations weren't mandatory! \$\endgroup\$
    – o0'.
    Dec 13, 2013 at 17:10
  • \$\begingroup\$ @Lohoris I think it is good that they aren't mandatory. This way, people can do what they want, and those brilliant fractal trees are legitimate answers. \$\endgroup\$
    – Justin
    Dec 13, 2013 at 17:12
  • \$\begingroup\$ @Quincunx you could add decorations even to fractal trees... \$\endgroup\$
    – o0'.
    Dec 13, 2013 at 17:13
  • \$\begingroup\$ @Lohoris yes, but they are plenty as is. The author of those trees could add decorations if they'd like to, but I'd rather not require it. \$\endgroup\$
    – Justin
    Dec 13, 2013 at 17:14

29 Answers 29

90
votes
\$\begingroup\$

Python

A fractal Christmas tree using the turtle package:

enter image description here

n = input()*1.

from turtle import *
speed("fastest")
left(90)
forward(3*n)
color("orange", "yellow")
begin_fill()
left(126)
for i in range(5):
    forward(n/5)
    right(144)
    forward(n/5)
    left(72)
end_fill()
right(126)

color("dark green")
backward(n*4.8)
def tree(d, s):
    if d <= 0: return
    forward(s)
    tree(d-1, s*.8)
    right(120)
    tree(d-3, s*.5)
    right(120)
    tree(d-3, s*.5)
    right(120)
    backward(s)
tree(15, n)
backward(n/2)

import time
time.sleep(60)

n is the size parameter, the shown tree is for n=50. Takes a minute or two to draw.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ This looks lovely :) \$\endgroup\$ Dec 18, 2013 at 12:00
76
votes
\$\begingroup\$

JavaScript

enter image description here

Show the animated tree online.

var size = 400;

var canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
document.body.appendChild(canvas);

var ctx = canvas.getContext('2d');

var p3d = [];

var p = [Math.random(), Math.random(), Math.random(), 0];

for (var i = 0; i < 100000; i++) {
    p3d.push([p[0],p[1],p[2],p[3]]);
    var t = Math.random();
    if (t<0.4) {
        _y = 0.4 * p[1];
        _x = 0.1 * p[0];
        _z = 0.6 * p[2];
        var r = Math.floor(3*t/0.4)/3.0;
        var rc = Math.cos(Math.PI*2.0*r);
        var rs = Math.sin(Math.PI*2.0*r);
        p[1] = _x+0.1*r+0.5*_y*_y;
        p[0] = _y*rc+_z*rs;
        p[2] = _z*rc-_y*rs;
        p[3] = 0.2*t + 0.8*p[3];
    } else {
        p[1] = 0.2 + 0.8*p[1];
        p[0] = 0.8 * p[0];
        p[2] = 0.8 * p[2];
        p[3] = 0.2 + 0.8*p[3];
    }
}

var rot = 0.0;

function render() {
    rot = rot + 0.1;
    var rc = Math.cos(rot);
    var rs = Math.sin(rot);

    ctx.strokeStyle='#FF7F00';
    ctx.lineWidth=2;
    ctx.beginPath();
    ctx.moveTo(size/2,size/8);
    ctx.lineTo(size/2,size*15/16);
    ctx.stroke();

    var img = ctx.getImageData(0, 0, size, size);
    for (var j = 0; j < size*size; j++) {
        img.data[4*j+0] = 0.5*img.data[4*j+0];
        img.data[4*j+1] = 0.5*img.data[4*j+1];
        img.data[4*j+2] = 0.5*img.data[4*j+2];
        img.data[4*j+3] = 255;
    }
    
    for (var i = 0; i < p3d.length; i++) {
        var px = p3d[i][0];
        var py = 0.5 - p3d[i][1];
        var pz = p3d[i][2];
        var col = Math.floor(128.0*p3d[i][3]);
        
        var _x = rc*px + rs*pz;
        var _z = rc*pz - rs*px;

        var z = 3.0 * size / (_z + 4.0);
        var x = size / 2 + Math.round(_x * z);        
        var y = size / 2 + Math.round(py * z);

        if(x>=0&&y>=0&&x<size&&y<size) {
            img.data[4 * (y * size + x) + 0] = col;
            img.data[4 * (y * size + x) + 1] = 128+col;
            img.data[4 * (y * size + x) + 2] = col;
            img.data[4 * (y * size + x) + 3] = 255;
        }
    }

    ctx.putImageData(img, 0, 0);
}

setInterval(render, 1000 / 30);
\$\endgroup\$
5
  • \$\begingroup\$ I like it, but you should add a trunk. \$\endgroup\$
    – Fels
    Dec 13, 2013 at 9:42
  • 1
    \$\begingroup\$ @Fels added trunk \$\endgroup\$
    – Howard
    Dec 13, 2013 at 10:16
  • 2
    \$\begingroup\$ This is very impressive, but what's up with those green pixels circling the tree? I didn't realize that Santa had little green elves who can fly. (they do not detract from the beauty of your answer, but I wonder where they came from) \$\endgroup\$
    – Justin
    Dec 15, 2013 at 6:24
  • \$\begingroup\$ @Quincunx: They seem to be initial sample points from before the iteration converges. Skipping the first 100 points gets rid of them. \$\endgroup\$ Dec 15, 2013 at 14:25
  • 2
    \$\begingroup\$ Really cool, but it doesn't quite look like a Christmas tree. It looks more like a stick with a few fern leaves attached to it. Also, could you make the view slowly scroll along the y axis, so the 3D-ness is more apparent? \$\endgroup\$ Dec 20, 2013 at 17:04
75
votes
\$\begingroup\$

Yet another Mathematica / Wolfram Language tree based on Vitaliy's answer:

PD = .5;
s[t_, f_] := t^.6 - f
dt[cl_, ps_, sg_, hf_, dp_, f_, flag_] :=
    Module[{sv, basePt},
           {PointSize[ps],
            sv = s[t, f];
            Hue[cl (1 + Sin[.02 t])/2, 1, .3 + sg .3 Sin[hf sv]],
            basePt = {-sg s[t, f] Sin[sv], -sg s[t, f] Cos[sv], dp + sv};
            Point[basePt],
           If[flag,
              {Hue[cl (1 + Sin[.1 t])/2, 1, .6 + sg .4 Sin[hf sv]], PointSize[RandomReal[.01]],
               Point[basePt + 1/2 RotationTransform[20 sv, {-Cos[sv], Sin[sv], 0}][{Sin[sv], Cos[sv], 0}]]},
              {}]
          }]

frames = ParallelTable[
                       Graphics3D[Table[{
                                         dt[1, .01, -1, 1, 0, f, True], dt[.45, .01, 1, 1, 0, f, True],
                                         dt[1, .005, -1, 4, .2, f, False], dt[.45, .005, 1, 4, .2, f, False]},
                                        {t, 0, 200, PD}],
                                  ViewPoint -> Left, BoxRatios -> {1, 1, 1.3}, 
                                  ViewVertical -> {0, 0, -1},
                                  ViewCenter -> {{0.5, 0.5, 0.5}, {0.5, 0.55}}, Boxed -> False,
                                  PlotRange -> {{-20, 20}, {-20, 20}, {0, 20}}, Background -> Black],
                       {f, 0, 1, .01}];

Export["tree.gif", frames]

Christmas tree

\$\endgroup\$
1
  • 7
    \$\begingroup\$ This is the best one!! \$\endgroup\$
    – Murta
    Dec 28, 2013 at 16:53
61
votes
\$\begingroup\$

Javascript

Output of given code sample

This is my first code golf!

var a=40,b=8,c=13,o="<div style='font-family:monospace;text-align:center;color:#094'>",w=1,x=0,y="|#|<br>";for(i=1;i<a;i++){for(j=0;j<w;j++){x%c==0?o+="<span style='color:#D00'>O</span>":o+="+";x++;}i%b==0?w-=4:w+=2;o+="<br>";}document.write(o+"<span style='color:#640'>"+y+y+y+"</span></div>");

It comes in at 295 Characters.

The size and decoration of the tree is set by the a,b,c variables:

  • a sets the amount of rows in the tree
  • b sets the amount of rows between decreases in width (set low for a skinny tree, high for a fat tree). Must be greater than or equal to 3.
  • c sets the amount of baubles (set zero for none, 1 for only baubles, higher numbers for less dense placement of baubles)

It looks best when a is a multiple of b, as in the example.

Paste into the console to create a tree. Looks better from far away!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ reduced to 264 a=40,b=8,c=13,o="<p style='font:monospace;color:#094' align='center'>",w=1,x=0,y="|#|<br>";for(i=1;i<a;i++){for(j=0;j<w;j++){x%c==0?o+="<b style='color:red'>O</b>":o+="+";x++;}i%b==0?w-=4:w+=2;o+="<br>";}document.write(o+"<b style='color:#640'>"+y+y+y+"</b></p>"); \$\endgroup\$
    – zoplonix
    Dec 13, 2013 at 16:45
  • 4
    \$\begingroup\$ You are really golfing on a popularity contest? Wow, I would have written nice readable code (easier to code in). +1 \$\endgroup\$
    – Justin
    Dec 13, 2013 at 17:06
  • 7
    \$\begingroup\$ ungh. You're using doc.write? No +1 from me. \$\endgroup\$ Dec 13, 2013 at 20:14
  • 2
    \$\begingroup\$ @JanDvorak, Why not? I'd thought this is golfing..... \$\endgroup\$
    – Pacerier
    Apr 30, 2015 at 18:33
61
votes
\$\begingroup\$

Javascript

Quasirealistic fully 3D procedural fir tree generator.

Featuring: extensive configuration with even more configuration options present in code; a zigzagy trunk; branching branches; growth animation; rotation of a fully grown tree.

Not featuring: jQuery, Underscore.js or any other library; hardware dependency - only canvas support is required; messy code (at least that was the intention)

Live page: http://fiddle.jshell.net/honnza/NMva7/show/

Edit page: http://jsfiddle.net/honnza/NMva7/

screenshot:

fir tree

HTML:

<canvas id=c width=200 height=300 style="display:none"></canvas>
<div id=config></div>

Javascript:

var TAU = 2*Math.PI,
    deg = TAU/360,

    TRUNK_VIEW      = {lineWidth:3, strokeStyle: "brown", zIndex: 1},
    BRANCH_VIEW     = {lineWidth:1, strokeStyle: "green", zIndex: 2},
    TRUNK_SPACING   = 1.5,
    TRUNK_BIAS_STR  = -0.5,
    TRUNK_SLOPE     = 0.25,
    BRANCH_LEN      = 1,
    BRANCH_P        = 0.01,
    MIN_SLOPE       = -5*deg,
    MAX_SLOPE       = 20*deg,
    INIT_SLOPE      = 10*deg,
    MAX_D_SLOPE     =  5*deg,
    DIR_KEEP_BIAS   = 10,
    GROWTH_MSPF     = 10, //ms per frame
    GROWTH_TPF      = 10, //ticks per frame
    ROTATE_MSPF     = 10,
    ROTATE_RPF      = 1*deg; //radians per frame

var configurables = [
//    {key: "TRUNK_SPACING", name: "Branch spacing", widget: "number",
//     description: "Distance between main branches on the trunk, in pixels"},
    {key: "TRUNK_BIAS_STR", name: "Branch distribution", widget: "number",
     description: "Angular distribution between nearby branches. High values tend towards one-sided trees, highly negative values tend towards planar trees. Zero means that branches grow in independent directions."},
    {key: "TRUNK_SLOPE", name: "Trunk slope", widget: "number",
     description: "Amount of horizontal movement of the trunk while growing"},
    {key: "BRANCH_P", name: "Branch frequency", widget: "number",
     description: "Branch frequency - if =1/100, a single twig will branch off approximately once per 100 px"},
    {key: "MIN_SLOPE", name: "Minimum slope", widget: "number", scale: deg,
     description: "Minimum slope of a branch, in degrees"},
    {key: "MAX_SLOPE", name: "Maximum slope", widget: "number", scale: deg,
     description: "Maximum slope of a branch, in degrees"},
    {key: "INIT_SLOPE", name: "Initial slope", widget: "number", scale: deg,
     description: "Angle at which branches leave the trunk"},
    {key: "DIR_KEEP_BIAS", name: "Directional inertia", widget: "number",
     description: "Tendency of twigs to keep their horizontal direction"},
    {get: function(){return maxY}, set: setCanvasSize, name: "Tree height",
     widget:"number"}
    ];

var config = document.getElementById("config"),
    canvas = document.getElementById("c"),
    maxX   = canvas.width/2,
    maxY   = canvas.height,
    canvasRatio = canvas.width / canvas.height,
    c;

function setCanvasSize(height){
    if(height === 'undefined') return maxY;
    maxY = canvas.height = height;
    canvas.width = height * canvasRatio;
    maxX = canvas.width/2;
x}

var nodes = [{x:0, y:0, z:0, dir:'up', isEnd:true}], buds = [nodes[0]],
    branches = [],
    branch,
    trunkDirBias = {x:0, z:0};

////////////////////////////////////////////////////////////////////////////////

configurables.forEach(function(el){
    var widget;
    switch(el.widget){
        case 'number':
            widget = document.createElement("input");
            if(el.key){
                widget.value = window[el.key] / (el.scale||1);
                el.set = function(value){window[el.key]=value * (el.scale||1)};
            }else{
                widget.value = el.get();
            }
            widget.onblur = function(){
                el.set(+widget.value);
            };
            break;
        default: throw "unknown widget type";
    }
    var p = document.createElement("p");
    p.textContent = el.name + ": ";
    p.appendChild(widget);
    p.title = el.description;
    config.appendChild(p);
});
var button = document.createElement("input");
button.type = "button";
button.value = "grow";
button.onclick = function(){
    button.value = "stop";
    button.onclick = function(){clearInterval(interval)};
    config.style.display="none";
    canvas.style.display="";
    c=canvas.getContext("2d");
    c.translate(maxX, maxY);                
    c.scale(1, -1);
    interval = setInterval(grow, GROWTH_MSPF);
}
document.body.appendChild(button);
function grow(){
    for(var tick=0; tick<GROWTH_TPF; tick++){
        var budId = 0 | Math.random() * buds.length,
            nodeFrom = buds[budId], nodeTo, branch,
            dir, slope, bias

        if(nodeFrom.dir === 'up' && nodeFrom.isEnd){
            nodeFrom.isEnd = false; 
            rndArg = Math.random()*TAU;
            nodeTo = {
                x:nodeFrom.x + TRUNK_SPACING * TRUNK_SLOPE * Math.sin(rndArg),
                y:nodeFrom.y + TRUNK_SPACING,
                z:nodeFrom.z + TRUNK_SPACING * TRUNK_SLOPE * Math.cos(rndArg), 
                dir:'up', isEnd:true}
            if(nodeTo.y > maxY){
                console.log("end");
                clearInterval(interval);
                rotateInit();
                return;
            }
            nodes.push(nodeTo);
            buds.push(nodeTo);
            branch = {from: nodeFrom, to: nodeTo, view: TRUNK_VIEW};
            branches.push(branch);
            renderBranch(branch);
        }else{ //bud is not a trunk top
            if(!(nodeFrom.dir !== 'up' && Math.random() < BRANCH_P)){
                buds.splice(buds.indexOf(nodeFrom), 1)
            }
            nodeFrom.isEnd = false; 
            if(nodeFrom.dir === 'up'){
                bias = {x:trunkDirBias.x * TRUNK_BIAS_STR,
                        z:trunkDirBias.z * TRUNK_BIAS_STR};
                slope = INIT_SLOPE;
            }else{
                bias = {x:nodeFrom.dir.x * DIR_KEEP_BIAS,
                        z:nodeFrom.dir.z * DIR_KEEP_BIAS};
                slope = nodeFrom.slope;
            }
            var rndLen = Math.random(),
                rndArg = Math.random()*TAU;
            dir = {x: rndLen * Math.sin(rndArg) + bias.x,
                   z: rndLen * Math.cos(rndArg) + bias.z};
            var uvFix = 1/Math.sqrt(dir.x*dir.x + dir.z*dir.z);
            dir = {x:dir.x*uvFix, z:dir.z*uvFix};
            if(nodeFrom.dir === "up") trunkDirBias = dir;
            slope += MAX_D_SLOPE * (2*Math.random() - 1);
            if(slope > MAX_SLOPE) slope = MAX_SLOPE;
            if(slope < MIN_SLOPE) slope = MIN_SLOPE;
            var length = BRANCH_LEN * Math.random();
            nodeTo = {
                x: nodeFrom.x + length * Math.cos(slope) * dir.x,
                y: nodeFrom.y + length * Math.sin(slope),
                z: nodeFrom.z + length * Math.cos(slope) * dir.z,
                dir: dir, slope: slope, isEnd: true
            }
            //if(Math.abs(nodeTo.x)/maxX + nodeTo.y/maxY > 1) return;
            nodes.push(nodeTo);
            buds.push(nodeTo);
            branch = {from: nodeFrom, to: nodeTo, view: BRANCH_VIEW};
            branches.push(branch);
            renderBranch(branch);
        }// end if-is-trunk
    }// end for-tick
}//end func-grow

function rotateInit(){
    branches.sort(function(a,b){
        return (a.view.zIndex-b.view.zIndex);
    });
    interval = setInterval(rotate, ROTATE_MSPF);
}

var time = 0;
var view = {x:1, z:0}
function rotate(){
    time++;
    view = {x: Math.cos(time * ROTATE_RPF),
            z: Math.sin(time * ROTATE_RPF)};
    c.fillStyle = "white"
    c.fillRect(-maxX, 0, 2*maxX, maxY);
    branches.forEach(renderBranch);
    c.stroke();
    c.beginPath();
}

var prevView = null;
function renderBranch(branch){
    if(branch.view !== prevView){
        c.stroke();
        for(k in branch.view) c[k] = branch.view[k];
        c.beginPath();
        prevView = branch.view;
    }
    c.moveTo(view.x * branch.from.x + view.z * branch.from.z,
             branch.from.y);
    c.lineTo(view.x * branch.to.x + view.z * branch.to.z,
             branch.to.y);
}
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I know because this answer has only one comment: because it leaves people speechless. \$\endgroup\$
    – Caridorc
    Dec 2, 2014 at 20:48
59
votes
\$\begingroup\$

C++

Let's make it in the spirit of IOCCC and have the code in the shape of a tree as well! :D

#include <iostream>
using namespace std;

               int
             main(){
              int a
                ; 
               cin
             >>a;int
           w=a*2+5;for
             (int x=
           0;x<a;++x){
         for(int y=2;y>=
       0;--y){for(int z=0;
           z<a+y-x;++z
        ){cout<<" ";}for(
     int z=0;z<x*2-y*2+5;++z
        ){cout<<".";}cout
      <<endl;}}for(int x=0;
    x<w/5+1;++x){for(int z=0;
  z<w/3+1;++z){cout<<" ";}for(
int z=0;z<w-(w/3+1)*2;z+=1){cout
           <<"#";}cout
           <<endl;;;}}

Takes an integer as input, and returns a Christmas tree with that many "stack levels". For example, an input of

5

Returns:

       .
      ...
     .....
      ...
     .....
    .......
     .....
    .......
   .........
    .......
   .........
  ...........
   .........
  ...........
 .............
      ###
      ###
      ###
      ###
\$\endgroup\$
6
  • \$\begingroup\$ output example? \$\endgroup\$ Dec 14, 2013 at 9:48
  • 32
    \$\begingroup\$ The code looks more like a chrismas tree than the output... \$\endgroup\$ Dec 16, 2013 at 8:57
  • 2
    \$\begingroup\$ @dystroy for the record, the instructions do not say it's the output that must containt the tree. \$\endgroup\$ Dec 16, 2013 at 9:20
  • 4
    \$\begingroup\$ @ArlaudPierre: But, the size must be adjustable, which is impossible with just the source code. I could make a program that outputs program code in the shape of a Christmas tree that outputs an actual Christmas tree, but I imagine that would be terribly difficult without resorting to just inserting stray semicolons everywhere. \$\endgroup\$
    – Joe Z.
    Dec 16, 2013 at 11:02
  • 1
    \$\begingroup\$ If you put a * at the top of the tree (source code), you will have my +1. \$\endgroup\$
    – Fabricio
    Dec 15, 2014 at 22:16
49
votes
\$\begingroup\$

Bash

Sample output:

ASCII Xmas tree

The tree size (i.e. number of lines) is passed on the command line, and is limited to values of 5 or greater. The image above was produced from the command ./xmastree.sh 12. Here's the source code:

#!/bin/bash
declare -a a=('.' '~' "'" 'O' "'" '~' '.' '*')
[[ $# = 0 ]] && s=9 || s=$1
[[ $s -gt 5 ]] || s=5
for (( w=1, r=7, n=1; n<=$s; n++ )) ; do
  for (( i=$s-n; i>0; i-- )) ;  do
    echo -n " "
  done
  for (( i=1; i<=w; i++ )) ; do
    echo -n "${a[r]}"
    [[ $r -gt 5 ]] && r=0 || r=$r+1
  done
  w=$w+2
  echo " "
done;
echo " "
\$\endgroup\$
1
  • \$\begingroup\$ That is very pretty. \$\endgroup\$
    – Pandu
    Dec 28, 2013 at 7:29
42
votes
\$\begingroup\$

Disclaimer: this is based on my LaTeX christmas tree, first posted here: https://tex.stackexchange.com/a/87921/8463

The following code will generate a chrismtas tree, with random decorations. You can change both the size of the tree, and the random seed to generate a different tree.

To change the seed modify the value inside \pgfmathsetseed{\year * 6} to any other numerical value (the default one will generate a new tree every year)

To change the size of the tree modify the order=10 to be either larger, or smaller depending on the size of the tree you want.

Examples. order=11:

enter image description here

order=8

enter image description here

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc, lindenmayersystems,shapes,decorations,decorations.shapes}
\begin{document}

\def\pointlistleft{}
\def\pointlistright{}
\pgfmathsetseed{\year * 6}

\makeatletter
\pgfdeclarelindenmayersystem{Christmas tree}{
    \symbol{C}{\pgfgettransform{\t} \expandafter\g@addto@macro\expandafter\pointlistleft\expandafter{\expandafter{\t}}}
    \symbol{c}{\pgfgettransform{\t} \expandafter\g@addto@macro\expandafter\pointlistright\expandafter{\expandafter{\t}}}
    \rule{S -> [+++G][---g]TS}
    \rule{G -> +H[-G]CL}
    \rule{H -> -G[+H]CL}
    \rule{g -> +h[-g]cL}
    \rule{h -> -g[+h]cL}
    \rule{T -> TL}
    \rule{L -> [-FFF][+FFF]F}
}
\makeatother

\begin{tikzpicture}[rotate=90]
\draw [color=green!50!black,l-system={Christmas tree,step=4pt,angle=16,axiom=LLLLLLSLFFF,order=10,randomize angle percent=20}] lindenmayer system -- cycle;

\pgfmathdeclarerandomlist{pointsleft}{\pointlistleft}
\pgfmathdeclarerandomlist{pointsright}{\pointlistright}
\pgfmathdeclarerandomlist{colors}{{red}{blue}{yellow}}

\foreach \i in {0,1,...,5}
{
    \pgfmathrandomitem{\c}{pointsleft}
    \pgfsettransform{\c}
    \pgfgettransformentries{\a}{\b}{\c}{\d}{\xx}{\yy}
    \pgfmathrandomitem{\c}{pointsright}
    \pgfsettransform{\c}
    \pgfgettransformentries{\a}{\b}{\c}{\d}{\XX}{\YY}
    \pgftransformreset

    \pgfmathsetmacro\calcy{min(\yy,\YY)-max((abs(\yy-\YY))/3,25pt)}

    \draw[draw=orange!50!black, fill=orange!50, decorate, decoration={shape backgrounds, shape=star, shape sep=3pt, shape size=4pt}, star points=5] (\xx,\yy) .. controls (\xx,\calcy pt) and (\XX,\calcy pt) .. (\XX,\YY);
}

\foreach \i in {0,1,...,15}
{
    \pgfmathrandomitem{\c}{pointsleft}
    \pgfsettransform{\c}
    \pgftransformresetnontranslations
    \draw[color=black] (0,0) -- (0,-4pt);
    \pgfmathrandomitem{\c}{colors}
    \shadedraw[ball color=\c] (0,-8pt) circle [radius=4pt];
}

\foreach \i in {0,1,...,15}
{
    \pgfmathrandomitem{\c}{pointsright}
    \pgfsettransform{\c}
    \pgftransformresetnontranslations
    \draw[color=black] (0,0) -- (0,-4pt);
    \pgfmathrandomitem{\c}{colors}
    \shadedraw[ball color=\c] (0,-8pt) circle [radius=4pt];
}

\end{tikzpicture}
\end{document}
\$\endgroup\$
2
  • \$\begingroup\$ I would say that this is based on Stefan Kottwitz's tree, first posted here. Saying that it is based on your tree makes it seem like you wrote all the code for it, which isn't true. Someone else wrote most the code for the actual tree. \$\endgroup\$ Dec 21, 2013 at 13:37
  • \$\begingroup\$ @RyanCarlson the idea to use the lindenmayer system as a basis for putting on decorations was my idea, and if you check carefully both of the code you can see, that the lindenmayer part of the code was redesigned to also have additional parts (like a root for the tree, which the original lacks). Besides that I didn't forget to reference the original, where anyone can check for the references (including the one to Peitgen and Saupe, who might've been the first to draft the equation of the tree) \$\endgroup\$
    – SztupY
    Dec 21, 2013 at 20:29
34
votes
\$\begingroup\$

Befunge 93

This is an undecorated tree:

&::00p20pv
vp010    <
v              p00<
>:0`!#v_" ",1-
v,"/"$<
>10g:2+v
vp01   <
>:0`!#v_" ",1-
v,"\"$<
>91+,00g1-::0`!#v_^
v        *2+1g02<
>:0`!#v_"-",1-
v g02$<
>91+,v
v    <
>:0`!#v_" ",1-
"||"$ <@,,

Sample output, input is 10:

          /\
         /  \
        /    \
       /      \
      /        \
     /          \
    /            \
   /              \
  /                \
 /                  \
----------------------
          ||

Let's add some decorations:

&::00p20pv
vp010    <
v              p00<
>:0`!#v_" ",1-
v,"/"$<
>10g:2+v             >"O" v
vp01   <            >?v
>:0`!#v_           >?>>" ">,1-
v,"\"$<             >?<
>91+,00g1-::0`!#v_^  >"*" ^
v        *2+1g02<
>:0`!#v_"-",1-
v g02$<
>91+,v
v    <
>:0`!#v_" ",1-
"||"$ <@,,

Sample output:

          /\
         /O \
        /    \
       /   ** \
      /     *  \
     /    **   O\
    /* O* *  *   \
   / O      O     \
  / *  **   O*     \
 /   *OO   *  OOO * \
----------------------
          ||
\$\endgroup\$
0
32
votes
\$\begingroup\$

HTML and CSS

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Scalable christmas tree</title>
<style>
body {
  background-color: skyblue;
  background-size: 11px 11px, 21px 21px, 31px 31px;
  background-image: radial-gradient(circle at 5px 5px,white,rgba(255,255,255,0) 1px), radial-gradient(circle at 5px 5px,white,rgba(255,255,255,0) 2px), radial-gradient(circle at 5px 5px,white 1px,skyblue 1px);
}
div {
  float: left;
  border-style: solid;
  border-color: transparent;
  border-bottom-color: green;
  border-width: 20px;
  border-top-width: 0;
  border-bottom-width: 30px;
  border-bottom-left-radius: 35%;
  border-bottom-right-radius: 35%;
  box-shadow: red 0 15px 5px -13px;
  animation-name: light;
  animation-duration: 1s;
  animation-direction: alternate;
  animation-iteration-count: infinite;
  -webkit-animation-name: light;
  -webkit-animation-duration: 1s;
  -webkit-animation-direction: alternate;
  -webkit-animation-iteration-count: infinite;
}
section {
  float: left;
}
header {
  color: yellow;
  font-size: 30px;
  text-align: center;
  text-shadow: red 0 0 10px;
  line-height: .5;
  margin-top: 10px;
  animation-name: star;
  animation-duration: 1.5s;
  animation-direction: alternate;
  animation-iteration-count: infinite;
  -webkit-animation-name: star;
  -webkit-animation-duration: 1.5s;
  -webkit-animation-direction: alternate;
  -webkit-animation-iteration-count: infinite;
}
footer {
  float: left;
  width: 100%;
  height: 20px;
  background-image: linear-gradient(to right, transparent 45%, brown 45%, #600 48%, #600 52%, brown 55%, transparent 55%);
}
:target {
  display: none;
}
@keyframes star {
  from { text-shadow: red 0 0 3px; }
  to { text-shadow: red 0 0 30px; }
}
@-webkit-keyframes star {
  from { text-shadow: red 0 0 3px; }
  to { text-shadow: red 0 0 30px; }
}
@keyframes light {
  from { box-shadow: red 0 15px 5px -13px; }
  to { box-shadow: blue 0 15px 5px -13px; }
}
@-webkit-keyframes light {
  from { box-shadow: red 0 15px 5px -13px; }
  to { box-shadow: blue 0 15px 5px -13px; }
}
</style>
</head>
<body>
<section>
<header>&#x2605;</header>
<div id="0"><div id="1"><div id="2"><div id="3"><div id="4"><div id="5"><div id="6"><div id="7"><div id="8"><div id="9"><div id="10"><div id="11"><div id="12"><div id="13"><div id="14"><div id="15"><div id="16"><div id="17"><div id="18"><div id="19"><div id="20"><div id="21"><div id="22"><div id="23"><div id="24">
</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
<footer></footer>
</section>
</body>
</html>

Sizes between 1 and 25 are supported – just add the size to the URL as fragment identifier.

Works in Chrome, Explorer and Firefox. In Opera is ugly, but the scaling part works.

Sample access:

http://localhost/xmas.html#5

christmas tree of size 5

Sample access:

http://localhost/xmas.html#15

christmas tree of size 15

Live view:

http://dabblet.com/gist/8026898

(The live view contains no vendor prefixed CSS and includes links to change the size.)

\$\endgroup\$
1
  • 1
    \$\begingroup\$ +1 I like the idea of using :target to scale the output :-) \$\endgroup\$
    – r3mainer
    Dec 14, 2013 at 23:03
32
votes
\$\begingroup\$

Java

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 *
 * @author Quincunx
 */
public class ChristmasTree {
    public static double scale = 1.2;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ChristmasTree();
            }
        });
    }

    public ChristmasTree() {
        try {
            URL url = new URL("http://imgs.xkcd.com/comics/tree.png");
            BufferedImage img = ImageIO.read(url);

            JFrame frame = new JFrame();
            frame.setUndecorated(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            Dimension d = Toolkit.getDefaultToolkit().getScreenSize();

            BufferedImage result = 
                    new BufferedImage((int)(img.getWidth() * scale), 
                            (int) (img.getHeight() * scale), 
                            BufferedImage.TYPE_INT_RGB);
            Graphics2D g = (Graphics2D) result.getGraphics();
            g.drawRenderedImage(img, AffineTransform.getScaleInstance(scale, scale));

            JImage image = new JImage(result);
            image.setToolTipText("Not only is that terrible in general, but you "
                    + "just KNOW Billy's going to open the root present first, "
                    + "and then everyone will have to wait while the heap is "
                    + "rebuilt.");
            frame.add(image);
            frame.pack();
            frame.setVisible(true);

        } catch (IOException ex) {

        }
    }

    class JImage extends JPanel implements MouseListener {

        BufferedImage img;

        public JImage(){
            this(null);
        }
        public JImage(BufferedImage image){
            img = image;
            setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
            addMouseListener(this);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(img, WIDTH, WIDTH, this);
        }

        public void setImage(BufferedImage image) {
            img = image;
            repaint();
        }

        @Override
        public void mouseClicked(MouseEvent me) {
            System.exit(0);
        }

        @Override
        public void mousePressed(MouseEvent me) {

        }

        @Override
        public void mouseReleased(MouseEvent me) {

        }

        @Override
        public void mouseEntered(MouseEvent me) {

        }

        @Override
        public void mouseExited(MouseEvent me) {

        }
    }
}

To alter the size, change scale to some other double value (keep it around 1 if you want to see anything).

Sample output (for 1.0 as scale, too lazy to take a screenshot, so just posted what it does):

enter image description here

The program takes this image from the internet, resizes it according to scale, then puts it in an undecorated window where it is displayed. Clicking on the window closes the program. Also, the tool tip text is there, but the link is not.

\$\endgroup\$
5
  • 3
    \$\begingroup\$ This isn't "drawing" anything. \$\endgroup\$
    – Mrchief
    Dec 17, 2013 at 18:28
  • \$\begingroup\$ @Mrchief The challenge isn't to draw something; it is to make a tree. This is making a tree, by taking the image \$\endgroup\$
    – Justin
    Dec 17, 2013 at 18:35
  • 2
    \$\begingroup\$ Did you generate the image yourself? No. You're just downloading it from somewhere. The question isn't to download an image and resize it. Check other answers and you'll understand what the OP actually asked for. \$\endgroup\$
    – Mrchief
    Dec 18, 2013 at 5:23
  • 10
    \$\begingroup\$ @Mrchief Did you notice who wrote the OP? This is one of the many possible solutions I was looking for when I asked this question. \$\endgroup\$
    – Justin
    Dec 18, 2013 at 5:44
  • 3
    \$\begingroup\$ Ha! Smack on the head! Since its your own post, I don't see much reason to argue, other than expressing my opinion that this is not same as your code "making" something (like rest of the answers do). Just like downloading a movie is not the same as making a movie. \$\endgroup\$
    – Mrchief
    Dec 18, 2013 at 19:24
28
votes
\$\begingroup\$

TI-89 Basic

Just because I wanted to see a Christmas Tree on my calculator. I shall type it here as it appears on my calculator.

:tree()
:Prgm
:
:Local size
:Prompt size
:
:Local d,x,y,str,orn
:size→x
:0→y
:{" "," "," "," "," "," "," "," ","°","∫","θ","O","©"}→orn
:size→d
:
:While d≥0
: d-1→d
: ""→str
: 
: While x≥0
:  str&" "→str
:  x-1→x
: EndWhile
: 
: str&"/"→str
: 2*y→x
:
: While x>0
:  str&elementAt(orn,rand(colDim(list▶mat(orn))))→str
:  x-1→x
: EndWhile
: 
: str&"\"→str
: Disp str
: y+1→y
: d→x
:EndWhile
:
:""→str
:
:For x,1,2*(size+2)
: str&"-"→str
:EndFor
:Disp str
:
:""→str
:For x,1,size+1
: str&" "→str
:EndFor
:str&"||"→str
:Disp str
:
:EndPrgm


:elementAt(l,i)
:Func
:Local m
:list▶mat(l)→m
:
:Local cd
:colDim(m)→cd
:
:If i>cd or i≤0 Then
: 1/0
:Else
: If i=cd Then
:  Return sum(mat▶list(subMat(m,1,i))) - sum(mat▶list(subMat(m,1,i+1)))
: Else
:  Return sum(mat▶list(subMat(m,1,i))) - sum(mat▶list(subMat(m,1,i+1)))
: EndIf
:EndIf
:EndFunc

This works the same way as my Befunge answer, but I use different ornaments. Yes, my elementAt function's repeated uses slows the program down because of many conversions between lists and matrices, but as I wrote it before, I decided against editing it. Also, I learned while typing this answer that © makes a comment (I thought it looked like @, but that is another character). Never knew what it was before now.

Sample output:

size?
7
        /\
       / O\
      /∫   \
     / ©  ∫°\
    / θ ∫   ∫\
   /°  °  ©  ©\
  /O  ∫  O °   \
 / θ  °©  ∫ O  θ\
------------------
        ||

I love those 's; they look like candy canes.

\$\endgroup\$
2
  • \$\begingroup\$ What version is this? \$\endgroup\$ Jun 6, 2015 at 1:25
  • \$\begingroup\$ @SuperJedi224 I added it to the answer \$\endgroup\$
    – Justin
    Jun 6, 2015 at 6:15
26
votes
\$\begingroup\$

Wolfram Language ( Mathematica )

As discussed at famed Reddit thread: t*sin (t) ≈ Christmas tree

PD = .5; s[t_, f_] := t^.6 - f;
 dt[cl_, ps_, sg_, hf_, dp_, f_] := 
    {PointSize[ps], Hue[cl, 1, .6 + sg .4 Sin[hf s[t, f]]], 
     Point[{-sg s[t, f] Sin[s[t, f]], -sg s[t, f] Cos[s[t, f]], dp + s[t, f]}]};

 frames = ParallelTable[

    Graphics3D[Table[{dt[1, .01, -1, 1, 0, f], dt[.45, .01, 1, 1, 0, f], 
                      dt[1, .005, -1, 4, .2, f], dt[.45, .005, 1, 4, .2, f]}, 
 {t, 0, 200, PD}],

     ViewPoint -> Left, BoxRatios -> {1, 1, 1.3}, ViewVertical -> {0, 0, -1}, 
    ViewCenter -> {{0.5, 0.5, 0.5}, {0.5, 0.55}}, Boxed -> False, Lighting -> "Neutral", 
    PlotRange -> {{-20, 20}, {-20, 20}, {0, 20}}, Background -> Black],

   {f, 0, 1, .01}];

Export["tree.gif", frames]

enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ Where you set the size and how it affects the generated tree? \$\endgroup\$
    – manatwork
    Dec 28, 2013 at 15:24
  • 2
    \$\begingroup\$ @manatwork this is basically graph t*sin(t) as i said in the post. So plotting for larger t's will just make a bigger tree: Table[..., {t, 0, 200, PD}] \$\endgroup\$ Dec 28, 2013 at 19:41
24
votes
\$\begingroup\$

Bash with Bc and ImageMagick

#!/bin/bash

size="${1:-10}"

width=$(( size*25 ))
height=$(( size*size+size*10 ))

cos=( $( bc -l <<< "for (i=0;i<3.14*2;i+=.05) c(i)*100" ) )
sin=( $( bc -l <<< "for (i=0;i<3.14*2;i+=.05) s(i)*100" ) )
cos=( "${cos[@]%.*}" )
sin=( "${sin[@]%.*}" )

cos=( "${cos[@]/%-/0}" )
sin=( "${sin[@]/%-/0}" )

snow=()
needle=()
decor=()
for ((i=2;i<size+2;i++)); do
  for ((j=3;j<=31;j++)); do
    (( x=width/2+i*10+cos[62-j]*i*10/100 ))
    (( y=i*i+sin[j]*i*5/100 ))

    for ((e=0;e<i;e++)); do
      needle+=(
        -draw "line $x,$y $(( x+RANDOM%i-i/2 )),$(( y+RANDOM%i-i/2 ))"
        -draw "line $(( width-x )),$y $(( width-x+RANDOM%i-i/2 )),$(( y+RANDOM%i-i/2 ))"
      )
    done

    (( RANDOM%2 )) && (( x=width-x ))
    snow+=(
      -draw "circle $x,$(( y-i/2 )) $(( x+i/3 )),$(( y-i/2+i/3 ))"
    )

    (( RANDOM%10 )) || decor+=(
      -fill "rgb($(( RANDOM%5*20+50 )),$(( RANDOM%5*20+50 )),$(( RANDOM%5*20+50 )))"
      -draw "circle $x,$(( y+i )) $(( x+i/2 )),$(( y+i+i/2 ))"
    )
  done
done

flake=()
for ((i=0;i<width*height/100;i++)); do
  flake+=(
    -draw "point $(( RANDOM%width )),$(( RANDOM%height ))"
  )
done

convert \
  -size "${width}x$height" \
  xc:skyblue \
  -stroke white \
  -fill white \
  "${snow[@]}" \
  -blur 5x5 \
  "${flake[@]}" \
  -stroke brown \
  -fill brown \
  -draw "polygon $(( width/2 )),0 $(( width/2-size )),$height, $(( width/2+size )),$height" \
  -stroke green \
  -fill none \
  "${needle[@]}" \
  -stroke none \
  -fill red \
  "${decor[@]}" \
  x:

Sample run:

bash-4.1$ ./xmas.sh 5

Sample output:

christmas tree of size 5

Sample run:

bash-4.1$ ./xmas.sh 15

Sample output:

christmas tree of size 15

\$\endgroup\$
22
votes
\$\begingroup\$

C

Sample output for depth=4, zoom=2.0

Tree

This answer employs an approach quite different from other answers. It generates a tree structure by recursively branching. Each branch is represented by a set of circles. And finally the main function samples the circles and fill with characters when circles are met. Since it is done by sampling a scene (like ray-tracing), it is intrinsically scalable. The downside is speed, since it traverse the whole tree structure for every "pixel"!

The first command-line argument controls the depth of branching. And the second controls scale (2 means 200%).

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define PI 3.14159265359

float sx, sy;

float sdCircle(float px, float py, float r) {
    float dx = px - sx, dy = py - sy;
    return sqrtf(dx * dx + dy * dy) - r;
}

float opUnion(float d1, float d2) {
    return d1 < d2 ? d1 : d2;
}

#define T px + scale * r * cosf(theta), py + scale * r * sin(theta)

float f(float px, float py, float theta, float scale, int n) {
    float d = 0.0f;
    for (float r = 0.0f; r < 0.8f; r += 0.02f)
        d = opUnion(d, sdCircle(T, 0.05f * scale * (0.95f - r)));

    if (n > 0)
        for (int t = -1; t <= 1; t += 2) {
            float tt = theta + t * 1.8f;
            float ss = scale * 0.9f;
            for (float r = 0.2f; r < 0.8f; r += 0.1f) {
                d = opUnion(d, f(T, tt, ss * 0.5f, n - 1));
                ss *= 0.8f;
            }
        }

    return d;
}

int ribbon() {
    float x = (fmodf(sy, 0.1f) / 0.1f - 0.5f) * 0.5f;
    return sx >= x - 0.05f && sx <= x + 0.05f;
}

int main(int argc, char* argv[]) {
    int n = argc > 1 ? atoi(argv[1]) : 3;
    float zoom = argc > 2 ? atof(argv[2]) : 1.0f;
    for (sy = 0.8f; sy > 0.0f; sy -= 0.02f / zoom, putchar('\n'))
        for (sx = -0.35f; sx < 0.35f; sx += 0.01f / zoom) {
            if (f(0, 0, PI * 0.5f, 1.0f, n) < 0.0f) {
                if (sy < 0.1f)
                    putchar('.');
                else {
                    if (ribbon())
                        putchar('=');
                    else
                        putchar("............................#j&o"[rand() % 32]);
                }
            }
            else
                putchar(' ');
        }
}
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I think it's safe to say this is the most technically impressive answer to this question so far. \$\endgroup\$
    – Stuntddude
    Mar 7, 2015 at 2:38
20
votes
\$\begingroup\$

Mathematica ASCII

I really like ASCII art, so I add another very different answer - especially so it's so short in Mathematica:

Column[Table[Row[RandomChoice[{"+", ".", "*", "~", "^", "o"}, k]], {k, 1, 35, 2}], 
Alignment -> Center]

enter image description here

And now with a bit more sophistication a scalable dynamic ASCII tree. Watch closely - the tree is also changing - snow sticks to branches then falls ;-)

DynamicModule[{atoms, tree, pos, snow, p = .8, sz = 15},

 atoms = {
   Style["+", White],
   Style["*", White],
   Style["o", White],
   Style[".", Green],
   Style["~", Green],
   Style["^", Green],
   Style["^", Green]
   };

 pos = Flatten[Table[{m, n}, {m, 18}, {n, 2 m - 1}], 1];

 tree = Table[RandomChoice[atoms, k], {k, 1, 35, 2}];

 snow = Table[
   RotateLeft[ArrayPad[{RandomChoice[atoms[[1 ;; 2]]]}, {0, sz}, " "],
     RandomInteger[sz]], {sz + 1}];

 Dynamic[Refresh[

   Overlay[{

     tree[[Sequence @@ RandomChoice[pos]]] = RandomChoice[atoms];
     Column[Row /@ tree, Alignment -> Center, Background -> Black],

     Grid[
      snow = 
       RotateRight[
        RotateLeft[#, 
           RandomChoice[{(1 - p)/2, p, (1 - p)/2} -> {-1, 0, 1}]] & /@
          snow
        , {1, 0}]]

     }, Alignment -> Center]

   , UpdateInterval -> 0, TrackedSymbols -> {}]
  ]
 ]

enter image description here

\$\endgroup\$
2
  • \$\begingroup\$ Nice one. Where you adjust the size? Is that 35 in Table's parameters? \$\endgroup\$
    – manatwork
    Jan 7, 2014 at 9:29
  • \$\begingroup\$ @manatwork yes the 35 in Table. Thanks ;) \$\endgroup\$ Jan 7, 2014 at 11:16
19
votes
\$\begingroup\$

I made this for a challenge on /r/dailyprogrammer, (not sure if reusing code is against the spirit/rules of this) but:

Brainfuck. Takes as input a number (length of bottom row of leaves) and two characters. One space between each.

Example input: 13 = +

Example output:

      +
     +++
    +++++
   +++++++
  +++++++++
 +++++++++++
+++++++++++++
     ===

Code:

                   >
                  ,--
                 -----
                -------
               ---------
              ---------[+
             +++++++++++++
            +++++++++++++++
           +++<[>>++++++++++
          <<-]>--------------
         ---------------------
        -------------[<+>-]>[<<
       +>>-]<,------------------
      --------------],>,,>>++++[<
     ++++++++>-]++++++++++>+<<<<<-
    [>>>>>>+>+<<<<<<<--]>>>>>>>[<<<
   <<<<++>>>>>>>-]<<<<<<<[>>>>>>[<<<
  .>>>>+<-]>[<+>-]<<[<<<.>>>>>+<<-]>>
 [<<+>>-]<<<.>>><-<++<<<<<--]>>...>>>-
--[>+<<<<..>>>--]<.>>[<<<.>>>>+<-]<<<<<
                ...>>>.
\$\endgroup\$
0
16
votes
\$\begingroup\$

Processing

The original fractal Christmas tree. Mouse Y position determines size, use up and down arrow keys to change the number of generations.

int size = 500;

int depth = 10;

void setup() {
  frameRate(30);
  size(size, size, P2D);
}

void draw() {
  background(255);
  tree(size/2.0, size, 0.0, radians(0), radians(108), (size - mouseY)/3, depth);
  tree(size/2.0, size, 0.0, radians(108), radians(0), (size - mouseY)/3, depth);
}

void keyPressed() {
  if(keyCode == UP) depth++;
  if(keyCode == DOWN) depth--;
  depth = max(depth, 1);
}

void tree(float posX, float posY, float angle, float forkRight, float forkLeft, float length, int generation) {
  if (generation > 0) {
    float nextX = posX + length * sin(angle);
    float nextY = posY - length * cos(angle);

    line(posX, posY, nextX, nextY);

    tree(nextX, nextY, angle + forkRight, forkRight, forkLeft, length*0.6, generation - 1);
    tree(nextX, nextY, angle - forkLeft,  forkRight, forkLeft, length*0.6, generation - 1);
  }
}

What a lovely Christmas tree, don't you think, ma?

Or, if you prefer a fuller tree:

int size = 500;

int depth = 10;

void setup() {
  frameRate(30);
  size(size, size, P2D);
}

void draw() {
  background(255);
  tree(size/2.0 - 5, size, 0.0, 0.0, (size - mouseY)/3, depth);
}

void keyPressed() {
  if(keyCode == UP) depth++;
  if(keyCode == DOWN) depth--;
  depth = max(depth, 1);
}

void tree(float posX, float posY, float angle, float fork, float length, int generation) {
  if (generation > 0) {
    float nextX = posX + length * sin(angle);
    float nextY = posY - length * cos(angle);

    line(posX, posY, nextX, nextY);

    tree(nextX, nextY, angle + fork + radians(108), fork, length*0.6, generation - 1);
    tree(nextX, nextY, angle - fork,                fork, length*0.6, generation - 1);
    tree(nextX, nextY, angle + fork - radians(108), fork, length*0.6, generation - 1);
  }
}

I'm super bummed that somebody beat me to posting a fractal tree even though I was was ahead of them in the idea and in actually starting work on it :/

\$\endgroup\$
0
14
votes
\$\begingroup\$

Ruby

((1..20).to_a+[6]*4).map{|i|puts ('*'*i*2).center(80)}

Happy New Year

You can customize output by changing *.
For a green tree: ((1..20).to_a+[6]*4).map{|i|puts "\e[32m"+('*'*i*2).center(80)}

Approach 2 (Xmas tree that doesn't look like an arrow)

((1..6).to_a+(3..9).to_a+(6..12).to_a+[3]*4).map{|i|puts "\e[32m"+('*'*i*4).center(80)}

Happy New Year

Approach 3

((1..20).to_a+[6]*4).map{|i|puts' '*(20-i)+'*'*(2*i-1)}
\$\endgroup\$
7
  • 2
    \$\begingroup\$ This is more an arrow, then a christmas tree! \$\endgroup\$
    – klingt.net
    Dec 14, 2013 at 13:40
  • 4
    \$\begingroup\$ @klingt.net *than. \$\endgroup\$
    – NARKOZ
    Dec 14, 2013 at 16:43
  • \$\begingroup\$ Time to light up the christmas tree. \$\endgroup\$
    – NARKOZ
    Dec 14, 2013 at 17:08
  • \$\begingroup\$ And where you specify the size? \$\endgroup\$
    – manatwork
    Dec 14, 2013 at 17:31
  • \$\begingroup\$ @manatwork in ranges. 1..20, 1..6, etc. \$\endgroup\$
    – NARKOZ
    Dec 14, 2013 at 17:56
14
votes
\$\begingroup\$

Turtle Graphics

enter image description here

Based on properties of the Euler spiral.

Code:

enter image description here

The scale is determined by the step size (move forward by: 6). An interactive version is available here.

P.S. Inspired by this question.

\$\endgroup\$
2
  • \$\begingroup\$ Where is this "scalable"? Other than that, this is great! \$\endgroup\$
    – Justin
    Jan 11, 2014 at 4:40
  • \$\begingroup\$ @Quincunx You can change scale by changing move forward by 6. For example, 10 will produce a larger tree. Actually there is no input in this "language" (or the whole code can be treated as input ;) ). \$\endgroup\$
    – ybeltukov
    Jan 11, 2014 at 18:14
14
votes
\$\begingroup\$

Processing

I made this tree generator using an L-System and a Turtle.

A Tree

code:

//My code, made from scratch:
final int THE_ITERATIONS = 7;
final float SCALE = 1;
final int ANGLE = 130;
final int SIZE = 4;

final int ITERATIONS = THE_ITERATIONS - 1;

int lineLength;

String lSystem;
ArrayList<Turtle> turtles;

int turtleIndex;
int lSystemIndex;

void setup()
{
  size(320, 420);
  background(255);
  translate(width / 2, height - 70);

  lineLength = ITERATIONS * 2 + 2;

  lSystem = "[-F][+F]F";

  turtles = new ArrayList<Turtle>(ITERATIONS + 1);
  lSystemIndex = 0;

  calculateLSystem();
  println(lSystem);

  turtles.add(new Turtle(0, 0));

  doTurtles();
  save("Tree.png");
}

void doTurtles()
{
  while(lSystemIndex < lSystem.length())
  {
    print("\"" + lSystem.charAt(lSystemIndex) + "\": ");
    if(lSystem.charAt(lSystemIndex) == 'F')
    {
      turtleForward();
    }
    else if(lSystem.charAt(lSystemIndex) == '[')
    {
      lineLength -= 2;
      addTurtle();
      println(turtles.size());
    }
    else if(lSystem.charAt(lSystemIndex) == ']')
    {
      lineLength += 2;
      removeTurtle();
      println(turtles.size());
    }
    else if(lSystem.charAt(lSystemIndex) == '+')
    {
      turtleRight();
    }
    else if(lSystem.charAt(lSystemIndex) == '-')
    {
      turtleLeft();
    }
    lSystemIndex++;
  }
}

void addTurtle()
{
  turtles.add(new Turtle(turtles.get(turtles.size() - 1)));
}

void removeTurtle()
{
  turtles.remove(turtles.size() - 1);
}

void turtleLeft()
{
  turtles.get(turtles.size() - 1).left(ANGLE + random(-5, 5));
}

void turtleRight()
{
  turtles.get(turtles.size() - 1).right(ANGLE + random(-5, 5));
}

void turtleForward()
{
  print(turtles.get(turtles.size() - 1) + ": ");
  strokeWeight(min(lineLength / SIZE, 1));
  stroke(5 + random(16), 90 + random(16), 15 + random(16));
  if(turtles.size() == 1)
  {
    strokeWeight(lineLength / 2);
    stroke(100, 75, 25);
  }
  turtles.get(turtles.size() - 1).right(random(-3, 3));
  turtles.get(turtles.size() - 1).forward(lineLength);
}

void calculateLSystem()
{
  for(int i = 0; i < ITERATIONS; i++)
  {
    lSystem = lSystem.replaceAll("F", "F[-F][+F][F]");
  }
  lSystem = "FF" + lSystem;
}

void draw()
{

}

//——————————————————————————————————————————————————————
// Turtle code, heavily based on code by Jamie Matthews
// http://j4mie.org/blog/simple-turtle-for-processing/
//——————————————————————————————————————————————————————

class Turtle {
  float x, y; // Current position of the turtle
  float angle = -90; // Current heading of the turtle
  boolean penDown = true; // Is pen down?
  int lineLength;

  // Set up initial position
  Turtle (float xin, float yin) {
    x = xin;
    y = yin;
    //lineLength = lineLengthin;
  }

  Turtle (Turtle turtle) {
    x = turtle.x;
    y = turtle.y;
    angle = turtle.angle;
    //lineLength = turtle.lineLength - 1;
  }

  // Move forward by the specified distance
  void forward (float distance) {
    distance = distance * SIZE * random(0.9, 1.1);
    // Calculate the new position
    float xtarget = x+cos(radians(angle)) * distance;
    float ytarget = y+sin(radians(angle)) * distance;

    // If the pen is down, draw a line to the new position
    if (penDown) line(x, y, xtarget, ytarget);
    println(x + ", " + y + ", " + xtarget + ", " + ytarget);
    // Update position
    x = xtarget;
    y = ytarget;
  }

  // Turn left by given angle
  void left (float turnangle) {
    angle -= turnangle;
    println(angle);
  }

  // Turn right by given angle
  void right (float turnangle) {
    angle += turnangle;
    println(angle);
  }
}
\$\endgroup\$
7
  • 2
    \$\begingroup\$ Yes! An L-System! \$\endgroup\$ Dec 18, 2013 at 8:47
  • 1
    \$\begingroup\$ I agree about it being the most realistic :) \$\endgroup\$
    – Timtech
    Dec 20, 2013 at 22:31
  • \$\begingroup\$ @Timtech I disagree. I think this is the second most realistic, after Jan Dvorak's Javascript answer. I mean, look at those needles (on this one)! They are all clustered together in patterns that I have not seen on an evergreen tree. However, Jan's tree looks a lot like a fir sapling to me. \$\endgroup\$
    – Justin
    Dec 21, 2013 at 0:20
  • 1
    \$\begingroup\$ @Quincunx I must have missed that one. Still, this one looks pretty good. \$\endgroup\$
    – Timtech
    Dec 21, 2013 at 0:38
  • \$\begingroup\$ @Quincunx Jan's tree—and Howard's, too—is really cool, but not necessarily completely realistic. Jan even admits that the tree is only "quasirealistic". – RyanCarlson 10 secs ago \$\endgroup\$ Dec 21, 2013 at 13:51
12
votes
\$\begingroup\$

JavaScript (run on any page in console)

I was golfing this but then decided not to, so as you can see there are TONS of magic numbers :P

// size
s = 300

document.write('<canvas id=c width=' + s + ' height=' + s + '>')
c = document.getElementById('c').getContext('2d')
c.fillStyle = '#0D0'
for (var i = s / 3; i <= s / 3 * 2; i += s / 6) {
  c.moveTo(s / 2, s / 10)
  c.lineTo(s / 3, i)
  c.lineTo(s / 3 * 2, i)
  c.fill()
}
c.fillStyle = '#DA0'
c.fillRect(s / 2 - s / 20, s / 3 * 2, s / 10, s / 6)

Result for s = 300:

screenshot

s = 600:

screenshot

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Are you going to decorate the tree? :-) \$\endgroup\$
    – Justin
    Dec 12, 2013 at 23:53
  • 1
    \$\begingroup\$ @Quincunx Possibly, I may work on that later :) \$\endgroup\$
    – Doorknob
    Dec 12, 2013 at 23:54
  • 2
    \$\begingroup\$ GAH! Where are the semicolons?! \$\endgroup\$ Dec 17, 2013 at 0:46
  • 2
    \$\begingroup\$ @AndrewLarsson: Semicolons are not required in JavaScript \$\endgroup\$
    – ProgramFOX
    Dec 18, 2013 at 14:08
10
votes
\$\begingroup\$

Game Maker Language

spr_tree

spr_tree

The tree's Create Event

image_speed=0
size=get_integer("How big do you want me to be?#Integer between 1 and 10, please!",10)
image_index=size-1

Room, 402 by 599

The tree is placed at (0,0)

Bonus! You can resize the Christmas tree after the initial input with the keys 0-9.

\$\endgroup\$
8
votes
\$\begingroup\$

AppleScript + SE Answer

Actually picked this up by accident while editing an answer in this question. Go figure.

Use this by running the code, typing in your number that you want, swiping over to the SE post and clicking in the text field. This exploits the fact that quotes stack.

tell application "System Events"
    set layers to (display dialog "" default answer "")'s text returned as number
    delay 5
    repeat layers times
        keystroke ">"
    end repeat
end tell

Output (input 50):

>

\$\endgroup\$
6
votes
\$\begingroup\$

Decorated FORTRAN tree

    character(len=36) :: f='/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\'
    character(len=18) :: g = '                  '
    character(len=14) :: p = ".x$.+oO+oO.#,~"
    character(len=10) :: q

    n = iargc()
    if (n /= 1) then 
        k = len(g)
    else
        call getarg(1,q) 
        read(q,*) k
    end if
    l = modulo(k,len(g))+1
    do i=1,l
        j = mod(i,len(p)-1)+1
        print *,g(1:l-i),p(j:j),f(1:2*i),p(j+1:J+1)
        end do
    print *,g(1:l-1),f(1:4)
    end

The tree has a limited range of sizes, but it believe it accurately reflects the life of most Christmas trees.

From infant tree:

$./tree
 x/\$
 /\/\

To adolescent tree:

$./tree 6
       x/\$
      $/\/\.
     ./\/\/\+
    +/\/\/\/\o
   o/\/\/\/\/\O
  O/\/\/\/\/\/\+
 +/\/\/\/\/\/\/\o
       /\/\

To adult:

$./tree 17
                  x/\$
                 $/\/\.
                ./\/\/\+
               +/\/\/\/\o
              o/\/\/\/\/\O
             O/\/\/\/\/\/\+
            +/\/\/\/\/\/\/\o
           o/\/\/\/\/\/\/\/\O
          O/\/\/\/\/\/\/\/\/\.
         ./\/\/\/\/\/\/\/\/\/\#
        #/\/\/\/\/\/\/\/\/\/\/\,
       ,/\/\/\/\/\/\/\/\/\/\/\/\~
      ./\/\/\/\/\/\/\/\/\/\/\/\/\x
     x/\/\/\/\/\/\/\/\/\/\/\/\/\/\$
    $/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\.
   ./\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\+
  +/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\o
 o/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\O
                  /\/\    
\$\endgroup\$
5
votes
\$\begingroup\$

Java

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;

import javax.imageio.ImageIO;


public class ChristmasTree {
    static class Point{
        Point(int a,int b){x=a;y=b;}
        int x,y;
        double distance(Point o){
            int dx=x-o.x;
            int dy=y-o.y;
            return Math.sqrt(dx*dx+dy*dy);
        }
    }
    static int siz;
    static BufferedImage b;
    static Random r=new Random();
    public static void main(String[]args) throws IOException{

        if(args.length==0){
            siz=(new Scanner(System.in)).nextInt();
        }else{
            siz=Integer.parseInt(args[0]);
        }
        b=new BufferedImage(20*siz,30*siz,BufferedImage.TYPE_INT_RGB);
        Graphics2D g=(Graphics2D)b.getGraphics();
        g.setColor(new Color(140,70,20));
        int h=b.getHeight();h*=0.4;
        for(int i=(int)(0.4*b.getWidth());i<(int)(0.6*b.getWidth());i++){
            if(r.nextDouble()<0.3){
                g.drawLine(i,b.getHeight(),i+r.nextInt(2)-1,h);
            }
        }
        for(int i=h;i<b.getHeight();i++){
            if(r.nextDouble()<0.3){
                g.drawLine((int)(0.4*b.getWidth()),i,(int)(0.6*b.getWidth()),i);
            }
        }
        for(int i=0;i<siz;i++){
            g.setColor(new Color(r.nextInt(4),150+r.nextInt(15),20+r.nextInt(7)));
            g.drawLine(b.getWidth()/2-(int)(b.getWidth()*0.42*i/siz),(int)(b.getHeight()*0.9)+r.nextInt(5)-2,b.getWidth()/2+(int)(b.getWidth()*0.42*i/siz),(int)(b.getHeight()*0.9)+r.nextInt(5)-2);
            g.setColor(new Color(r.nextInt(4),150+r.nextInt(15),20+r.nextInt(7)));
            g.drawLine(b.getWidth()/2-(int)(b.getWidth()*0.42*i/siz),(int)(b.getHeight()*0.9),b.getWidth()/2,(int)(b.getHeight()*(0.1+(.06*i)/siz)));
            g.setColor(new Color(r.nextInt(4),150+r.nextInt(15),20+r.nextInt(7)));
            g.drawLine(b.getWidth()/2+(int)(b.getWidth()*0.42*i/siz),(int)(b.getHeight()*0.9),b.getWidth()/2,(int)(b.getHeight()*(0.1+(.06*i)/siz)));
        }
        g.setColor(new Color(150,120,40));
        g.fillOval((b.getWidth()-siz-2)/2,b.getHeight()/10,siz+2,siz+2);
        g.setColor(new Color(250,240,80));
        g.fillOval((b.getWidth()-siz)/2,b.getHeight()/10,siz,siz);
        List<Color>c=Arrays.asList(new Color(0,255,0),new Color(255,0,0),new Color(130,0,100),new Color(0,0,200),new Color(110,0,200),new Color(200,205,210),new Color(0,240,255),new Color(255,100,0));
        List<Point>pts=new ArrayList<>();
        pts.add(new Point((b.getWidth()-siz)/2,b.getHeight()/10));
        loop:for(int i=0;i<8+siz/4;i++){
            int y=r.nextInt((8*b.getHeight())/11);
            int x=1+(int)(b.getWidth()*0.35*y/((8*b.getHeight())/11));
            x=r.nextInt(2*x)-x+b.getWidth()/2;
            y+=b.getHeight()/8;
            g.setColor(c.get(r.nextInt(c.size())));
            x-=siz/2;
            Point p=new Point(x,y);
            for(Point q:pts){
                if(q.distance(p)<1+2*siz)continue loop;
            }
            pts.add(p);
            g.fillOval(x,y,siz,siz);
        }
        ImageIO.write(b,"png",new File("tree.png"));
    }
}

Sample results:

Size=3:

enter image description here

Size=4:

enter image description here

Size=5:

enter image description here

Size=12:

enter image description here

Size=20:

enter image description here

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I didn't notice this before. Super cool +1 \$\endgroup\$ Dec 22, 2015 at 20:04
4
votes
\$\begingroup\$

Rebol

With a dialect to display the symbols. To change the tree size, just change the parameter of make-tree.

make-tree: func [int /local tr] [
  tr: copy []
  length: (int * 2) + 3
  repeat i int [
      repeat j 3 [
            ast: to-integer ((i * 2) - 1 + (j * 2) - 2)
            sp: to-integer (length - ast) / 2
            append/dup tr space sp 
            append/dup tr "*" ast 
            append tr lf
      ]
  ]
  append/dup tr space (length - 1) / 2
  append tr "|"
  append tr lf
  tr
]

print make-tree 3

a tree with 3 layers a tree with 5 layers

\$\endgroup\$
4
  • 3
    \$\begingroup\$ Where you set the size and how it affects the generated tree? \$\endgroup\$
    – manatwork
    Dec 24, 2013 at 16:13
  • \$\begingroup\$ @manatwork To change the tree block. You can resize it or change some of the symbols at your will. \$\endgroup\$
    – Wayne Cui
    Dec 24, 2013 at 16:19
  • \$\begingroup\$ @WayneTsui this doesn't conform to the OP's requirements, "size must be choosable by some input method" and "larger inputs should produce a larger tree." \$\endgroup\$
    – ulidtko
    Dec 25, 2013 at 14:28
  • 1
    \$\begingroup\$ a new make-tree function from @kealist \$\endgroup\$
    – Wayne Cui
    Dec 25, 2013 at 15:49
4
votes
\$\begingroup\$

Python

import sys
w = sys.stdout.write
def t(n,s):
    for i in range(n):
        for a in range(n-i):
            w(" ")
        w("[")
        for l in range(i<<1):
            if i==n-1:
                w("_")
            else:
                w("~")
        w("]")
        print("")
    for o in range(s):
        for i in range(n):
            w(" ")
        print("[]")

t(10, 2)

enter image description here

\$\endgroup\$
1
vote
\$\begingroup\$

Ti-Basic 84

Asks for a size:

              :
            Input 
           S:Lbl 1
            S-1→S
              :
             "||
            "+""→
           Str1:" 
             "→Str2
         :Disp Str2+
        "   **":Disp 
       Str2+"  "+"*"+
      Str1+"*":Disp " 
     "+Str1+"*-"+"||-*
    ":Disp Str1+"*--||-
   -*":Disp "   *---||--
  -*":Disp "  *----||----
 *":Disp Str1+"   "+Str2+"
":If S>0:Goto 2:Goto 1:Lbl 
              2

Output (size 1):

       **
      *||*
     *-||-*
    *--||--*
   *---||---*
  *----||----*
       ||
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Where is the size choosable? This is a fixed-size tree \$\endgroup\$
    – Justin
    Dec 20, 2013 at 23:00
  • 1
    \$\begingroup\$ @Quincunx Fixed. \$\endgroup\$
    – Timtech
    Dec 20, 2013 at 23:33
  • 4
    \$\begingroup\$ -1; you can't just insert arbitrary whitespace into a TI-Basic program. \$\endgroup\$
    – lirtosiast
    Oct 6, 2015 at 4:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.