/* Copyright 2003-2009 Jetro Lauha - http://iki.fi/jetro/ * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * $Id: steppulse.pde 75 2009-10-10 20:47:01Z tonic $ * $Revision: 75 $ * * Based on code from: * "Texturing and Modeling: A Procedural Approach", Ebert et al. * http://tinyurl.com/texturing-and-modeling-book */ import processing.pdf.*; final int unitSize = 60; final int coordSize = 75; final int STEPS = unitSize * 3; final int OVERSHOOT = STEPS / 3; static float sClamp(float value, float min, float max) { return value < min ? min : (value > max ? max : value); } static float sStep(float value, float stepPosition) { return (float)(value < stepPosition ? 0 : 1); } static float sPulse(float value, float startPosition, float endPosition) { return sStep(value, startPosition) - sStep(value, endPosition); } static float sBoxStep(float value, float slopeStart, float slopeEnd) { float diff = slopeEnd - slopeStart; if (diff == 0) return sStep(value, slopeStart); return sClamp((value - slopeStart) / diff, 0, 1); } static float sSmoothStep(float value, float slopeStart, float slopeEnd) { float diff; if (value < slopeStart) return 0; if (value >= slopeEnd) return 1; diff = slopeEnd - slopeStart; if (diff == 0) return sStep(value, slopeStart); value = (value - slopeStart) / diff; /* normalize to [0..1] */ return value * value * (3 - 2 * value); } static float sBoxPulse(float value, float upSlopeStart, float upSlopeEnd, float downSlopeStart, float downSlopeEnd) { return sBoxStep(value, upSlopeStart, upSlopeEnd) - sBoxStep(value, downSlopeStart, downSlopeEnd); } static float sSmoothPulse(float value, float upSlopeStart, float upSlopeEnd, float downSlopeStart, float downSlopeEnd) { return sSmoothStep(value, upSlopeStart, upSlopeEnd) - sSmoothStep(value, downSlopeStart, downSlopeEnd); } class Plotter { float px, py; int cx, cy; void reset(int cx, int cy) { px = -1; py = -1; this.cx = cx; this.cy = cy; noFill(); stroke(0); strokeWeight(2.5f); beginShape(); } void plot(float x, float y) { x = cx + x * unitSize; y = cy - y * unitSize; if (px >= 0 && py >= 0) { //line(px, py, x, y); vertex(x, y); } px = x; py = y; } void end() { endShape(); fill(0); } } PFont font; static int frame = 0; Plotter plotter = new Plotter(); void setup() { size(400, 800); stroke(0); noFill(); strokeWeight(2.5); //println(PFont.list()); font = createFont("Droid Sans", 15); textAlign(CENTER, CENTER); } void coordAxes(int x, int y) { strokeWeight(1); stroke(240); line(x, y - unitSize, x + unitSize, y - unitSize); line(x + unitSize, y - unitSize, x + unitSize, y); stroke(160); line(x, y, x, y - coordSize); line(x, y, x + coordSize, y); line(x - unitSize / 25, y - unitSize, x + unitSize / 25, y - unitSize); line(x + unitSize, y - unitSize / 25, x + unitSize, y + unitSize / 25); fill(160); textFont(font, 10); textAlign(CENTER, CENTER); text("0", x - 3, y + 6); text("1", x - 5, y - unitSize - 1); text("1", x + unitSize, y + 6); textFont(font, 15); textAlign(LEFT, CENTER); } void draw() { if (frame == 0) beginRecord(PDF, "steppulse.pdf"); background(0xffffffff); smooth(); strokeJoin(MITER); float descTextX = unitSize * 1.2; float descTextY = unitSize / 2; int cx = 50, cy; cy = 100; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sClamp(2 * x, 0.25, 0.75); plotter.plot(x, y); } plotter.end(); text("y = clamp(2x, 0.25, 0.75)", cx + descTextX, cy - descTextY); cy = 200; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sStep(x, 0.5); plotter.plot(x, y); } plotter.end(); text("y = step(x, 0.5)", cx + descTextX, cy - descTextY); cy = 300; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sPulse(x, 0.25, 0.75); plotter.plot(x, y); } plotter.end(); text("y = pulse(x, 0.25, 0.75)", cx + descTextX, cy - descTextY); cy = 400; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sBoxStep(x, 0, 1); plotter.plot(x, y); } plotter.end(); text("y = boxStep(x, 0, 1)", cx + descTextX, cy - descTextY); cy = 500; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sSmoothStep(x, 0, 1); plotter.plot(x, y); } plotter.end(); text("y = smoothStep(x, 0, 1)", cx + descTextX, cy - descTextY); cy = 600; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sBoxPulse(x, 0, 0.4, 0.6, 1); plotter.plot(x, y); } plotter.end(); text("y = boxPulse(x, 0, 0.4, 0.6, 1)", cx + descTextX, cy - descTextY); cy = 700; coordAxes(cx, cy); plotter.reset(cx, cy); for (int a = -OVERSHOOT; a < STEPS + OVERSHOOT; ++a) { float x = (float)a / STEPS; float y = sSmoothPulse(x, 0, 0.4, 0.6, 1); plotter.plot(x, y); } plotter.end(); text("y = smoothPulse(x, 0, 0.4, 0.6, 1)", cx + descTextX, cy - descTextY); if (frame == 0) endRecord(); ++frame; }