/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2006/07/08, 15:37
!  AUTHOR(S): KOGA, Junichiro
!  File : WignerSeitzCell.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.acviewer.fbz;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Vector;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.Material;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.RestrictedAccessException;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransparencyAttributes;
import javax.media.j3d.TriangleFanArray;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.CylinderCreator;
import ciss.phase_viewer.acviewer.geom.BoundPlane;
import ciss.phase_viewer.acviewer.geom.Plane;
import ciss.phase_viewer.acviewer.geom.Side;

/**
 * Wigner-Seitz cellを表すクラス. 差し当たり, セルベクトルか逆格子ベクトルでのみ 正しく描画するようなプログラムになっている.
 * 
 * @author
 */
public class WignerSeitzCell extends BranchGroup {
    private Logger logger = Logger.getLogger(WignerSeitzCell.class.getName());

    private Point3f avec;

    private Point3f bvec;

    private Point3f cvec;

    private WignerSeitzAttributes wsattrs;

    private Vector selectedSymmetryPoints = new Vector();

    private Vector kpointListeners = new Vector();

    private String parentDir = System.getProperty("user.dir");

    private KpointGenerator kpointGenerator;

    /**
     * a軸, b軸, c軸, 原点が分かればよい.
     * 
     * @param avec
     *            a軸
     * @param bvec
     *            b軸
     * @param cvec
     *            c軸
     */
    public WignerSeitzCell(Point3f avec, Point3f bvec, Point3f cvec) {
        this.avec = avec;
        this.bvec = bvec;
        this.cvec = cvec;
        wsattrs = new WignerSeitzAttributes();

        init();
        try {
            createWSCell();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /**
     * a軸, b軸, c軸, 原点が分かればよい.
     * 
     * @param avec
     *            a軸
     * @param bvec
     *            b軸
     * @param cvec
     *            c軸
     * @param parentDir
     *            k点ファイルなどがあるディレクトリー
     */
    public WignerSeitzCell(Point3f avec, Point3f bvec, Point3f cvec,
            String parentDir) {
        this.avec = avec;
        this.bvec = bvec;
        this.cvec = cvec;
        this.parentDir = parentDir;
        wsattrs = new WignerSeitzAttributes();

        init();
        try {
            createWSCell();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /**
     * a軸, b軸, c軸, 原点が分かればよい.
     * 
     * @param avec
     *            a軸
     * @param bvec
     *            b軸
     * @param cvec
     *            c軸
     * @param parentDir
     *            k点ファイルなどがあるディレクトリー
     * @param kpointGenerator
     *            k点作成オブジェクト
     */
    public WignerSeitzCell(Point3f avec, Point3f bvec, Point3f cvec,
            String parentDir, KpointGenerator kpointGenerator) {
        this.avec = avec;
        this.bvec = bvec;
        this.cvec = cvec;
        this.parentDir = parentDir;
        this.kpointGenerator = kpointGenerator;
        wsattrs = new WignerSeitzAttributes();

        init();
        try {
            createWSCell();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /**
     * a軸, b軸, c軸, 原点が分かればよい.
     * 
     * @param avec
     *            a軸
     * @param bvec
     *            b軸
     * @param cvec
     *            c軸
     */
    public WignerSeitzCell(Point3f avec, Point3f bvec, Point3f cvec,
            WignerSeitzAttributes wsattrs) {
        this.avec = avec;
        this.bvec = bvec;
        this.cvec = cvec;
        this.wsattrs = wsattrs;
        init();
        try {
            createWSCell();
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    /**
     * k点Generatorをセットする。もし設定されている場合, "k点のファイルからの読み込み"はこのオブジェクトを介して行われる
     * 
     * @param k点generator
     *            ; 読み込みも可能なはず
     */
    public void setKpointGenerator(KpointGenerator kpointGenerator) {
        this.kpointGenerator = kpointGenerator;
    }

    public void doSymmetricPoint(SymmetryPointBG symmetryPoint) {
        if (selectedSymmetryPoints.size() == 0) {
            addSymmetricPoint(symmetryPoint);
            return;
        }

        if (selectedSymmetryPoints.get(selectedSymmetryPoints.size() - 1) == symmetryPoint) {
            removeSymmetricPoint(symmetryPoint);
        } else {
            addSymmetricPoint(symmetryPoint);
        }
    }

    /**
     * 現在, "選択状態"にあるk点の配列を取得する
     * 
     * @return 選択状態のk点
     */
    public Kpoint[] getSelectedKpoints() {
        Vector retvec = new Vector();
        for (int i = 0; i < selectedSymmetryPoints.size(); i++) {
            SymmetryPointBG symp = (SymmetryPointBG) selectedSymmetryPoints
                    .get(i);
            Kpoint kp = new Kpoint(symp);
            retvec.addElement(kp);
        }
        Kpoint[] ret = new Kpoint[retvec.size()];
        retvec.copyInto(ret);
        return ret;
    }

    public void addSymmetricPoint(SymmetryPointBG symmetryPoint) {
        selectedSymmetryPoints.add(symmetryPoint);
        addSymmetryLine();
        Kpoint kp = new Kpoint(symmetryPoint);
        for (int i = 0; i < kpointListeners.size(); i++) {
            ((KpointsListener) kpointListeners.get(i)).kpointAdded(kp);
        }
    }

    public void removeSymmetricPoint(SymmetryPointBG symmetryPoint) {
        ((SymmetryPointBG) selectedSymmetryPoints.get(selectedSymmetryPoints
                .size() - 1)).setSelected(false);
        if (selectedSymmetryPoints.size() >= 2) {
            ((SymmetryPointBG) selectedSymmetryPoints
                    .get(selectedSymmetryPoints.size() - 2)).setSelected(true);
        }
        selectedSymmetryPoints.remove(selectedSymmetryPoints.size() - 1);
        removeSymmetryLine();
        Kpoint kp = new Kpoint(symmetryPoint);
        for (int i = 0; i < kpointListeners.size(); i++) {
            ((KpointsListener) kpointListeners.get(i)).kpointRemoved(kp);
        }
    }

    /**
     * k点に変更があったら通知を受けるリスナーを登録する.
     * 
     * @param kpointLitener
     *            通知したいリスナー
     */
    public void addKPointListener(KpointsListener kpointListener) {
        kpointListeners.addElement(kpointListener);
    }

    /**
     * k点に変更があったら通知を受けるリスナーを削除する.
     * 
     * @param kpointLitener
     *            削除したいリスナー
     */
    public void removeKPointListener(KpointsListener kpointListener) {
        kpointListeners.remove(kpointListener);
    }

    private Vector symmetryLine = new Vector();

    private void addSymmetryLine() {
        if (selectedSymmetryPoints.size() == 0) {
            return;
        }
        SymmetryPointBG sym = (SymmetryPointBG) selectedSymmetryPoints
                .get(selectedSymmetryPoints.size() - 1);
        sym.setSelected(true);
        if (selectedSymmetryPoints.size() == 1) {
            return;
        }
        SymmetryPointBG prevsym = (SymmetryPointBG) selectedSymmetryPoints
                .get(selectedSymmetryPoints.size() - 2);
        prevsym.setSelected(false);

        Vector3f prevpos = prevsym.getPos();
        Vector3f pos = sym.getPos();
        LineArray lineArray = new LineArray(2, LineArray.COORDINATES
                | LineArray.COLOR_3);
        lineArray.setCoordinate(0, new Point3f(prevpos));
        lineArray.setColor(0, wsattrs.symmetricLineColor);
        lineArray.setCoordinate(1, new Point3f(pos));
        lineArray.setColor(1, wsattrs.symmetricLineColor);
        LineAttributes lattr = new LineAttributes();
        lattr.setLineWidth(wsattrs.symmetricLineWidth);
        Appearance app = new Appearance();
        app.setLineAttributes(lattr);
        Shape3D s3d = new Shape3D();
        try {
            lineArray.setCapability(LineArray.ALLOW_INTERSECT);
        } catch (RestrictedAccessException rae) {
        }
        s3d.setGeometry(lineArray);
        s3d.setAppearance(app);
        BranchGroup bg = new BranchGroup();
        bg.setCapability(BranchGroup.ALLOW_DETACH);
        bg.addChild(s3d);
        addChild(bg);
        symmetryLine.add(bg);
    }

    public void removeAllSymmetryLines() {
        for (int i = 0; i < symmetryLine.size(); i++) {
            BranchGroup b = (BranchGroup) symmetryLine.get(i);
            b.detach();
            b = null;
        }

        for (int i = 0; i < selectedSymmetryPoints.size(); i++) {
            ((SymmetryPointBG) selectedSymmetryPoints.get(i))
                    .setSelected(false);
        }

        symmetryLine.removeAllElements();
        selectedSymmetryPoints.removeAllElements();

        for (int i = 0; i < kpointListeners.size(); i++) {
            ((KpointsListener) kpointListeners.get(i)).kpointsInitialized();
        }
    }

    private Vector selectedSymmetryPointsCache;

    private void cacheSelectedSymmetryPoints() {
        selectedSymmetryPointsCache = new Vector();
        for (int i = 0; i < selectedSymmetryPoints.size(); i++) {
            selectedSymmetryPointsCache.addElement(selectedSymmetryPoints
                    .get(i));
        }
    }

    private void removeSymmetryLine() {
        if (symmetryLine.size() == 0) {
            return;
        }
        BranchGroup b = (BranchGroup) symmetryLine.get(symmetryLine.size() - 1);
        symmetryLine.remove(symmetryLine.size() - 1);
        b.detach();
        b = null;
    }

    /**
     * このWigner-Seitz胞の属性値を保持するオブジェクトをセット.
     * 
     * @param wsattrs
     *            Wigner-Seitz胞の, 描画に関わる情報
     */
    public void setAttributes(WignerSeitzAttributes wsattrs) {
        this.wsattrs = wsattrs;
    }

    /**
     * このWigner-Seitz胞の持つ属性値を保持するオブジェクトを取得.
     * 
     * @return このWigner-Seitz胞の持つ属性値を保持するオブジェクト
     */
    public WignerSeitzAttributes getAttributes() {
        return this.wsattrs;
    }

    /**
     * Wigner-Seitz Cellの面の配列を取得する.
     * 
     * @return Wigner-Seitz Cellの面
     */
    public BoundPlane[] getPlanes() {
        if (planes == null || planes.size() == 0) {
            return null;
        }
        Vector tmpvec = new Vector();
        for (int i = 0; i < planes.size(); i++) {
            BoundPlane plane = (BoundPlane) planes.get(i);
            if (plane.getVertices().length >= 3) {
                tmpvec.add(plane);
            }
        }
        BoundPlane[] pls = new BoundPlane[tmpvec.size()];
        tmpvec.copyInto(pls);
        return pls;
    }

    private Point3f jusin;

    private Vector vertices = new Vector();

    private Vector planes = new Vector();

    private Transform3D smallTransform;

    private void init() {
        setCapability(BranchGroup.ALLOW_DETACH);
        setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
        setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);

        smallTransform = new Transform3D();
        smallTransform.rotX(Math.PI / 33.33d);
        smallTransform.rotY(Math.PI / 81.33d);
        smallTransform.rotZ(Math.PI / 57.33d);
        smallTransform.transform(avec);
        smallTransform.transform(bvec);
        smallTransform.transform(cvec);

        /* まず, 頂点配列を作成 */
        float bigx = -1000f;
        float bigy = -1000f;
        float bigz = -1000f;

        bigx = avec.x;
        if (bvec.x > bigx) {
            bigx = bvec.x;
        }
        if (cvec.x > bigx) {
            bigx = cvec.x;
        }

        bigy = avec.y;
        if (bvec.y > bigy) {
            bigy = bvec.y;
        }
        if (cvec.y > bigy) {
            bigy = cvec.y;
        }

        bigz = avec.z;
        if (bvec.z > bigz) {
            bigz = bvec.z;
        }
        if (cvec.z > bigz) {
            bigz = cvec.z;
        }
        bigx = 10f;
        bigy = 10f;
        bigz = 10f;
        Point3f initx = new Point3f(bigx, 0, 0);
        Point3f inity = new Point3f(0, bigy, 0);
        Point3f initz = new Point3f(0, 0, bigz);

        Point3f[] initialVertices = new Point3f[8];
        for (int i = 0; i < initialVertices.length; i++) {
            initialVertices[i] = new Point3f(0f, 0f, 0f);
        }

        initialVertices[0].add(initx);
        initialVertices[0].sub(inity);
        initialVertices[0].sub(initz);

        initialVertices[1].add(initx);
        initialVertices[1].add(inity);
        initialVertices[1].sub(initz);

        initialVertices[2].sub(initx);
        initialVertices[2].add(inity);
        initialVertices[2].sub(initz);

        initialVertices[3].sub(initx);
        initialVertices[3].sub(inity);
        initialVertices[3].sub(initz);

        initialVertices[4].add(initx);
        initialVertices[4].sub(inity);
        initialVertices[4].add(initz);

        initialVertices[5].add(initx);
        initialVertices[5].add(inity);
        initialVertices[5].add(initz);

        initialVertices[6].sub(initx);
        initialVertices[6].add(inity);
        initialVertices[6].add(initz);

        initialVertices[7].sub(initx);
        initialVertices[7].sub(inity);
        initialVertices[7].add(initz);

        for (int i = 0; i < initialVertices.length; i++) {
            vertices.addElement(initialVertices[i]);
        }

        /* 「初期の面」を作成; 初期の面は6枚ある. WSCellより充分大きな面で囲む, みたいな感じ */
        BoundPlane plane1 = new BoundPlane();
        plane1.setVertices(new Point3f[] { initialVertices[0],
                initialVertices[1], initialVertices[2], initialVertices[3] });
        plane1.generateNormal();

        BoundPlane plane2 = new BoundPlane();
        plane2.setVertices(new Point3f[] { initialVertices[4],
                initialVertices[5], initialVertices[6], initialVertices[7] });
        plane2.generateNormal();

        BoundPlane plane3 = new BoundPlane();
        plane3.setVertices(new Point3f[] { initialVertices[0],
                initialVertices[1], initialVertices[5], initialVertices[4] });
        plane3.generateNormal();

        BoundPlane plane4 = new BoundPlane();
        plane4.setVertices(new Point3f[] { initialVertices[3],
                initialVertices[2], initialVertices[6], initialVertices[7] });
        plane4.generateNormal();

        BoundPlane plane5 = new BoundPlane();
        plane5.setVertices(new Point3f[] { initialVertices[5],
                initialVertices[1], initialVertices[2], initialVertices[6] });
        plane5.generateNormal();

        BoundPlane plane6 = new BoundPlane();
        plane6.setVertices(new Point3f[] { initialVertices[0],
                initialVertices[3], initialVertices[7], initialVertices[4] });
        plane6.generateNormal();

        planes.addElement(plane1);
        planes.addElement(plane2);
        planes.addElement(plane3);
        planes.addElement(plane4);
        planes.addElement(plane5);
        planes.addElement(plane6);
    }

    private DecimalFormat format = new DecimalFormat("0.00000");

    private void createWSCell() {
        createPlanes();
        drawWSCell();
        loadFromFile();
    }

    private String bandkptin = "bandkpt.in";

    private void loadFromFile() {
        Kpoint[] kpts = null;
        if (kpointGenerator != null) {
            kpts = kpointGenerator.getKpointsFrom("");
        }

        File foo = new File(parentDir + System.getProperty("file.separator")
                + bandkptin);
        if (symmetryPoints == null) {
            return;
        }

        if (kpts == null) {
            if (foo.exists()) {
                BufferedReader reader = null;
                Vector tmpvec = new Vector();
                try {
                    reader = new BufferedReader(new FileReader(foo));
                    String l = "";
                    reader.readLine(); // dk
                    reader.readLine(); // b1
                    reader.readLine(); // b2
                    reader.readLine(); // b3
                    while ((l = reader.readLine()) != null) {
                        String symbol = "";
                        int nx = 0;
                        int ny = 0;
                        int nz = 0;
                        int denom = 1;
                        String[] sphash = l.split("#");

                        if (sphash.length == 2) {
                            symbol = sphash[1].trim();
                        }
                        String[] kps = sphash[0].trim().split("\\s+");
                        if (kps == null || kps.length < 4) {
                            continue;
                        }
                        nx = Integer.parseInt(kps[0].trim());
                        ny = Integer.parseInt(kps[1].trim());
                        nz = Integer.parseInt(kps[2].trim());
                        denom = Integer.parseInt(kps[3]);
                        float fx = ((float) nx) / ((float) denom);
                        float fy = ((float) ny) / ((float) denom);
                        float fz = ((float) nz) / ((float) denom);
                        Point3f kp = new Point3f(fx, fy, fz);
                        // tmpvec.add(kp);
                        Kpoint kpoi = new Kpoint(new float[] { fx, fy, fz });
                        kpoi.setSymbol(symbol);
                        tmpvec.add(kpoi);
                    }
                    kpts = new Kpoint[tmpvec.size()];
                    tmpvec.copyInto(kpts);
                } catch (Exception exc) {
                    exc.printStackTrace();
                } finally {
                    try {
                        reader.close();
                    } catch (IOException ioe) {
                    }
                }

            }
        }

        if (kpts == null)
            return;

        for (int j = 0; j < kpts.length; j++) {
            Kpoint kp = kpts[j];
            for (int i = 0; i < symmetryPoints.numChildren(); i++) {
                SymmetryPointBG sym = symmetryPoints.getSymmetryPoint(i);
                if (sym.getScaledPos().epsilonEquals(kp.getPoint3f(), 0.001f)) {
                    sym.setSymbol(kp.getSymbol());
                    doSymmetricPoint(sym);
                    continue;
                }
            }
        }

    }

    private void createPlanes() {
        float verySmall = 0.0001f;
        int n = 1;

        Vector normals = new Vector();
        for (int i = -n; i <= n; i++) {
            for (int j = -n; j <= n; j++) {
                for (int k = -n; k <= n; k++) {
                    if (i == 0 && j == 0 && k == 0) {
                        continue;
                    }

                    /* 平行二等分面作成 */
                    Point3f av = new Point3f();
                    Point3f bv = new Point3f();
                    Point3f cv = new Point3f();
                    av.scale((float) i, avec);
                    bv.scale((float) j, bvec);
                    cv.scale((float) k, cvec);

                    Point3f vector = new Point3f();
                    vector.add(av);
                    vector.add(bv);
                    vector.add(cv);

                    Point3f origin = new Point3f(vector);
                    origin.scale(0.5f);

                    Plane normalPlane = new Plane(vector, origin);
                    normals.add(normalPlane);
                }
            }
        }

        /* 短い順にソート; 長いやつは往々にして中途半端な多角形を残すので厄介 */
        Plane[] normalPlanes = new Plane[normals.size()];
        normals.copyInto(normalPlanes);
        Arrays.sort(normalPlanes);

        for (int i = 0; i < normalPlanes.length; i++) {
            Plane normalPlane = normalPlanes[i];

            /* かすりもしないなら何もしなくていい. */
            BoundPlane[] bplanes = new BoundPlane[planes.size()];
            planes.copyInto(bplanes);
            boolean cros = false;
            for (int l = 0; l < bplanes.length; l++) {
                Point3f[] bpv = bplanes[l].getVertices();
                for (int m = 0; m < bpv.length; m++) {
                    if (normalPlane.getDistanceFrom(bpv[m]) < verySmall) {
                        cros = true;
                    }
                }
            }
            if (!cros) {
                continue;
            }

            /* 平行二等分面で今ある多面体を切る */
            BoundPlane newPlane = new BoundPlane();
            for (int l = 0; l < planes.size(); l++) {
                BoundPlane plane = (BoundPlane) planes.get(l);
                plane.sortVertices();
                Side[] sides = plane.getSides();
                if (sides == null || sides.length == 0) {
                    // planes.remove(plane);
                    continue;
                }

                for (int m = 0; m < sides.length; m++) {
                    Point3f p1 = sides[m].getPoint1();
                    Point3f p2 = sides[m].getPoint2();
                    float d1 = normalPlane.getDistanceFrom(p1);
                    float d2 = normalPlane.getDistanceFrom(p2);
                    if (d1 > verySmall && d2 < verySmall || d1 < verySmall
                            && d2 > verySmall) { // 「距離の正負」が逆ならその面は横切る
                        float ad1 = Math.abs(d1);
                        float ad2 = Math.abs(d2);
                        float scale = ad1 / (ad1 + ad2);

                        Point3f p3 = new Point3f();
                        Point3f tmp = new Point3f();
                        tmp.sub(p2, p1);
                        p3.scaleAdd(scale, tmp, p1);

                        p3.x = Float.parseFloat(format.format(p3.x));
                        p3.y = Float.parseFloat(format.format(p3.y));
                        p3.z = Float.parseFloat(format.format(p3.z));
                        logger.debug("new vertex from plane " + l + ": " + p3);

                        plane.addVertex(p3);
                        newPlane.addVertex(p3);
                        vertices.add(p3);
                    }
                }

                /* 新しい面より外側のバーテックスはすべてはずす */
                Point3f[] vers = plane.getVertices();
                for (int m = 0; m < vers.length; m++) {
                    float dis = normalPlane.getDistanceFrom(vers[m]);
                    if (dis > verySmall) {
                        plane.removeVertex(vers[m]);
                        for (int nn = 0; nn < vertices.size(); nn++) {
                            Point3f tmp3f = (Point3f) vertices.get(nn);
                            if (tmp3f.epsilonEquals(vers[m], verySmall)) {
                                vertices.remove(nn);
                            }
                        }
                    }
                }
            }

            /* 結局あらたに足されたバーテックスが2未満なら考慮しなくてよい */
            if (newPlane.getVertices().length < 2) {
                continue;
            }

            /* 新しく作成された面を追加 */
            newPlane.sortVertices();
            planes.addElement(newPlane);

            /* 孤立した面を取り除く */
            for (int l = 0; l < planes.size(); l++) {
                BoundPlane plane = (BoundPlane) planes.get(l);
                Side[] sds = plane.getSides();
                if (sds == null || sds.length < 3) {
                    // planes.remove(plane);
                    continue;
                }
                Point3f[] verts = plane.getVertices();
                boolean found = false;

                for (int m = 0; m < verts.length; m++) {
                    for (int nn = 0; nn < vertices.size(); nn++) {
                        Point3f tmp3f = (Point3f) vertices.get(nn);
                        if (tmp3f.epsilonEquals(verts[m], verySmall)) {
                            found = true;
                            break;
                        }
                    }
                }
                // if ( !found ) {
                // planes.remove(plane);
                // }

                // if ( plane.getVertices().length <= 2 ) {
                // planes.remove(plane);
                // }
            }

            /* 最後に, ソート */
            for (int l = 0; l < planes.size(); l++) {
                ((BoundPlane) planes.get(l)).sortVertices();
            }

        }

        for (int i = planes.size() - 1; i >= 0; i--) {
            if (((BoundPlane) planes.get(i)).getVertices().length <= 2) {
                logger.debug("removing plane...");
                planes.remove(i);
            }
        }

        for (int i = planes.size() - 1; i >= 0; i--) {
            ((BoundPlane) planes.get(i)).generateNormal();
            ((BoundPlane) planes.get(i)).generateOrigin();
        }

    }

    private void drawWSCell() {
        /* 軸を少しずらしたので元に戻す */
        smallTransform.invert();
        for (int i = 0; i < planes.size(); i++) {
            BoundPlane pl = (BoundPlane) planes.get(i);
            Point3f[] verts = pl.getVertices();
            for (int j = 0; j < verts.length; j++) {
                smallTransform.transform(verts[j]);
            }
        }
        smallTransform.transform(avec);
        smallTransform.transform(bvec);
        smallTransform.transform(cvec);

        if (wsattrs.drawEdge) {
            createBGEdges();
        }
        if (wsattrs.drawPlane) {
            createBGPlane();
        }
        if (wsattrs.drawAxis) {
            createBGAxis();
        }
        if (wsattrs.drawSymmetryPoints && wsattrs.isReciprocalSpace) {
            createSymmetryPoints();
        }
    }

    private BranchGroup bgaxis;

    private BranchGroup bgplane;

    private BranchGroup bgedge;

    public void recreate() {
        logger.debug("recreating WSCell.");
        try {
            if (bgaxis != null)
                bgaxis.detach();
            if (bgplane != null)
                bgplane.detach();
            if (bgedge != null)
                bgedge.detach();

            cacheSelectedSymmetryPoints();
            removeAllSymmetryLines();
            if (symmetryPoints != null)
                symmetryPoints.detach();

            wsattrs.loadFromProps();
            if (wsattrs.drawEdge) {
                createBGEdges();
            }
            if (wsattrs.drawPlane) {
                createBGPlane();
            }
            if (wsattrs.drawAxis) {
                createBGAxis();
            }
            if (wsattrs.drawSymmetryPoints && wsattrs.isReciprocalSpace) {
                createSymmetryPoints();
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        }

        if (selectedSymmetryPointsCache != null) {
            for (int i = 0; i < selectedSymmetryPointsCache.size(); i++) {
                SymmetryPointBG test = (SymmetryPointBG) selectedSymmetryPointsCache
                        .get(i);
                for (int j = 0; j < symmetryPoints.numChildren(); j++) {
                    if (symmetryPoints.getSymmetryPoint(j).getPos()
                            .epsilonEquals(test.getPos(), 0.001f)) {
                        doSymmetricPoint(symmetryPoints.getSymmetryPoint(j));
                    }
                }
            }
        }

    }

    /**
     * デバッグ用; productiveコードでは使わないこと.
     * 
     * @param point
     *            足す点
     */
    public void addPoint(SymmetryPointBG point) {
        addChild(point);
    }

    private SymmetryPointSet symmetryPoints;

    private void createSymmetryPoints() {
        Point3f[] lat = new Point3f[3];
        lat[0] = avec;
        lat[1] = bvec;
        lat[2] = cvec;

        symmetryPoints = new SymmetryPointSet();
        symmetryPoints.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        symmetryPoints.setCapability(BranchGroup.ALLOW_DETACH);

        if (wsattrs.gammaIsSymmetric) {
            SymmetryPointBG gamma = new SymmetryPointBG(
                    new Vector3f(0f, 0f, 0f), lat, wsattrs);
            symmetryPoints.addChild(gamma);
        }
        for (int i = 0; i < planes.size(); i++) {
            BoundPlane plane = (BoundPlane) planes.get(i);
            Point3f[] vertices = plane.getVertices();
            Point3f midpoint = new Point3f();
            for (int j = 0; j < vertices.length; j++) {
                vertices[j].x = Float.parseFloat(format.format(vertices[j].x));
                vertices[j].y = Float.parseFloat(format.format(vertices[j].y));
                vertices[j].z = Float.parseFloat(format.format(vertices[j].z));
                midpoint.x += vertices[j].x;
                midpoint.y += vertices[j].y;
                midpoint.z += vertices[j].z;
                if (wsattrs.vertexIsSymmetric) {
                    SymmetryPointBG symmvert = new SymmetryPointBG(
                            new Vector3f(vertices[j]), lat, wsattrs);
                    symmetryPoints.addChild(symmvert);
                }
            }
            midpoint.x /= (float) vertices.length;
            midpoint.y /= (float) vertices.length;
            midpoint.z /= (float) vertices.length;
            if (wsattrs.centerOfPlaneIsSymmetric) {
                SymmetryPointBG symm = new SymmetryPointBG(new Vector3f(
                        midpoint), lat, wsattrs);
                symmetryPoints.addChild(symm);
            }
            plane.sortVertices();
            Side[] side = plane.getSides();
            if (side != null && side.length >= 3) {
                for (int j = 0; j < side.length; j++) {
                    Point3f p1 = side[j].getPoint1();
                    Point3f p2 = side[j].getPoint2();
                    Vector3f mid = new Vector3f((p1.x + p2.x) * 0.5f,
                            (p1.y + p2.y) * 0.5f, (p1.z + p2.z) * 0.5f);
                    logger.debug("p1, p2, mid: " + p1 + ", " + p2 + ", " + mid);
                    if (wsattrs.centerOfEdgeIsSymmetric) {
                        SymmetryPointBG symmside = new SymmetryPointBG(mid,
                                lat, wsattrs);
                        symmetryPoints.addChild(symmside);
                    }
                }
            }
        }
        addChild(symmetryPoints);
    }

    private void createBGPlane() {
        BranchGroup group = new BranchGroup();
        group.setCapability(BranchGroup.ALLOW_DETACH);

        Vector wsverts = new Vector();
        Vector normals = new Vector();
        Vector stripCount = new Vector();

        int countstrip = 0;
        for (int i = 0; i < planes.size(); i++) {
            BoundPlane plane = (BoundPlane) planes.get(i);
            Point3f[] verts = plane.getVertices();
            if (verts.length >= 3) {
                for (int j = 0; j < verts.length; j++) {
                    logger.debug("adding vertex " + verts[j] + " to plane " + i);
                    wsverts.addElement(verts[j]);
                    countstrip++;
                }
                stripCount.addElement(new Integer(countstrip));
                normals.addElement(plane.getNormalVector());
                countstrip = 0;
            }
        }

        int[] strcount = new int[stripCount.size()];
        for (int i = 0; i < strcount.length; i++) {
            strcount[i] = ((Integer) stripCount.elementAt(i)).intValue();
        }

        GeometryArray garray = new TriangleFanArray(wsverts.size(),
                TriangleFanArray.COORDINATES | TriangleFanArray.COLOR_3
                        | TriangleFanArray.NORMALS, strcount);

        garray.setCapability(TriangleFanArray.ALLOW_COLOR_WRITE);

        Appearance app = new Appearance();
        app.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE);
        app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);

        TransparencyAttributes tattr = new TransparencyAttributes();
        tattr.setCapability(TransparencyAttributes.ALLOW_VALUE_READ);
        tattr.setTransparency(wsattrs.planeTransparency);
        if (wsattrs.planeTransparency == 0) {
            tattr.setTransparencyMode(TransparencyAttributes.NONE);
        } else {
            tattr.setTransparencyMode(TransparencyAttributes.BLENDED);
        }
        app.setTransparencyAttributes(tattr);

        PolygonAttributes pattr = new PolygonAttributes();
        pattr.setCullFace(PolygonAttributes.CULL_NONE);
        pattr.setPolygonMode(PolygonAttributes.POLYGON_FILL);
        pattr.setBackFaceNormalFlip(true);
        app.setPolygonAttributes(pattr);

        int count = 0;
        for (int i = 0; i < strcount.length; i++) {
            int numstr = strcount[i];
            // Color3f c3f = new
            // Color3f(1f,0.5f,((float)i)/((float)strcount.length));
            // if ( i%2 == 0 ) {
            // c3f = new Color3f(0.5f,1f,((float)i)/((float)strcount.length));
            // }
            Color3f c3f = wsattrs.planeColor;
            Point3f norm = (Point3f) normals.get(i);
            for (int j = 0; j < numstr; j++) {
                Point3f p3 = (Point3f) wsverts.get(count);
                garray.setCoordinate(count, p3);
                // garray.setNormal(count,new Vector3f(norm));
                garray.setColor(count, c3f);
                count++;
            }
        }

        Shape3D s3d = new Shape3D();
        s3d.setGeometry(garray);
        s3d.setAppearance(app);
        group.addChild(s3d);
        bgplane = group;
        addChild(group);
    }

    private void createBGEdges() {
        BranchGroup edges = new BranchGroup();
        edges.setCapability(BranchGroup.ALLOW_DETACH);
        CylinderCreator cylindercreate = new CylinderCreator();
        for (int i = 0; i < planes.size(); i++) {
            BoundPlane pl = (BoundPlane) planes.get(i);
            Side[] sides = pl.getSides();
            if (sides == null) {
                continue;
            }

            Appearance app = new Appearance();
            Material material = new Material();
            material.setDiffuseColor(wsattrs.edgeColor);
            material.setShininess(120.0f);
            app.setMaterial(material);

            for (int j = 0; j < sides.length; j++) {
                Point3f p1 = sides[j].getPoint1();
                Point3f p2 = sides[j].getPoint2();
                Point3d d1 = new Point3d(p1);
                Point3d d2 = new Point3d(p2);
                edges.addChild(cylindercreate.create(d1, d2, wsattrs.edgeWidth,
                        app));
            }
        }
        bgedge = edges;
        addChild(edges);
    }

    private void createBGAxis() {
        Point3d gam = new Point3d();
        Point3d ad = new Point3d(avec);
        Point3d bd = new Point3d(bvec);
        Point3d cd = new Point3d(cvec);
        BranchGroup bg = new BranchGroup();
        bg.setCapability(BranchGroup.ALLOW_DETACH);

        Appearance app = new Appearance();
        Material material = new Material();
        material.setDiffuseColor(wsattrs.axisColor);
        material.setShininess(120.0f);
        app.setMaterial(material);

        CylinderCreator cylindercreate = new CylinderCreator();
        bg.addChild(cylindercreate.create(gam, ad, wsattrs.axisWidth,
                (float) wsattrs.axisWidth * 4, (float) wsattrs.axisWidth * 8,
                app));
        bg.addChild(cylindercreate.create(gam, bd, wsattrs.axisWidth,
                (float) wsattrs.axisWidth * 4, (float) wsattrs.axisWidth * 8,
                app));
        bg.addChild(cylindercreate.create(gam, cd, wsattrs.axisWidth,
                (float) wsattrs.axisWidth * 4, (float) wsattrs.axisWidth * 8,
                app));
        bgaxis = bg;
        addChild(bg);
    }

}
