net.paulhertz.aifile
Class BezMultiCurve

java.lang.Object
  extended by net.paulhertz.aifile.DisplayComponent
      extended by net.paulhertz.aifile.BezShape
          extended by net.paulhertz.aifile.BezMultiCurve
All Implemented Interfaces:
ColorableINF, Visitable

public class BezMultiCurve
extends BezShape

Provides factory methods to create an open path consisting of multiple Bezier curves.

Example shows how to draw BezMultiLine and BezMultiCurve objects, also how to use a DocumentComponent as a display list, how to turn layers on and off, and how to set and export opacity. The signature of the fade in and fade out methods gets altered during export to JavaDocs, which apparently ignores code in angle brackets. The sample file is fine. Here's the right signature:
public boolean fadeOut(ArrayList<? extends BezShape> shapes, int step)

+Example
/**
 * March 14, 2012 3:35:47 PM CDT
 * Updated June 25, 2013 for IgonoCodeLib 0.3 release.
 * Sample code for IgnoCodeLib 0.3 Processing library by Paul Hertz
 * Not compatible with earlier versions.
 * Shows how to create multi-segment lines and curves. 
 * Also: hide and show layers, set opacity on lists of shapes, 
 * use document as a display list in the draw() method.
 */

import net.paulhertz.aifile.*;
import net.paulhertz.util.RandUtil;
import java.io.*;
import java.util.ArrayList;

/** list of straight line shapes */
public ArrayList multiLines; 
/** list of curved line shapes */
public ArrayList multiCurves;
/** layers we put the shapes into */
public LayerComponent zigzagLayer;
public LayerComponent curvyLayer;
/** the document, top of the display list hierarchy */
public DocumentComponent document;
/** random number utility  */
public RandUtil rand;
/** storage for colors (as Integers) */
public ArrayList farben;
/** grid spacing */
int spacer = 40;
/** number of horizontal grid cells */
int horizontalGrid = 20;
/** number of vertical grid cells */
int verticalGrid = 16;
/** color channel values in range 0..255 to use for our curves and lines */
int[] zigzagChannelValues = {34, 47, 55, 76, 89};
int[] curvyChannelValues = {89, 123, 144, 199, 233};
/** IgnoCodeLib library */
IgnoCodeLib igno;


public void setup() {
  size(spacer * horizontalGrid, spacer * verticalGrid);
  smooth();
  // Set up the library, which will store a reference to the host PApplet 
  // for other classes in the library to use.
  igno = new IgnoCodeLib(this);
  multiLines = new ArrayList();
  multiCurves = new ArrayList();
  // arroy of colors
  farben = new ArrayList();
  rand = new RandUtil();
  createMultiLines();
  createMultiCurves();
  // create the document up front because 
  // we're going to use its display list functionality
  createDocument();
  printHelp();
}


public void draw() {
  background(233, 233, 246);
  // the simplest way to draw everything is just to call document.draw()
  // just put new components into the document as you create them and they will draw 
  document.draw();
}

public void printHelp() {
  println("\nType 's' to save file");
  println("Type 'x' to change colors");
  println("Type '1' or '2' to show and hide layers");
  println("Press '3,' '4,' to fade curves and '5,' or '6' to fade zigzag lines out or in");
  println("Type 'h' to show this help message");
}


public void keyReleased() {
  if (key == 's' || key == 'S') {
    // save file, put shapes in two layers
    String filename = "Multi.ai";
    saveAI(filename, farben, document);
    println("saved" + "");
  }
  else if (key == 'x' || key == 'X') {
    // make colors, multiCurves and multiLines again
    farben.clear();
    changeStroke(multiCurves, curvyChannelValues, 4, 12);
    changeStroke(multiLines, zigzagChannelValues, 2, 8);

  }
  else if (key == '1') {
    // hide or show curvyLayer
    if (curvyLayer.isVisible()) {
      curvyLayer.hide();
    }
    else {
      curvyLayer.show();
    }
  }
  else if (key == '2') {
    // hide or show zigzagLayer
    if (zigzagLayer.isVisible()) {
      zigzagLayer.hide();
    }
    else {
      zigzagLayer.show();
    }
  }
  else if (key == 'h' || key == 'H') {
    printHelp();
  }
  else {
    ;
  }
}

public void keyPressed() {
  // key presses are used to fade layers in and out by steps
  // holding a key down repeats the fade action
  int fadeStep = 8;
  if (key == '3') {
    fadeOut(multiCurves, fadeStep);
  }
  else if (key == '4') {
    fadeIn(multiCurves, fadeStep);
  }
  else if (key == '5') {
    fadeOut(multiLines, fadeStep);
  }
  else if (key == '6') {
    fadeIn(multiLines, fadeStep);
  }
}

  
/**
 * Create some multi-segment lines, save them in {@code multiLines}.
 */
private void  createMultiLines() {
  int numberOfPoints = horizontalGrid;
  float[] gridCoords = new float[numberOfPoints * 2];
  int j = 0;
  for (int i = 0; i < numberOfPoints; i++) {
    gridCoords[j++] = i * spacer + spacer * 0.5f;
    gridCoords[j++] = (i % 2) == 1 ? spacer * 0.5f : spacer * 1.5f;
  }
  int count = 0;
  while (count < verticalGrid - 1) {
    multiLines.add(ziggyLine(gridCoords));
    for (int i = 0; i < gridCoords.length;) {
      i++;                                  // step over x-coord
      float y = gridCoords[i] + spacer;     // y = spacer + y-coord
      gridCoords[i++] = y;                  // set y-coord
    }
    count++;
  }
}

/**
 * @param coords   coordinate points for a multi-segment line
 * @return         a BezMultiLine instance created from coords
 */
private BezMultiLine ziggyLine(float[]coords) {
  // int[] zigzagChannelValues = {34, 47, 55, 76, 89};
  int c = Palette.randColor(zigzagChannelValues);
  farben.add(c);
  stroke(c);
  strokeWeight(rand.randomInRange(2, 8));
  BezMultiLine mul = BezMultiLine.makeMultiLine(coords);
  return mul;
}


/**
 * create some multi-segment curves, save them in {@code multiCurves}
 */
public void createMultiCurves() {
  // bias determines the amount of curvature
  float bias = 0.5f;
  int numberOfPoints = horizontalGrid;
  float[] gridCoords = new float[numberOfPoints * 2];
  int j = 0;
  // fill a list with grid points
  for (int i = 0; i < numberOfPoints; i++) {
    gridCoords[j++] = i * spacer + spacer * 0.5f;
    gridCoords[j++] = (i % 2) == 1 ? spacer * 1.5f : spacer * 0.5f;
  }
  // Set aside storage for curve vertices. the first vertex uses
  // 2 coordinates for one anchor point, subsequent vertices require 6
  // for two control points and one anchor point
  float[] curveCoords = new float[2 + (numberOfPoints -1) * 6];
  float x = gridCoords[0];
  float y = gridCoords[1];
  curveCoords[0] = x;
  curveCoords[1] = y;
  j = 2;
  float offset = spacer * bias;
  // create the first curvy line
  for (int i = 2; i < gridCoords.length;) {
    // first control point, offset from most recent anchor point
    curveCoords[j++] = x + offset;
    curveCoords[j++] = y;
    // set x and y to coordinates of the next anchor point
    x = gridCoords[i++];
    y = gridCoords[i++];
    // second control point, offset from next anchor point
    curveCoords[j++] = x - offset;
    curveCoords[j++] = y;
    // store next anchor point
    curveCoords[j++] = x;
    curveCoords[j++] = y;
  }
  multiCurves.add(curvyLine(curveCoords));
  int count = 1;
  // create the rest of the curvy lines
  while (count < verticalGrid - 1) {
    for (int i = 0; i < gridCoords.length;) {
      i++;                             // step over x-coord
      y = gridCoords[i] + spacer;      // y = spacer + y-coord
      gridCoords[i++] = y;             // set y-coord
    }
    x = gridCoords[0];
    y = gridCoords[1];
    curveCoords[0] = x;
    curveCoords[1] = y;
    j = 2;
    for (int i = 2; i < gridCoords.length;) {
      // first control point
      curveCoords[j++] = x + offset;
      curveCoords[j++] = y;
      x = gridCoords[i++];
      y = gridCoords[i++];
      // second control point
      curveCoords[j++] = x - offset;
      curveCoords[j++] = y;
      // anchor point
      curveCoords[j++] = x;
      curveCoords[j++] = y;
    }
    multiCurves.add(curvyLine(curveCoords));
    count++;
  }
}

/**
 * @param coords   coordinate points for a multi-segment curve
 * @return         a BezMultiLine instance created from coords
 */
private BezMultiCurve curvyLine(float[]coords) {
  // int[] curvyChannelValues = {89, 123, 144, 199, 233};
  int c = Palette.randColor(curvyChannelValues);
  farben.add(c);
  stroke(c);
  strokeWeight(rand.randomInRange(4, 12));
  BezMultiCurve mul = BezMultiCurve.makeMultiCurve(coords);
  return mul;
}


public void createDocument() {
  document = new DocumentComponent("MultiCurves and MultiLines");
  // get lots of feedback as we save
  document.setVerbose(true);
  document.setCreator("Ignotus");
  document.setOrg("IgnoStudio");
  document.setWidth(width);
  document.setHeight(height);
  curvyLayer = new LayerComponent("Curvy");
  curvyLayer.add(multiCurves);
  document.add(curvyLayer);
  zigzagLayer = new LayerComponent("Zigzag");
  zigzagLayer.add(multiLines);
  document.add(zigzagLayer);
}


/**
 * @param shapes   list of BezShape
 * @param step     amount to fade
 * @return         true when fade out is complete (opacity == 0)
 */
public boolean fadeOut(ArrayList shapes, int step) {
  int opacity = shapes.get(0).strokeOpacity();
  opacity = opacity < step ? 0 : opacity - step;
  //println("fade out opacity = " + opacity);
  boolean isFadedOut = (opacity == 0);
  for (BezShape sh : shapes) {
      sh.setStrokeOpacity(opacity);
  }
  return isFadedOut;
}

/**
 * @param shapes   list of BezShape
 * @param step     amount to fade
 * @return         true when fade in is complete (opacity == 255)
 */
public boolean fadeIn(ArrayList shapes, int step) {
  int opacity = shapes.get(0).strokeOpacity();
  opacity = opacity + step > 255 ? 255 : opacity + step;
  //println("fade in opacity = " + opacity);
  boolean isFadedIn = (opacity == 255);
  for (BezShape sh : shapes) {
      sh.setStrokeOpacity(opacity);
  }
  return isFadedIn;
}


/**
 * @param shapes          list of shapes to change
 * @param channelValues   possible values for color channels
 * @param weightLo        low end of range for stroke weight
 * @param weightHi        high end of range for stroke weight
 */
public void changeStroke(ArrayList shapes, int[] channelValues, float weightLo, float weightHi) {
  for (BezShape sh : shapes) {
    int c = Palette.randColor(channelValues);
    farben.add(c);
    sh.setStrokeColor(c);
    sh.setWeight(rand.randomInRange(weightLo, weightHi));
  }
}

/**
 * Saves shapes to an Adobe Illustrator file.
 * @param aiFilename      name of the file, should end with ".ai"
 * @param paletteColors   a list of Integers (colors)
 * @param document        the DocumentComponent to save
 */
public void saveAI(String aiFilename, ArrayList paletteColors, DocumentComponent document) {
  // tell BezShape to use opacity values when exporting to AI
  AIFileWriter.setUseTransparency(true);
  println("saving Adobe Illustrator file " + aiFilename + "...");
  PrintWriter output = createWriter(aiFilename);
  Palette pal = document.getPalette();
  // include black and white in the palette
  pal.addBlackWhiteGray();
  // add our colors
  pal.addColors(paletteColors);
  // write the file, transforming geometry from the Processing coordinate system 
  // into the Adobe Illustrator coordinate system
  document.writeWithAITransform(output);
}



Nested Class Summary
 
Nested classes/interfaces inherited from class net.paulhertz.aifile.BezShape
BezShape.BezType
 
Field Summary
 
Fields inherited from class net.paulhertz.aifile.BezShape
bezType, CURVE_SEGMENT, KAPPA, LINE_SEGMENT
 
Method Summary
static BezMultiCurve makeMultiCurve(float[] coords)
          Returns a multi-segment Bˇzier curve from an array of float.
static BezMultiCurve makeMultiCurve(PApplet parent, float[] coords)
          Returns a multi-segment Bˇzier curve from an array of float.
 
Methods inherited from class net.paulhertz.aifile.BezShape
accept, accept, add, add, append, append, append, asPolygon, asPolygon, asPolygon, asPolygon, bezCircle, bezCurve, bezCurveShape, bezEllipse, bezLine, bezMultiCurve, bezMultiLine, bezPoly, bezRectangle, bezRegularPoly, bezTriangle, bezTriangle, bezType, bounds, bounds, boundsRect, calculateCenter, centerVertex, children, clone, containsPoint, containsPoint, curveIterator, curves, curvesCopy, draw, draw, drawQuick, fillColor, fillOpacity, get, getAnchorCenter, getBoundsCenter, getCoords, getCtm, getGeoCenter, getMatrix, hasFill, hasStroke, isClosed, isTerminal, iterator, moveTo, polySize, polySize, polySteps, remove, rotateShape, rotateShape, scaleShape, scaleShape, scaleShape, scaleShape, setBezType, setCenter, setCenter, setCenter, setColors, setCtm, setCtm, setCtm, setCurves, setFillColor, setFillOpacity, setHasFill, setHasStroke, setIsClosed, setNoFill, setNoStroke, setPolySteps, setStartPoint, setStartPoint, setStartPoint, setStrokeColor, setStrokeOpacity, setUseTransparency, setWeight, setX, setY, startVertex, startVertexArray, strokeColor, strokeOpacity, transform, transform, transformShape, translateShape, useTransparency, weight, write, write, x, xcoords, xcoords, xctr, y, ycoords, ycoords, yctr
 
Methods inherited from class net.paulhertz.aifile.DisplayComponent
hide, id, isLocked, isVisible, parentComponent, setLocked, setParentComponent, setVisible, show
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

makeMultiCurve

public static BezMultiCurve makeMultiCurve(PApplet parent,
                                           float[] coords)
Returns a multi-segment Bˇzier curve from an array of float. Fill, stroke, and weight are set from their values in the Processing environment. The first coordinate pair is the initial anchor point, the following coordinate pairs correspond to the first control point, second control point, and final anchor point of each additional curve. The shape will be open and consist only of Bezier curves with no straight lines.

Parameters:
parent - reference to host PApplet used for calls to Processing environment
coords - an array of coordinate pairs
Returns:
a multi-segment curved line of type BezShape.BEZ_MULTICURVE

makeMultiCurve

public static BezMultiCurve makeMultiCurve(float[] coords)
Returns a multi-segment Bˇzier curve from an array of float. Fill, stroke, and weight are set from their values in the Processing environment. The first coordinate pair is the initial anchor point, the following coordinate pairs correspond to the first control point, second control point, and final anchor point of each additional curve. The shape will be open and consist only of Bezier curves with no straight lines. PApplet used for calls to the Processing environment is obtained from IgnoCodeLib, which must be correctly initialized in setup. If IgnoCodeLib does not have a reference to a PApplet, it throws a NullPointerException.

Parameters:
coords - an array of coordinate pairs
Returns:
a multi-segment curved line of type BezShape.BEZ_MULTICURVE


Processing library IgnoCodeLib by Paul Hertz. (C) 2013