content: /*****************************************************************************
   *                        Shapeways, Inc Copyright (c) 2011
   *                               Java Source
   *
   * This source is licensed under the GNU LGPL v2.1
   * Please read http://www.gnu.org/copyleft/lgpl.html for more information
   *
   * This software comes with the standard NO WARRANTY disclaimer for any
   * purpose. Use it at your own risk. If there's a problem you get to fix it.
   *
   ****************************************************************************/
  
  package abfab3d.transforms;
  
  import javax.vecmath.Vector3d;
  import java.util.ArrayList;
  
  import abfab3d.util.ReflectionGroup;
  import abfab3d.util.SymmetryGenerator;
  
  import abfab3d.param.SNodeListParameter;
  import abfab3d.param.DoubleParameter;
  import abfab3d.param.Vector3dParameter;
  import abfab3d.param.Parameter;
  import abfab3d.param.BaseParameterizable;
  import abfab3d.param.SNodeFactory;
  import abfab3d.param.BaseSNodeFactory;
  
  
  import static java.lang.Math.sin;
  import static java.lang.Math.cos;
  import static java.lang.Math.sqrt;
  import static java.lang.Math.PI;
  
  import static abfab3d.core.Units.MM;
  
  /**
     assortmens of various symmetry groups generated by reflections in planes and inversions in spheres 
     
     @author Vladimir Bulatov
  
   */
  public class ReflectionSymmetries {
  
      
      static String sm_symNames[] = new String[]{
          "plane", 
          "dihedral",
          "quad1",
      };
      static String sm_symClasses[] = new String[]{
          "abfab3d.transforms.ReflectionSymmetries$Plane",
          "abfab3d.transforms.ReflectionSymmetries$Dihedral",
          "abfab3d.transforms.ReflectionSymmetries$Quad1",
      };
  
      static SNodeFactory sm_factory;
      
      public static SNodeFactory getFactory(){
          if(sm_factory == null)
              sm_factory = new BaseSNodeFactory(sm_symNames, sm_symClasses);
          return sm_factory;
      }
  
  
      /**
         single plane 
       */
      public static class Plane extends BaseParameterizable implements SymmetryGenerator {
          
          Vector3dParameter mp_normal = new Vector3dParameter("normal","plane normal", new Vector3d(-1,0,0));
          DoubleParameter mp_distance = new DoubleParameter("distance","distance to origin", 0.);
          Parameter m_params [] = new Parameter[]{
              mp_normal, 
              mp_distance, 
          };
  
          public Plane(){
              super.addParams(m_params);
          }
  
          public ReflectionGroup.SPlane[] getFundamentalDomain(){
              return new ReflectionGroup.SPlane[]{new ReflectionGroup.Plane(mp_normal.getValue(), mp_distance.getValue())};
          }
      } // class Plane 
  
      public static class Plane1 extends BaseParameterizable implements SymmetryGenerator {
          
          Vector3dParameter mp_normal = new Vector3dParameter("normal","plane normal", new Vector3d(-1,0,0));
          DoubleParameter mp_distance = new DoubleParameter("distance","distance to origin", 0.);
          Parameter m_params [] = new Parameter[]{
              mp_normal, 
              mp_distance, 
          };
  
          public Plane1(){
              super.addParams(m_params);
          }
  
          public ReflectionGroup.SPlane[] getFundamentalDomain(){
              return new ReflectionGroup.SPlane[]{new ReflectionGroup.Plane(mp_normal.getValue(), mp_distance.getValue())};
          }
      } // class Plane 
  
      /**
         dihedral symmetry of given order 
       */
      public static class Dihedral extends BaseParameterizable implements SymmetryGenerator {
          
          DoubleParameter mp_order = new DoubleParameter("order","order of rosette", 6.);
          Parameter m_params [] = new Parameter[]{
              mp_order, 
          };
  
          public Dihedral(){
              super.addParams(m_params);
          }
  
          public ReflectionGroup.SPlane[] getFundamentalDomain(){
  
              double alpha = PI/mp_order.getValue();
              
              return new ReflectionGroup.SPlane[] {
                  new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.),
                  new ReflectionGroup.Plane(new Vector3d(-sin(alpha), cos(alpha),0), 0.),
              };   
          }
      } // class Dihedral 
  
  
  
      /**
         quad formed by 2 planes P1 and P2 orthogonal to Z-plane      
         and 2 spheres S3 and S4 orthogonal to P1 and P2 and to each other
         P1 and P2 form dihedral group
       */
      public static class Quad1 extends BaseParameterizable implements SymmetryGenerator {
          
          DoubleParameter mp_n12 = new DoubleParameter("n12","fracton angle between planes P1 and P2", 3.);
          DoubleParameter mp_r3 = new DoubleParameter("r3","radius of sphere S3", 7.6*MM);
          DoubleParameter mp_c3 = new DoubleParameter("c3","center of sphere S3", 10.*MM);
          DoubleParameter mp_c4 = new DoubleParameter("c4","center of sphere S4", 20.*MM);
          Parameter m_params [] = new Parameter[]{
              mp_n12, 
              mp_r3,
              mp_c3,
              mp_c4,
          };
  
          public Quad1(){
              super.addParams(m_params);
          }
  
          public Quad1(int n12, double c3, double r3, double c4){
  
              super.addParams(m_params);
  
              mp_n12.setValue(n12);
              mp_r3.setValue(r3);
              mp_c3.setValue(c3);
              mp_c4.setValue(c4);
          }
  
          public ReflectionGroup.SPlane[] getFundamentalDomain(){
  
              double a12 = PI/mp_n12.getValue();
              double c3 = mp_c3.getValue();
              double c4 = mp_c4.getValue();
              double r3 = mp_r3.getValue();
              double r4 = sqrt(c3*c3 + c4*c4 - 2*c3*c4*cos(a12) - r3*r3);
                          
              return new ReflectionGroup.SPlane[] {
                  new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.),
                  new ReflectionGroup.Plane(new Vector3d(-sin(a12), cos(a12),0), 0.),
                  new ReflectionGroup.Sphere(new Vector3d(c3, 0,0), -r3),
                  new ReflectionGroup.Sphere(new Vector3d(c4*cos(a12), c4*sin(a12),0), -r4),
              };   
          }
      } // class Quad1
  
      public static class General extends BaseParameterizable implements SymmetryGenerator {
          
  
          SNodeListParameter  mp_splanes = new SNodeListParameter("splanes","array of splanes");
  
          Parameter m_params [] = new Parameter[]{
              mp_splanes, 
          };
  
          public General(ReflectionGroup.SPlane[] splanes){
              
              super.addParams(m_params);
              setSPlanes(splanes);
  
          }
  
          public void setSPlanes(ReflectionGroup.SPlane[] splanes){
              ArrayList arr = new ArrayList();
              for(int i = 0; i < splanes.length; i++){
                  arr.add(new SPlaneTransform(splanes[i]));
              }
  
              mp_splanes.setValue(arr);
          }
  
          public ReflectionGroup.SPlane[] getFundamentalDomain(){
              
              ReflectionGroup.SPlane[] splanes = new ReflectionGroup.SPlane[ mp_splanes.size()];
  
              for(int i = 0; i < splanes.length; i++){                
                  SPlaneTransform st = (SPlaneTransform)mp_splanes.get(i);
                  splanes[i] = st.getSPlane();                
              }
              return splanes;
  
          }
      } // class General 
  
  
      public static ReflectionGroup getTwoPlanes(double x1, double x2){
          
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(-1,0,0), -x1), // right of  plane 1
              new ReflectionGroup.Plane(new Vector3d(1,0,0), x2), // left of plane 2
          };
          return new ReflectionGroup(s);
  
      }
  
      /**
         hyperbolic circular quadrilateral made by 2 orthogonal planes and 2 spheres. 
         angles of quadrilateral are (pi/2, pi/2, pi/2, alpha)
         r - radius of common sphere to which both spheres are orthogonal 
         the equator of that sphere will be place where all the tiles accumulate 
         r1 radius of one sphere 
         alpha angle between spheres
       */
      public static ReflectionGroup getQuad_1(double r, double r1, double alpha){
          double r2 = r*r/(r1*cos(alpha));
          double x1 = sqrt(r1*r1 + r*r);
          double y2 = sqrt(r2*r2 + r*r);
          
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(-1,0,0), 0.), // 
              new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.), // 
              new ReflectionGroup.Sphere(new Vector3d(x1,0,0), -r1), // outside of sphere 1
              new ReflectionGroup.Sphere(new Vector3d(0,y2,0), -r2), // outside of sphere 2           
          };   
          return new ReflectionGroup(s);
          
      }
  
      public static ReflectionGroup getQuad_2(double r1, double r2, double x1, double alpha){
  
          double y2 = sqrt(r1*r1 + r2*r2 + 2*r1*r2*cos(alpha) - x1*x1);
          
          //printf("r1: %7.5f x1: %7.5f r2: %7.5f y2: %7.5f\n", r1, x1, r2, y2);
  
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(-1,0,0), 0.), // 
              new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.), // 
              new ReflectionGroup.Sphere(new Vector3d(x1,0,0), -r1), // outside of sphere  
              new ReflectionGroup.Sphere(new Vector3d(0,y2,0), -r2), // outside of sphere              
          };   
          return new ReflectionGroup(s);
      }
  
      public static ReflectionGroup getTwoSpheres(double r1, double r2){
          
          if(r1 > r2) {
              double t = r2;
              r2 = r1;
              r1 = t;
          }
  
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Sphere(new Vector3d(0,0,0), -r1), // outside of smaller sphere 
              new ReflectionGroup.Sphere(new Vector3d(0,0,0), r2), // inside of larger sphere  
          };
          return new ReflectionGroup(s);
      }
      
      public static ReflectionGroup getPlaneAndSphere(double x, double r){
          double n = (r > 0)? (-1) : (1);
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(n,0,0), 0.), // right of yz plane
              new ReflectionGroup.Sphere(new Vector3d(x,0,0), -r), // outside of sphere  
          };
          return new ReflectionGroup(s);        
      }
  
      public static ReflectionGroup getIcosahedralKaleidoscope(){
  
          double t = (sqrt(5.)+1)/2;
  
          Vector3d v5 = new Vector3d(1,0,t); // vertex of icosahedron 
          Vector3d v3 = new Vector3d(0,1/t,t); // vertex of dodecahedron 
          Vector3d p35 = new Vector3d(); p35.cross(v3, v5); p35.normalize();
  
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(-1,0,0), 0.), 
              new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.), 
              new ReflectionGroup.Plane(p35, 0.),
          };
          return new ReflectionGroup(s);          
          
      }
  
      public static ReflectionGroup getXYZ(double r, double ax,double ay, double az){
          double dx = r*cos(ax);
          double dy = r*cos(ay);
          double dz = r*cos(az);
          
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(-1,0,0), 0.), // 
              new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.), // 
              new ReflectionGroup.Plane(new Vector3d(0,0,-1), 0.), // 
              new ReflectionGroup.Sphere(new Vector3d(dx, dy, dz), -r), // outside of sphere  
          };   
          return new ReflectionGroup(s);
          
      }
  
      /**
         two mirrows with angle between them PI/order (order >= 2);
       */
      public static ReflectionGroup getDihedralKaleidoscope(int order){
  
          double alpha = PI/order;        
          
          ReflectionGroup.SPlane[] s = new ReflectionGroup.SPlane[] {
              new ReflectionGroup.Plane(new Vector3d(0,-1,0), 0.),
              new ReflectionGroup.Plane(new Vector3d(-sin(alpha), cos(alpha),0), 0.),
          };   
          return new ReflectionGroup(s);
          
      }
  
  }
  