/*
 * @(#)RegularShape.java    25 July 2007
 *
 * Copyright 2007
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.gui.*;
import edu.neu.ccs.util.*;

/**
 * <p>Class <code>RegularShape</code> provides factory methods
 * to create regular polygons and regular star shapes.</p>
 * 
 * <p>This class also provides the mathematical methods that
 * return the vertex arrays in case the caller wishes to deal
 * directly with this data.</p>
 * 
 * <p>This class cannot be instantiated.</p>
 * 
 * @author  Richard Rasala
 * @version 2.6.0
 * @since   2.6.0
 */
public class RegularShape
{
    /** Prevent instantiation. */
    private RegularShape() { }
    
    
    /**
     * <p>Creates a regular polygon
     * with center (x,y) and radius r with the initial
     * vertex positioned vertically above the center
     * and with the given number of vertices.</p>
     * 
     * @param x the x-coordinate of the center
     * @param y the y-coordinate of the center
     * @param r the radius
     * @param vertices the number of polygon vertices
     */
    public static PolygonShape polygon
        (double x, double y, double r, int vertices)
    {
        return new PolygonShape
            (regularShapeDataFloat(x, y, r, vertices, 1));
    }
    
    
    /**
     * <p>Creates a regular star shape
     * with center (x,y) and radius r with the initial
     * vertex positioned vertically above the center
     * and with the given number of outer vertices and
     * the given jump.</p>
     * 
     * <p>Conceptually, the jump determines the distance
     * between outer vertices that are "connected" by a
     * line in the star.  In particular, if jump is 1,
     * then the shape reduces to a regular polygon .</p>
     * 
     * <p>In the actual shape construction, we do not in
     * fact directly connect the outer vertices of a star.
     * Instead, we compute a sequence of inner vertices
     * positioned midway between the outer vertices and
     * then build the star by connecting vertices in an
     * alternating pattern: outer, inner, outer, inner,
     * etc.  This methodology makes the star a shape with
     * no self-intersections.</p>
     * 
     * @param x the x-coordinate of the center
     * @param y the y-coordinate of the center
     * @param r the radius
     * @param vertices the number of outer star vertices
     * @param jump the jump between outer star vertices
     */
    public static PolygonShape star
        (double x, double y, double r, int vertices, int jump)
    {
        return new PolygonShape
            (regularShapeDataFloat(x, y, r, vertices, jump));
    }
    
    
    /**
     * <p>Returns the array of coordinate data for
     * a regular polygon or regular star shape
     * with center (x,y), radius r,
     * and the given number of outer vertices
     * and the given jump between outer vertices.</p>
     * 
     * <p>Let <code>V</code> stand for the <code>vertices</code>
     * parameter.  Then:</p>
     * 
     * <p>If <code>jump==1</code>, the array returned has shape
     * <code>double[V][2]</code>.</p>
     * 
     * <p>If <code>jump!=1</code>, the array returned has shape
     * <code>double[2*V][2]</code> to account for both
     * the outer and the inner vertices of the star.</p>
     * 
     * <p>The <code>vertices</code> parameter should be at least
     * <code>3</code> and the <code>jump</code> parameter should
     * be at least <code>1</code> and less than <code>vertices</code>.
     * Invalid parameters will be adjusted to the most reasonable
     * valid values.</p>
     * 
     * @param x the x-coordinate of the center
     * @param y the y-coordinate of the center
     * @param r the radius
     * @param vertices the number of outer star vertices
     * @param jump the jump between outer star vertices
     */
    public static double[][] regularShapeData
        (double x, double y, double r, int vertices, int jump)
    {
        // error check vertices
        vertices = Math.abs(vertices);
        
        if (vertices < 3)
            vertices = 3;
        
        // error check jump
        if (vertices < 5) {
            jump = 1;
        }
        else {
            jump = Math.abs(jump);
            
            jump = jump % vertices;
            
            int half = vertices / 2;
            
            if (jump > half)
                jump = vertices - jump;
            
            if ((jump == 0) || ((2 * jump) == vertices))
                jump = 1;
        }
        
        double[][] result = null;
        
        if (jump == 1)
            // polygon
        {
            int V = vertices;
            int I = 0;
            
            result = new double[V][2];
            
            double T = 0;           // initial angle
            double C = 360.0 / V;   // angular change
            
            while (I < V) {
                // polygon vertex
                result[I][0] =
                    x + r * MathUtilities.sindeg(T);
                
                result[I][1] =
                    y - r * MathUtilities.cosdeg(T);
                
                I++;
                T += C;
            }
        }
        else
            // star
        {
            // star has 2 * vertices points
            int V = 2 * vertices;
            int I = 0;
            
            result = new double[V][2];
            
            double T = 0;           // initial angle
            double C = 360.0 / V;   // angular change
            
            // Use Law of Sines to compute inner star radius s
            double A = 90.0  - C * jump;
            double B = 180.0 - A - C;
            
            double sinA = MathUtilities.sindeg(A);
            double sinB = MathUtilities.sindeg(B);
            
            double s = r * sinA / sinB;
            
            while (I < V) {
                // outer vertex at radius r
                result[I][0] =
                    x + r * MathUtilities.sindeg(T);
                
                result[I][1] =
                    y - r * MathUtilities.cosdeg(T);
                
                I++;
                T += C;
                
                // inner vertex at radius s
                result[I][0] =
                    x + s * MathUtilities.sindeg(T);
                
                result[I][1] =
                    y - s * MathUtilities.cosdeg(T);
                
                I++;
                T += C;
            }
        }
        
        return result;
    }
    
    
    /**
     * <p>Returns the same coordinate information as the method
     * <code>regularShapeData</code> except as an array of
     * <code>float</code>.</p>
     * 
     * @param x the x-coordinate of the center
     * @param y the y-coordinate of the center
     * @param r the radius
     * @param vertices the number of outer star vertices
     * @param jump the jump between outer star vertices
     */
    public static float[][] regularShapeDataFloat
        (double x, double y, double r, int vertices, int jump)
    {
        double[][] dataD =
            regularShapeData(x, y, r, vertices, jump);
        
        int V = dataD.length;
        
        float[][] dataF = new float[V][2];
        
        for (int i = 0; i < V; i++)
            for (int j = 0; j < 2; j++)
                dataF[i][j] = (float) dataD[i][j];
        
        return dataF;
    }
     
}

