
//  NOTE: The Group definitions in this file, and many of the comments,
//  came directly from the Mac version of Kali by Jeff Weeks.  Thanks
//  to Jeff for his lucid programming style and copius comments!
//    mbp Mon Sep 16 17:06:26 1996


/**
 * The SymmetryGroup class holds the data for a particular symmetry
 * group.  A symmetry group may be generated by
 *
 * <ul>
 *	<li>a reflection
 *	<li>a glide reflection
 *	<li>a rotation
 *	<li>up to two translations
 * </ul>
 * <p>
 * Kali always performs the generators in that order.  Given a line
 * segment upon which the group is to act, first the line segment is
 * reflected (if a reflection is present), then the line and its
 * reflection are glide reflected (if a glide reflection is present),
 * then the images are rotated (if a rotation is present) and finally
 * all images are translated according to whatever translations are
 * present.
 *
 * <p>
 * Note: all the math happens in the
 * <a href="Panorama.html#_top_">Panorama</a> object; the SymmetryGroup
 * object just holds the group data.
 *
 * @see SymmetryGroups
 * @see Panorama
 */
class SymmetryGroup implements Constants {

/**
 * The index of the group (value is one of the constants defined
 * Constants.java)
 */
  public int	index;

/**
 * The name of the group (for debug printing, etc)
 */
  public String name;

/**
 * reflectionType has one of the values AXIS_NONE, AXIS_X0,
 * AXIS_X4, AXIS_Y0, or AXIS_Y4.  If reflectionType != AXIS_NONE,
 * do a reflection about the specified axis.
 */
  public int reflectionType;

/**
 * If glideReflectionType != axis_none, do a glide reflection
 * a distance 1/2 along the specified axis.
 */
  public int glideReflectionType;

/**
 * rotationOrder gives the order of the rotation about
 * the origin if a rotation is present.  Otherwise it's zero.
 */
  public int rotationOrder;

/**
 * numTranslations tells how many independent translations are
 * present.  It will be 2 for a wallpaper group, 1 for a frieze
 * group, and 0 for a rosette group.  The array "translations"
 * gives the translation vectors.
 */
  public int numTranslations;
  public DVector[] translations;               // [2]



  /**
   * The number of mirror segments (same as mirrorSegments.length).
   */
  public int numMirrorSegments;        // maximum is four
  /**
   * The locations of the mirror segments.  NOTE: currently
   * Java Kali does not display these; they're included here
   * because they were present in the Mac version, and a future
   * version of Java Kali might display them.
   */
  public Segment[] mirrorSegments;     // [4]

  /**
   * XYZZYNOTE: currently
   * Java Kali does not display these; they're included here
   * because they were present in the Mac version, and a future
   * version of Java Kali might display them.
   */

  /**
   * The number of glide segments (same as mirrorSegments.length).
   */
  public int numGlideSegments;         // maximum is two
  /**
   * The locations of the glide segments.
   */
  public Segment[] glideSegments;      // [2]

  /**
   * The number of gyration points (same as mirrorSegments.length).
   */
  public int numGyrationPoints;        // maximum is four
  /**
   * The locations of the gyration points.
   */
  public DVector[] gyrationPoints;     // [4]

  /**
   * Create a new SymmetryGroup object with the given data (and no
   * mirror segments, glide segments, or gyration points).
   */
  public SymmetryGroup(int index,
                       String name,
                       int reflectionType,
                       int glideReflectionType,
                       int rotationOrder) {
    this.index                  = index;
    this.name                   = name;
    this.reflectionType         = reflectionType;
    this.glideReflectionType    = glideReflectionType;
    this.rotationOrder          = rotationOrder;
    this.numTranslations        = 0;
    this.translations           = null;
    this.numMirrorSegments      = 0;
    this.mirrorSegments         = null;
    this.numGlideSegments       = 0;
    this.glideSegments          = null;
    this.numGyrationPoints      = 0;
    this.gyrationPoints         = null;
  }

  /**
   * Create a new SymmetryGroup object with the given data.
   */
  public SymmetryGroup(int index,
                       String name,
                       int reflectionType,
                       int glideReflectionType,
                       int rotationOrder,
                       DVector[] translations,
                       Segment[] mirrorSegments,
                       Segment[] glideSegments,
                       DVector[] gyrationPoints) {
    this.index                  = index;
    this.name                   = name;
    this.reflectionType         = reflectionType;
    this.glideReflectionType    = glideReflectionType;
    this.rotationOrder          = rotationOrder;
    this.translations           = translations;
    if (translations != null) {
      this.numTranslations      = translations.length;
    } else {
      this.numTranslations      = 0;
    }
    this.mirrorSegments         = mirrorSegments;
    if (mirrorSegments != null) {
      this.numMirrorSegments    = mirrorSegments.length;
    } else {
      this.numMirrorSegments    = 0;
    }
    this.glideSegments          = glideSegments;
    if (glideSegments != null) {
      this.numGlideSegments     = glideSegments.length;
    } else {
      this.numGlideSegments     = 0;
    }
    this.gyrationPoints         = gyrationPoints;
    if (gyrationPoints != null) {
      this.numGyrationPoints    = gyrationPoints.length;
    } else {
      this.numGyrationPoints    = 0;
    }
  }
  
  /**
   * Convert an axis integer to a string for printing
   */
  private String axisString(int axis) {
    switch (axis) {
      case AXIS_NONE: return ("AXIS_NONE");
      case AXIS_X0: return ("AXIS_X0");
      case AXIS_X4: return ("AXIS_X4");
      case AXIS_Y0: return ("AXIS_Y0");
      case AXIS_Y4: return ("AXIS_Y4");
      default: return ("???");
    }
  }

  /**
   * Convert a SymmetryGroup to a string for printing
   */
  public String toString() {
    return ("SymmetryGroup["
            + axisString(reflectionType)
            + ", " + axisString(glideReflectionType)
            + ", " + rotationOrder
            + ", " + DVector.aString(translations)
            + ", " + Segment.aString(mirrorSegments)
            + ", " + Segment.aString(glideSegments)
            + ", " + DVector.aString(gyrationPoints)
            + "]");
  }


}
/**
 * The SymmetryGroups (note the 's' on the end!) object holds a list
 * of all the groups used in the program (each entry in this list is
 * an instance of the SymmetryGroup (no 's' at the end) class.  The
 * SymmetryGroups class is not meant to be instanced; just refer to
 * its <i>group</i> field and <i>getCount()</i> method.
 *
 * @see SymmetryGroup
 * @see Panorama
 */
class SymmetryGroups implements Constants {

  /**
   * The list of groups
   */
  public static SymmetryGroup[] group = {

    //
    // wallpaper groups
    //

    new SymmetryGroup(GROUP_w2222,
                      "2222",
                      AXIS_NONE,
                      AXIS_NONE,
                      2,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      null,
                      null,
                      DVector.array(0.0, 0.0,
                                   0.5, 0.0,
                                   0.0, 0.5,
                                   0.5, 0.5)),

    new SymmetryGroup(GROUP_w333,
                      "333",
                      AXIS_NONE,
                      AXIS_NONE,
                      3,
                      DVector.array(1.0, 0.0,
                                    0.5, 0.5*ROOT3),
                      null,
                      null,
                      DVector.array(0.0, 0.0,
                                   0.5, 0.5/ROOT3,
                                   0.5, -0.5/ROOT3)),

    new SymmetryGroup(GROUP_w442,
                      "442",
                      AXIS_NONE,
                      AXIS_NONE,
                      4,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      null,
                      null,
                      DVector.array(0.0, 0.0,
                                   0.5, 0.0,
                                   0.5, 0.5)),

    new SymmetryGroup(GROUP_w632,
                      "632",
                      AXIS_NONE,
                      AXIS_NONE,
                      6,
                      DVector.array(1.0, 0.0,
                                    0.5, 0.5*ROOT3),
                      null,
                      null,
                      DVector.array(0.0, 0.0,
                                   0.5, 0.0,
                                   0.5, 0.5/ROOT3)),

    new SymmetryGroup(GROUP_wx2222,
                      "*2222",
                      AXIS_X0,
                      AXIS_NONE,
                      2,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      Segment.array(0.0, 0.0, 0.5, 0.0,
                                    0.5, 0.0, 0.5, 0.5,
                                    0.5, 0.5, 0.0, 0.5,
                                    0.0, 0.5, 0.0, 0.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_wx333,
                      "*333",
                      AXIS_X0,
                      AXIS_NONE,
                      3,
                      DVector.array(1.0, 0.0,
                                    0.5, 0.5*ROOT3),
                      Segment.array(0.0, 0.0, 0.5, -0.5/ROOT3,
                                    0.5, -0.5/ROOT3, 0.5, 0.5/ROOT3,
                                    0.5, 0.5/ROOT3, 0.0, 0.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_wx442,
                      "*442",
                      AXIS_X0,
                      AXIS_NONE,
                      4,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      Segment.array(0.0, 0.0, 0.5, 0.0,
                                    0.5, 0.0, 0.5, 0.5,
                                    0.5, 0.5, 0.0, 0.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_wx632,
                      "*632",
                      AXIS_X0,
                      AXIS_NONE,
                      6,
                      DVector.array(1.0, 0.0,
                                    0.5, 0.5*ROOT3),
                      Segment.array(0.0, 0.0, 0.25, 0.25*ROOT3,
                                    0.25, 0.25*ROOT3, 0.0, 1.0/ROOT3,
                                    0.0, 1.0/ROOT3, 0.0, 0.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_w4x2,
                      "4*2",
                      AXIS_X4,
                      AXIS_NONE,
                      4,
                      DVector.array(0.5, 0.5,
                                    0.5, -0.5),
                      Segment.array(-0.25, 0.25, 0.25, 0.25),
                      null,
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_w3x3,
                      "3*3",
                      AXIS_X0,
                      AXIS_NONE,
                      3,
                      DVector.array(0.5*ROOT3, 0.5,
                                    0.0, 1.0),
                      Segment.array(0.0, 0.0, 0.25*ROOT3, 0.25,
                                    0.0, 0.0, 0.25*ROOT3, -0.25),
                      null,
                      DVector.array(1.0/ROOT3, 0.0)),


    //	2x22
    //
    // This group is easily defined by a reflection followed by an
    // order two rotation.  The only problem is that the translations
    // are then at odd angles, making it hard to deform the shape of
    // the fundamental domain.  So instead we throw an "unnecessary"
    // glide reflection into the definition: it makes the fundamental
    // domain twice as big, and makes the translations orthogonal to
    // one another.
    //
    // This all makes more sense if you draw a picture.  Start with a
    // rectangle with mirrored edges to represent *2222, and mod out
    // by a half turn about its center to get 2*22.  Bisect the
    // rectangle to get a fundamental domain for the orbifold (I cut
    // it with a diagonal, but any bisection will do).  Mark the
    // origin at the center of the rectangle, and draw a dozen or so
    // of its nearest translates in the universal cover.  Color in
    // your original fundamental domain (half the original rectangle).
    // Then color its reflection in the line x = (width of
    // rectangle)/2.  Then take the union of the two colored regions
    // and color their image under a glide reflection a distance
    // (height of rectangle)/2 along the y-axis.  Finally, take the
    // union of all that and color its image under a half turn about
    // the origin.  Your final colored region will tile the plane
    // under the action of the generators x -> x + 2*width, y -> y +
    // 2*height.  The point of all this is that we can do the initial
    // calculation for a square, and then stretch it after the fact.
    //
    new SymmetryGroup(GROUP_w2x22,
                      "2*22",
                      AXIS_X4,
                      AXIS_X0,
                      2,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      Segment.array(0.25, -0.25, 0.25, 0.25,
                                    -0.25, -0.25, 0.25, -0.25),
                      Segment.array(0.0, 0.0, 0.0, -0.25),
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_w22x,
                      "22*",
                      AXIS_X4,
                      AXIS_NONE,
                      2,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      Segment.array(0.25, -0.5, 0.25, 0.5),
                      null,
                      DVector.array(0.0, 0.0,
                                   0.0, 0.5)),

    new SymmetryGroup(GROUP_wxx,
                      "**",
                      AXIS_X0,
                      AXIS_NONE,
                      1,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      Segment.array(0.0, -0.5, 0.0, 0.5,
                                    0.5, -0.5, 0.5, 0.5),
                      null,
                      null),

    new SymmetryGroup(GROUP_wxo,
                      "*o",
                      AXIS_X0,
                      AXIS_X4,
                      1,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      Segment.array(0.0, 0.0, 0.0, 1.0),
                      Segment.array(0.25, 0.0, 0.25, 0.5),
                      null),

    new SymmetryGroup(GROUP_woo,
                      "oo",
                      AXIS_NONE,
                      AXIS_X0,
                      1,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      null,
                      Segment.array(0.0, 0.0, 0.0, 0.5,
                                    0.5, 0.0, 0.5, 0.5),
                      null),

    new SymmetryGroup(GROUP_w22o,
                      "22o",
                      AXIS_NONE,
                      AXIS_X4,
                      2,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      null,
                      Segment.array(0.25, 0.0, 0.25, 0.5),
                      DVector.array(0.0, 0.0,
                                   0.0, 0.5)),

    new SymmetryGroup(GROUP_wt,         // torus
                      "0",
                      AXIS_NONE,
                      AXIS_NONE,
                      1,
                      DVector.array(1.0, 0.0,
                                    0.0, 1.0),
                      null,
                      null,
                      null),

    //
    // frieze groups
    //

    new SymmetryGroup(GROUP_fii,
                      "oooo",
                      AXIS_NONE,
                      AXIS_NONE,
                      1,
                      DVector.array(1.0, 0.0),
                      null,
                      null,
                      null),


    new SymmetryGroup(GROUP_fix,
                      "oo*",
                      AXIS_Y0,
                      AXIS_NONE,
                      1,
                      DVector.array(1.0, 0.0),
                      Segment.array(0.0, 0.0, 1.0, 0.0),
                      null,
                      null),


    new SymmetryGroup(GROUP_fxii,
                      "*oooo",
                      AXIS_X0,
                      AXIS_NONE,
                      1,
                      DVector.array(1.0, 0.0),
                      Segment.array(0.0, -1.0, 0.0, 1.0,
                                    0.5, -1.0, 0.5, 1.0),
                      null,
                      null),


    new SymmetryGroup(GROUP_f22i,
                      "22oo",
                      AXIS_NONE,
                      AXIS_NONE,
                      2,
                      DVector.array(1.0, 0.0),
                      null,
                      null,
                      DVector.array(0.0, 0.0,
                                   0.5, 0.0)),


    new SymmetryGroup(GROUP_fx22i,
                      "*22oo",
                      AXIS_Y0,
                      AXIS_NONE,
                      2,
                      DVector.array(1.0, 0.0),
                      Segment.array(0.0, 0.0, 0.5, 0.0,
                                    0.0, 0.0, 0.0, 1.0,
                                    0.5, 0.0, 0.5, 1.0),
                      null,
                      null),


    new SymmetryGroup(GROUP_f2xi,
                      "2*oo",
                      AXIS_X4,
                      AXIS_NONE,
                      2,
                      DVector.array(1.0, 0.0),
                      Segment.array(0.25, -1.0, 0.25, 1.0),
                      null,
                      DVector.array(0.0, 0.0)),


    new SymmetryGroup(GROUP_fio,
                      "ooo",
                      AXIS_NONE,
                      AXIS_Y0,
                      1,
                      DVector.array(1.0, 0.0),
                      null,
                      Segment.array(0.0, 0.0, 0.5, 0.0),
                      null),

    //
    // rosette groups
    //

    new SymmetryGroup(GROUP_r1,
                      "1",
                      AXIS_NONE,
                      AXIS_NONE,
                      1,
                      null,
                      null,
                      null,
                      null),

    new SymmetryGroup(GROUP_r2,
                      "2",
                      AXIS_NONE,
                      AXIS_NONE,
                      2,
                      null,
                      null,
                      null,
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_r3,
                      "3",
                      AXIS_NONE,
                      AXIS_NONE,
                      3,
                      null,
                      null,
                      null,
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_r4,
                      "4",
                      AXIS_NONE,
                      AXIS_NONE,
                      4,
                      null,
                      null,
                      null,
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_r5,
                      "5",
                      AXIS_NONE,
                      AXIS_NONE,
                      5,
                      null,
                      null,
                      null,
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_r6,
                      "6",
                      AXIS_NONE,
                      AXIS_NONE,
                      6,
                      null,
                      null,
                      null,
                      DVector.array(0.0, 0.0)),

    new SymmetryGroup(GROUP_rx1,
                      "*1",
                      AXIS_X0,
                      AXIS_NONE,
                      1,
                      null,
                      Segment.array(0.0, 0.0, 0.0, 1.0,
                                    0.0, 0.0, 0.0, -1.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_rx2,
                      "*2",
                      AXIS_X0,
                      AXIS_NONE,
                      2,
                      null,
                      Segment.array(0.0, 0.0, 0.0, 1.0,
                                    0.0, 0.0, 1.0, 0.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_rx3,
                      "*3",
                      AXIS_X0,
                      AXIS_NONE,
                      3,
                      null,
                      Segment.array(0.0, 0.0, 0.0, 1.0,
                                    0.0, 0.0, 0.0, -1.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_rx4,
                      "*4",
                      AXIS_X0,
                      AXIS_NONE,
                      4,
                      null,
                      Segment.array(0.0, 0.0, 0.0, 1.0,
                                    0.0, 0.0, 0.5*ROOT2, 0.5*ROOT2),
                      null,
                      null),

    new SymmetryGroup(GROUP_rx5,
                      "*5",
                      AXIS_X0,
                      AXIS_NONE,
                      5,
                      null,
                      Segment.array(0.0, 0.0, 0.0, 1.0,
                                    0.0, 0.0, 0.0, -1.0),
                      null,
                      null),

    new SymmetryGroup(GROUP_rx6,
                      "*6",
                      AXIS_X0,
                      AXIS_NONE,
                      6,
                      null,
                      Segment.array(0.0, 0.0, 0.0, 1.0,
                                    0.0, 0.0, 0.5, 0.5*ROOT3),
                      null,
                      null),

   };

  /**
   * Return the number of groups in the list.
   */
  public static int getCount() {
    return group.length;
  }

}
