// package dlb.processing.snowflake; /*** * Processing Snowflake generator by Design Less Better. (snowflake.pde) * Created by: Design Less Better, Nicholas Senske and Paul Tulipana. 11/29/2007 * Last updated by : Paul Tulipana, 12/10/2007 * * This program creates unique snowflake images or patterns from a list of family initials * The output defaults to 800 x 800 onscreen images. * * For .pdf's suitable for direct printing or export to other applications, * uncomment the marked lines and follow the instructions as you go. * * HOW IT WORKS: * The number of names in the family determines the number of branches and the overall size * of the letterforms used. The surname is used for the trunk; first and last branches are * the parents; the inbetween branches are the children. Capital letters are used for the * surname and parents; lowercase for children. * * Note: * All snowflakes created by this method have six "arms" which comprise the "trunk". * The next level are the "branches" which the letterforms or "leaves" are distributed over. * */ import processing.pdf.*; /*** * CONFIGURATION * Change these to alter the behavior of the Snowflake. ***/ boolean createPDF = false; // Change this to true if you want to create a PDF. boolean usingLocally = false; // Change this to false if you are running this over the web. String[] myFamily = { // Change these values to your family's names "Family Name (Surname)", // Your last name "Father's Name", // The father or first person in your family's name "Child's Name", // A child's name "Child's Name", // Add more of these if you want! "Mother's Name" }; // The mother or last family member's name String pdfName = "MyFamilySnowflake.pdf"; // The name of your PDF file. /*** You shouldn't have to change anything below here...unless you want to! ***/ /*** FONTS ***/ PFont trunkFont; // Font to use for the trunk PFont branchFont; // Font to use for the branches float trunkFontSize = 100; // Size of the font for the trunk float maxLengthOfArm = 250; // Maximum length of a trunk arm float branchFontSize; // Size of the font for the branches /*** COLORS ***/ /* Note: If you want a custom single color palatte, edit sfSingle and use switch '0' * for the variable colorPalette. For a two-color palatte, edit sfTwoA and sfTwoB and * use the switch '1'. */ color sfSingleA = color(0, 114, 188); // A nice ice blue for the winter color scheme color sfSingleB = color(110, 211, 255); // A nice ice blue for the winter color scheme (light variant) color sfTwoA = color(57, 181, 74); // A frosty green for the christmas color scheme color sfTwoB = color(158, 11, 15); // A deep christmas red color sfBlack = color(0,0,0); // Black! color sfWhite = color(255,255,255); // White! color sfTemp1 = color(0,0,0); // Stores a temporary color value color sfTemp2 = color(0,0,0); // Stores a temporary color value int nextColor = 0; // Switch for alternating fill colors float colorVariationLight = 35; // How much variation between colors a value between 0 and 50 is recommended, but may change depending on the colors chosen float colorVariationDark = 15; // How much variation between colors a value between 0 and 50 is recommended, but may change depending on the colors chosen /*** NAMES, LETTERS ***/ String familyNameString = ""; // This string comes from our HTML Form char[] familyNames; // The array of first characters for all proper names in the family // arranged [F,D,c1,c2..cn,M]: (Family Name, Dad, child1, child2...childN, Mom) int numOfNames; // Determines the number of branches and other variables /*** MATH ***/ float[][] sfMatrix; // Snowflake matrix float angle = random(45,90); // The angle for the branches ranges from 45 to 90 degrees and is constant int colorPalette = 0; // Switch to choose the palette: 0 = Winter (blue), 1 = Christmas (red,green), 2 = export (black on white) float ratio = 0.65; // Ratio between trunck and branch font sizes /*** * SETUP is the first method Processing runs when an application starts. */ void setup() { size(600,600); // Set the size of the screen colorMode(RGB, 255); // Set the color mode to RGB smooth(); // Turn on the font-smoothing if (!usingLocally) { // If we're running this as an applet: trunkFont = loadFont("Egyptian710-100.vlw"); // This is the font we use on our web app. branchFont = loadFont("Egyptian710-100.vlw"); // Feel free to change it to your own. familyNameString = param("family"); // Load up the family name stuff from our param } else { // If we are running this from Processing: hint(ENABLE_NATIVE_FONTS); // It's a good idea to go with native fonts trunkFont = createFont("Arial", 100); // and createFont(). branchFont = createFont("Arial", 100); // Again, feel free to change Arial to your own font. StringBuffer buffer = new StringBuffer(); // This block creates a StringBuffer and parses out all if (myFamily != null) // the first letter of all the names you entered above { // into our familyNameString (using the appropriate for(int i=0; i < myFamily.length; i++) // capitalization). { // For all the strings in the myFamily array if ((i==0)||(i==1)||(i==myFamily.length-1)) // For the first two and last names (surname, father and { // mother's name) we capitalize the letter. buffer.append(Character.toUpperCase(myFamily[i].charAt(0))); } else // All the children's names we set to lower-case. { buffer.append(Character.toLowerCase(myFamily[i].charAt(0))); } } familyNameString = buffer.toString(); } } familyNames = familyNameString.toCharArray(); // Explode it. numOfNames = familyNames.length; // Number of people in the family. sfMatrix = new float[numOfNames][3]; // Initialize Snowflake Matrix. branchFontSize = maxLengthOfArm/numOfNames; // Set the font size of our branches. } /*** * Processing runs draw() every frame. It's the zoetrope method. */ void draw() { if (createPDF) { beginRecord(PDF, pdfName); // If you told us to, start recording PDF. } background(sfWhite); // White background defineSfMatrix(); // Calls our method to define the snowflake matrix makeSnowFlakeTrunk(); // Calls our method to make the snowflake trunk makeSnowFlakeBranches(); // Calls our method to make the snowflake branches if (createPDF) { endRecord(); // All good things must come to an end. } noLoop(); // Explain to P5 not to run an animation (one draw is fine) } /*** * On mouseClick, set a random angle for the snowflake to draw at, and redraw. */ void mouseClicked() { angle = random(45,90); loop(); } /*** * You can change the color palette with your keyboard. Press 1,2,3 to alternate palettes. */ void keyPressed() { if (key == '1') { colorPalette = 0; angle = random(45,90); loop(); } if (key == '2') { colorPalette = 1; angle = random(45,90); loop(); } if (key == '3') { colorPalette = 2; angle = random(45,90); loop(); } } /*** * DEFINE SNOWFLAKE MATRIX determines the 'code' of each snowflake, generated from a combination * of inputs taken from the interface and the dataset of initials. A snowflake is basically a * multi-dimensional array. It has a number of branches equal to the number of initials in the inputs. * Each branch is an array with the following data structure: * branch = {f, h, l}; * f: flip, which direction the leaf faces. This is either 1 or -1; up or down, respectively. * h: height, where it lays in the local Y-axis (how high) on the trunk. * l: length, how long the branch is; in number of letterforms. * If you're so inclined, you can comment out this method and hardcode an array to create a * less random piece. */ void defineSfMatrix(){ // NOTE: There are four types of snowflakes: [0->Random, 1->Two-stage, 2->Solid, 3->Tapering] int sfType = int(floor(random(0,4))); // Randomly choose a type float spacing = branchFontSize; // Set the vertical spacing here float initLength = 0; // Initialize the variable for length int extra = numOfNames/2; // With larger families, you get smaller fonts; this is a hack to preserve the snowflake shapes when this happens switch(sfType) { case 0: // Random: All settings are completely random for(int i=0;i 6) { currentSide = 90; // Draw the opposite side, so flip 180 degrees } else { currentSide = 180 - ((angle - 45) * 2); } pushMatrix(); translate(width/2, height/2); // Move to the center of the stage rotate(radians(sfTrunkArm * 60)); translate(0, branchHeight); // Move up to the branch height if(sfMatrix[currentBranch][0] == 1) { rotate(radians(angle + currentSide)); } else { rotate(radians(-(angle + currentSide))); }; if(sfTrunkArm > 6) { for(float distAlongBranch = 0; distAlongBranch < sfMatrix[currentBranch][2]; distAlongBranch ++) // Start drawing leaves (letterforms) { makeLetterBranch(familyNames[currentBranch], branchFont); translate(leafSpacing, 0); } } else { count = 0; translate(-textWidth(familyNames[currentBranch]),0); for(float distAlongBranch = sfMatrix[currentBranch][2]; distAlongBranch > 0; distAlongBranch--) // Start drawing leaves (letterforms) { makeLetterBranch(familyNames[currentBranch], branchFont); count++; translate(-leafSpacing, 0); } } popMatrix(); } } } /*** * MAKE SNOW FLAKE TRUNK draws the central trunk of the snowflake. */ void makeSnowFlakeTrunk() { float armSpacing = trunkFontSize * 0.45; textFont(trunkFont); newSubPalette(); for(int sfTrunkArm=0; sfTrunkArm < 6; sfTrunkArm++) // Do this six times, once for each arm of the trunk { pushMatrix(); translate(width/2, height/2); // Move to the center of the stage rotate(radians(sfTrunkArm * 60)); // Rotate to the correct arm of the trunk char x = familyNames[0]; translate( -textWidth(x)/2,0); for(float distAlongArm = 0; distAlongArm < maxLengthOfArm; distAlongArm += armSpacing) { translate(0, armSpacing); // Move up the trunk if(distAlongArm == 0) { // Do nothing. } else { makeLetterTrunk(familyNames[0], trunkFont); } } popMatrix(); } } /*** * Helper method to make a letterform from the trunk * @param letter - char to draw * @param whichFont - reference to font (PFont) to draw /letter/ with. ***/ void makeLetterTrunk(char letter, PFont whichFont) { setColor(); textFont(whichFont); text(letter,0,0); } /*** * Helper method to make a letterform from a branch * @param letter - char to draw * @param whichFont - reference to font (PFont) to draw /letter/ with. ***/ void makeLetterBranch(char letter, PFont whichFont) { setColor(); textFont(whichFont, branchFontSize); text(letter,0,0); } /*** * Helper method to create a new variation of the palette colors for each set of branches. */ void newSubPalette() { colorMode(HSB, 100); switch(colorPalette) { case 0: // Create two different tints of one color sfTemp1 = color (hue(sfSingleA), saturation(sfSingleA) + random(-colorVariationDark,colorVariationLight), brightness(sfSingleA) + random(-colorVariationDark,colorVariationLight)); sfTemp2 = color (hue(sfSingleB), saturation(sfSingleB) + random(-colorVariationDark,colorVariationLight), brightness(sfSingleB) + random(-colorVariationDark,colorVariationLight)); break; case 1: // Create different tints for two different colors sfTemp1 = color (hue(sfTwoA), saturation(sfTwoA) + random(-colorVariationDark,colorVariationLight), brightness(sfTwoA) + random(-colorVariationDark,colorVariationLight)); sfTemp2 = color (hue(sfTwoB), saturation(sfTwoB) + random(-colorVariationDark,colorVariationLight), brightness(sfTwoB) + random(-colorVariationDark,colorVariationLight)); break; case 2: fill(sfBlack); break; } } /*** * Helper method to set the appropriate fill() color depending upon the palette settings. * Basically, we call this instead of fill(), and it figures out what to do. */ void setColor() { if(colorPalette != 2) // If we're not just going to use black... { if(nextColor == 0) { nextColor = 1; // Alternate between colors in the branch's palette fill(sfTemp2); } else { nextColor = 0; fill(sfTemp1); }; } }