/** * Copyright 2004-2008 Ricard Marxer * * This file is part of Geomerative. * * Geomerative is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * Geomerative is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * Geomerative. If not, see . */ package geomerative; import processing.core.PApplet; import processing.core.PConstants; import processing.core.PGraphics; /** * RG is a static class containing all the states, modes, etc.. Geomerative is * mostly used by calling RG methods. e.g. RShape s = RG.getEllipse(30, 40, 80, * 80) */ public class RG { /** * @invisible */ private static boolean initialized = false; /** * @invisible */ private static PApplet parent; /** * @invisible */ public static boolean ignoreStyles = false; /** * @invisible */ public static boolean useFastClip = true; /** * The adaptor adapts the shape to a particular shape by adapting each of * the groups points. This can cause deformations of the individual elements * in the group. */ public final static int BYPOINT = 0; /** * The adaptor adapts the shape to a particular shape by adapting each of * the groups elements positions. This mantains the proportions of the * shapes. */ public final static int BYELEMENTPOSITION = 1; /** * The adaptor adapts the shape to a particular shape by adapting each of * the groups elements indices. This mantains the proportions of the shapes. */ public final static int BYELEMENTINDEX = 2; /** * @invisible */ static int adaptorType = BYELEMENTPOSITION; /** * @invisible */ static float adaptorScale = 1F; /** * @invisible */ static float adaptorLengthOffset = 0F; /** * ADAPTATIVE segmentator minimizes the number of segments avoiding * perceptual artifacts like angles or cusps. Use this in order to have * polygons and meshes with the fewest possible vertices. */ public static int ADAPTATIVE = RCommand.ADAPTATIVE; /** * UNIFORMLENGTH segmentator is the slowest segmentator and it segments the * curve on segments of equal length. This can be useful for very specific * applications when for example drawing incrementaly a shape with a uniform * speed. */ public static int UNIFORMLENGTH = RCommand.UNIFORMLENGTH; /** * UNIFORMSTEP segmentator is the fastest segmentator and it segments the * curve based on a constant value of the step of the curve parameter, or on * the number of segments wanted. This can be useful when segmpointsentating * very often a Shape or when we know the amount of segments necessary for * our specific application. */ public static int UNIFORMSTEP = RCommand.UNIFORMSTEP; static int dpi = 72; /** * @invisible */ public static class LibraryNotInitializedException extends NullPointerException { private static final long serialVersionUID = -3710605630786298671L; LibraryNotInitializedException() { super("Must call RG.init(this); before using this library."); } } /** * @invisible */ public static class FontNotLoadedException extends NullPointerException { private static final long serialVersionUID = -3710605630786298672L; FontNotLoadedException() { super("Use RG.loadFont(filename) and RG.textFont(font, size) to load and set fonts first."); } } /** * @invisible */ public static class NoPathInitializedException extends NullPointerException { private static final long serialVersionUID = -3710605630786298673L; NoPathInitializedException() { super("Must initialize a path by calling RG.beginShape() first."); } } static RShape shape; static RFont fntLoader = null; // Font methods /** * Load and get the font object that can be used in the textFont method. * * @eexample loadFont * @param fontFile the filename of the font to be loaded * @return RFont, the font object */ public static RFont loadFont(String fontFile) { RFont newFntLoader = new RFont(fontFile); if (fntLoader == null) { fntLoader = newFntLoader; } return newFntLoader; } /** * Draw text to the screen using the font set using the textFont method. * * @eexample text * @param text the string to be drawn on the screen */ public static void text(String text) { RShape grp = getText(text); grp.draw(); } /** * Set the font object to be used in all text calls. * * @eexample textFont * @param font the font object to be set * @param size the size of the font */ public static void textFont(RFont font, int size) { font.setSize(size); fntLoader = font; } /** * Get the shape corresponding to a text. Use the textFont method to select * the font and size. * * @eexample getText * @param font the filename of the font to be loaded * @param text the string to be created * @param size the size of the font to be used * @param align the alignment. Use RG.CENTER, RG.LEFT or RG.RIGHT * @return RShape, the shape created */ public static RShape getText(String text, String font, int size, int align) { RFont tempFntLoader = new RFont(font, size, align); return tempFntLoader.toShape(text); } public static RShape getText(String text) { if (fntLoader == null) { throw new FontNotLoadedException(); } return fntLoader.toShape(text); } // Shape methods /** * Draw a shape to a given position on the screen. * * @eexample shape * @param shp the shape to be drawn * @param x the horizontal coordinate * @param y the vertical coordinate * @param w the width with which we draw the shape * @param h the height with which we draw the shape */ public static void shape(RShape shp, float x, float y, float w, float h) { RShape tshp = new RShape(shp); RMatrix transf = new RMatrix(); transf.translate(x, y); transf.scale(w / tshp.getOrigWidth(), h / tshp.getOrigHeight()); tshp.transform(transf); tshp.draw(); } public static void shape(RShape shp, float x, float y) { RShape tshp = new RShape(shp); RMatrix transf = new RMatrix(); transf.translate(x, y); tshp.transform(transf); tshp.draw(); } public static void shape(RShape shp) { shp.draw(); } /** * Create a shape from an array of point arrays. * * @param points * @return * @eexample createShape */ public static RShape createShape(RPoint[][] points) { return new RShape(points); } /** * Load a shape object from a file. * * @return * @eexample loadShape * @param filename the SVG file to be loaded. Must be in the data directory */ public static RShape loadShape(String filename) { RSVG svgLoader = new RSVG(); return svgLoader.toShape(filename); } /** * Save a shape object to a file. * * @eexample saveShape * @param filename the SVG file to be saved. * @param shape the shape to be saved. */ public static void saveShape(String filename, RShape shape) { RSVG svgSaver = new RSVG(); String str = svgSaver.fromShape(shape); String[] strs = PApplet.split(str, "\n"); RG.parent().saveStrings(filename, strs); } // Methods to create shapes /** * Begin to create a shape. * * @eexample createShape */ public static void beginShape() { shape = new RShape(); } /** * Begin a new path in the current shape. Can only be called inside * beginShape() and endShape(). * * @param endMode if called with RG.CLOSE it closes the current path before * starting the new one. * @eexample createShape */ public static void breakShape(int endMode) { if (endMode == PConstants.CLOSE) { shape.addClose(); } shape.updateOrigParams(); breakShape(); } public static void breakShape() { shape.addPath(); } /** * Add a vertex to the shape. Can only be called inside beginShape() and * endShape(). * * @eexample createShape * @param x the x coordinate of the vertex * @param y the y coordinate of the vertex */ public static void vertex(float x, float y) { if (shape.countPaths() == 0) { shape.addMoveTo(x, y); } else { shape.addLineTo(x, y); } } /** * Add a bezierVertex to the shape. Can only be called inside beginShape() * and endShape(). * * @eexample createShape * @param cx1 the x coordinate of the first control point * @param cy1 the y coordinate of the first control point * @param cx2 the x coordinate of the second control point * @param cy2 the y coordinate of the second control point * @param x the x coordinate of the end point * @param y the y coordinate of the end point */ public static void bezierVertex(float cx1, float cy1, float cx2, float cy2, float x, float y) { if (shape.countPaths() == 0) { throw new NoPathInitializedException(); } else { shape.addBezierTo(cx1, cy1, cx2, cy2, x, y); } } /** * End the shape being created and draw it to the screen or the PGraphics * passed as parameter. * * @eexample createShape * @param g the canvas on which to draw. By default it draws on the screen */ public static void endShape(PGraphics g) { shape.draw(g); shape = null; } public static void endShape() { shape.draw(); shape = null; } /** * End the shape being created and get it as an object. * * @return * @eexample getShape */ public static RShape getShape() { RShape returningGroup = new RShape(); returningGroup.addChild(shape); shape = null; returningGroup.updateOrigParams(); return returningGroup; } /** * Get an ellipse as a shape object. * * @eexample getEllipse * @param x x coordinate of the center of the shape * @param y y coordinate of the center of the shape * @param w width of the ellipse * @param h height of the ellipse * @return RShape, the shape created */ public static RShape getEllipse(float x, float y, float w, float h) { return RShape.createEllipse(x, y, w, h); } public static RShape getEllipse(float x, float y, float w) { return getEllipse(x, y, w, w); } /** * Get a line as a shape object. * * @eexample getLine * @param x1 x coordinate of the first point of the line * @param y1 y coordinate of the first point of the line * @param x2 x coordinate of the last point of the line * @param y2 y coordinate of the last point of the line * @return RShape, the shape created */ public static RShape getLine(float x1, float y1, float x2, float y2) { return RShape.createLine(x1, y1, x2, y2); } /** * Get an rectangle as a shape object. * * @eexample getRect * @param x x coordinate of the top left corner of the shape * @param y y coordinate of the top left of the shape * @param w width of the rectangle * @param h height of the rectangle * @return RShape, the shape created */ public static RShape getRect(float x, float y, float w, float h) { return RShape.createRectangle(x, y, w, h); } public static RShape getRect(float x, float y, float w) { return getRect(x, y, w, w); } /** * Get a star as a shape object. * * @eexample getStar * @param x x coordinate of the center of the shape * @param y y coordinate of the center of the shape * @param widthBig the outter width of the star polygon * @param widthSmall the inner width of the star polygon * @param spikes the amount of spikes on the star polygon * @return RShape, the shape created */ public static RShape getStar(float x, float y, float widthBig, float widthSmall, int spikes) { return RShape.createStar(x, y, widthBig, widthSmall, spikes); } /** * Get a ring as a shape object. * * @eexample getRing * @param x x coordinate of the center of the shape * @param y y coordinate of the center of the shape * @param widthBig the outter width of the ring polygon * @param widthSmall the inner width of the ring polygon * @return RShape, the shape created */ public static RShape getRing(float x, float y, float widthBig, float widthSmall) { return RShape.createRing(x, y, widthBig, widthSmall); } // Transformation methods public static RShape centerIn(RShape grp, PGraphics g, float margin) { RShape ret = new RShape(grp); ret.centerIn(g, margin); return ret; } public static RShape centerIn(RShape grp, PGraphics g) { return centerIn(grp, g, 0); } /** * Split a shape along the curve length in two parts. * * @eexample split * @param shp the shape to be splited * @param t the proportion (a value from 0 to 1) along the curve where to * split * @return RShape[], an array of shapes with two elements, one for each side * of the split */ public static RShape[] split(RShape shp, float t) { return shp.split(t); } /** * Adapt a shape along the curve of another shape. * * @eexample split * @param shp the shape to be adapted * @param path the shape which curve will be followed * @return RShape the adapted shape * @related setAdaptor ( ) */ public static RShape adapt(RShape shp, RShape path) { RShape ret = new RShape(shp); ret.adapt(path); return ret; } /** * Polygonize a shape. * * @eexample split * @param shp the shape to be polygonized * @return RShape, the polygonized shape * @related setPolygonizer ( ) */ public static RShape polygonize(RShape shp) { RShape ret = new RShape(shp); ret.polygonize(); return ret; } // State methods /** * Initialize the library. Must be called before any call to Geomerative * methods. Must be called by passing the PApplet. e.g. RG.init(this) * * @param _parent */ public static void init(PApplet _parent) { parent = _parent; initialized = true; } /** * @return @invisible */ public static boolean initialized() { return initialized; } /** * @return @invisible */ protected static PApplet parent() { if (parent == null) { throw new LibraryNotInitializedException(); } return parent; } /** * @return @invisible */ protected static int dpi() { return dpi; } /** * Use this to set the resolution of the display. This specifies the Dots * Per Inch of the display. * * @param _dpi the dots per inch of the display * */ public static void setDpi(int _dpi) { dpi = _dpi; } /** * Binary difference between two shapes. * * @eexample binaryOps * @param a first shape to operate on * @param b second shape to operate on * @return RShape, the result of the operation * @related diff ( ) * @related union ( ) * @related intersection ( ) * @related xor ( ) */ public static RShape diff(RShape a, RShape b) { return a.diff(b); } /** * Binary union between two shapes. * * @eexample binaryOps * @param a first shape to operate on * @param b second shape to operate on * @return RShape, the result of the operation * @related diff ( ) * @related union ( ) * @related intersection ( ) * @related xor ( ) */ public static RShape union(RShape a, RShape b) { return a.union(b); } /** * Binary intersection between two shapes. * * @eexample binaryOps * @param a first shape to operate on * @param b second shape to operate on * @return RShape, the result of the operation * @related diff ( ) * @related union ( ) * @related intersection ( ) * @related xor ( ) */ public static RShape intersection(RShape a, RShape b) { return a.intersection(b); } /** * Binary xor between two shapes. * * @eexample binaryOps * @param a first shape to operate on * @param b second shape to operate on * @return RShape, the result of the operation * @related diff ( ) * @related union ( ) * @related intersection ( ) * @related xor ( ) */ public static RShape xor(RShape a, RShape b) { return a.xor(b); } /** * Ignore the styles of the shapes when drawing and use the Processing style * methods. * * @eexample ignoreStyles * @param value value to which the ignoreStyles state should be set */ public static void ignoreStyles(boolean value) { ignoreStyles = value; } public static void ignoreStyles() { ignoreStyles = true; } /** * Use this to set the adaptor type. * * @eexample RShape_setAdaptor * @param adptorType it can be RG.BYPOINT, RG.BYELEMENTPOSITION or * RG.BYELEMENTINDEX * @related BYPOINT * @related BYELEMENTPOSITION * @related BYELEMENTINDEX */ public static void setAdaptor(int adptorType) { adaptorType = adptorType; } /** * Use this to set the adaptor scaling. This scales the transformation of * the adaptor. * * @eexample RShape_setAdaptor * @param adptorScale the scaling coefficient */ public static void setAdaptorScale(float adptorScale) { adaptorScale = adptorScale; } /** * Use this to set the adaptor length offset. This specifies where to start * adapting the group to the shape. * * @eexample RShape_setAdaptorLengthOffset * @param adptorLengthOffset the offset along the curve of the shape. Must * be a value between 0 and 1; * */ public static void setAdaptorLengthOffset(float adptorLengthOffset) throws RuntimeException { if (adptorLengthOffset >= 0F && adptorLengthOffset <= 1F) { adaptorLengthOffset = adptorLengthOffset; } else { throw new RuntimeException("The adaptor length offset must take a value between 0 and 1."); } } /** * Use this to set the polygonizer type. * * @param segmenterMethod can be RG.ADAPTATIVE, RG.UNIFORMLENGTH or * RG.UNIFORMSTEP. * * @eexample setPolygonizer * @related ADAPTATIVE * @related UNIFORMLENGTH * @related UNIFORMSTEP * */ public static void setPolygonizer(int segmenterMethod) { RCommand.setSegmentator(segmenterMethod); } /** * Use this to set the segmentator angle tolerance for the ADAPTATIVE * segmentator and set the segmentator to ADAPTATIVE. * * @eexample setPolygonizerAngle * @param angle an angle from 0 to PI/2 it defines the maximum angle between * segments. * @related ADAPTATIVE * */ public static void setPolygonizerAngle(float angle) { RCommand.setSegmentAngle(angle); } /** * Use this to set the segmentator length for the UNIFORMLENGTH segmentator * and set the segmentator to UNIFORMLENGTH. * * @eexample setPolygonizerLength * @param length the length of each resulting segment. * @related UNIFORMLENGTH * @related polygonize ( ) */ public static void setPolygonizerLength(float length) { RCommand.setSegmentLength(length); } /** * Use this to set the segmentator step for the UNIFORMSTEP segmentator and * set the segmentator to UNIFORMSTEP. * * @eexample setSegmentStep * @param step if a float from +0.0 to 1.0 is passed it's considered as the * step, else it's considered as the number of steps. When a value of 0.0 is * used the steps will be calculated automatically depending on an * estimation of the length of the curve. The special value -1 is the same * as 0.0 but also turning of the segmentation of lines (faster * segmentation). * @related UNIFORMSTEP * @related polygonize ( ) */ public static void setPolygonizerStep(float step) { RCommand.setSegmentStep(step); } }